@signe/room 2.8.3 → 2.9.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,5 +1,13 @@
1
1
  var __defProp = Object.defineProperty;
2
- var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
2
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
+ var __decorateClass = (decorators, target, key, kind) => {
4
+ var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
5
+ for (var i = decorators.length - 1, decorator; i >= 0; i--)
6
+ if (decorator = decorators[i])
7
+ result = (kind ? decorator(target, key, result) : decorator(result)) || result;
8
+ if (kind && result) __defProp(target, key, result);
9
+ return result;
10
+ };
3
11
 
4
12
  // src/decorators.ts
5
13
  function Action(name, bodyValidation) {
@@ -13,7 +21,13 @@ function Action(name, bodyValidation) {
13
21
  });
14
22
  };
15
23
  }
16
- __name(Action, "Action");
24
+ function UnhandledAction() {
25
+ return function(target, propertyKey) {
26
+ target.constructor._unhandledActionMetadata = {
27
+ key: propertyKey
28
+ };
29
+ };
30
+ }
17
31
  function Request2(options, bodyValidation) {
18
32
  return function(target, propertyKey) {
19
33
  if (!target.constructor._requestMetadata) {
@@ -30,7 +44,6 @@ function Request2(options, bodyValidation) {
30
44
  });
31
45
  };
32
46
  }
33
- __name(Request2, "Request");
34
47
  function Room(options) {
35
48
  return function(target) {
36
49
  target.path = options.path;
@@ -43,46 +56,40 @@ function Room(options) {
43
56
  }
44
57
  };
45
58
  }
46
- __name(Room, "Room");
47
59
  function RoomGuard(guards) {
48
60
  return function(target) {
49
61
  target["_roomGuards"] = guards;
50
62
  };
51
63
  }
52
- __name(RoomGuard, "RoomGuard");
53
64
  function Guard(guards) {
54
65
  return function(target, propertyKey, descriptor) {
55
66
  if (!target.constructor["_actionGuards"]) {
56
67
  target.constructor["_actionGuards"] = /* @__PURE__ */ new Map();
57
68
  }
58
69
  if (!Array.isArray(guards)) {
59
- guards = [
60
- guards
61
- ];
70
+ guards = [guards];
62
71
  }
63
72
  target.constructor["_actionGuards"].set(propertyKey, guards);
64
73
  };
65
74
  }
66
- __name(Guard, "Guard");
67
75
 
68
76
  // ../sync/src/utils.ts
69
77
  function generateShortUUID() {
70
78
  const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
79
+ const randomBytes = typeof globalThis.crypto?.getRandomValues === "function" ? globalThis.crypto.getRandomValues(new Uint8Array(8)) : null;
71
80
  let uuid = "";
72
81
  for (let i = 0; i < 8; i++) {
73
- const randomIndex = Math.floor(Math.random() * chars.length);
74
- uuid += chars[randomIndex];
82
+ const randomValue = randomBytes?.[i] ?? Math.floor(Math.random() * 256);
83
+ uuid += chars[randomValue % chars.length];
75
84
  }
76
85
  return uuid;
77
86
  }
78
- __name(generateShortUUID, "generateShortUUID");
79
87
 
80
88
  // src/storage.ts
81
89
  var Storage = class {
82
- static {
83
- __name(this, "Storage");
90
+ constructor() {
91
+ this.memory = /* @__PURE__ */ new Map();
84
92
  }
85
- memory = /* @__PURE__ */ new Map();
86
93
  async put(key, value) {
87
94
  this.memory.set(key, value);
88
95
  }
@@ -100,22 +107,27 @@ var Storage = class {
100
107
  // src/server.ts
101
108
  import { dset as dset2 } from "dset";
102
109
  import z from "zod";
103
- import { createStatesSnapshot, getByPath, load, syncClass, DELETE_TOKEN, generateShortUUID as generateShortUUID2, createStatesSnapshotDeep } from "@signe/sync";
110
+ import {
111
+ createStatesSnapshot,
112
+ getByPath,
113
+ load,
114
+ syncClass,
115
+ DELETE_TOKEN,
116
+ generateShortUUID as generateShortUUID2,
117
+ createStatesSnapshotDeep
118
+ } from "@signe/sync";
104
119
 
105
120
  // src/utils.ts
106
121
  import { dset } from "dset";
107
122
  function isPromise(value) {
108
123
  return value instanceof Promise;
109
124
  }
110
- __name(isPromise, "isPromise");
111
125
  async function awaitReturn(val) {
112
126
  return isPromise(val) ? await val : val;
113
127
  }
114
- __name(awaitReturn, "awaitReturn");
115
128
  function isClass(obj) {
116
129
  return typeof obj === "function" && obj.prototype && obj.prototype.constructor === obj;
117
130
  }
118
- __name(isClass, "isClass");
119
131
  function throttle(func, wait) {
120
132
  let timeout = null;
121
133
  let lastArgs = null;
@@ -134,7 +146,6 @@ function throttle(func, wait) {
134
146
  }
135
147
  };
136
148
  }
137
- __name(throttle, "throttle");
138
149
  function extractParams(pattern, str) {
139
150
  const regexPattern = pattern.replace(/{(\w+)}/g, "(?<$1>[\\w-]+)");
140
151
  const regex = new RegExp(`^${regexPattern}$`);
@@ -147,7 +158,6 @@ function extractParams(pattern, str) {
147
158
  return null;
148
159
  }
149
160
  }
150
- __name(extractParams, "extractParams");
151
161
  function dremove(obj, keys) {
152
162
  if (typeof keys === "string") {
153
163
  keys = keys.split(".");
@@ -167,7 +177,6 @@ function dremove(obj, keys) {
167
177
  delete t[k];
168
178
  }
169
179
  }
170
- __name(dremove, "dremove");
171
180
  function buildObject(valuesMap, allMemory) {
172
181
  let memoryObj = {};
173
182
  for (let path of valuesMap.keys()) {
@@ -181,33 +190,25 @@ function buildObject(valuesMap, allMemory) {
181
190
  }
182
191
  return memoryObj;
183
192
  }
184
- __name(buildObject, "buildObject");
185
193
  function response(status, body) {
186
194
  return new Response(JSON.stringify(body), {
187
195
  status,
188
- headers: {
189
- "Content-Type": "application/json"
190
- }
196
+ headers: { "Content-Type": "application/json" }
191
197
  });
192
198
  }
193
- __name(response, "response");
194
199
 
195
200
  // src/request/response.ts
196
201
  var ServerResponse = class {
197
- static {
198
- __name(this, "ServerResponse");
199
- }
200
- interceptors;
201
- statusCode = 200;
202
- responseBody = {};
203
- responseHeaders = {
204
- "Content-Type": "application/json"
205
- };
206
202
  /**
207
203
  * Creates a new ServerResponse instance
208
204
  * @param interceptors Array of interceptor functions that can modify the response
209
205
  */
210
206
  constructor(interceptors = []) {
207
+ this.statusCode = 200;
208
+ this.responseBody = {};
209
+ this.responseHeaders = {
210
+ "Content-Type": "application/json"
211
+ };
211
212
  this.interceptors = interceptors;
212
213
  }
213
214
  /**
@@ -245,10 +246,7 @@ var ServerResponse = class {
245
246
  * @returns this instance for chaining
246
247
  */
247
248
  setHeaders(headers) {
248
- this.responseHeaders = {
249
- ...this.responseHeaders,
250
- ...headers
251
- };
249
+ this.responseHeaders = { ...this.responseHeaders, ...headers };
252
250
  return this;
253
251
  }
254
252
  /**
@@ -373,9 +371,7 @@ var ServerResponse = class {
373
371
  * @returns Promise<Response> The final Response object
374
372
  */
375
373
  async notPermitted(message = "Not permitted") {
376
- return this.status(403).json({
377
- error: message
378
- });
374
+ return this.status(403).json({ error: message });
379
375
  }
380
376
  /**
381
377
  * Creates an error response with status 401
@@ -383,9 +379,7 @@ var ServerResponse = class {
383
379
  * @returns Promise<Response> The final Response object
384
380
  */
385
381
  async unauthorized(message = "Unauthorized") {
386
- return this.status(401).json({
387
- error: message
388
- });
382
+ return this.status(401).json({ error: message });
389
383
  }
390
384
  /**
391
385
  * Creates an error response with status 404
@@ -393,9 +387,7 @@ var ServerResponse = class {
393
387
  * @returns Promise<Response> The final Response object
394
388
  */
395
389
  async notFound(message = "Not found") {
396
- return this.status(404).json({
397
- error: message
398
- });
390
+ return this.status(404).json({ error: message });
399
391
  }
400
392
  /**
401
393
  * Creates an error response with status 500
@@ -403,9 +395,7 @@ var ServerResponse = class {
403
395
  * @returns Promise<Response> The final Response object
404
396
  */
405
397
  async serverError(message = "Internal Server Error") {
406
- return this.status(500).json({
407
- error: message
408
- });
398
+ return this.status(500).json({ error: message });
409
399
  }
410
400
  };
411
401
 
@@ -440,11 +430,9 @@ function cors(res, options = {}) {
440
430
  headers: newHeaders
441
431
  });
442
432
  }
443
- __name(cors, "cors");
444
433
  function createCorsInterceptor(options = {}) {
445
434
  return (res) => cors(res, options);
446
435
  }
447
- __name(createCorsInterceptor, "createCorsInterceptor");
448
436
 
449
437
  // src/server.ts
450
438
  var Message = z.object({
@@ -452,37 +440,31 @@ var Message = z.object({
452
440
  value: z.any()
453
441
  });
454
442
  var Server = class {
455
- static {
456
- __name(this, "Server");
457
- }
458
- room;
459
- subRoom;
460
- rooms;
461
443
  /**
462
- * @constructor
463
- * @param {Party.Room} room - The room object representing the current game or application instance.
464
- *
465
- * @example
466
- * ```typescript
467
- * const server = new MyServer(new ServerIo("game"));
468
- * ```
469
- */
444
+ * @constructor
445
+ * @param {Party.Room} room - The room object representing the current game or application instance.
446
+ *
447
+ * @example
448
+ * ```typescript
449
+ * const server = new MyServer(new ServerIo("game"));
450
+ * ```
451
+ */
470
452
  constructor(room) {
471
453
  this.room = room;
472
454
  this.subRoom = null;
473
455
  this.rooms = [];
474
456
  }
475
457
  /**
476
- * @readonly
477
- * @property {boolean} isHibernate - Indicates whether the server is in hibernate mode.
478
- *
479
- * @example
480
- * ```typescript
481
- * if (!server.isHibernate) {
482
- * console.log("Server is active");
483
- * }
484
- * ```
485
- */
458
+ * @readonly
459
+ * @property {boolean} isHibernate - Indicates whether the server is in hibernate mode.
460
+ *
461
+ * @example
462
+ * ```typescript
463
+ * if (!server.isHibernate) {
464
+ * console.log("Server is active");
465
+ * }
466
+ * ```
467
+ */
486
468
  get isHibernate() {
487
469
  return !!this["options"]?.hibernate;
488
470
  }
@@ -506,35 +488,31 @@ var Server = class {
506
488
  }
507
489
  }
508
490
  /**
509
- * @method onStart
510
- * @async
511
- * @description Initializes the server and creates the initial room if not in hibernate mode.
512
- * @returns {Promise<void>}
513
- *
514
- * @example
515
- * ```typescript
516
- * async function initServer() {
517
- * await server.onStart();
518
- * console.log("Server started");
519
- * }
520
- * ```
521
- */
491
+ * @method onStart
492
+ * @async
493
+ * @description Initializes the server and creates the initial room if not in hibernate mode.
494
+ * @returns {Promise<void>}
495
+ *
496
+ * @example
497
+ * ```typescript
498
+ * async function initServer() {
499
+ * await server.onStart();
500
+ * console.log("Server started");
501
+ * }
502
+ * ```
503
+ */
522
504
  async onStart() {
523
505
  if (!this.isHibernate) {
524
506
  this.subRoom = await this.createRoom();
525
507
  }
526
508
  }
527
509
  async runGarbageCollector() {
528
- await this.garbageCollector({
529
- sessionExpiryTime: -1
530
- });
510
+ await this.garbageCollector({ sessionExpiryTime: -1 });
531
511
  }
532
512
  async garbageCollector(options) {
533
513
  const subRoom = await this.getSubRoom();
534
514
  if (!subRoom) return;
535
- const activeConnections = [
536
- ...this.room.getConnections()
537
- ];
515
+ const activeConnections = [...this.room.getConnections()];
538
516
  const activePrivateIds = new Set(activeConnections.map((conn) => conn.id));
539
517
  try {
540
518
  const sessions = await this.room.storage.list();
@@ -569,21 +547,21 @@ var Server = class {
569
547
  }
570
548
  }
571
549
  /**
572
- * @method createRoom
573
- * @private
574
- * @async
575
- * @param {CreateRoomOptions} [options={}] - Options for creating the room.
576
- * @returns {Promise<Object>} The created room instance.
577
- *
578
- * @example
579
- * ```typescript
580
- * // This method is private and called internally
581
- * async function internalCreateRoom() {
582
- * const room = await this.createRoom({ getMemoryAll: true });
583
- * console.log("Room created:", room);
584
- * }
585
- * ```
586
- */
550
+ * @method createRoom
551
+ * @private
552
+ * @async
553
+ * @param {CreateRoomOptions} [options={}] - Options for creating the room.
554
+ * @returns {Promise<Object>} The created room instance.
555
+ *
556
+ * @example
557
+ * ```typescript
558
+ * // This method is private and called internally
559
+ * async function internalCreateRoom() {
560
+ * const room = await this.createRoom({ getMemoryAll: true });
561
+ * console.log("Room created:", room);
562
+ * }
563
+ * ```
564
+ */
587
565
  async createRoom(options = {}) {
588
566
  let instance;
589
567
  let init = true;
@@ -598,7 +576,7 @@ var Server = class {
598
576
  if (!instance) {
599
577
  return null;
600
578
  }
601
- const loadMemory = /* @__PURE__ */ __name(async () => {
579
+ const loadMemory = async () => {
602
580
  const root = await this.room.storage.get(".");
603
581
  const memory = await this.room.storage.list();
604
582
  const tmpObject = root || {};
@@ -612,7 +590,7 @@ var Server = class {
612
590
  dset2(tmpObject, key, value);
613
591
  }
614
592
  load(instance, tmpObject, true);
615
- }, "loadMemory");
593
+ };
616
594
  instance.$memoryAll = {};
617
595
  instance.$autoSync = instance["autoSync"] !== false;
618
596
  instance.$pendingSync = /* @__PURE__ */ new Map();
@@ -712,7 +690,7 @@ var Server = class {
712
690
  return null;
713
691
  }
714
692
  };
715
- const syncCb = /* @__PURE__ */ __name((values) => {
693
+ const syncCb = (values) => {
716
694
  if (options.getMemoryAll) {
717
695
  buildObject(values, instance.$memoryAll);
718
696
  }
@@ -728,13 +706,16 @@ var Server = class {
728
706
  return;
729
707
  }
730
708
  const packet = buildObject(values, instance.$memoryAll);
731
- this.broadcast({
732
- type: "sync",
733
- value: packet
734
- }, instance);
709
+ this.broadcast(
710
+ {
711
+ type: "sync",
712
+ value: packet
713
+ },
714
+ instance
715
+ );
735
716
  values.clear();
736
- }, "syncCb");
737
- const persistCb = /* @__PURE__ */ __name(async (values) => {
717
+ };
718
+ const persistCb = async (values) => {
738
719
  if (initPersist) {
739
720
  values.clear();
740
721
  return;
@@ -749,7 +730,7 @@ var Server = class {
749
730
  }
750
731
  }
751
732
  values.clear();
752
- }, "persistCb");
733
+ };
753
734
  syncClass(instance, {
754
735
  onSync: instance["throttleSync"] ? throttle(syncCb, instance["throttleSync"]) : syncCb,
755
736
  onPersist: instance["throttleStorage"] ? throttle(persistCb, instance["throttleStorage"]) : persistCb
@@ -760,21 +741,21 @@ var Server = class {
760
741
  return instance;
761
742
  }
762
743
  /**
763
- * @method getSubRoom
764
- * @private
765
- * @async
766
- * @param {Object} [options={}] - Options for getting the sub-room.
767
- * @returns {Promise<Object>} The sub-room instance.
768
- *
769
- * @example
770
- * ```typescript
771
- * // This method is private and called internally
772
- * async function internalGetSubRoom() {
773
- * const subRoom = await this.getSubRoom();
774
- * console.log("Sub-room retrieved:", subRoom);
775
- * }
776
- * ```
777
- */
744
+ * @method getSubRoom
745
+ * @private
746
+ * @async
747
+ * @param {Object} [options={}] - Options for getting the sub-room.
748
+ * @returns {Promise<Object>} The sub-room instance.
749
+ *
750
+ * @example
751
+ * ```typescript
752
+ * // This method is private and called internally
753
+ * async function internalGetSubRoom() {
754
+ * const subRoom = await this.getSubRoom();
755
+ * console.log("Sub-room retrieved:", subRoom);
756
+ * }
757
+ * ```
758
+ */
778
759
  async getSubRoom(options = {}) {
779
760
  let subRoom;
780
761
  if (this.isHibernate) {
@@ -785,20 +766,20 @@ var Server = class {
785
766
  return subRoom;
786
767
  }
787
768
  /**
788
- * @method getUsersProperty
789
- * @private
790
- * @param {Object} subRoom - The sub-room instance.
791
- * @returns {Object|null} The users property of the sub-room, or null if not found.
792
- *
793
- * @example
794
- * ```typescript
795
- * // This method is private and called internally
796
- * function internalGetUsers(subRoom) {
797
- * const users = this.getUsersProperty(subRoom);
798
- * console.log("Users:", users);
799
- * }
800
- * ```
801
- */
769
+ * @method getUsersProperty
770
+ * @private
771
+ * @param {Object} subRoom - The sub-room instance.
772
+ * @returns {Object|null} The users property of the sub-room, or null if not found.
773
+ *
774
+ * @example
775
+ * ```typescript
776
+ * // This method is private and called internally
777
+ * function internalGetUsers(subRoom) {
778
+ * const users = this.getUsersProperty(subRoom);
779
+ * console.log("Users:", users);
780
+ * }
781
+ * ```
782
+ */
802
783
  getUsersProperty(subRoom) {
803
784
  const meta = subRoom.constructor["_propertyMetadata"];
804
785
  const propId = meta?.get("users");
@@ -814,12 +795,12 @@ var Server = class {
814
795
  return metadata.get("users");
815
796
  }
816
797
  /**
817
- * Retrieves the connection status property from a user object.
818
- *
819
- * @param {any} user - The user object to get the connection property from.
820
- * @returns {Function|null} - The connection property signal function or null if not found.
821
- * @private
822
- */
798
+ * Retrieves the connection status property from a user object.
799
+ *
800
+ * @param {any} user - The user object to get the connection property from.
801
+ * @returns {Function|null} - The connection property signal function or null if not found.
802
+ * @private
803
+ */
823
804
  getUserConnectionProperty(user) {
824
805
  if (!user) return null;
825
806
  const metadata = user.constructor._propertyMetadata;
@@ -829,13 +810,13 @@ var Server = class {
829
810
  return user[connectedPropName];
830
811
  }
831
812
  /**
832
- * Updates a user's connection status in the signal.
833
- *
834
- * @param {any} user - The user object to update.
835
- * @param {boolean} isConnected - The new connection status.
836
- * @returns {boolean} - Whether the update was successful.
837
- * @private
838
- */
813
+ * Updates a user's connection status in the signal.
814
+ *
815
+ * @param {any} user - The user object to update.
816
+ * @param {boolean} isConnected - The new connection status.
817
+ * @returns {boolean} - Whether the update was successful.
818
+ * @private
819
+ */
839
820
  updateUserConnectionStatus(user, isConnected) {
840
821
  const connectionSignal = this.getUserConnectionProperty(user);
841
822
  if (connectionSignal) {
@@ -845,17 +826,17 @@ var Server = class {
845
826
  return false;
846
827
  }
847
828
  /**
848
- * @method getSession
849
- * @private
850
- * @param {string} privateId - The private ID of the session.
851
- * @returns {Promise<Object|null>} The session object, or null if not found.
852
- *
853
- * @example
854
- * ```typescript
855
- * const session = await server.getSession("privateId");
856
- * console.log(session);
857
- * ```
858
- */
829
+ * @method getSession
830
+ * @private
831
+ * @param {string} privateId - The private ID of the session.
832
+ * @returns {Promise<Object|null>} The session object, or null if not found.
833
+ *
834
+ * @example
835
+ * ```typescript
836
+ * const session = await server.getSession("privateId");
837
+ * console.log(session);
838
+ * ```
839
+ */
859
840
  async getSession(privateId) {
860
841
  if (!privateId) return null;
861
842
  try {
@@ -876,23 +857,20 @@ var Server = class {
876
857
  async updateSessionConnection(privateId, connected) {
877
858
  const session = await this.getSession(privateId);
878
859
  if (session) {
879
- await this.saveSession(privateId, {
880
- ...session,
881
- connected
882
- });
860
+ await this.saveSession(privateId, { ...session, connected });
883
861
  }
884
862
  }
885
863
  /**
886
- * @method deleteSession
887
- * @private
888
- * @param {string} privateId - The private ID of the session to delete.
889
- * @returns {Promise<void>}
890
- *
891
- * @example
892
- * ```typescript
893
- * await server.deleteSession("privateId");
894
- * ```
895
- */
864
+ * @method deleteSession
865
+ * @private
866
+ * @param {string} privateId - The private ID of the session to delete.
867
+ * @returns {Promise<void>}
868
+ *
869
+ * @example
870
+ * ```typescript
871
+ * await server.deleteSession("privateId");
872
+ * ```
873
+ */
896
874
  async deleteSession(privateId) {
897
875
  await this.room.storage.delete(`session:${privateId}`);
898
876
  }
@@ -904,10 +882,8 @@ var Server = class {
904
882
  conn.close();
905
883
  return;
906
884
  }
907
- const sessionExpiryTime = subRoom.constructor.sessionExpiryTime;
908
- await this.garbageCollector({
909
- sessionExpiryTime
910
- });
885
+ const sessionExpiryTime = subRoom.sessionExpiryTime ?? subRoom.constructor.sessionExpiryTime ?? 5 * 60 * 1e3;
886
+ await this.garbageCollector({ sessionExpiryTime });
911
887
  const roomGuards = subRoom.constructor["_roomGuards"] || [];
912
888
  for (const guard of roomGuards) {
913
889
  const isAuthorized = await guard(conn, ctx, this.room);
@@ -975,21 +951,21 @@ var Server = class {
975
951
  }
976
952
  }
977
953
  /**
978
- * @method onConnect
979
- * @async
980
- * @param {Party.Connection} conn - The connection object for the new user.
981
- * @param {Party.ConnectionContext} ctx - The context of the connection.
982
- * @description Handles a new user connection, creates a user object, and sends initial sync data.
983
- * @returns {Promise<void>}
984
- *
985
- * @example
986
- * ```typescript
987
- * server.onConnect = async (conn, ctx) => {
988
- * await server.onConnect(conn, ctx);
989
- * console.log("New user connected:", conn.id);
990
- * };
991
- * ```
992
- */
954
+ * @method onConnect
955
+ * @async
956
+ * @param {Party.Connection} conn - The connection object for the new user.
957
+ * @param {Party.ConnectionContext} ctx - The context of the connection.
958
+ * @description Handles a new user connection, creates a user object, and sends initial sync data.
959
+ * @returns {Promise<void>}
960
+ *
961
+ * @example
962
+ * ```typescript
963
+ * server.onConnect = async (conn, ctx) => {
964
+ * await server.onConnect(conn, ctx);
965
+ * console.log("New user connected:", conn.id);
966
+ * };
967
+ * ```
968
+ */
993
969
  async onConnect(conn, ctx) {
994
970
  if (ctx.request?.headers.has("x-shard-id")) {
995
971
  this.onConnectShard(conn, ctx);
@@ -998,13 +974,13 @@ var Server = class {
998
974
  }
999
975
  }
1000
976
  /**
1001
- * @method onConnectShard
1002
- * @private
1003
- * @param {Party.Connection} conn - The connection object for the new shard.
1004
- * @param {Party.ConnectionContext} ctx - The context of the shard connection.
1005
- * @description Handles a new shard connection, setting up the necessary state.
1006
- * @returns {void}
1007
- */
977
+ * @method onConnectShard
978
+ * @private
979
+ * @param {Party.Connection} conn - The connection object for the new shard.
980
+ * @param {Party.ConnectionContext} ctx - The context of the shard connection.
981
+ * @description Handles a new shard connection, setting up the necessary state.
982
+ * @returns {void}
983
+ */
1008
984
  onConnectShard(conn, ctx) {
1009
985
  const shardId = ctx.request?.headers.get("x-shard-id") || "unknown-shard";
1010
986
  conn.setState({
@@ -1015,13 +991,13 @@ var Server = class {
1015
991
  });
1016
992
  }
1017
993
  /**
1018
- * @method onMessage
1019
- * @async
1020
- * @param {string} message - The message received from a user or shard.
1021
- * @param {Party.Connection} sender - The connection object of the sender.
1022
- * @description Processes incoming messages, handling differently based on if sender is shard or client.
1023
- * @returns {Promise<void>}
1024
- */
994
+ * @method onMessage
995
+ * @async
996
+ * @param {string} message - The message received from a user or shard.
997
+ * @param {Party.Connection} sender - The connection object of the sender.
998
+ * @description Processes incoming messages, handling differently based on if sender is shard or client.
999
+ * @returns {Promise<void>}
1000
+ */
1025
1001
  async onMessage(message, sender) {
1026
1002
  if (sender.state && sender.state.shard) {
1027
1003
  await this.handleShardMessage(message, sender);
@@ -1050,38 +1026,54 @@ var Server = class {
1050
1026
  }
1051
1027
  }
1052
1028
  const actions = subRoom.constructor["_actionMetadata"];
1053
- if (actions) {
1054
- const signal2 = this.getUsersProperty(subRoom);
1055
- const { publicId } = sender.state;
1056
- const user = signal2?.()[publicId];
1057
- const actionName = actions.get(result.data.action);
1058
- if (actionName) {
1059
- const guards = subRoom.constructor["_actionGuards"]?.get(actionName.key) || [];
1060
- for (const guard of guards) {
1061
- const isAuthorized = await guard(sender, result.data.value);
1062
- if (!isAuthorized) {
1063
- return;
1064
- }
1029
+ const signal2 = this.getUsersProperty(subRoom);
1030
+ const { publicId } = sender.state;
1031
+ const user = signal2?.()[publicId];
1032
+ const actionName = actions?.get(result.data.action);
1033
+ if (actionName) {
1034
+ const guards = subRoom.constructor["_actionGuards"]?.get(actionName.key) || [];
1035
+ for (const guard of guards) {
1036
+ const isAuthorized = await guard(sender, result.data.value, this.room);
1037
+ if (!isAuthorized) {
1038
+ return;
1065
1039
  }
1066
- if (actionName.bodyValidation) {
1067
- const bodyResult = actionName.bodyValidation.safeParse(result.data.value);
1068
- if (!bodyResult.success) {
1069
- return;
1070
- }
1040
+ }
1041
+ if (actionName.bodyValidation) {
1042
+ const bodyResult = actionName.bodyValidation.safeParse(
1043
+ result.data.value
1044
+ );
1045
+ if (!bodyResult.success) {
1046
+ return;
1047
+ }
1048
+ }
1049
+ await awaitReturn(
1050
+ subRoom[actionName.key](user, result.data.value, sender)
1051
+ );
1052
+ return;
1053
+ }
1054
+ const unhandledAction = subRoom.constructor["_unhandledActionMetadata"];
1055
+ if (unhandledAction) {
1056
+ const guards = subRoom.constructor["_actionGuards"]?.get(unhandledAction.key) || [];
1057
+ for (const guard of guards) {
1058
+ const isAuthorized = await guard(sender, result.data, this.room);
1059
+ if (!isAuthorized) {
1060
+ return;
1071
1061
  }
1072
- await awaitReturn(subRoom[actionName.key](user, result.data.value, sender));
1073
1062
  }
1063
+ await awaitReturn(
1064
+ subRoom[unhandledAction.key](user, result.data, sender)
1065
+ );
1074
1066
  }
1075
1067
  }
1076
1068
  /**
1077
- * @method handleShardMessage
1078
- * @private
1079
- * @async
1080
- * @param {string} message - The message received from a shard.
1081
- * @param {Party.Connection} shardConnection - The connection object of the shard.
1082
- * @description Processes messages from shards, extracting client information.
1083
- * @returns {Promise<void>}
1084
- */
1069
+ * @method handleShardMessage
1070
+ * @private
1071
+ * @async
1072
+ * @param {string} message - The message received from a shard.
1073
+ * @param {Party.Connection} shardConnection - The connection object of the shard.
1074
+ * @description Processes messages from shards, extracting client information.
1075
+ * @returns {Promise<void>}
1076
+ */
1085
1077
  async handleShardMessage(message, shardConnection) {
1086
1078
  let parsedMessage;
1087
1079
  try {
@@ -1107,14 +1099,14 @@ var Server = class {
1107
1099
  }
1108
1100
  }
1109
1101
  /**
1110
- * @method handleShardClientConnect
1111
- * @private
1112
- * @async
1113
- * @param {Object} message - The client connection message from a shard.
1114
- * @param {Party.Connection} shardConnection - The connection object of the shard.
1115
- * @description Handles a new client connection via a shard.
1116
- * @returns {Promise<void>}
1117
- */
1102
+ * @method handleShardClientConnect
1103
+ * @private
1104
+ * @async
1105
+ * @param {Object} message - The client connection message from a shard.
1106
+ * @param {Party.Connection} shardConnection - The connection object of the shard.
1107
+ * @description Handles a new client connection via a shard.
1108
+ * @returns {Promise<void>}
1109
+ */
1118
1110
  async handleShardClientConnect(message, shardConnection) {
1119
1111
  const { privateId, requestInfo } = message;
1120
1112
  const shardState = shardConnection.state;
@@ -1127,22 +1119,22 @@ var Server = class {
1127
1119
  };
1128
1120
  const virtualConnection = {
1129
1121
  id: privateId,
1130
- send: /* @__PURE__ */ __name((data) => {
1122
+ send: (data) => {
1131
1123
  shardConnection.send(JSON.stringify({
1132
1124
  targetClientId: privateId,
1133
1125
  data
1134
1126
  }));
1135
- }, "send"),
1127
+ },
1136
1128
  state: {},
1137
- setState: /* @__PURE__ */ __name((state) => {
1129
+ setState: (state) => {
1138
1130
  const clients = shardState.clients;
1139
1131
  const currentState = clients.get(privateId) || {};
1140
1132
  const mergedState = Object.assign({}, currentState, state);
1141
1133
  clients.set(privateId, mergedState);
1142
1134
  virtualConnection.state = clients.get(privateId);
1143
1135
  return virtualConnection.state;
1144
- }, "setState"),
1145
- close: /* @__PURE__ */ __name(() => {
1136
+ },
1137
+ close: () => {
1146
1138
  shardConnection.send(JSON.stringify({
1147
1139
  type: "shard.closeClient",
1148
1140
  privateId
@@ -1150,7 +1142,7 @@ var Server = class {
1150
1142
  if (shardState.clients) {
1151
1143
  shardState.clients.delete(privateId);
1152
1144
  }
1153
- }, "close")
1145
+ }
1154
1146
  };
1155
1147
  if (!shardState.clients.has(privateId)) {
1156
1148
  shardState.clients.set(privateId, {});
@@ -1158,41 +1150,39 @@ var Server = class {
1158
1150
  await this.onConnectClient(virtualConnection, virtualContext);
1159
1151
  }
1160
1152
  /**
1161
- * @method handleShardClientMessage
1162
- * @private
1163
- * @async
1164
- * @param {Object} message - The client message from a shard.
1165
- * @param {Party.Connection} shardConnection - The connection object of the shard.
1166
- * @description Handles a message from a client via a shard.
1167
- * @returns {Promise<void>}
1168
- */
1153
+ * @method handleShardClientMessage
1154
+ * @private
1155
+ * @async
1156
+ * @param {Object} message - The client message from a shard.
1157
+ * @param {Party.Connection} shardConnection - The connection object of the shard.
1158
+ * @description Handles a message from a client via a shard.
1159
+ * @returns {Promise<void>}
1160
+ */
1169
1161
  async handleShardClientMessage(message, shardConnection) {
1170
1162
  const { privateId, publicId, payload } = message;
1171
1163
  const shardState = shardConnection.state;
1172
1164
  const clients = shardState.clients;
1173
1165
  if (!clients.has(privateId)) {
1174
1166
  console.warn(`Received message from unknown client ${privateId}, creating virtual connection`);
1175
- clients.set(privateId, {
1176
- publicId
1177
- });
1167
+ clients.set(privateId, { publicId });
1178
1168
  }
1179
1169
  const virtualConnection = {
1180
1170
  id: privateId,
1181
- send: /* @__PURE__ */ __name((data) => {
1171
+ send: (data) => {
1182
1172
  shardConnection.send(JSON.stringify({
1183
1173
  targetClientId: privateId,
1184
1174
  data
1185
1175
  }));
1186
- }, "send"),
1176
+ },
1187
1177
  state: clients.get(privateId),
1188
- setState: /* @__PURE__ */ __name((state) => {
1178
+ setState: (state) => {
1189
1179
  const currentState = clients.get(privateId) || {};
1190
1180
  const mergedState = Object.assign({}, currentState, state);
1191
1181
  clients.set(privateId, mergedState);
1192
1182
  virtualConnection.state = clients.get(privateId);
1193
1183
  return virtualConnection.state;
1194
- }, "setState"),
1195
- close: /* @__PURE__ */ __name(() => {
1184
+ },
1185
+ close: () => {
1196
1186
  shardConnection.send(JSON.stringify({
1197
1187
  type: "shard.closeClient",
1198
1188
  privateId
@@ -1200,20 +1190,20 @@ var Server = class {
1200
1190
  if (shardState.clients) {
1201
1191
  shardState.clients.delete(privateId);
1202
1192
  }
1203
- }, "close")
1193
+ }
1204
1194
  };
1205
1195
  const payloadString = typeof payload === "string" ? payload : JSON.stringify(payload);
1206
1196
  await this.onMessage(payloadString, virtualConnection);
1207
1197
  }
1208
1198
  /**
1209
- * @method handleShardClientDisconnect
1210
- * @private
1211
- * @async
1212
- * @param {Object} message - The client disconnection message from a shard.
1213
- * @param {Party.Connection} shardConnection - The connection object of the shard.
1214
- * @description Handles a client disconnection via a shard.
1215
- * @returns {Promise<void>}
1216
- */
1199
+ * @method handleShardClientDisconnect
1200
+ * @private
1201
+ * @async
1202
+ * @param {Object} message - The client disconnection message from a shard.
1203
+ * @param {Party.Connection} shardConnection - The connection object of the shard.
1204
+ * @description Handles a client disconnection via a shard.
1205
+ * @returns {Promise<void>}
1206
+ */
1217
1207
  async handleShardClientDisconnect(message, shardConnection) {
1218
1208
  const { privateId, publicId } = message;
1219
1209
  const shardState = shardConnection.state;
@@ -1225,33 +1215,34 @@ var Server = class {
1225
1215
  }
1226
1216
  const virtualConnection = {
1227
1217
  id: privateId,
1228
- send: /* @__PURE__ */ __name(() => {
1229
- }, "send"),
1218
+ send: () => {
1219
+ },
1220
+ // No-op since client is disconnecting
1230
1221
  state: clientState,
1231
- setState: /* @__PURE__ */ __name(() => {
1222
+ setState: () => {
1232
1223
  return {};
1233
- }, "setState"),
1234
- close: /* @__PURE__ */ __name(() => {
1235
- }, "close")
1224
+ },
1225
+ close: () => {
1226
+ }
1236
1227
  };
1237
1228
  await this.onClose(virtualConnection);
1238
1229
  clients.delete(privateId);
1239
1230
  }
1240
1231
  /**
1241
- * @method onClose
1242
- * @async
1243
- * @param {Party.Connection} conn - The connection object of the disconnecting user.
1244
- * @description Handles user disconnection, removing them from the room and triggering the onLeave event..
1245
- * @returns {Promise<void>}
1246
- *
1247
- * @example
1248
- * ```typescript
1249
- * server.onClose = async (conn) => {
1250
- * await server.onClose(conn);
1251
- * console.log("User disconnected:", conn.id);
1252
- * };
1253
- * ```
1254
- */
1232
+ * @method onClose
1233
+ * @async
1234
+ * @param {Party.Connection} conn - The connection object of the disconnecting user.
1235
+ * @description Handles user disconnection, removing them from the room and triggering the onLeave event..
1236
+ * @returns {Promise<void>}
1237
+ *
1238
+ * @example
1239
+ * ```typescript
1240
+ * server.onClose = async (conn) => {
1241
+ * await server.onClose(conn);
1242
+ * console.log("User disconnected:", conn.id);
1243
+ * };
1244
+ * ```
1245
+ */
1255
1246
  async onClose(conn) {
1256
1247
  const subRoom = await this.getSubRoom();
1257
1248
  if (!subRoom) {
@@ -1274,9 +1265,7 @@ var Server = class {
1274
1265
  if (!connectionUpdated) {
1275
1266
  this.broadcast({
1276
1267
  type: "user_disconnected",
1277
- value: {
1278
- publicId
1279
- }
1268
+ value: { publicId }
1280
1269
  }, subRoom);
1281
1270
  }
1282
1271
  }
@@ -1289,12 +1278,12 @@ var Server = class {
1289
1278
  await awaitReturn(subRoom["onError"]?.(connection, error));
1290
1279
  }
1291
1280
  /**
1292
- * @method onRequest
1293
- * @async
1294
- * @param {Party.Request} req - The HTTP request to handle
1295
- * @description Handles HTTP requests, either directly from clients or forwarded by shards
1296
- * @returns {Promise<Response>} The response to return to the client
1297
- */
1281
+ * @method onRequest
1282
+ * @async
1283
+ * @param {Party.Request} req - The HTTP request to handle
1284
+ * @description Handles HTTP requests, either directly from clients or forwarded by shards
1285
+ * @returns {Promise<Response>} The response to return to the client
1286
+ */
1298
1287
  async onRequest(req) {
1299
1288
  const isFromShard = req.headers.has("x-forwarded-by-shard");
1300
1289
  const shardId = req.headers.get("x-shard-id");
@@ -1310,14 +1299,14 @@ var Server = class {
1310
1299
  return this.handleDirectRequest(req, res);
1311
1300
  }
1312
1301
  /**
1313
- * @method handleSessionRestore
1314
- * @private
1315
- * @async
1316
- * @param {Party.Request} req - The HTTP request for session restore
1317
- * @param {ServerResponse} res - The response object
1318
- * @description Handles session restoration from transfer data, creates session from privateId
1319
- * @returns {Promise<Response>} The response to return to the client
1320
- */
1302
+ * @method handleSessionRestore
1303
+ * @private
1304
+ * @async
1305
+ * @param {Party.Request} req - The HTTP request for session restore
1306
+ * @param {ServerResponse} res - The response object
1307
+ * @description Handles session restoration from transfer data, creates session from privateId
1308
+ * @returns {Promise<Response>} The response to return to the client
1309
+ */
1321
1310
  async handleSessionRestore(req, res) {
1322
1311
  try {
1323
1312
  const transferData = await req.json();
@@ -1342,14 +1331,16 @@ var Server = class {
1342
1331
  if (signal2 && usersPropName) {
1343
1332
  const { classType } = signal2.options;
1344
1333
  const user = isClass(classType) ? new classType() : classType();
1345
- const hydratedSnapshot = await awaitReturn(subRoom["onSessionRestore"]?.({
1346
- userSnapshot,
1347
- user,
1348
- publicId,
1349
- privateId,
1350
- sessionState,
1351
- room: this.room
1352
- })) ?? userSnapshot;
1334
+ const hydratedSnapshot = await awaitReturn(
1335
+ subRoom["onSessionRestore"]?.({
1336
+ userSnapshot,
1337
+ user,
1338
+ publicId,
1339
+ privateId,
1340
+ sessionState,
1341
+ room: this.room
1342
+ })
1343
+ ) ?? userSnapshot;
1353
1344
  signal2()[publicId] = user;
1354
1345
  load(user, hydratedSnapshot, true);
1355
1346
  await this.room.storage.put(`${usersPropName}.${publicId}`, userSnapshot);
@@ -1361,22 +1352,20 @@ var Server = class {
1361
1352
  publicId,
1362
1353
  restored: true
1363
1354
  });
1364
- return res.success({
1365
- transferToken
1366
- });
1355
+ return res.success({ transferToken });
1367
1356
  } catch (error) {
1368
1357
  console.error("Error restoring session:", error);
1369
1358
  return res.serverError("Failed to restore session");
1370
1359
  }
1371
1360
  }
1372
1361
  /**
1373
- * @method handleDirectRequest
1374
- * @private
1375
- * @async
1376
- * @param {Party.Request} req - The HTTP request received directly from a client
1377
- * @description Processes requests received directly from clients
1378
- * @returns {Promise<Response>} The response to return to the client
1379
- */
1362
+ * @method handleDirectRequest
1363
+ * @private
1364
+ * @async
1365
+ * @param {Party.Request} req - The HTTP request received directly from a client
1366
+ * @description Processes requests received directly from clients
1367
+ * @returns {Promise<Response>} The response to return to the client
1368
+ */
1380
1369
  async handleDirectRequest(req, res) {
1381
1370
  const subRoom = await this.getSubRoom();
1382
1371
  if (!subRoom) {
@@ -1400,14 +1389,14 @@ var Server = class {
1400
1389
  return res.success(legacyResponse);
1401
1390
  }
1402
1391
  /**
1403
- * @method tryMatchRequestHandler
1404
- * @private
1405
- * @async
1406
- * @param {Party.Request} req - The HTTP request to handle
1407
- * @param {Object} subRoom - The room instance
1408
- * @description Attempts to match the request to a registered @Request handler
1409
- * @returns {Promise<Response | null>} The response or null if no handler matched
1410
- */
1392
+ * @method tryMatchRequestHandler
1393
+ * @private
1394
+ * @async
1395
+ * @param {Party.Request} req - The HTTP request to handle
1396
+ * @param {Object} subRoom - The room instance
1397
+ * @description Attempts to match the request to a registered @Request handler
1398
+ * @returns {Promise<Response | null>} The response or null if no handler matched
1399
+ */
1411
1400
  async tryMatchRequestHandler(req, res, subRoom) {
1412
1401
  const requestHandlers = subRoom.constructor["_requestMetadata"];
1413
1402
  if (!requestHandlers) {
@@ -1415,8 +1404,7 @@ var Server = class {
1415
1404
  }
1416
1405
  const url = new URL(req.url);
1417
1406
  const method = req.method;
1418
- let pathname = url.pathname;
1419
- pathname = "/" + pathname.split("/").slice(4).join("/");
1407
+ const pathname = this.normalizeRequestPath(url.pathname);
1420
1408
  for (const [routeKey, handler] of requestHandlers.entries()) {
1421
1409
  const firstColonIndex = routeKey.indexOf(":");
1422
1410
  const handlerMethod = routeKey.substring(0, firstColonIndex);
@@ -1437,23 +1425,20 @@ var Server = class {
1437
1425
  }
1438
1426
  }
1439
1427
  let bodyData = null;
1440
- if (handler.bodyValidation && [
1441
- "POST",
1442
- "PUT",
1443
- "PATCH"
1444
- ].includes(method)) {
1428
+ if (handler.bodyValidation && ["POST", "PUT", "PATCH"].includes(method)) {
1445
1429
  try {
1446
1430
  const contentType = req.headers.get("content-type") || "";
1447
- if (contentType.includes("application/json")) {
1448
- const body = await req.json();
1449
- const validation = handler.bodyValidation.safeParse(body);
1450
- if (!validation.success) {
1451
- return res.badRequest("Invalid request body", {
1452
- details: validation.error
1453
- });
1454
- }
1455
- bodyData = validation.data;
1431
+ if (!contentType.includes("application/json")) {
1432
+ return res.badRequest("Content-Type must be application/json");
1456
1433
  }
1434
+ const body = await req.json();
1435
+ const validation = handler.bodyValidation.safeParse(body);
1436
+ if (!validation.success) {
1437
+ return res.badRequest("Invalid request body", {
1438
+ details: validation.error
1439
+ });
1440
+ }
1441
+ bodyData = validation.data;
1457
1442
  } catch (error) {
1458
1443
  return res.badRequest("Failed to parse request body");
1459
1444
  }
@@ -1461,7 +1446,9 @@ var Server = class {
1461
1446
  try {
1462
1447
  req["data"] = bodyData;
1463
1448
  req["params"] = params;
1464
- const result = await awaitReturn(subRoom[handler.key](req, res));
1449
+ const result = await awaitReturn(
1450
+ subRoom[handler.key](req, res)
1451
+ );
1465
1452
  if (result instanceof Response) {
1466
1453
  return result;
1467
1454
  }
@@ -1475,26 +1462,24 @@ var Server = class {
1475
1462
  return null;
1476
1463
  }
1477
1464
  /**
1478
- * @method pathMatches
1479
- * @private
1480
- * @param {string} requestPath - The path from the request
1481
- * @param {string} handlerPath - The path pattern from the handler
1482
- * @description Checks if a request path matches a handler path pattern
1483
- * @returns {boolean} True if the paths match
1484
- */
1465
+ * @method pathMatches
1466
+ * @private
1467
+ * @param {string} requestPath - The path from the request
1468
+ * @param {string} handlerPath - The path pattern from the handler
1469
+ * @description Checks if a request path matches a handler path pattern
1470
+ * @returns {boolean} True if the paths match
1471
+ */
1485
1472
  pathMatches(requestPath, handlerPath) {
1486
- const pathRegexString = handlerPath.replace(/\//g, "\\/").replace(/:([^\/]+)/g, "([^/]+)");
1487
- const pathRegex = new RegExp(`^${pathRegexString}`);
1488
- return pathRegex.test(requestPath);
1473
+ return this.pathPatternToRegex(handlerPath).test(requestPath);
1489
1474
  }
1490
1475
  /**
1491
- * @method extractPathParams
1492
- * @private
1493
- * @param {string} requestPath - The path from the request
1494
- * @param {string} handlerPath - The path pattern from the handler
1495
- * @description Extracts path parameters from the request path based on the handler pattern
1496
- * @returns {Object} An object containing the path parameters
1497
- */
1476
+ * @method extractPathParams
1477
+ * @private
1478
+ * @param {string} requestPath - The path from the request
1479
+ * @param {string} handlerPath - The path pattern from the handler
1480
+ * @description Extracts path parameters from the request path based on the handler pattern
1481
+ * @returns {Object} An object containing the path parameters
1482
+ */
1498
1483
  extractPathParams(requestPath, handlerPath) {
1499
1484
  const params = {};
1500
1485
  const paramNames = [];
@@ -1503,8 +1488,7 @@ var Server = class {
1503
1488
  paramNames.push(segment.substring(1));
1504
1489
  }
1505
1490
  });
1506
- const pathRegexString = handlerPath.replace(/\//g, "\\/").replace(/:([^\/]+)/g, "([^/]+)");
1507
- const pathRegex = new RegExp(`^${pathRegexString}`);
1491
+ const pathRegex = this.pathPatternToRegex(handlerPath);
1508
1492
  const matches = requestPath.match(pathRegex);
1509
1493
  if (matches && matches.length > 1) {
1510
1494
  for (let i = 0; i < paramNames.length; i++) {
@@ -1513,15 +1497,32 @@ var Server = class {
1513
1497
  }
1514
1498
  return params;
1515
1499
  }
1500
+ normalizeRequestPath(pathname) {
1501
+ const parts = pathname.split("/").filter(Boolean);
1502
+ if (parts[0] === "parties" && parts.length >= 3) {
1503
+ const routePath = parts.slice(3).join("/");
1504
+ return routePath ? `/${routePath}` : "/";
1505
+ }
1506
+ return pathname || "/";
1507
+ }
1508
+ pathPatternToRegex(handlerPath) {
1509
+ const segments = handlerPath.split("/").map((segment) => {
1510
+ if (segment.startsWith(":")) {
1511
+ return "([^/]+)";
1512
+ }
1513
+ return segment.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1514
+ });
1515
+ return new RegExp(`^${segments.join("/")}$`);
1516
+ }
1516
1517
  /**
1517
- * @method handleShardRequest
1518
- * @private
1519
- * @async
1520
- * @param {Party.Request} req - The HTTP request forwarded by a shard
1521
- * @param {string | null} shardId - The ID of the shard that forwarded the request
1522
- * @description Processes requests forwarded by shards, preserving client context
1523
- * @returns {Promise<Response>} The response to return to the shard (which will forward it to the client)
1524
- */
1518
+ * @method handleShardRequest
1519
+ * @private
1520
+ * @async
1521
+ * @param {Party.Request} req - The HTTP request forwarded by a shard
1522
+ * @param {string | null} shardId - The ID of the shard that forwarded the request
1523
+ * @description Processes requests forwarded by shards, preserving client context
1524
+ * @returns {Promise<Response>} The response to return to the shard (which will forward it to the client)
1525
+ */
1525
1526
  async handleShardRequest(req, res, shardId) {
1526
1527
  const subRoom = await this.getSubRoom();
1527
1528
  if (!subRoom) {
@@ -1548,13 +1549,13 @@ var Server = class {
1548
1549
  }
1549
1550
  }
1550
1551
  /**
1551
- * @method createEnhancedRequest
1552
- * @private
1553
- * @param {Party.Request} originalReq - The original request received from the shard
1554
- * @param {string | null} originalClientIp - The original client IP, if available
1555
- * @description Creates an enhanced request object that preserves the original client context
1556
- * @returns {Party.Request} The enhanced request object
1557
- */
1552
+ * @method createEnhancedRequest
1553
+ * @private
1554
+ * @param {Party.Request} originalReq - The original request received from the shard
1555
+ * @param {string | null} originalClientIp - The original client IP, if available
1556
+ * @description Creates an enhanced request object that preserves the original client context
1557
+ * @returns {Party.Request} The enhanced request object
1558
+ */
1558
1559
  createEnhancedRequest(originalReq, originalClientIp) {
1559
1560
  const clonedReq = originalReq.clone();
1560
1561
  clonedReq.viaShard = true;
@@ -1567,18 +1568,6 @@ var Server = class {
1567
1568
 
1568
1569
  // src/shard.ts
1569
1570
  var Shard = class {
1570
- static {
1571
- __name(this, "Shard");
1572
- }
1573
- room;
1574
- ws;
1575
- connectionMap;
1576
- mainServerStub;
1577
- worldUrl;
1578
- worldId;
1579
- lastReportedConnections;
1580
- statsInterval;
1581
- statsIntervalId;
1582
1571
  constructor(room) {
1583
1572
  this.room = room;
1584
1573
  this.connectionMap = /* @__PURE__ */ new Map();
@@ -1701,9 +1690,7 @@ var Shard = class {
1701
1690
  })
1702
1691
  });
1703
1692
  if (!response2.ok) {
1704
- const errorData = await response2.json().catch(() => ({
1705
- error: "Unknown error"
1706
- }));
1693
+ const errorData = await response2.json().catch(() => ({ error: "Unknown error" }));
1707
1694
  console.error(`Failed to update World stats: ${response2.status} - ${errorData.error || "Unknown error"}`);
1708
1695
  return false;
1709
1696
  }
@@ -1715,17 +1702,15 @@ var Shard = class {
1715
1702
  }
1716
1703
  }
1717
1704
  /**
1718
- * @method onRequest
1719
- * @async
1720
- * @param {Party.Request} req - The HTTP request to handle
1721
- * @description Forwards HTTP requests to the main server, preserving client context
1722
- * @returns {Promise<Response>} The response from the main server
1723
- */
1705
+ * @method onRequest
1706
+ * @async
1707
+ * @param {Party.Request} req - The HTTP request to handle
1708
+ * @description Forwards HTTP requests to the main server, preserving client context
1709
+ * @returns {Promise<Response>} The response from the main server
1710
+ */
1724
1711
  async onRequest(req) {
1725
1712
  if (!this.mainServerStub) {
1726
- return response(503, {
1727
- error: "Shard not connected to main server"
1728
- });
1713
+ return response(503, { error: "Shard not connected to main server" });
1729
1714
  }
1730
1715
  try {
1731
1716
  const url = new URL(req.url);
@@ -1753,32 +1738,28 @@ var Shard = class {
1753
1738
  const response2 = await this.mainServerStub.fetch(path, requestInit);
1754
1739
  return response2;
1755
1740
  } catch (error) {
1756
- return response(500, {
1757
- error: "Error forwarding request"
1758
- });
1741
+ return response(500, { error: "Error forwarding request" });
1759
1742
  }
1760
1743
  }
1761
1744
  /**
1762
- * @method onAlarm
1763
- * @async
1764
- * @description Executed periodically, used to perform maintenance tasks
1765
- */
1745
+ * @method onAlarm
1746
+ * @async
1747
+ * @description Executed periodically, used to perform maintenance tasks
1748
+ */
1766
1749
  async onAlarm() {
1767
1750
  await this.updateWorldStats();
1768
1751
  }
1769
1752
  };
1770
1753
 
1771
1754
  // src/testing.ts
1772
- async function testRoom(Room3, options = {}) {
1773
- const createServer = /* @__PURE__ */ __name((io2) => {
1755
+ async function testRoom(Room2, options = {}) {
1756
+ const createServer = (io2) => {
1774
1757
  const server2 = new Server(io2);
1775
- server2.rooms = [
1776
- Room3
1777
- ];
1758
+ server2.rooms = [Room2];
1778
1759
  return server2;
1779
- }, "createServer");
1760
+ };
1780
1761
  const isShard = options.shard || false;
1781
- const io = new ServerIo(Room3.path, isShard ? {
1762
+ const io = new ServerIo(Room2.path, isShard ? {
1782
1763
  parties: {
1783
1764
  game: createServer,
1784
1765
  ...options.parties || {}
@@ -1790,9 +1771,9 @@ async function testRoom(Room3, options = {}) {
1790
1771
  partyFn: options.partyFn,
1791
1772
  env: options.env
1792
1773
  });
1793
- Room3.prototype.throttleSync = 0;
1794
- Room3.prototype.throttleStorage = 0;
1795
- Room3.prototype.options = options;
1774
+ Room2.prototype.throttleSync = 0;
1775
+ Room2.prototype.throttleStorage = 0;
1776
+ Room2.prototype.options = options;
1796
1777
  let server;
1797
1778
  if (options.shard) {
1798
1779
  const shardServer = new Shard(io);
@@ -1817,41 +1798,31 @@ async function testRoom(Room3, options = {}) {
1817
1798
  return {
1818
1799
  server,
1819
1800
  room: server.subRoom,
1820
- createClient: /* @__PURE__ */ __name(async (id2, opts) => {
1801
+ createClient: async (id2, opts) => {
1821
1802
  const client = await io.connection(server, id2, opts);
1822
1803
  return client;
1823
- }, "createClient"),
1824
- getServerUser: /* @__PURE__ */ __name(async (client, prop = "users") => {
1804
+ },
1805
+ getServerUser: async (client, prop = "users") => {
1825
1806
  const privateId = client.conn.id;
1826
1807
  const session = await server.getSession(privateId);
1827
1808
  return server.subRoom[prop]()[session?.publicId];
1828
- }, "getServerUser")
1809
+ }
1829
1810
  };
1830
1811
  }
1831
- __name(testRoom, "testRoom");
1832
1812
  async function request(room, path, options = {
1833
1813
  method: "GET"
1834
1814
  }) {
1835
1815
  const url = new URL("http://localhost" + path);
1836
- const request1 = new Request(url.toString(), options);
1837
- const response2 = await room.onRequest(request1);
1816
+ const request2 = new Request(url.toString(), options);
1817
+ const response2 = await room.onRequest(request2);
1838
1818
  return response2;
1839
1819
  }
1840
- __name(request, "request");
1841
1820
  function tick(ms = 0) {
1842
1821
  return new Promise((resolve) => setTimeout(resolve, ms));
1843
1822
  }
1844
- __name(tick, "tick");
1845
1823
 
1846
1824
  // src/mock.ts
1847
1825
  var MockPartyClient = class {
1848
- static {
1849
- __name(this, "MockPartyClient");
1850
- }
1851
- server;
1852
- events;
1853
- id;
1854
- conn;
1855
1826
  constructor(server, id2) {
1856
1827
  this.server = server;
1857
1828
  this.events = /* @__PURE__ */ new Map();
@@ -1887,12 +1858,7 @@ var MockPartyClient = class {
1887
1858
  return this.server.onMessage(JSON.stringify(data), this.conn);
1888
1859
  }
1889
1860
  };
1890
- var MockLobby = class MockLobby2 {
1891
- static {
1892
- __name(this, "MockLobby");
1893
- }
1894
- server;
1895
- lobbyId;
1861
+ var MockLobby = class {
1896
1862
  constructor(server, lobbyId) {
1897
1863
  this.server = server;
1898
1864
  this.lobbyId = lobbyId;
@@ -1910,12 +1876,7 @@ var MockLobby = class MockLobby2 {
1910
1876
  return request(this.server, baseUrl + url, options);
1911
1877
  }
1912
1878
  };
1913
- var MockContext = class MockContext2 {
1914
- static {
1915
- __name(this, "MockContext");
1916
- }
1917
- room;
1918
- parties;
1879
+ var MockContext = class {
1919
1880
  constructor(room, options = {}) {
1920
1881
  this.room = room;
1921
1882
  this.parties = {
@@ -1925,13 +1886,13 @@ var MockContext = class MockContext2 {
1925
1886
  if (options.partyFn) {
1926
1887
  const serverCache = /* @__PURE__ */ new Map();
1927
1888
  this.parties.main = {
1928
- get: /* @__PURE__ */ __name(async (lobbyId) => {
1889
+ get: async (lobbyId) => {
1929
1890
  if (!serverCache.has(lobbyId)) {
1930
1891
  const server = await options.partyFn(lobbyId);
1931
1892
  serverCache.set(lobbyId, new MockLobby(server, lobbyId));
1932
1893
  }
1933
1894
  return serverCache.get(lobbyId);
1934
- }, "get")
1895
+ }
1935
1896
  };
1936
1897
  } else {
1937
1898
  for (let lobbyId in parties) {
@@ -1941,15 +1902,7 @@ var MockContext = class MockContext2 {
1941
1902
  }
1942
1903
  }
1943
1904
  };
1944
- var MockPartyRoom = class MockPartyRoom2 {
1945
- static {
1946
- __name(this, "MockPartyRoom");
1947
- }
1948
- id;
1949
- clients;
1950
- storage;
1951
- context;
1952
- env;
1905
+ var MockPartyRoom = class {
1953
1906
  constructor(id2, options = {}) {
1954
1907
  this.id = id2;
1955
1908
  this.clients = /* @__PURE__ */ new Map();
@@ -1977,9 +1930,7 @@ var MockPartyRoom = class MockPartyRoom2 {
1977
1930
  ...opts?.headers || {}
1978
1931
  }
1979
1932
  });
1980
- await server.onConnect(socket.conn, {
1981
- request: request2
1982
- });
1933
+ await server.onConnect(socket.conn, { request: request2 });
1983
1934
  this.clients.set(socket.id, socket);
1984
1935
  return socket;
1985
1936
  }
@@ -1999,19 +1950,12 @@ var MockPartyRoom = class MockPartyRoom2 {
1999
1950
  }
2000
1951
  };
2001
1952
  var MockConnection = class {
2002
- static {
2003
- __name(this, "MockConnection");
2004
- }
2005
- client;
2006
- server;
2007
- id;
2008
1953
  constructor(client) {
2009
1954
  this.client = client;
2010
1955
  this.state = {};
2011
1956
  this.server = client.server;
2012
1957
  this.id = client.id;
2013
1958
  }
2014
- state;
2015
1959
  setState(value) {
2016
1960
  this.state = value;
2017
1961
  }
@@ -2030,17 +1974,8 @@ import { signal } from "@signe/reactive";
2030
1974
  import { sync, id, persist } from "@signe/sync";
2031
1975
  import { z as z2 } from "zod";
2032
1976
 
2033
- // src/types/party.ts
2034
- var party_exports = {};
2035
-
2036
1977
  // src/jwt.ts
2037
1978
  var JWTAuth = class {
2038
- static {
2039
- __name(this, "JWTAuth");
2040
- }
2041
- secret;
2042
- encoder;
2043
- decoder;
2044
1979
  /**
2045
1980
  * Constructor for the JWTAuth class
2046
1981
  * @param {string} secret - The secret key used for signing and verifying tokens
@@ -2061,18 +1996,16 @@ var JWTAuth = class {
2061
1996
  const keyData = this.encoder.encode(this.secret);
2062
1997
  return await crypto.subtle.importKey(
2063
1998
  "raw",
1999
+ // format
2064
2000
  keyData,
2001
+ // key data
2065
2002
  {
2066
2003
  name: "HMAC",
2067
- hash: {
2068
- name: "SHA-256"
2069
- }
2004
+ hash: { name: "SHA-256" }
2070
2005
  },
2071
2006
  false,
2072
- [
2073
- "sign",
2074
- "verify"
2075
- ]
2007
+ // extractable
2008
+ ["sign", "verify"]
2076
2009
  // key usages
2077
2010
  );
2078
2011
  }
@@ -2144,9 +2077,11 @@ var JWTAuth = class {
2144
2077
  const encodedPayload = this.base64UrlEncode(this.encoder.encode(JSON.stringify(fullPayload)));
2145
2078
  const signatureBase = `${encodedHeader}.${encodedPayload}`;
2146
2079
  const key = await this.getSecretKey();
2147
- const signature = await crypto.subtle.sign({
2148
- name: "HMAC"
2149
- }, key, this.encoder.encode(signatureBase));
2080
+ const signature = await crypto.subtle.sign(
2081
+ { name: "HMAC" },
2082
+ key,
2083
+ this.encoder.encode(signatureBase)
2084
+ );
2150
2085
  const encodedSignature = this.base64UrlEncode(signature);
2151
2086
  return `${signatureBase}.${encodedSignature}`;
2152
2087
  }
@@ -2178,9 +2113,12 @@ var JWTAuth = class {
2178
2113
  const key = await this.getSecretKey();
2179
2114
  const signatureBase = `${encodedHeader}.${encodedPayload}`;
2180
2115
  const signature = this.base64UrlDecode(encodedSignature);
2181
- const isValid = await crypto.subtle.verify({
2182
- name: "HMAC"
2183
- }, key, signature, this.encoder.encode(signatureBase));
2116
+ const isValid = await crypto.subtle.verify(
2117
+ { name: "HMAC" },
2118
+ key,
2119
+ signature,
2120
+ this.encoder.encode(signatureBase)
2121
+ );
2184
2122
  if (!isValid) {
2185
2123
  throw new Error("Invalid signature");
2186
2124
  }
@@ -2195,7 +2133,7 @@ var JWTAuth = class {
2195
2133
  };
2196
2134
 
2197
2135
  // src/world.guard.ts
2198
- var guardManageWorld = /* @__PURE__ */ __name(async (_, req, room) => {
2136
+ var guardManageWorld = async (_, req, room) => {
2199
2137
  const tokenShard = req.headers.get("x-access-shard");
2200
2138
  if (tokenShard) {
2201
2139
  if (tokenShard !== room.env.SHARD_SECRET) {
@@ -2218,46 +2156,22 @@ var guardManageWorld = /* @__PURE__ */ __name(async (_, req, room) => {
2218
2156
  return false;
2219
2157
  }
2220
2158
  return true;
2221
- }, "guardManageWorld");
2159
+ };
2222
2160
 
2223
2161
  // src/world.ts
2224
- function _ts_decorate(decorators, target, key, desc) {
2225
- var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
2226
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
2227
- else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
2228
- return c > 3 && r && Object.defineProperty(target, key, r), r;
2229
- }
2230
- __name(_ts_decorate, "_ts_decorate");
2231
- function _ts_metadata(k, v) {
2232
- if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
2233
- }
2234
- __name(_ts_metadata, "_ts_metadata");
2235
2162
  var MAX_PLAYERS_PER_SHARD = 75;
2236
2163
  var RoomConfigSchema = z2.object({
2237
2164
  name: z2.string(),
2238
- balancingStrategy: z2.enum([
2239
- "round-robin",
2240
- "least-connections",
2241
- "random"
2242
- ]),
2165
+ balancingStrategy: z2.enum(["round-robin", "least-connections", "random"]),
2243
2166
  public: z2.boolean(),
2244
2167
  maxPlayersPerShard: z2.number().int().positive(),
2245
2168
  minShards: z2.number().int().min(0),
2246
2169
  maxShards: z2.number().int().positive().optional()
2247
2170
  });
2248
- var RegisterShardSchema = z2.object({
2249
- shardId: z2.string(),
2250
- roomId: z2.string(),
2251
- url: z2.string().url(),
2252
- maxConnections: z2.number().int().positive()
2253
- });
2254
2171
  var UpdateShardStatsSchema = z2.object({
2172
+ shardId: z2.string(),
2255
2173
  connections: z2.number().int().min(0),
2256
- status: z2.enum([
2257
- "active",
2258
- "maintenance",
2259
- "draining"
2260
- ]).optional()
2174
+ status: z2.enum(["active", "maintenance", "draining"]).optional()
2261
2175
  });
2262
2176
  var ScaleRoomSchema = z2.object({
2263
2177
  roomId: z2.string(),
@@ -2267,94 +2181,77 @@ var ScaleRoomSchema = z2.object({
2267
2181
  maxConnections: z2.number().int().positive()
2268
2182
  }).optional()
2269
2183
  });
2270
- var RoomConfig = class RoomConfig2 {
2271
- static {
2272
- __name(this, "RoomConfig");
2273
- }
2274
- id;
2275
- name = signal("");
2276
- balancingStrategy = signal("round-robin");
2277
- public = signal(true);
2278
- maxPlayersPerShard = signal(MAX_PLAYERS_PER_SHARD);
2279
- minShards = signal(1);
2280
- maxShards = signal(void 0);
2184
+ var RoomConfig = class {
2185
+ constructor() {
2186
+ this.name = signal("");
2187
+ this.balancingStrategy = signal("round-robin");
2188
+ this.public = signal(true);
2189
+ this.maxPlayersPerShard = signal(MAX_PLAYERS_PER_SHARD);
2190
+ this.minShards = signal(1);
2191
+ this.maxShards = signal(void 0);
2192
+ }
2281
2193
  };
2282
- _ts_decorate([
2283
- id(),
2284
- _ts_metadata("design:type", String)
2285
- ], RoomConfig.prototype, "id", void 0);
2286
- _ts_decorate([
2194
+ __decorateClass([
2195
+ id()
2196
+ ], RoomConfig.prototype, "id", 2);
2197
+ __decorateClass([
2287
2198
  sync()
2288
- ], RoomConfig.prototype, "name", void 0);
2289
- _ts_decorate([
2199
+ ], RoomConfig.prototype, "name", 2);
2200
+ __decorateClass([
2290
2201
  sync()
2291
- ], RoomConfig.prototype, "balancingStrategy", void 0);
2292
- _ts_decorate([
2202
+ ], RoomConfig.prototype, "balancingStrategy", 2);
2203
+ __decorateClass([
2293
2204
  sync()
2294
- ], RoomConfig.prototype, "public", void 0);
2295
- _ts_decorate([
2205
+ ], RoomConfig.prototype, "public", 2);
2206
+ __decorateClass([
2296
2207
  sync()
2297
- ], RoomConfig.prototype, "maxPlayersPerShard", void 0);
2298
- _ts_decorate([
2208
+ ], RoomConfig.prototype, "maxPlayersPerShard", 2);
2209
+ __decorateClass([
2299
2210
  sync()
2300
- ], RoomConfig.prototype, "minShards", void 0);
2301
- _ts_decorate([
2211
+ ], RoomConfig.prototype, "minShards", 2);
2212
+ __decorateClass([
2302
2213
  sync()
2303
- ], RoomConfig.prototype, "maxShards", void 0);
2304
- var ShardInfo = class ShardInfo2 {
2305
- static {
2306
- __name(this, "ShardInfo");
2307
- }
2308
- id;
2309
- roomId = signal("");
2310
- url = signal("");
2311
- currentConnections = signal(0);
2312
- maxConnections = signal(MAX_PLAYERS_PER_SHARD);
2313
- status = signal("active");
2314
- lastHeartbeat = signal(0);
2214
+ ], RoomConfig.prototype, "maxShards", 2);
2215
+ var ShardInfo = class {
2216
+ constructor() {
2217
+ this.roomId = signal("");
2218
+ this.url = signal("");
2219
+ this.currentConnections = signal(0);
2220
+ this.maxConnections = signal(MAX_PLAYERS_PER_SHARD);
2221
+ this.status = signal("active");
2222
+ this.lastHeartbeat = signal(0);
2223
+ }
2315
2224
  };
2316
- _ts_decorate([
2317
- id(),
2318
- _ts_metadata("design:type", String)
2319
- ], ShardInfo.prototype, "id", void 0);
2320
- _ts_decorate([
2225
+ __decorateClass([
2226
+ id()
2227
+ ], ShardInfo.prototype, "id", 2);
2228
+ __decorateClass([
2321
2229
  sync()
2322
- ], ShardInfo.prototype, "roomId", void 0);
2323
- _ts_decorate([
2230
+ ], ShardInfo.prototype, "roomId", 2);
2231
+ __decorateClass([
2324
2232
  sync()
2325
- ], ShardInfo.prototype, "url", void 0);
2326
- _ts_decorate([
2233
+ ], ShardInfo.prototype, "url", 2);
2234
+ __decorateClass([
2327
2235
  sync({
2328
2236
  persist: false
2329
2237
  })
2330
- ], ShardInfo.prototype, "currentConnections", void 0);
2331
- _ts_decorate([
2238
+ ], ShardInfo.prototype, "currentConnections", 2);
2239
+ __decorateClass([
2332
2240
  sync()
2333
- ], ShardInfo.prototype, "maxConnections", void 0);
2334
- _ts_decorate([
2241
+ ], ShardInfo.prototype, "maxConnections", 2);
2242
+ __decorateClass([
2335
2243
  sync()
2336
- ], ShardInfo.prototype, "status", void 0);
2337
- _ts_decorate([
2244
+ ], ShardInfo.prototype, "status", 2);
2245
+ __decorateClass([
2338
2246
  sync()
2339
- ], ShardInfo.prototype, "lastHeartbeat", void 0);
2247
+ ], ShardInfo.prototype, "lastHeartbeat", 2);
2340
2248
  var WorldRoom = class {
2341
- static {
2342
- __name(this, "WorldRoom");
2343
- }
2344
- room;
2345
- // Synchronized state
2346
- rooms;
2347
- shards;
2348
- // Only persisted state (not synced to clients)
2349
- rrCounters;
2350
- // Configuration
2351
- defaultShardUrlTemplate;
2352
- defaultMaxConnectionsPerShard;
2353
2249
  constructor(room) {
2354
2250
  this.room = room;
2355
2251
  this.rooms = signal({});
2356
2252
  this.shards = signal({});
2357
2253
  this.rrCounters = signal({});
2254
+ // Configuration
2358
2255
  this.defaultShardUrlTemplate = signal("{shardId}");
2359
2256
  this.defaultMaxConnectionsPerShard = signal(MAX_PLAYERS_PER_SHARD);
2360
2257
  const { AUTH_JWT_SECRET, SHARD_SECRET } = this.room.env;
@@ -2392,9 +2289,14 @@ var WorldRoom = class {
2392
2289
  });
2393
2290
  setTimeout(() => this.cleanupInactiveShards(), 6e4);
2394
2291
  }
2395
- // Actions
2396
- async registerRoom(req) {
2397
- const roomConfig = await req.json();
2292
+ async registerRoom(req, res) {
2293
+ const roomConfigResult = RoomConfigSchema.safeParse(await req.json());
2294
+ if (!roomConfigResult.success) {
2295
+ return res?.badRequest("Invalid room configuration", {
2296
+ details: roomConfigResult.error
2297
+ });
2298
+ }
2299
+ const roomConfig = roomConfigResult.data;
2398
2300
  const roomId = roomConfig.name;
2399
2301
  if (!this.rooms()[roomId]) {
2400
2302
  const newRoom = new RoomConfig();
@@ -2421,7 +2323,13 @@ var WorldRoom = class {
2421
2323
  }
2422
2324
  }
2423
2325
  async updateShardStats(req, res) {
2424
- const body = await req.json();
2326
+ const bodyResult = UpdateShardStatsSchema.safeParse(await req.json());
2327
+ if (!bodyResult.success) {
2328
+ return res.badRequest("Invalid shard statistics", {
2329
+ details: bodyResult.error
2330
+ });
2331
+ }
2332
+ const body = bodyResult.data;
2425
2333
  const { shardId, connections, status } = body;
2426
2334
  const shard = this.shards()[shardId];
2427
2335
  if (!shard) {
@@ -2434,7 +2342,13 @@ var WorldRoom = class {
2434
2342
  shard.lastHeartbeat.set(Date.now());
2435
2343
  }
2436
2344
  async scaleRoom(req, res) {
2437
- const data = await req.json();
2345
+ const dataResult = ScaleRoomSchema.safeParse(await req.json());
2346
+ if (!dataResult.success) {
2347
+ return res.badRequest("Invalid scale request", {
2348
+ details: dataResult.error
2349
+ });
2350
+ }
2351
+ const data = dataResult.data;
2438
2352
  const { targetShardCount, shardTemplate, roomId } = data;
2439
2353
  const room = this.rooms()[roomId];
2440
2354
  if (!room) {
@@ -2449,14 +2363,11 @@ var WorldRoom = class {
2449
2363
  });
2450
2364
  }
2451
2365
  if (targetShardCount < previousShardCount) {
2452
- const shardsToRemove = [
2453
- ...roomShards
2454
- ].sort((a, b) => {
2366
+ const shardsToRemove = [...roomShards].sort((a, b) => {
2455
2367
  if (a.status() === "draining" && b.status() !== "draining") return -1;
2456
2368
  if (a.status() !== "draining" && b.status() === "draining") return 1;
2457
2369
  return a.currentConnections() - b.currentConnections();
2458
2370
  }).slice(0, previousShardCount - targetShardCount);
2459
- const shardsToKeep = roomShards.filter((shard) => !shardsToRemove.some((s) => s.id === shard.id));
2460
2371
  for (const shard of shardsToRemove) {
2461
2372
  delete this.shards()[shard.id];
2462
2373
  }
@@ -2465,7 +2376,11 @@ var WorldRoom = class {
2465
2376
  if (targetShardCount > previousShardCount) {
2466
2377
  const newShards = [];
2467
2378
  for (let i = 0; i < targetShardCount - previousShardCount; i++) {
2468
- const newShard = await this.createShard(roomId, shardTemplate?.urlTemplate, shardTemplate?.maxConnections);
2379
+ const newShard = await this.createShard(
2380
+ roomId,
2381
+ shardTemplate?.urlTemplate,
2382
+ shardTemplate?.maxConnections
2383
+ );
2469
2384
  if (newShard) {
2470
2385
  newShards.push(newShard);
2471
2386
  }
@@ -2507,26 +2422,22 @@ var WorldRoom = class {
2507
2422
  if (!room) {
2508
2423
  if (autoCreate) {
2509
2424
  const mockRequest = {
2510
- json: /* @__PURE__ */ __name(async () => ({
2425
+ json: async () => ({
2511
2426
  name: roomId,
2512
2427
  balancingStrategy: "round-robin",
2513
2428
  public: true,
2514
2429
  maxPlayersPerShard: this.defaultMaxConnectionsPerShard(),
2515
2430
  minShards: 1,
2516
2431
  maxShards: void 0
2517
- }), "json")
2432
+ })
2518
2433
  };
2519
2434
  await this.registerRoom(mockRequest);
2520
2435
  room = this.rooms()[roomId];
2521
2436
  if (!room) {
2522
- return {
2523
- error: `Failed to create room ${roomId}`
2524
- };
2437
+ return { error: `Failed to create room ${roomId}` };
2525
2438
  }
2526
2439
  } else {
2527
- return {
2528
- error: `Room ${roomId} does not exist`
2529
- };
2440
+ return { error: `Room ${roomId} does not exist` };
2530
2441
  }
2531
2442
  }
2532
2443
  const roomShards = Object.values(this.shards()).filter((shard) => shard.roomId() === roomId);
@@ -2539,27 +2450,24 @@ var WorldRoom = class {
2539
2450
  url: newShard.url()
2540
2451
  };
2541
2452
  } else {
2542
- return {
2543
- error: `Failed to create shard for room ${roomId}`
2544
- };
2453
+ return { error: `Failed to create shard for room ${roomId}` };
2545
2454
  }
2546
2455
  } else {
2547
- return {
2548
- error: `No shards available for room ${roomId}`
2549
- };
2456
+ return { error: `No shards available for room ${roomId}` };
2550
2457
  }
2551
2458
  }
2552
2459
  const activeShards = roomShards.filter((shard) => shard && shard.status() === "active");
2553
2460
  if (activeShards.length === 0) {
2554
- return {
2555
- error: `No active shards available for room ${roomId}`
2556
- };
2461
+ return { error: `No active shards available for room ${roomId}` };
2557
2462
  }
2558
2463
  const balancingStrategy = room.balancingStrategy();
2559
2464
  let selectedShard;
2560
2465
  switch (balancingStrategy) {
2561
2466
  case "least-connections":
2562
- selectedShard = activeShards.reduce((min, shard) => shard.currentConnections() < min.currentConnections() ? shard : min, activeShards[0]);
2467
+ selectedShard = activeShards.reduce(
2468
+ (min, shard) => shard.currentConnections() < min.currentConnections() ? shard : min,
2469
+ activeShards[0]
2470
+ );
2563
2471
  break;
2564
2472
  case "random":
2565
2473
  selectedShard = activeShards[Math.floor(Math.random() * activeShards.length)];
@@ -2600,82 +2508,52 @@ var WorldRoom = class {
2600
2508
  return newShard;
2601
2509
  }
2602
2510
  };
2603
- _ts_decorate([
2511
+ __decorateClass([
2604
2512
  sync(RoomConfig)
2605
- ], WorldRoom.prototype, "rooms", void 0);
2606
- _ts_decorate([
2513
+ ], WorldRoom.prototype, "rooms", 2);
2514
+ __decorateClass([
2607
2515
  sync(ShardInfo)
2608
- ], WorldRoom.prototype, "shards", void 0);
2609
- _ts_decorate([
2516
+ ], WorldRoom.prototype, "shards", 2);
2517
+ __decorateClass([
2610
2518
  persist()
2611
- ], WorldRoom.prototype, "rrCounters", void 0);
2612
- _ts_decorate([
2519
+ ], WorldRoom.prototype, "rrCounters", 2);
2520
+ __decorateClass([
2613
2521
  Request2({
2614
2522
  path: "register-room",
2615
2523
  method: "POST"
2616
2524
  }),
2617
- Guard([
2618
- guardManageWorld
2619
- ]),
2620
- _ts_metadata("design:type", Function),
2621
- _ts_metadata("design:paramtypes", [
2622
- typeof party_exports === "undefined" || typeof void 0 === "undefined" ? Object : void 0
2623
- ]),
2624
- _ts_metadata("design:returntype", Promise)
2625
- ], WorldRoom.prototype, "registerRoom", null);
2626
- _ts_decorate([
2525
+ Guard([guardManageWorld])
2526
+ ], WorldRoom.prototype, "registerRoom", 1);
2527
+ __decorateClass([
2627
2528
  Request2({
2628
2529
  path: "update-shard",
2629
2530
  method: "POST"
2630
2531
  }),
2631
- Guard([
2632
- guardManageWorld
2633
- ]),
2634
- _ts_metadata("design:type", Function),
2635
- _ts_metadata("design:paramtypes", [
2636
- typeof party_exports === "undefined" || typeof void 0 === "undefined" ? Object : void 0,
2637
- typeof ServerResponse === "undefined" ? Object : ServerResponse
2638
- ]),
2639
- _ts_metadata("design:returntype", Promise)
2640
- ], WorldRoom.prototype, "updateShardStats", null);
2641
- _ts_decorate([
2532
+ Guard([guardManageWorld])
2533
+ ], WorldRoom.prototype, "updateShardStats", 1);
2534
+ __decorateClass([
2642
2535
  Request2({
2643
2536
  path: "scale-room",
2644
2537
  method: "POST"
2645
2538
  }),
2646
- Guard([
2647
- guardManageWorld
2648
- ]),
2649
- _ts_metadata("design:type", Function),
2650
- _ts_metadata("design:paramtypes", [
2651
- typeof party_exports === "undefined" || typeof void 0 === "undefined" ? Object : void 0,
2652
- typeof ServerResponse === "undefined" ? Object : ServerResponse
2653
- ]),
2654
- _ts_metadata("design:returntype", Promise)
2655
- ], WorldRoom.prototype, "scaleRoom", null);
2656
- _ts_decorate([
2539
+ Guard([guardManageWorld])
2540
+ ], WorldRoom.prototype, "scaleRoom", 1);
2541
+ __decorateClass([
2657
2542
  Request2({
2658
2543
  path: "connect",
2659
2544
  method: "POST"
2660
- }),
2661
- _ts_metadata("design:type", Function),
2662
- _ts_metadata("design:paramtypes", [
2663
- typeof party_exports === "undefined" || typeof void 0 === "undefined" ? Object : void 0,
2664
- typeof ServerResponse === "undefined" ? Object : ServerResponse
2665
- ]),
2666
- _ts_metadata("design:returntype", Promise)
2667
- ], WorldRoom.prototype, "connect", null);
2668
- WorldRoom = _ts_decorate([
2545
+ })
2546
+ ], WorldRoom.prototype, "connect", 1);
2547
+ WorldRoom = __decorateClass([
2669
2548
  Room({
2670
2549
  path: "world-{worldId}",
2671
2550
  maxUsers: 100,
2551
+ // Limit for admin connections
2672
2552
  throttleStorage: 2e3,
2553
+ // Throttle storage updates (ms)
2673
2554
  throttleSync: 500
2674
- }),
2675
- _ts_metadata("design:type", Function),
2676
- _ts_metadata("design:paramtypes", [
2677
- typeof party_exports === "undefined" || typeof void 0 === "undefined" ? Object : void 0
2678
- ])
2555
+ // Throttle sync updates (ms)
2556
+ })
2679
2557
  ], WorldRoom);
2680
2558
 
2681
2559
  // src/session.guard.ts
@@ -2700,8 +2578,7 @@ function createRequireSessionGuard(storage) {
2700
2578
  }
2701
2579
  };
2702
2580
  }
2703
- __name(createRequireSessionGuard, "createRequireSessionGuard");
2704
- var requireSession = /* @__PURE__ */ __name(async (sender, value, room) => {
2581
+ var requireSession = async (sender, value, room) => {
2705
2582
  if (!sender || !sender.id) {
2706
2583
  return false;
2707
2584
  }
@@ -2719,7 +2596,7 @@ var requireSession = /* @__PURE__ */ __name(async (sender, value, room) => {
2719
2596
  console.error("Error checking session in requireSession guard:", error);
2720
2597
  return false;
2721
2598
  }
2722
- }, "requireSession");
2599
+ };
2723
2600
  export {
2724
2601
  Action,
2725
2602
  ClientIo,
@@ -2732,6 +2609,7 @@ export {
2732
2609
  ServerIo,
2733
2610
  ServerResponse,
2734
2611
  Shard,
2612
+ UnhandledAction,
2735
2613
  WorldRoom,
2736
2614
  createRequireSessionGuard,
2737
2615
  request,