@signe/room 2.0.1 → 2.2.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.d.ts CHANGED
@@ -324,10 +324,55 @@ declare class Server implements Server$1 {
324
324
  */
325
325
  private getUsersProperty;
326
326
  private getUsersPropName;
327
- private getSession;
327
+ /**
328
+ * Retrieves the connection status property from a user object.
329
+ *
330
+ * @param {any} user - The user object to get the connection property from.
331
+ * @returns {Function|null} - The connection property signal function or null if not found.
332
+ * @private
333
+ */
334
+ private getUserConnectionProperty;
335
+ /**
336
+ * Updates a user's connection status in the signal.
337
+ *
338
+ * @param {any} user - The user object to update.
339
+ * @param {boolean} isConnected - The new connection status.
340
+ * @returns {boolean} - Whether the update was successful.
341
+ * @private
342
+ */
343
+ private updateUserConnectionStatus;
344
+ /**
345
+ * @method getSession
346
+ * @private
347
+ * @param {string} privateId - The private ID of the session.
348
+ * @returns {Promise<Object|null>} The session object, or null if not found.
349
+ *
350
+ * @example
351
+ * ```typescript
352
+ * const session = await server.getSession("privateId");
353
+ * console.log(session);
354
+ * ```
355
+ */
356
+ getSession(privateId: string): Promise<{
357
+ publicId: string;
358
+ state?: any;
359
+ created?: number;
360
+ connected?: boolean;
361
+ } | null>;
328
362
  private saveSession;
329
363
  private updateSessionConnection;
330
- private deleteSession;
364
+ /**
365
+ * @method deleteSession
366
+ * @private
367
+ * @param {string} privateId - The private ID of the session to delete.
368
+ * @returns {Promise<void>}
369
+ *
370
+ * @example
371
+ * ```typescript
372
+ * await server.deleteSession("privateId");
373
+ * ```
374
+ */
375
+ deleteSession(privateId: string): Promise<void>;
331
376
  onConnectClient(conn: Connection, ctx: ConnectionContext): Promise<void>;
332
377
  /**
333
378
  * @method onConnect
@@ -501,7 +546,7 @@ declare class MockPartyClient {
501
546
  private events;
502
547
  id: string;
503
548
  conn: MockConnection;
504
- constructor(server: Server);
549
+ constructor(server: Server, id?: string);
505
550
  addEventListener(event: any, cb: any): void;
506
551
  removeEventListener(event: any, cb: any): void;
507
552
  _trigger(event: any, data: any): void;
@@ -521,7 +566,7 @@ declare class MockPartyRoom {
521
566
  context: MockContext;
522
567
  env: {};
523
568
  constructor(id?: string, options?: any);
524
- connection(server: Server): Promise<MockPartyClient>;
569
+ connection(server: Server, id?: string): Promise<MockPartyClient>;
525
570
  broadcast(data: any): void;
526
571
  getConnection(id: string): MockPartyClient;
527
572
  getConnections(): MockConnection[];
@@ -616,9 +661,9 @@ declare function testRoom(Room: any, options?: {
616
661
  }): Promise<{
617
662
  server: Server | Shard;
618
663
  room: any;
619
- createClient: () => Promise<MockPartyClient>;
664
+ createClient: (id?: string) => Promise<MockPartyClient>;
620
665
  }>;
621
- declare function request(room: Server, path: string, options?: {
666
+ declare function request(room: Server | Shard, path: string, options?: {
622
667
  method: 'GET' | 'POST' | 'PUT' | 'DELETE';
623
668
  body?: any;
624
669
  headers?: Record<string, string>;
package/dist/index.js CHANGED
@@ -97,140 +97,6 @@ var Storage = class {
97
97
  }
98
98
  };
99
99
 
100
- // src/mock.ts
101
- var MockPartyClient = class {
102
- static {
103
- __name(this, "MockPartyClient");
104
- }
105
- server;
106
- events;
107
- id;
108
- conn;
109
- constructor(server) {
110
- this.server = server;
111
- this.events = /* @__PURE__ */ new Map();
112
- this.id = generateShortUUID();
113
- this.conn = new MockConnection(this);
114
- }
115
- addEventListener(event, cb) {
116
- this.events.set(event, cb);
117
- }
118
- removeEventListener(event, cb) {
119
- this.events.delete(event);
120
- }
121
- _trigger(event, data) {
122
- this.events.get(event)?.(data);
123
- }
124
- send(data) {
125
- return this.server.onMessage(JSON.stringify(data), this.conn);
126
- }
127
- };
128
- var MockLobby = class MockLobby2 {
129
- static {
130
- __name(this, "MockLobby");
131
- }
132
- server;
133
- constructor(server) {
134
- this.server = server;
135
- }
136
- socket() {
137
- return new MockPartyClient(this.server);
138
- }
139
- };
140
- var MockContext = class MockContext2 {
141
- static {
142
- __name(this, "MockContext");
143
- }
144
- room;
145
- parties;
146
- constructor(room, options = {}) {
147
- this.room = room;
148
- this.parties = {
149
- main: /* @__PURE__ */ new Map()
150
- };
151
- const parties = options.parties || {};
152
- for (let lobbyId in parties) {
153
- this.parties.main.set(lobbyId, new MockLobby(parties[lobbyId](room)));
154
- }
155
- }
156
- };
157
- var MockPartyRoom = class MockPartyRoom2 {
158
- static {
159
- __name(this, "MockPartyRoom");
160
- }
161
- id;
162
- clients;
163
- storage;
164
- context;
165
- env;
166
- constructor(id2, options = {}) {
167
- this.id = id2;
168
- this.clients = /* @__PURE__ */ new Map();
169
- this.storage = new Storage();
170
- this.env = {};
171
- this.id = id2 || generateShortUUID();
172
- this.context = new MockContext(this, {
173
- parties: options.parties || {}
174
- });
175
- this.env = options.env || {};
176
- }
177
- async connection(server) {
178
- const socket = new MockPartyClient(server);
179
- const url = new URL("http://localhost");
180
- const request2 = new Request(url.toString(), {
181
- method: "GET",
182
- headers: {
183
- "Content-Type": "application/json"
184
- }
185
- });
186
- await server.onConnect(socket.conn, {
187
- request: request2
188
- });
189
- this.clients.set(socket.id, socket);
190
- return socket;
191
- }
192
- broadcast(data) {
193
- this.clients.forEach((client) => {
194
- client._trigger("message", data);
195
- });
196
- }
197
- getConnection(id2) {
198
- return this.clients.get(id2);
199
- }
200
- getConnections() {
201
- return Array.from(this.clients.values()).map((client) => client.conn);
202
- }
203
- clear() {
204
- this.clients.clear();
205
- }
206
- };
207
- var MockConnection = class {
208
- static {
209
- __name(this, "MockConnection");
210
- }
211
- client;
212
- server;
213
- id;
214
- constructor(client) {
215
- this.client = client;
216
- this.state = {};
217
- this.server = client.server;
218
- this.id = client.id;
219
- }
220
- state;
221
- setState(value) {
222
- this.state = value;
223
- }
224
- send(data) {
225
- this.client._trigger("message", data);
226
- }
227
- close() {
228
- this.server.onClose(this);
229
- }
230
- };
231
- var ServerIo = MockPartyRoom;
232
- var ClientIo = MockPartyClient;
233
-
234
100
  // src/server.ts
235
101
  import { dset as dset2 } from "dset";
236
102
  import z from "zod";
@@ -546,7 +412,8 @@ var ServerResponse = class {
546
412
  // src/request/cors.ts
547
413
  function cors(res, options = {}) {
548
414
  const newHeaders = new Headers(res.headers);
549
- newHeaders.set("Access-Control-Allow-Origin", options.origin || "*");
415
+ const requestOrigin = options.origin || "*";
416
+ newHeaders.set("Access-Control-Allow-Origin", requestOrigin);
550
417
  if (options.credentials) {
551
418
  newHeaders.set("Access-Control-Allow-Credentials", "true");
552
419
  }
@@ -565,6 +432,8 @@ function cors(res, options = {}) {
565
432
  }
566
433
  if (options.maxAge) {
567
434
  newHeaders.set("Access-Control-Max-Age", options.maxAge.toString());
435
+ } else {
436
+ newHeaders.set("Access-Control-Max-Age", "86400");
568
437
  }
569
438
  return new Response(res.body, {
570
439
  status: res.status,
@@ -828,9 +697,54 @@ var Server = class {
828
697
  return null;
829
698
  }
830
699
  getUsersPropName(subRoom) {
831
- const meta = subRoom.constructor["_propertyMetadata"];
832
- return meta?.get("users");
700
+ if (!subRoom) return null;
701
+ const metadata = subRoom.constructor._propertyMetadata;
702
+ if (!metadata) return null;
703
+ return metadata.get("users");
704
+ }
705
+ /**
706
+ * Retrieves the connection status property from a user object.
707
+ *
708
+ * @param {any} user - The user object to get the connection property from.
709
+ * @returns {Function|null} - The connection property signal function or null if not found.
710
+ * @private
711
+ */
712
+ getUserConnectionProperty(user) {
713
+ if (!user) return null;
714
+ const metadata = user.constructor._propertyMetadata;
715
+ if (!metadata) return null;
716
+ const connectedPropName = metadata.get("connected");
717
+ if (!connectedPropName) return null;
718
+ return user[connectedPropName];
833
719
  }
720
+ /**
721
+ * Updates a user's connection status in the signal.
722
+ *
723
+ * @param {any} user - The user object to update.
724
+ * @param {boolean} isConnected - The new connection status.
725
+ * @returns {boolean} - Whether the update was successful.
726
+ * @private
727
+ */
728
+ updateUserConnectionStatus(user, isConnected) {
729
+ const connectionSignal = this.getUserConnectionProperty(user);
730
+ if (connectionSignal) {
731
+ connectionSignal.set(isConnected);
732
+ return true;
733
+ }
734
+ return false;
735
+ }
736
+ /**
737
+ * @method getSession
738
+ * @private
739
+ * @param {string} privateId - The private ID of the session.
740
+ * @returns {Promise<Object|null>} The session object, or null if not found.
741
+ *
742
+ * @example
743
+ * ```typescript
744
+ * const session = await server.getSession("privateId");
745
+ * console.log(session);
746
+ * ```
747
+ */
834
748
  async getSession(privateId) {
835
749
  if (!privateId) return null;
836
750
  try {
@@ -857,6 +771,17 @@ var Server = class {
857
771
  });
858
772
  }
859
773
  }
774
+ /**
775
+ * @method deleteSession
776
+ * @private
777
+ * @param {string} privateId - The private ID of the session to delete.
778
+ * @returns {Promise<void>}
779
+ *
780
+ * @example
781
+ * ```typescript
782
+ * await server.deleteSession("privateId");
783
+ * ```
784
+ */
860
785
  async deleteSession(privateId) {
861
786
  await this.room.storage.delete(`session:${privateId}`);
862
787
  }
@@ -892,6 +817,8 @@ var Server = class {
892
817
  signal2()[publicId] = user;
893
818
  const snapshot = createStatesSnapshot(user);
894
819
  this.room.storage.put(`${usersPropName}.${publicId}`, snapshot);
820
+ } else {
821
+ user = signal2()[existingSession.publicId];
895
822
  }
896
823
  if (!existingSession) {
897
824
  await this.saveSession(conn.id, {
@@ -901,6 +828,7 @@ var Server = class {
901
828
  await this.updateSessionConnection(conn.id, true);
902
829
  }
903
830
  }
831
+ this.updateUserConnectionStatus(user, true);
904
832
  await awaitReturn(subRoom["onJoin"]?.(user, conn, ctx));
905
833
  conn.setState({
906
834
  ...conn.state,
@@ -978,6 +906,10 @@ var Server = class {
978
906
  return;
979
907
  }
980
908
  const subRoom = await this.getSubRoom();
909
+ if (!subRoom) {
910
+ console.warn("Room not found");
911
+ return;
912
+ }
981
913
  const roomGuards = subRoom.constructor["_roomGuards"] || [];
982
914
  for (const guard of roomGuards) {
983
915
  const isAuthorized = await guard(sender, result.data.value, this.room);
@@ -1052,17 +984,14 @@ var Server = class {
1052
984
  * @returns {Promise<void>}
1053
985
  */
1054
986
  async handleShardClientConnect(message, shardConnection) {
1055
- const { privateId, connectionInfo } = message;
987
+ const { privateId, requestInfo } = message;
1056
988
  const shardState = shardConnection.state;
1057
989
  const virtualContext = {
1058
- request: {
1059
- headers: new Headers({
1060
- "x-forwarded-for": connectionInfo.ip,
1061
- "user-agent": connectionInfo.userAgent
1062
- }),
1063
- method: "GET",
1064
- url: ""
1065
- }
990
+ request: requestInfo ? {
991
+ headers: new Headers(requestInfo.headers),
992
+ method: requestInfo.method,
993
+ url: requestInfo.url
994
+ } : void 0
1066
995
  };
1067
996
  const virtualConnection = {
1068
997
  id: privateId,
@@ -1204,14 +1133,17 @@ var Server = class {
1204
1133
  const { publicId } = conn.state;
1205
1134
  const user = signal2?.()[publicId];
1206
1135
  if (!user) return;
1207
- await awaitReturn(subRoom["onLeave"]?.(user, conn));
1208
1136
  await this.updateSessionConnection(privateId, false);
1209
- this.broadcast({
1210
- type: "user_disconnected",
1211
- value: {
1212
- publicId
1213
- }
1214
- }, subRoom);
1137
+ const connectionUpdated = this.updateUserConnectionStatus(user, false);
1138
+ await awaitReturn(subRoom["onLeave"]?.(user, conn));
1139
+ if (!connectionUpdated) {
1140
+ this.broadcast({
1141
+ type: "user_disconnected",
1142
+ value: {
1143
+ publicId
1144
+ }
1145
+ }, subRoom);
1146
+ }
1215
1147
  }
1216
1148
  async onAlarm() {
1217
1149
  const subRoom = await this.getSubRoom();
@@ -1234,6 +1166,9 @@ var Server = class {
1234
1166
  const res = new ServerResponse([
1235
1167
  createCorsInterceptor()
1236
1168
  ]);
1169
+ if (req.method === "OPTIONS") {
1170
+ return res.status(200).send({});
1171
+ }
1237
1172
  if (isFromShard) {
1238
1173
  return this.handleShardRequest(req, res, shardId);
1239
1174
  }
@@ -1507,13 +1442,21 @@ var Shard = class {
1507
1442
  }
1508
1443
  onConnect(conn, ctx) {
1509
1444
  this.connectionMap.set(conn.id, conn);
1445
+ const headers = {};
1446
+ if (ctx.request?.headers) {
1447
+ ctx.request.headers.forEach((value, key) => {
1448
+ headers[key] = value;
1449
+ });
1450
+ }
1451
+ const requestInfo = ctx.request ? {
1452
+ headers,
1453
+ url: ctx.request.url,
1454
+ method: ctx.request.method
1455
+ } : null;
1510
1456
  this.ws.send(JSON.stringify({
1511
1457
  type: "shard.clientConnected",
1512
1458
  privateId: conn.id,
1513
- connectionInfo: {
1514
- ip: ctx.request?.headers.get("x-forwarded-for") || "unknown",
1515
- userAgent: ctx.request?.headers.get("user-agent") || "unknown"
1516
- }
1459
+ requestInfo
1517
1460
  }));
1518
1461
  this.updateWorldStats();
1519
1462
  }
@@ -1652,6 +1595,9 @@ async function testRoom(Room3, options = {}) {
1652
1595
  const shardServer = new Shard(io);
1653
1596
  shardServer.subRoom = null;
1654
1597
  server = shardServer;
1598
+ for (const lobby of io.context.parties.main.values()) {
1599
+ await lobby.server.onStart();
1600
+ }
1655
1601
  } else {
1656
1602
  server = await createServer(io);
1657
1603
  }
@@ -1659,8 +1605,8 @@ async function testRoom(Room3, options = {}) {
1659
1605
  return {
1660
1606
  server,
1661
1607
  room: server.subRoom,
1662
- createClient: /* @__PURE__ */ __name(async () => {
1663
- const client = await io.connection(server);
1608
+ createClient: /* @__PURE__ */ __name(async (id2) => {
1609
+ const client = await io.connection(server, id2);
1664
1610
  return client;
1665
1611
  }, "createClient")
1666
1612
  };
@@ -1676,6 +1622,143 @@ async function request(room, path, options = {
1676
1622
  }
1677
1623
  __name(request, "request");
1678
1624
 
1625
+ // src/mock.ts
1626
+ var MockPartyClient = class {
1627
+ static {
1628
+ __name(this, "MockPartyClient");
1629
+ }
1630
+ server;
1631
+ events;
1632
+ id;
1633
+ conn;
1634
+ constructor(server, id2) {
1635
+ this.server = server;
1636
+ this.events = /* @__PURE__ */ new Map();
1637
+ this.id = id2 || generateShortUUID();
1638
+ this.conn = new MockConnection(this);
1639
+ }
1640
+ addEventListener(event, cb) {
1641
+ this.events.set(event, cb);
1642
+ }
1643
+ removeEventListener(event, cb) {
1644
+ this.events.delete(event);
1645
+ }
1646
+ _trigger(event, data) {
1647
+ this.events.get(event)?.(data);
1648
+ }
1649
+ send(data) {
1650
+ return this.server.onMessage(JSON.stringify(data), this.conn);
1651
+ }
1652
+ };
1653
+ var MockLobby = class MockLobby2 {
1654
+ static {
1655
+ __name(this, "MockLobby");
1656
+ }
1657
+ server;
1658
+ constructor(server) {
1659
+ this.server = server;
1660
+ }
1661
+ socket() {
1662
+ return new MockPartyClient(this.server);
1663
+ }
1664
+ fetch(url, options) {
1665
+ return request(this.server, url, options);
1666
+ }
1667
+ };
1668
+ var MockContext = class MockContext2 {
1669
+ static {
1670
+ __name(this, "MockContext");
1671
+ }
1672
+ room;
1673
+ parties;
1674
+ constructor(room, options = {}) {
1675
+ this.room = room;
1676
+ this.parties = {
1677
+ main: /* @__PURE__ */ new Map()
1678
+ };
1679
+ const parties = options.parties || {};
1680
+ for (let lobbyId in parties) {
1681
+ this.parties.main.set(lobbyId, new MockLobby(parties[lobbyId](room)));
1682
+ }
1683
+ }
1684
+ };
1685
+ var MockPartyRoom = class MockPartyRoom2 {
1686
+ static {
1687
+ __name(this, "MockPartyRoom");
1688
+ }
1689
+ id;
1690
+ clients;
1691
+ storage;
1692
+ context;
1693
+ env;
1694
+ constructor(id2, options = {}) {
1695
+ this.id = id2;
1696
+ this.clients = /* @__PURE__ */ new Map();
1697
+ this.storage = new Storage();
1698
+ this.env = {};
1699
+ this.id = id2 || generateShortUUID();
1700
+ this.context = new MockContext(this, {
1701
+ parties: options.parties || {}
1702
+ });
1703
+ this.env = options.env || {};
1704
+ }
1705
+ async connection(server, id2) {
1706
+ const socket = new MockPartyClient(server, id2);
1707
+ const url = new URL("http://localhost");
1708
+ const request2 = new Request(url.toString(), {
1709
+ method: "GET",
1710
+ headers: {
1711
+ "Content-Type": "application/json"
1712
+ }
1713
+ });
1714
+ await server.onConnect(socket.conn, {
1715
+ request: request2
1716
+ });
1717
+ this.clients.set(socket.id, socket);
1718
+ return socket;
1719
+ }
1720
+ broadcast(data) {
1721
+ this.clients.forEach((client) => {
1722
+ client._trigger("message", data);
1723
+ });
1724
+ }
1725
+ getConnection(id2) {
1726
+ return this.clients.get(id2);
1727
+ }
1728
+ getConnections() {
1729
+ return Array.from(this.clients.values()).map((client) => client.conn);
1730
+ }
1731
+ clear() {
1732
+ this.clients.clear();
1733
+ }
1734
+ };
1735
+ var MockConnection = class {
1736
+ static {
1737
+ __name(this, "MockConnection");
1738
+ }
1739
+ client;
1740
+ server;
1741
+ id;
1742
+ constructor(client) {
1743
+ this.client = client;
1744
+ this.state = {};
1745
+ this.server = client.server;
1746
+ this.id = client.id;
1747
+ }
1748
+ state;
1749
+ setState(value) {
1750
+ this.state = value;
1751
+ }
1752
+ send(data) {
1753
+ this.client._trigger("message", data);
1754
+ }
1755
+ close() {
1756
+ this.server.onClose(this);
1757
+ }
1758
+ };
1759
+ var ServerIo = MockPartyRoom;
1760
+ var ClientIo = MockPartyClient;
1761
+
1679
1762
  // src/world.ts
1680
1763
  import { signal } from "@signe/reactive";
1681
1764
  import { sync, id, persist } from "@signe/sync";