@signe/room 2.1.0 → 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";
@@ -831,9 +697,54 @@ var Server = class {
831
697
  return null;
832
698
  }
833
699
  getUsersPropName(subRoom) {
834
- const meta = subRoom.constructor["_propertyMetadata"];
835
- 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];
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;
836
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
+ */
837
748
  async getSession(privateId) {
838
749
  if (!privateId) return null;
839
750
  try {
@@ -860,6 +771,17 @@ var Server = class {
860
771
  });
861
772
  }
862
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
+ */
863
785
  async deleteSession(privateId) {
864
786
  await this.room.storage.delete(`session:${privateId}`);
865
787
  }
@@ -895,6 +817,8 @@ var Server = class {
895
817
  signal2()[publicId] = user;
896
818
  const snapshot = createStatesSnapshot(user);
897
819
  this.room.storage.put(`${usersPropName}.${publicId}`, snapshot);
820
+ } else {
821
+ user = signal2()[existingSession.publicId];
898
822
  }
899
823
  if (!existingSession) {
900
824
  await this.saveSession(conn.id, {
@@ -904,6 +828,7 @@ var Server = class {
904
828
  await this.updateSessionConnection(conn.id, true);
905
829
  }
906
830
  }
831
+ this.updateUserConnectionStatus(user, true);
907
832
  await awaitReturn(subRoom["onJoin"]?.(user, conn, ctx));
908
833
  conn.setState({
909
834
  ...conn.state,
@@ -1059,17 +984,14 @@ var Server = class {
1059
984
  * @returns {Promise<void>}
1060
985
  */
1061
986
  async handleShardClientConnect(message, shardConnection) {
1062
- const { privateId, connectionInfo } = message;
987
+ const { privateId, requestInfo } = message;
1063
988
  const shardState = shardConnection.state;
1064
989
  const virtualContext = {
1065
- request: {
1066
- headers: new Headers({
1067
- "x-forwarded-for": connectionInfo.ip,
1068
- "user-agent": connectionInfo.userAgent
1069
- }),
1070
- method: "GET",
1071
- url: ""
1072
- }
990
+ request: requestInfo ? {
991
+ headers: new Headers(requestInfo.headers),
992
+ method: requestInfo.method,
993
+ url: requestInfo.url
994
+ } : void 0
1073
995
  };
1074
996
  const virtualConnection = {
1075
997
  id: privateId,
@@ -1211,14 +1133,17 @@ var Server = class {
1211
1133
  const { publicId } = conn.state;
1212
1134
  const user = signal2?.()[publicId];
1213
1135
  if (!user) return;
1214
- await awaitReturn(subRoom["onLeave"]?.(user, conn));
1215
1136
  await this.updateSessionConnection(privateId, false);
1216
- this.broadcast({
1217
- type: "user_disconnected",
1218
- value: {
1219
- publicId
1220
- }
1221
- }, 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
+ }
1222
1147
  }
1223
1148
  async onAlarm() {
1224
1149
  const subRoom = await this.getSubRoom();
@@ -1517,13 +1442,21 @@ var Shard = class {
1517
1442
  }
1518
1443
  onConnect(conn, ctx) {
1519
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;
1520
1456
  this.ws.send(JSON.stringify({
1521
1457
  type: "shard.clientConnected",
1522
1458
  privateId: conn.id,
1523
- connectionInfo: {
1524
- ip: ctx.request?.headers.get("x-forwarded-for") || "unknown",
1525
- userAgent: ctx.request?.headers.get("user-agent") || "unknown"
1526
- }
1459
+ requestInfo
1527
1460
  }));
1528
1461
  this.updateWorldStats();
1529
1462
  }
@@ -1662,6 +1595,9 @@ async function testRoom(Room3, options = {}) {
1662
1595
  const shardServer = new Shard(io);
1663
1596
  shardServer.subRoom = null;
1664
1597
  server = shardServer;
1598
+ for (const lobby of io.context.parties.main.values()) {
1599
+ await lobby.server.onStart();
1600
+ }
1665
1601
  } else {
1666
1602
  server = await createServer(io);
1667
1603
  }
@@ -1669,8 +1605,8 @@ async function testRoom(Room3, options = {}) {
1669
1605
  return {
1670
1606
  server,
1671
1607
  room: server.subRoom,
1672
- createClient: /* @__PURE__ */ __name(async () => {
1673
- const client = await io.connection(server);
1608
+ createClient: /* @__PURE__ */ __name(async (id2) => {
1609
+ const client = await io.connection(server, id2);
1674
1610
  return client;
1675
1611
  }, "createClient")
1676
1612
  };
@@ -1686,6 +1622,143 @@ async function request(room, path, options = {
1686
1622
  }
1687
1623
  __name(request, "request");
1688
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
+
1689
1762
  // src/world.ts
1690
1763
  import { signal } from "@signe/reactive";
1691
1764
  import { sync, id, persist } from "@signe/sync";