@signe/room 1.0.1 → 1.0.3

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,3 +1,6 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
3
+
1
4
  // src/decorators.ts
2
5
  function Action(name, bodyValidation) {
3
6
  return function(target, propertyKey) {
@@ -10,6 +13,7 @@ function Action(name, bodyValidation) {
10
13
  });
11
14
  };
12
15
  }
16
+ __name(Action, "Action");
13
17
  function Room(options) {
14
18
  return function(target) {
15
19
  target.path = options.path;
@@ -18,12 +22,14 @@ function Room(options) {
18
22
  target.throttleSync = options.throttleSync;
19
23
  };
20
24
  }
25
+ __name(Room, "Room");
21
26
 
22
27
  // ../sync/src/utils.ts
23
28
  function isClass(obj) {
24
29
  return typeof obj === "function" && obj.prototype && obj.prototype.constructor === obj;
25
30
  }
26
- var isObject = (item) => item && typeof item === "object" && !Array.isArray(item) && item !== null;
31
+ __name(isClass, "isClass");
32
+ var isObject = /* @__PURE__ */ __name((item) => item && typeof item === "object" && !Array.isArray(item) && item !== null, "isObject");
27
33
  function generateShortUUID() {
28
34
  const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
29
35
  let uuid = "";
@@ -33,13 +39,15 @@ function generateShortUUID() {
33
39
  }
34
40
  return uuid;
35
41
  }
42
+ __name(generateShortUUID, "generateShortUUID");
36
43
 
37
44
  // src/mock.ts
38
- var MockPartySocket = class {
39
- constructor() {
40
- this.events = /* @__PURE__ */ new Map();
41
- this.id = generateShortUUID();
45
+ var MockPartySocket = class MockPartySocket2 {
46
+ static {
47
+ __name(this, "MockPartySocket");
42
48
  }
49
+ events = /* @__PURE__ */ new Map();
50
+ id = generateShortUUID();
43
51
  addEventListener(event, cb) {
44
52
  this.events.set(event, cb);
45
53
  }
@@ -50,10 +58,11 @@ var MockPartySocket = class {
50
58
  this.events.get(event)?.(data);
51
59
  }
52
60
  };
53
- var MockStorage = class {
54
- constructor() {
55
- this.storage = /* @__PURE__ */ new Map();
61
+ var MockStorage = class MockStorage2 {
62
+ static {
63
+ __name(this, "MockStorage");
56
64
  }
65
+ storage = /* @__PURE__ */ new Map();
57
66
  async get(key) {
58
67
  return this.storage.get(key);
59
68
  }
@@ -64,7 +73,13 @@ var MockStorage = class {
64
73
  return this.storage;
65
74
  }
66
75
  };
67
- var MockPartyRoom = class {
76
+ var MockPartyRoom = class MockPartyRoom2 {
77
+ static {
78
+ __name(this, "MockPartyRoom");
79
+ }
80
+ id;
81
+ clients;
82
+ storage;
68
83
  constructor(id) {
69
84
  this.id = id;
70
85
  this.clients = /* @__PURE__ */ new Map();
@@ -86,9 +101,10 @@ var MockPartyRoom = class {
86
101
  }
87
102
  };
88
103
  var MockConnection = class {
89
- constructor() {
90
- this.state = {};
104
+ static {
105
+ __name(this, "MockConnection");
91
106
  }
107
+ state = {};
92
108
  setState(value) {
93
109
  this.state = value;
94
110
  }
@@ -101,33 +117,29 @@ import { dset as dset2 } from "dset";
101
117
  import z from "zod";
102
118
 
103
119
  // ../sync/src/core.ts
104
- import {
105
- ArraySubject,
106
- ObjectSubject,
107
- isSignal
108
- } from "@signe/reactive";
109
- var syncClass = (instance, options = {}) => {
120
+ import { ArraySubject, ObjectSubject, isSignal } from "@signe/reactive";
121
+ var syncClass = /* @__PURE__ */ __name((instance, options = {}) => {
110
122
  const cacheSync = /* @__PURE__ */ new Map();
111
123
  const cachePersist = /* @__PURE__ */ new Set();
112
124
  instance.$valuesChanges = {
113
- set: (path, value) => {
125
+ set: /* @__PURE__ */ __name((path, value) => {
114
126
  cacheSync.set(path, value);
115
127
  options.onSync?.(cacheSync);
116
- },
117
- setPersist: (path) => {
128
+ }, "set"),
129
+ setPersist: /* @__PURE__ */ __name((path) => {
118
130
  if (path == "") path = ".";
119
131
  cachePersist.add(path);
120
132
  options.onPersist?.(cachePersist);
121
- },
122
- has: (path) => {
133
+ }, "setPersist"),
134
+ has: /* @__PURE__ */ __name((path) => {
123
135
  return cacheSync.has(path);
124
- },
125
- get: (path) => {
136
+ }, "has"),
137
+ get: /* @__PURE__ */ __name((path) => {
126
138
  return cacheSync.get(path);
127
- }
139
+ }, "get")
128
140
  };
129
141
  createSyncClass(instance);
130
- };
142
+ }, "syncClass");
131
143
  function createStatesSnapshot(instance) {
132
144
  let persistObject = {};
133
145
  if (instance.$snapshot) {
@@ -145,6 +157,7 @@ function createStatesSnapshot(instance) {
145
157
  }
146
158
  return persistObject;
147
159
  }
160
+ __name(createStatesSnapshot, "createStatesSnapshot");
148
161
  function setMetadata(target, key, value) {
149
162
  const meta = target.constructor._propertyMetadata;
150
163
  const propId = meta?.get(key);
@@ -156,7 +169,8 @@ function setMetadata(target, key, value) {
156
169
  }
157
170
  }
158
171
  }
159
- var createSyncClass = (currentClass, parentKey = null, parentClass = null, path = "") => {
172
+ __name(setMetadata, "setMetadata");
173
+ var createSyncClass = /* @__PURE__ */ __name((currentClass, parentKey = null, parentClass = null, path = "") => {
160
174
  currentClass.$path = path;
161
175
  if (parentClass) {
162
176
  currentClass.$valuesChanges = parentClass.$valuesChanges;
@@ -171,7 +185,9 @@ var createSyncClass = (currentClass, parentKey = null, parentClass = null, path
171
185
  const persist = signal.options.persist ?? true;
172
186
  let value = signal();
173
187
  if (isObject(value) || Array.isArray(value)) {
174
- value = { ...value };
188
+ value = {
189
+ ...value
190
+ };
175
191
  }
176
192
  const newPath = (path ? path + "." : "") + key;
177
193
  if (syncToClient) {
@@ -182,7 +198,7 @@ var createSyncClass = (currentClass, parentKey = null, parentClass = null, path
182
198
  }
183
199
  }
184
200
  }
185
- };
201
+ }, "createSyncClass");
186
202
 
187
203
  // ../sync/src/load.ts
188
204
  import { isSignal as isSignal2 } from "@signe/reactive";
@@ -193,16 +209,19 @@ function load(rootInstance, values, valueIsObject) {
193
209
  loadFromPaths(rootInstance, values);
194
210
  }
195
211
  }
212
+ __name(load, "load");
196
213
  function loadFromPaths(rootInstance, values) {
197
214
  for (const [path, value] of Object.entries(values)) {
198
215
  const parts = path.split(".");
199
216
  loadValue(rootInstance, parts, value);
200
217
  }
201
218
  }
219
+ __name(loadFromPaths, "loadFromPaths");
202
220
  function loadFromObject(rootInstance, values, currentPath = "") {
203
- for (const [key, value] of Object.entries(values)) {
221
+ for (let key in values) {
222
+ const value = values[key];
204
223
  const newPath = currentPath ? `${currentPath}.${key}` : key;
205
- if (typeof value === "object" && !Array.isArray(value)) {
224
+ if (typeof value === "object" && !Array.isArray(value) && value !== null) {
206
225
  loadFromObject(rootInstance, value, newPath);
207
226
  } else {
208
227
  const parts = newPath.split(".");
@@ -210,6 +229,7 @@ function loadFromObject(rootInstance, values, currentPath = "") {
210
229
  }
211
230
  }
212
231
  }
232
+ __name(loadFromObject, "loadFromObject");
213
233
  function loadValue(rootInstance, parts, value) {
214
234
  let current = rootInstance;
215
235
  for (let i = 0; i < parts.length; i++) {
@@ -229,10 +249,7 @@ function loadValue(rootInstance, parts, value) {
229
249
  }
230
250
  const currentValue = current[part];
231
251
  if (currentValue === void 0) {
232
- const parentInstance = getByPath(
233
- rootInstance,
234
- parts.slice(0, i).join(".")
235
- );
252
+ const parentInstance = getByPath(rootInstance, parts.slice(0, i).join("."));
236
253
  const classType = parentInstance?.options?.classType;
237
254
  if (classType) {
238
255
  current[part] = !isClass(classType) ? classType(part) : new classType();
@@ -245,6 +262,7 @@ function loadValue(rootInstance, parts, value) {
245
262
  }
246
263
  }
247
264
  }
265
+ __name(loadValue, "loadValue");
248
266
  function getByPath(root, path) {
249
267
  const parts = path.split(".");
250
268
  let current = root;
@@ -260,18 +278,22 @@ function getByPath(root, path) {
260
278
  }
261
279
  return current;
262
280
  }
281
+ __name(getByPath, "getByPath");
263
282
 
264
283
  // src/utils.ts
265
284
  import { dset } from "dset";
266
285
  function isPromise(value) {
267
286
  return value instanceof Promise;
268
287
  }
288
+ __name(isPromise, "isPromise");
269
289
  async function awaitReturn(val) {
270
290
  return isPromise(val) ? await val : val;
271
291
  }
292
+ __name(awaitReturn, "awaitReturn");
272
293
  function isClass2(obj) {
273
294
  return typeof obj === "function" && obj.prototype && obj.prototype.constructor === obj;
274
295
  }
296
+ __name(isClass2, "isClass");
275
297
  function throttle(func, wait) {
276
298
  let timeout = null;
277
299
  let lastArgs = null;
@@ -290,6 +312,7 @@ function throttle(func, wait) {
290
312
  }
291
313
  };
292
314
  }
315
+ __name(throttle, "throttle");
293
316
  function extractParams(pattern, str) {
294
317
  const regexPattern = pattern.replace(/{(\w+)}/g, "(?<$1>[\\w-]+)");
295
318
  const regex = new RegExp(`^${regexPattern}$`);
@@ -302,6 +325,7 @@ function extractParams(pattern, str) {
302
325
  return null;
303
326
  }
304
327
  }
328
+ __name(extractParams, "extractParams");
305
329
  function dremove(obj, keys) {
306
330
  if (typeof keys === "string") {
307
331
  keys = keys.split(".");
@@ -321,6 +345,7 @@ function dremove(obj, keys) {
321
345
  delete t[k];
322
346
  }
323
347
  }
348
+ __name(dremove, "dremove");
324
349
  function buildObject(valuesMap, allMemory) {
325
350
  let memoryObj = {};
326
351
  for (let path of valuesMap.keys()) {
@@ -334,6 +359,7 @@ function buildObject(valuesMap, allMemory) {
334
359
  }
335
360
  return memoryObj;
336
361
  }
362
+ __name(buildObject, "buildObject");
337
363
 
338
364
  // src/server.ts
339
365
  var Message = z.object({
@@ -341,70 +367,76 @@ var Message = z.object({
341
367
  value: z.any()
342
368
  });
343
369
  var Server = class {
370
+ static {
371
+ __name(this, "Server");
372
+ }
373
+ room;
374
+ subRoom;
375
+ rooms;
344
376
  /**
345
- * @constructor
346
- * @param {Party.Room} room - The room object representing the current game or application instance.
347
- *
348
- * @example
349
- * ```typescript
350
- * const server = new MyServer(new ServerIo("game"));
351
- * ```
352
- */
377
+ * @constructor
378
+ * @param {Party.Room} room - The room object representing the current game or application instance.
379
+ *
380
+ * @example
381
+ * ```typescript
382
+ * const server = new MyServer(new ServerIo("game"));
383
+ * ```
384
+ */
353
385
  constructor(room) {
354
386
  this.room = room;
355
387
  this.subRoom = null;
356
388
  this.rooms = [];
357
389
  }
358
390
  /**
359
- * @readonly
360
- * @property {boolean} isHibernate - Indicates whether the server is in hibernate mode.
361
- *
362
- * @example
363
- * ```typescript
364
- * if (!server.isHibernate) {
365
- * console.log("Server is active");
366
- * }
367
- * ```
368
- */
391
+ * @readonly
392
+ * @property {boolean} isHibernate - Indicates whether the server is in hibernate mode.
393
+ *
394
+ * @example
395
+ * ```typescript
396
+ * if (!server.isHibernate) {
397
+ * console.log("Server is active");
398
+ * }
399
+ * ```
400
+ */
369
401
  get isHibernate() {
370
402
  return !!this["options"]?.hibernate;
371
403
  }
372
404
  /**
373
- * @method onStart
374
- * @async
375
- * @description Initializes the server and creates the initial room if not in hibernate mode.
376
- * @returns {Promise<void>}
377
- *
378
- * @example
379
- * ```typescript
380
- * async function initServer() {
381
- * await server.onStart();
382
- * console.log("Server started");
383
- * }
384
- * ```
385
- */
405
+ * @method onStart
406
+ * @async
407
+ * @description Initializes the server and creates the initial room if not in hibernate mode.
408
+ * @returns {Promise<void>}
409
+ *
410
+ * @example
411
+ * ```typescript
412
+ * async function initServer() {
413
+ * await server.onStart();
414
+ * console.log("Server started");
415
+ * }
416
+ * ```
417
+ */
386
418
  async onStart() {
387
419
  if (!this.isHibernate) {
388
420
  this.subRoom = await this.createRoom();
389
421
  }
390
422
  }
391
423
  /**
392
- * @method createRoom
393
- * @private
394
- * @async
395
- * @param {CreateRoomOptions} [options={}] - Options for creating the room.
396
- * @returns {Promise<Object>} The created room instance.
397
- * @throws {Error} If no matching room is found.
398
- *
399
- * @example
400
- * ```typescript
401
- * // This method is private and called internally
402
- * async function internalCreateRoom() {
403
- * const room = await this.createRoom({ getMemoryAll: true });
404
- * console.log("Room created:", room);
405
- * }
406
- * ```
407
- */
424
+ * @method createRoom
425
+ * @private
426
+ * @async
427
+ * @param {CreateRoomOptions} [options={}] - Options for creating the room.
428
+ * @returns {Promise<Object>} The created room instance.
429
+ * @throws {Error} If no matching room is found.
430
+ *
431
+ * @example
432
+ * ```typescript
433
+ * // This method is private and called internally
434
+ * async function internalCreateRoom() {
435
+ * const room = await this.createRoom({ getMemoryAll: true });
436
+ * console.log("Room created:", room);
437
+ * }
438
+ * ```
439
+ */
408
440
  async createRoom(options = {}) {
409
441
  let instance;
410
442
  let init = true;
@@ -418,7 +450,7 @@ var Server = class {
418
450
  if (!instance) {
419
451
  throw new Error("Room not found");
420
452
  }
421
- const loadMemory = async () => {
453
+ const loadMemory = /* @__PURE__ */ __name(async () => {
422
454
  const root = await this.room.storage.get(".");
423
455
  const memory = await this.room.storage.list();
424
456
  const tmpObject = root || {};
@@ -429,10 +461,10 @@ var Server = class {
429
461
  dset2(tmpObject, key, value);
430
462
  }
431
463
  load(instance, tmpObject);
432
- };
464
+ }, "loadMemory");
433
465
  await loadMemory();
434
466
  instance.$memoryAll = {};
435
- const syncCb = (values) => {
467
+ const syncCb = /* @__PURE__ */ __name((values) => {
436
468
  if (options.getMemoryAll) {
437
469
  buildObject(values, instance.$memoryAll);
438
470
  }
@@ -441,22 +473,20 @@ var Server = class {
441
473
  return;
442
474
  }
443
475
  const packet = buildObject(values, instance.$memoryAll);
444
- this.room.broadcast(
445
- JSON.stringify({
446
- type: "sync",
447
- value: packet
448
- })
449
- );
476
+ this.room.broadcast(JSON.stringify({
477
+ type: "sync",
478
+ value: packet
479
+ }));
450
480
  values.clear();
451
- };
452
- const persistCb = async (values) => {
481
+ }, "syncCb");
482
+ const persistCb = /* @__PURE__ */ __name(async (values) => {
453
483
  for (let path of values) {
454
484
  const _instance = path == "." ? instance : getByPath(instance, path);
455
485
  const itemValue = createStatesSnapshot(_instance);
456
486
  await this.room.storage.put(path, itemValue);
457
487
  }
458
488
  values.clear();
459
- };
489
+ }, "persistCb");
460
490
  syncClass(instance, {
461
491
  onSync: throttle(syncCb, instance["throttleSync"] ?? 500),
462
492
  onPersist: throttle(persistCb, instance["throttleStorage"] ?? 2e3)
@@ -464,21 +494,21 @@ var Server = class {
464
494
  return instance;
465
495
  }
466
496
  /**
467
- * @method getSubRoom
468
- * @private
469
- * @async
470
- * @param {Object} [options={}] - Options for getting the sub-room.
471
- * @returns {Promise<Object>} The sub-room instance.
472
- *
473
- * @example
474
- * ```typescript
475
- * // This method is private and called internally
476
- * async function internalGetSubRoom() {
477
- * const subRoom = await this.getSubRoom();
478
- * console.log("Sub-room retrieved:", subRoom);
479
- * }
480
- * ```
481
- */
497
+ * @method getSubRoom
498
+ * @private
499
+ * @async
500
+ * @param {Object} [options={}] - Options for getting the sub-room.
501
+ * @returns {Promise<Object>} The sub-room instance.
502
+ *
503
+ * @example
504
+ * ```typescript
505
+ * // This method is private and called internally
506
+ * async function internalGetSubRoom() {
507
+ * const subRoom = await this.getSubRoom();
508
+ * console.log("Sub-room retrieved:", subRoom);
509
+ * }
510
+ * ```
511
+ */
482
512
  async getSubRoom(options = {}) {
483
513
  let subRoom;
484
514
  if (this.isHibernate) {
@@ -489,20 +519,20 @@ var Server = class {
489
519
  return subRoom;
490
520
  }
491
521
  /**
492
- * @method getUsersProperty
493
- * @private
494
- * @param {Object} subRoom - The sub-room instance.
495
- * @returns {Object|null} The users property of the sub-room, or null if not found.
496
- *
497
- * @example
498
- * ```typescript
499
- * // This method is private and called internally
500
- * function internalGetUsers(subRoom) {
501
- * const users = this.getUsersProperty(subRoom);
502
- * console.log("Users:", users);
503
- * }
504
- * ```
505
- */
522
+ * @method getUsersProperty
523
+ * @private
524
+ * @param {Object} subRoom - The sub-room instance.
525
+ * @returns {Object|null} The users property of the sub-room, or null if not found.
526
+ *
527
+ * @example
528
+ * ```typescript
529
+ * // This method is private and called internally
530
+ * function internalGetUsers(subRoom) {
531
+ * const users = this.getUsersProperty(subRoom);
532
+ * console.log("Users:", users);
533
+ * }
534
+ * ```
535
+ */
506
536
  getUsersProperty(subRoom) {
507
537
  const meta = subRoom.constructor["_propertyMetadata"];
508
538
  const propId = meta?.get("users");
@@ -512,21 +542,21 @@ var Server = class {
512
542
  return null;
513
543
  }
514
544
  /**
515
- * @method onConnect
516
- * @async
517
- * @param {Party.Connection} conn - The connection object for the new user.
518
- * @param {Party.ConnectionContext} ctx - The context of the connection.
519
- * @description Handles a new user connection, creates a user object, and sends initial sync data.
520
- * @returns {Promise<void>}
521
- *
522
- * @example
523
- * ```typescript
524
- * server.onConnect = async (conn, ctx) => {
525
- * await server.onConnect(conn, ctx);
526
- * console.log("New user connected:", conn.id);
527
- * };
528
- * ```
529
- */
545
+ * @method onConnect
546
+ * @async
547
+ * @param {Party.Connection} conn - The connection object for the new user.
548
+ * @param {Party.ConnectionContext} ctx - The context of the connection.
549
+ * @description Handles a new user connection, creates a user object, and sends initial sync data.
550
+ * @returns {Promise<void>}
551
+ *
552
+ * @example
553
+ * ```typescript
554
+ * server.onConnect = async (conn, ctx) => {
555
+ * await server.onConnect(conn, ctx);
556
+ * console.log("New user connected:", conn.id);
557
+ * };
558
+ * ```
559
+ */
530
560
  async onConnect(conn, ctx) {
531
561
  const subRoom = await this.getSubRoom({
532
562
  getMemoryAll: true
@@ -540,33 +570,33 @@ var Server = class {
540
570
  signal()[publicId] = user;
541
571
  }
542
572
  await awaitReturn(subRoom["onJoin"]?.(user, conn, ctx));
543
- conn.setState({ publicId });
544
- conn.send(
545
- JSON.stringify({
546
- type: "sync",
547
- value: {
548
- pId: publicId,
549
- ...subRoom.$memoryAll
550
- }
551
- })
552
- );
573
+ conn.setState({
574
+ publicId
575
+ });
576
+ conn.send(JSON.stringify({
577
+ type: "sync",
578
+ value: {
579
+ pId: publicId,
580
+ ...subRoom.$memoryAll
581
+ }
582
+ }));
553
583
  }
554
584
  /**
555
- * @method onMessage
556
- * @async
557
- * @param {string} message - The message received from a user.
558
- * @param {Party.Connection} sender - The connection object of the sender.
559
- * @description Processes incoming messages and triggers corresponding actions in the sub-room.
560
- * @returns {Promise<void>}
561
- *
562
- * @example
563
- * ```typescript
564
- * server.onMessage = async (message, sender) => {
565
- * await server.onMessage(message, sender);
566
- * console.log("Message processed from:", sender.id);
567
- * };
568
- * ```
569
- */
585
+ * @method onMessage
586
+ * @async
587
+ * @param {string} message - The message received from a user.
588
+ * @param {Party.Connection} sender - The connection object of the sender.
589
+ * @description Processes incoming messages and triggers corresponding actions in the sub-room.
590
+ * @returns {Promise<void>}
591
+ *
592
+ * @example
593
+ * ```typescript
594
+ * server.onMessage = async (message, sender) => {
595
+ * await server.onMessage(message, sender);
596
+ * console.log("Message processed from:", sender.id);
597
+ * };
598
+ * ```
599
+ */
570
600
  async onMessage(message, sender) {
571
601
  let json;
572
602
  try {
@@ -587,34 +617,30 @@ var Server = class {
587
617
  const actionName = actions.get(result.data.action);
588
618
  if (actionName) {
589
619
  if (actionName.bodyValidation) {
590
- const bodyResult = actionName.bodyValidation.safeParse(
591
- result.data.value
592
- );
620
+ const bodyResult = actionName.bodyValidation.safeParse(result.data.value);
593
621
  if (!bodyResult.success) {
594
622
  return;
595
623
  }
596
624
  }
597
- await awaitReturn(
598
- subRoom[actionName.key](user, result.data.value, sender)
599
- );
625
+ await awaitReturn(subRoom[actionName.key](user, result.data.value, sender));
600
626
  }
601
627
  }
602
628
  }
603
629
  /**
604
- * @method onClose
605
- * @async
606
- * @param {Party.Connection} conn - The connection object of the disconnecting user.
607
- * @description Handles user disconnection, removing them from the room and triggering the onLeave event.
608
- * @returns {Promise<void>}
609
- *
610
- * @example
611
- * ```typescript
612
- * server.onClose = async (conn) => {
613
- * await server.onClose(conn);
614
- * console.log("User disconnected:", conn.id);
615
- * };
616
- * ```
617
- */
630
+ * @method onClose
631
+ * @async
632
+ * @param {Party.Connection} conn - The connection object of the disconnecting user.
633
+ * @description Handles user disconnection, removing them from the room and triggering the onLeave event.
634
+ * @returns {Promise<void>}
635
+ *
636
+ * @example
637
+ * ```typescript
638
+ * server.onClose = async (conn) => {
639
+ * await server.onClose(conn);
640
+ * console.log("User disconnected:", conn.id);
641
+ * };
642
+ * ```
643
+ */
618
644
  async onClose(conn) {
619
645
  const subRoom = await this.getSubRoom();
620
646
  const signal = this.getUsersProperty(subRoom);
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/decorators.ts","../../sync/src/utils.ts","../src/mock.ts","../src/server.ts","../../sync/src/core.ts","../../sync/src/load.ts","../src/utils.ts"],"sourcesContent":["export function Action(name: string, bodyValidation?) {\n return function (target: any, propertyKey: string) {\n if (!target.constructor._actionMetadata) {\n target.constructor._actionMetadata = new Map();\n }\n target.constructor._actionMetadata.set(name, {\n key: propertyKey,\n bodyValidation,\n });\n };\n}\n\nexport interface RoomOptions {\n path: string;\n maxUsers?: number;\n throttleStorage?: number;\n throttleSync?: number;\n hibernate?: boolean;\n}\n\nexport function Room(options: RoomOptions) {\n return function (target: any) {\n target.path = options.path;\n target.maxUsers = options.maxUsers;\n target.throttleStorage = options.throttleStorage;\n target.throttleSync = options.throttleSync;\n };\n}\n","/**\n * Checks if the given value is a function.\n *\n * @param {unknown} val - The value to check.\n * @returns {boolean} - True if the value is a function, false otherwise.\n * @example\n * isFunction(function() {}); // true\n * isFunction(() => {}); // true\n * isFunction(123); // false\n */\nexport function isFunction(val: unknown): boolean {\n return {}.toString.call(val) === \"[object Function]\";\n}\n\n/**\n * Checks if the given object is a class.\n *\n * @param {any} obj - The object to check.\n * @returns {boolean} - True if the object is a class, false otherwise.\n * @example\n * class MyClass {}\n * isClass(MyClass); // true\n * isClass(() => {}); // false\n */\nexport function isClass(obj: any): boolean {\n return (\n typeof obj === \"function\" &&\n obj.prototype &&\n obj.prototype.constructor === obj\n );\n}\n\n/**\n * Checks if the given item is an object.\n *\n * @param {any} item - The item to check.\n * @returns {boolean} - True if the item is an object, false otherwise.\n * @example\n * isObject({}); // true\n * isObject(null); // false\n * isObject([]); // false\n */\nexport const isObject = (item: any): boolean =>\n item && typeof item === \"object\" && !Array.isArray(item) && item !== null;\n\n/**\n * Checks if the given value is an instance of a class.\n *\n * @param {unknown} value - The value to check.\n * @returns {boolean} - True if the value is an instance of a class, false otherwise.\n * @example\n * class MyClass {}\n * const instance = new MyClass();\n * isInstanceOfClass(instance); // true\n * isInstanceOfClass({}); // false\n */\nexport function isInstanceOfClass(value: unknown): boolean {\n if (\n value === null ||\n typeof value !== \"object\" ||\n value === undefined ||\n Array.isArray(value)\n ) {\n return false;\n }\n return Object.getPrototypeOf(value) !== Object.prototype;\n}\n\nexport function generateShortUUID(): string {\n const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';\n let uuid = '';\n for (let i = 0; i < 8; i++) {\n const randomIndex = Math.floor(Math.random() * chars.length);\n uuid += chars[randomIndex];\n }\n return uuid;\n}","import { generateShortUUID } from \"../../sync/src/utils\";\n\nclass MockPartySocket {\n private events: Map<string, Function> = new Map();\n id = generateShortUUID()\n \n addEventListener(event, cb) {\n this.events.set(event, cb);\n }\n\n removeEventListener(event, cb) {\n this.events.delete(event);\n }\n\n _trigger(event, data) {\n this.events.get(event)?.(data);\n }\n}\n\nclass MockStorage {\n private storage: Map<string, any> = new Map();\n \n async get(key: string) {\n return this.storage.get(key);\n }\n \n async put(key: string, value: any) {\n this.storage.set(key, value);\n }\n \n async list() {\n return this.storage\n }\n}\n\nclass MockPartyRoom {\n private clients: Map<string, MockPartySocket> = new Map();\n storage = new MockStorage();\n\n constructor(public id?: string) {\n this.id = id || generateShortUUID()\n }\n\n connection(client) {\n const socket = new MockPartySocket();\n this.clients.set(socket.id, client);\n client.id = socket.id;\n }\n\n broadcast(data: any) {\n this.clients.forEach((client) => {\n client._trigger('message', data);\n });\n }\n\n clear() {\n this.clients.clear();\n }\n}\n\nexport class MockConnection {\n state: any = {};\n\n setState(value: any) {\n this.state = value;\n }\n}\n\nexport const ServerIo = MockPartyRoom;\nexport const ClientIo = MockPartySocket;\n","import { dset } from \"dset\";\nimport z from \"zod\";\nimport {\n createStatesSnapshot,\n getByPath,\n load,\n syncClass,\n} from \"../../sync/src\";\nimport { generateShortUUID } from \"../../sync/src/utils\";\nimport type * as Party from \"./types/party\";\nimport {\n awaitReturn,\n buildObject,\n extractParams,\n isClass,\n throttle,\n} from \"./utils\";\n\nconst Message = z.object({\n action: z.string(),\n value: z.any(),\n});\n\ntype CreateRoomOptions = {\n getMemoryAll?: boolean;\n};\n\n/**\n * @class Server\n * @implements {Party.Server}\n * @description Represents a server that manages rooms and connections for a multiplayer game or application.\n * \n * @example\n * ```typescript\n * import { Room, Server, ServerIo } from \"@yourpackage/room\";\n * \n * @Room({ path: \"game\" })\n * class GameRoom {\n * // Room implementation\n * }\n * \n * class MyServer extends Server {\n * rooms = [GameRoom];\n * }\n * \n * const server = new MyServer(new ServerIo(\"game\"));\n * server.onStart();\n * ```\n */\nexport class Server implements Party.Server {\n subRoom = null;\n rooms: any[] = [];\n\n /**\n * @constructor\n * @param {Party.Room} room - The room object representing the current game or application instance.\n * \n * @example\n * ```typescript\n * const server = new MyServer(new ServerIo(\"game\"));\n * ```\n */\n constructor(readonly room: Party.Room) {}\n\n /**\n * @readonly\n * @property {boolean} isHibernate - Indicates whether the server is in hibernate mode.\n * \n * @example\n * ```typescript\n * if (!server.isHibernate) {\n * console.log(\"Server is active\");\n * }\n * ```\n */\n get isHibernate(): boolean {\n return !!this[\"options\"]?.hibernate;\n }\n\n /**\n * @method onStart\n * @async\n * @description Initializes the server and creates the initial room if not in hibernate mode.\n * @returns {Promise<void>}\n * \n * @example\n * ```typescript\n * async function initServer() {\n * await server.onStart();\n * console.log(\"Server started\");\n * }\n * ```\n */\n\n async onStart() {\n // Only create a room if not in hibernate mode\n // This prevents unnecessary resource allocation for inactive rooms\n if (!this.isHibernate) {\n this.subRoom = await this.createRoom();\n }\n }\n\n /**\n * @method createRoom\n * @private\n * @async\n * @param {CreateRoomOptions} [options={}] - Options for creating the room.\n * @returns {Promise<Object>} The created room instance.\n * @throws {Error} If no matching room is found.\n * \n * @example\n * ```typescript\n * // This method is private and called internally\n * async function internalCreateRoom() {\n * const room = await this.createRoom({ getMemoryAll: true });\n * console.log(\"Room created:\", room);\n * }\n * ```\n */\n private async createRoom(options: CreateRoomOptions = {}) {\n let instance\n let init = true\n\n // Find the appropriate room based on the current room ID\n for (let room of this.rooms) {\n const params = extractParams(room.path, this.room.id);\n if (params) {\n instance = new room(this.room, params);\n break;\n }\n }\n\n if (!instance) {\n throw new Error(\"Room not found\");\n }\n\n // Load the room's memory from storage\n // This ensures persistence across server restarts\n const loadMemory = async () => {\n const root = await this.room.storage.get(\".\");\n const memory = await this.room.storage.list();\n const tmpObject: any = root || {};\n for (let [key, value] of memory) {\n if (key == \".\") {\n continue;\n }\n dset(tmpObject, key, value);\n }\n load(instance, tmpObject);\n };\n\n await loadMemory();\n\n instance.$memoryAll = {}\n\n // Sync callback: Broadcast changes to all clients\n const syncCb = (values) => {\n if (options.getMemoryAll) {\n buildObject(values, instance.$memoryAll);\n }\n if (init && this.isHibernate) {\n init = false;\n return;\n }\n const packet = buildObject(values, instance.$memoryAll);\n this.room.broadcast(\n JSON.stringify({\n type: \"sync\",\n value: packet,\n })\n );\n values.clear();\n }\n\n // Persist callback: Save changes to storage\n const persistCb = async (values) => {\n for (let path of values) {\n const _instance =\n path == \".\" ? instance : getByPath(instance, path);\n const itemValue = createStatesSnapshot(_instance);\n await this.room.storage.put(path, itemValue);\n }\n values.clear();\n }\n\n // Set up syncing and persistence with throttling to optimize performance\n syncClass(instance, {\n onSync: throttle(syncCb, instance[\"throttleSync\"] ?? 500),\n onPersist: throttle(persistCb, instance[\"throttleStorage\"] ?? 2000),\n });\n\n return instance\n }\n\n /**\n * @method getSubRoom\n * @private\n * @async\n * @param {Object} [options={}] - Options for getting the sub-room.\n * @returns {Promise<Object>} The sub-room instance.\n * \n * @example\n * ```typescript\n * // This method is private and called internally\n * async function internalGetSubRoom() {\n * const subRoom = await this.getSubRoom();\n * console.log(\"Sub-room retrieved:\", subRoom);\n * }\n * ```\n */\n private async getSubRoom(options = {}) {\n let subRoom\n if (this.isHibernate) {\n subRoom = await this.createRoom(options)\n }\n else {\n subRoom = this.subRoom\n }\n return subRoom\n }\n\n /**\n * @method getUsersProperty\n * @private\n * @param {Object} subRoom - The sub-room instance.\n * @returns {Object|null} The users property of the sub-room, or null if not found.\n * \n * @example\n * ```typescript\n * // This method is private and called internally\n * function internalGetUsers(subRoom) {\n * const users = this.getUsersProperty(subRoom);\n * console.log(\"Users:\", users);\n * }\n * ```\n */\n\n private getUsersProperty(subRoom) {\n const meta = subRoom.constructor[\"_propertyMetadata\"];\n const propId = meta?.get(\"users\");\n if (propId) {\n return subRoom[propId];\n }\n return null;\n }\n\n /**\n * @method onConnect\n * @async\n * @param {Party.Connection} conn - The connection object for the new user.\n * @param {Party.ConnectionContext} ctx - The context of the connection.\n * @description Handles a new user connection, creates a user object, and sends initial sync data.\n * @returns {Promise<void>}\n * \n * @example\n * ```typescript\n * server.onConnect = async (conn, ctx) => {\n * await server.onConnect(conn, ctx);\n * console.log(\"New user connected:\", conn.id);\n * };\n * ```\n */\n async onConnect(conn: Party.Connection, ctx: Party.ConnectionContext) {\n const subRoom = await this.getSubRoom({\n getMemoryAll: true,\n })\n // Generate a unique public ID for the user\n const publicId = generateShortUUID()\n let user = null;\n const signal = this.getUsersProperty(subRoom);\n if (signal) {\n const { classType } = signal.options;\n // Create a new user instance based on the defined class type\n user = isClass(classType) ? new classType() : classType(conn, ctx);\n signal()[publicId] = user;\n }\n // Call the room's onJoin method if it exists\n await awaitReturn(subRoom[\"onJoin\"]?.(user, conn, ctx));\n conn.setState({ publicId });\n // Send initial sync data to the new connection\n conn.send(\n JSON.stringify({\n type: \"sync\",\n value: {\n pId: publicId,\n ...subRoom.$memoryAll,\n },\n })\n );\n }\n\n /**\n * @method onMessage\n * @async\n * @param {string} message - The message received from a user.\n * @param {Party.Connection} sender - The connection object of the sender.\n * @description Processes incoming messages and triggers corresponding actions in the sub-room.\n * @returns {Promise<void>}\n * \n * @example\n * ```typescript\n * server.onMessage = async (message, sender) => {\n * await server.onMessage(message, sender);\n * console.log(\"Message processed from:\", sender.id);\n * };\n * ```\n */\n\n async onMessage(message: string, sender: Party.Connection) {\n let json\n try {\n json = JSON.parse(message)\n }\n catch (e) {\n return;\n }\n // Validate incoming messages\n const result = Message.safeParse(json);\n if (!result.success) {\n return;\n }\n const subRoom = await this.getSubRoom()\n const actions = subRoom.constructor[\"_actionMetadata\"];\n if (actions) {\n const signal = this.getUsersProperty(subRoom);\n const { publicId } = sender.state as any;\n const user = signal?.()[publicId];\n const actionName = actions.get(result.data.action);\n if (actionName) {\n // Validate action body if a validation schema is defined\n if (actionName.bodyValidation) {\n const bodyResult = actionName.bodyValidation.safeParse(\n result.data.value\n );\n if (!bodyResult.success) {\n return;\n }\n }\n // Execute the action\n await awaitReturn(\n subRoom[actionName.key](user, result.data.value, sender)\n );\n }\n }\n }\n\n /**\n * @method onClose\n * @async\n * @param {Party.Connection} conn - The connection object of the disconnecting user.\n * @description Handles user disconnection, removing them from the room and triggering the onLeave event.\n * @returns {Promise<void>}\n * \n * @example\n * ```typescript\n * server.onClose = async (conn) => {\n * await server.onClose(conn);\n * console.log(\"User disconnected:\", conn.id);\n * };\n * ```\n */\n async onClose(conn: Party.Connection) {\n const subRoom = await this.getSubRoom()\n const signal = this.getUsersProperty(subRoom);\n const { publicId } = conn.state as any;\n const user = signal?.()[publicId];\n // Call the room's onLeave method if it exists\n await awaitReturn(subRoom[\"onLeave\"]?.(user, conn));\n if (signal) {\n // Remove the user from the room\n delete signal()[publicId];\n }\n }\n}\n","import {\n ArraySubject,\n ObjectSubject,\n isSignal,\n type WritableSignal,\n} from \"@signe/reactive\";\nimport { isInstanceOfClass, isObject } from \"./utils\";\n\ninterface SyncOptions {\n onSync?: (value: Map<string, any>) => void;\n onPersist?: (value: Set<string>) => void;\n}\n\ninterface TypeOptions {\n syncToClient?: boolean;\n persist?: boolean;\n classType?: any;\n}\n\n/**\n * Synchronizes an instance by adding `$valuesChanges` methods for state management.\n *\n * This function initializes a cache for syncing and persisting values. It adds methods to the instance\n * to set values, mark values for persistence, and check and retrieve values from the cache.\n * Optionally, callbacks can be provided to handle synchronization and persistence events.\n *\n * @param {Record<string, any>} instance - The instance to be synchronized.\n * @param {SyncOptions} [options={}] - Optional synchronization options.\n * @param {Function} [options.onSync] - Callback function to be called on value sync with the current cache.\n * @param {Function} [options.onPersist] - Callback function to be called on value persistence with the current cache.\n *\n * @example\n * class TestClass {\n * @sync() count = signal(0);\n * @sync() text = signal('hello');\n * }\n * const instance = new TestClass();\n * syncClass(instance, {\n * onSync: (cache) => console.log('Sync cache:', cache),\n * onPersist: (cache) => console.log('Persist cache:', cache),\n * });\n */\nexport const syncClass = (instance: any, options: SyncOptions = {}) => {\n const cacheSync = new Map();\n const cachePersist = new Set<string>();\n instance.$valuesChanges = {\n set: (path: string, value: any) => {\n cacheSync.set(path, value);\n options.onSync?.(cacheSync);\n },\n setPersist: (path: string) => {\n if (path == \"\") path = \".\";\n cachePersist.add(path);\n options.onPersist?.(cachePersist);\n },\n has: (path: string) => {\n return cacheSync.has(path);\n },\n get: (path: string) => {\n return cacheSync.get(path);\n },\n };\n createSyncClass(instance);\n};\n\n/**\n * Creates a snapshot of the current state of an instance's signals.\n *\n * This function iterates over the signals stored in the instance's $snapshot property.\n * If a signal's value is not an object or array and the signal's persist option is true or undefined,\n * it adds the signal's value to the returned snapshot object.\n *\n * @param {Record<string, any>} instance - The instance containing the $snapshot map of signals.\n * @returns {Record<string, any>} - An object representing the persisted snapshot of the instance's state.\n *\n * @example\n * ```typescript\n * class TestClass {\n * @sync() count = signal(0);\n * @sync() text = signal('hello');\n * }\n * const instance = new TestClass();\n * syncClass(instance);\n * const snapshot = createStatesSnapshot(instance);\n * console.log(snapshot); // { count: 0, text: 'hello' }\n * ```\n */\nexport function createStatesSnapshot(instance: Record<string, any>): Record<string, any> {\n let persistObject: any = {};\n if (instance.$snapshot) {\n for (const key of instance.$snapshot.keys()) {\n const signal = instance.$snapshot.get(key);\n const persist = signal.options.persist ?? true;\n let value = signal();\n if (isObject(value) || Array.isArray(value)) {\n break;\n }\n if (persist) {\n persistObject[key] = value;\n }\n }\n }\n return persistObject;\n}\n\nexport function setMetadata(target: any, key: string, value: any) {\n const meta = target.constructor._propertyMetadata;\n const propId = meta?.get(key);\n if (propId) {\n if (isSignal(target[propId])) {\n target[propId].set(value);\n } else {\n target[propId] = value;\n }\n }\n}\n\nexport const createSyncClass = (\n currentClass: any,\n parentKey: any = null,\n parentClass = null,\n path = \"\"\n) => {\n currentClass.$path = path;\n if (parentClass) {\n currentClass.$valuesChanges = parentClass.$valuesChanges;\n }\n if (parentKey) {\n setMetadata(currentClass, \"id\", parentKey);\n }\n if (currentClass.$snapshot) {\n for (const key of currentClass.$snapshot.keys()) {\n const signal = currentClass.$snapshot.get(key);\n const syncToClient = signal.options.syncToClient ?? true;\n const persist = signal.options.persist ?? true;\n let value = signal();\n if (isObject(value) || Array.isArray(value)) {\n value = { ...value };\n }\n const newPath = (path ? path + \".\" : \"\") + key;\n if (syncToClient) {\n currentClass.$valuesChanges.set(newPath, value);\n }\n if (persist) {\n if (parentClass) currentClass.$valuesChanges.setPersist(path);\n }\n }\n }\n};\n\nexport const type = (\n _signal: any,\n path: string,\n options: TypeOptions = {},\n currentInstance: any\n): WritableSignal<any> => {\n const syncToClient = options.syncToClient ?? true;\n const persist = options.persist ?? true;\n let init = true;\n _signal.options = options;\n _signal.observable.subscribe((value) => {\n const check = currentInstance.$valuesChanges;\n\n function savePath(propPath, value) {\n if (syncToClient) check.set(propPath, value);\n if (persist) {\n check.setPersist(currentInstance.$path);\n }\n }\n\n if (init) {\n init = false;\n return;\n }\n if (currentInstance.$path !== undefined) {\n const propPath =\n (currentInstance.$path ? currentInstance.$path + \".\" : \"\") + path;\n if (_signal._subject instanceof ObjectSubject) {\n const newPath =\n (currentInstance.$path ? currentInstance.$path + \".\" : \"\") +\n path +\n \".\" +\n value.key;\n\n if (value.type == \"add\") {\n if (isInstanceOfClass(value.value)) {\n createSyncClass(value.value, value.key, currentInstance, newPath);\n } else {\n savePath(newPath, value.value);\n }\n } else if (value.type == \"update\") {\n if (isObject(value.value) || Array.isArray(value.value)) {\n createSyncClass(value.value, value.key, currentInstance, newPath);\n } else {\n savePath(newPath, value.value);\n }\n } else if (value.type == \"remove\") {\n savePath(newPath, \"$delete\");\n }\n } else if (_signal._subject instanceof ArraySubject) {\n const newPath = propPath + \".\" + value.index;\n const firstItem = value.items[0];\n if (value.type == \"add\") {\n if (isInstanceOfClass(firstItem)) {\n createSyncClass(firstItem, value.key, currentInstance, newPath);\n } else {\n savePath(newPath, firstItem);\n }\n } else if (value.type == \"update\") {\n if (isObject(firstItem) || Array.isArray(firstItem)) {\n createSyncClass(firstItem, value.key, currentInstance, newPath);\n } else {\n savePath(newPath, firstItem);\n }\n } else if (value.type == \"remove\") {\n savePath(newPath, \"$delete\");\n }\n } else {\n savePath(propPath, value);\n }\n }\n });\n\n if (!currentInstance.$snapshot) {\n currentInstance.$snapshot = new Map();\n }\n\n currentInstance.$snapshot.set(path, _signal);\n\n return _signal;\n};\n","import { isSignal } from \"@signe/reactive\";\nimport { setMetadata } from \"./core\";\nimport { isClass } from \"./utils\";\n\n/**\n * Loads values into the root instance by paths or from an object.\n * \n * @param {object} rootInstance - The instance into which values will be loaded.\n * @param {object} values - The values to load, either as paths or an object.\n * @param {boolean} [valueIsObject=false] - If true, `values` is treated as an object.\n * @example\n * // Using paths:\n * load(instance, { 'position.x': 10, 'position.y': 20 });\n * \n * // Using an object:\n * load(instance, { position: { x: 10, y: 20 } }, true);\n */\nexport function load(rootInstance: any, values: { [path: string]: any }): void;\nexport function load(\n rootInstance: any,\n values: object,\n valueIsObject: true\n): void;\nexport function load(\n rootInstance: any,\n values: { [path: string]: any } | object,\n valueIsObject?: boolean\n) {\n if (valueIsObject) {\n loadFromObject(rootInstance, values);\n } else {\n loadFromPaths(rootInstance, values);\n }\n}\n\n/**\n * Loads values into the root instance using paths.\n * \n * @param {object} rootInstance - The instance into which values will be loaded.\n * @param {object} values - The values to load, with keys as paths.\n * @example\n * loadFromPaths(instance, { 'position.x': 10, 'position.y': 20 });\n */\nfunction loadFromPaths(rootInstance: any, values: { [path: string]: any }) {\n for (const [path, value] of Object.entries(values)) {\n const parts = path.split(\".\");\n loadValue(rootInstance, parts, value);\n }\n}\n\n/**\n * Recursively loads values from an object into the root instance.\n * \n * @param {object} rootInstance - The instance into which values will be loaded.\n * @param {object} values - The values to load.\n * @param {string} [currentPath=\"\"] - The current path in the recursion.\n * @example\n * loadFromObject(instance, { position: { x: 10, y: 20 } });\n */\nfunction loadFromObject(\n rootInstance: any,\n values: object,\n currentPath: string = \"\"\n) {\n for (const [key, value] of Object.entries(values)) {\n const newPath = currentPath ? `${currentPath}.${key}` : key;\n if (typeof value === \"object\" && !Array.isArray(value)) {\n loadFromObject(rootInstance, value, newPath);\n } else {\n const parts = newPath.split(\".\");\n loadValue(rootInstance, parts, value);\n }\n }\n}\n\n/**\n * Sets a value in the root instance by navigating through the path parts.\n * \n * @param {object} rootInstance - The instance into which the value will be set.\n * @param {string[]} parts - The parts of the path.\n * @param {any} value - The value to set.\n * @example\n * loadValue(instance, ['position', 'x'], 10);\n */\nfunction loadValue(rootInstance: any, parts: string[], value: any) {\n let current: any = rootInstance;\n\n for (let i = 0; i < parts.length; i++) {\n const part = parts[i];\n\n if (i === parts.length - 1) {\n if (value == '$delete') {\n if (isSignal(current)) {\n current = current();\n }\n Reflect.deleteProperty(current, part);\n }\n else if (current[part]?._subject) {\n current[part].set(value);\n }\n } else {\n if (isSignal(current)) {\n current = current();\n }\n const currentValue = current[part];\n if (currentValue === undefined) {\n const parentInstance = getByPath(\n rootInstance,\n parts.slice(0, i).join(\".\")\n );\n const classType = parentInstance?.options?.classType;\n if (classType) {\n current[part] = !isClass(classType) ? classType(part) : new classType();\n setMetadata(current[part], 'id', part)\n } else {\n current[part] = {};\n }\n }\n current = current[part];\n }\n }\n}\n\n/**\n * Retrieves a value from the root instance by a path.\n * \n * @param {object} root - The root instance.\n * @param {string} path - The path to the value.\n * @returns {any} - The value at the specified path.\n * @example\n * const value = getByPath(instance, 'position.x');\n */\nexport function getByPath(root: any, path: string) {\n const parts = path.split(\".\");\n let current = root;\n for (const part of parts) {\n if (isSignal(current)) {\n current = current();\n }\n if (current[part]) {\n current = current[part];\n } else {\n return undefined;\n }\n }\n return current;\n}\n","import { dset } from \"dset\";\n\n/**\n * Checks if a value is a Promise.\n *\n * @param {unknown} value - The value to check.\n * @returns {boolean} - Returns true if the value is a Promise, otherwise false.\n *\n * @example\n * isPromise(Promise.resolve()); // true\n * isPromise(42); // false\n */\nexport function isPromise(value: unknown): value is Promise<any> {\n return value instanceof Promise;\n}\n\n/**\n * Awaits the given value if it is a Promise, otherwise returns the value directly.\n *\n * @param {unknown} val - The value to await or return.\n * @returns {Promise<any>} - Returns a Promise that resolves to the value.\n *\n * @example\n * awaitReturn(Promise.resolve(42)); // 42\n * awaitReturn(42); // 42\n */\nexport async function awaitReturn(val: unknown): Promise<any> {\n return isPromise(val) ? await val : val;\n}\n\n/**\n * Checks if a value is a class.\n *\n * @param {unknown} obj - The value to check.\n * @returns {boolean} - Returns true if the value is a class, otherwise false.\n *\n * @example\n * class MyClass {}\n * isClass(MyClass); // true\n * isClass(() => {}); // false\n */\nexport function isClass(obj: unknown): boolean {\n return (\n typeof obj === \"function\" &&\n obj.prototype &&\n obj.prototype.constructor === obj\n );\n}\n\n\n/**\n * Creates a throttled function that only invokes the provided function at most once per every wait milliseconds.\n *\n * The throttled function comes with a cancel method to cancel delayed invocations.\n * If the throttled function is invoked more than once during the wait timeout,\n * it will call the provided function with the latest arguments.\n *\n * @template F - The type of the function to throttle.\n * @param {F} func - The function to throttle.\n * @param {number} wait - The number of milliseconds to throttle invocations to.\n * @returns {(...args: Parameters<F>) => void} - Returns the new throttled function.\n *\n * @example\n * const log = throttle((message) => console.log(message), 1000);\n * log(\"Hello\"); // Will log \"Hello\" immediately\n * log(\"World\"); // Will log \"World\" after 1 second, if no other calls to log() are made within the 1 second.\n */\nexport function throttle<F extends (...args: any[]) => any>(\n func: F,\n wait: number\n): (...args: Parameters<F>) => void {\n let timeout: ReturnType<typeof setTimeout> | null = null;\n let lastArgs: Parameters<F> | null = null;\n\n return function (...args: Parameters<F>) {\n if (!timeout) {\n func(...args);\n timeout = setTimeout(() => {\n if (lastArgs) {\n func(...lastArgs);\n lastArgs = null;\n }\n timeout = null;\n }, wait);\n } else {\n lastArgs = args;\n }\n };\n}\n\n/**\n * Extracts parameters from a given string based on a specified pattern.\n *\n * The pattern can include placeholders in the form of {paramName}, which will be\n * extracted from the input string if they match.\n *\n * @param {string} pattern - The pattern containing placeholders.\n * @param {string} str - The string to extract parameters from.\n * @returns {{ [key: string]: string } | null} - An object containing the extracted parameters,\n * or null if the string does not match the pattern.\n *\n * @example\n * // returns { id: '123' }\n * extractParams('game-{id}', 'game-123');\n *\n * @example\n * // returns { foo: 'abc', bar: 'xyz' }\n * extractParams('test-{foo}-{bar}', 'test-abc-xyz');\n *\n */\nexport function extractParams(\n pattern: string,\n str: string\n): { [key: string]: string } | null {\n // Replace placeholders in the pattern with named capture groups\n const regexPattern = pattern.replace(/{(\\w+)}/g, \"(?<$1>[\\\\w-]+)\");\n\n // Create a strict regular expression from the pattern\n const regex = new RegExp(`^${regexPattern}$`);\n const match = regex.exec(str);\n\n // If a match is found and groups are present, return the captured groups\n if (match && match.groups) {\n return match.groups;\n } else if (pattern === str) {\n // If the pattern exactly matches the string, return an empty object\n return {};\n } else {\n // Otherwise, return null\n return null;\n }\n}\n\n/**\n * Removes a property from an object based on a dot-separated key string or an array of keys.\n *\n * The function modifies the original object by deleting the specified property.\n * It safely handles dangerous keys like __proto__, constructor, and prototype.\n *\n * @param {Record<string, any>} obj - The object from which to remove the property.\n * @param {string | string[]} keys - The key(s) specifying the property to remove. Can be a dot-separated string or an array of strings.\n *\n * @example\n * const obj = { a: { b: { c: 3 } } };\n * dremove(obj, 'a.b.c');\n * // obj is now { a: { b: {} } }\n *\n * @example\n * const obj = { a: 1, b: 2 };\n * dremove(obj, 'a');\n * // obj is now { b: 2 }\n *\n * @example\n * const obj = { a: { b: { c: 3 } } };\n * dremove(obj, ['a', 'b', 'c']);\n * // obj is now { a: { b: {} } }\n */\nexport function dremove(\n obj: Record<string, any>,\n keys: string | string[]\n): void {\n // If keys is a string, convert it to an array using the \".\" separator\n if (typeof keys === \"string\") {\n keys = keys.split(\".\");\n }\n\n let i = 0;\n const l = keys.length;\n let t = obj;\n let k;\n\n while (i < l - 1) {\n k = keys[i++];\n if (k === \"__proto__\" || k === \"constructor\" || k === \"prototype\") return; // Avoid dangerous keys\n if (typeof t[k] !== \"object\" || t[k] === null) return; // If the object doesn't exist, stop\n t = t[k];\n }\n\n k = keys[i];\n if (\n t &&\n typeof t === \"object\" &&\n !(k === \"__proto__\" || k === \"constructor\" || k === \"prototype\")\n ) {\n delete t[k];\n }\n}\n\n/**\n * Builds an object from a map of values and updates the provided memory object.\n *\n * For each key-value pair in the map, this function sets the value at the given path in the `memoryObj`.\n * If the value is \"$delete\", it removes the corresponding path from `allMemory`.\n *\n * @param {Map<string, any>} valuesMap - A map where the keys are paths and the values are the values to set at those paths.\n * @param {Record<string, any>} allMemory - The object to update based on the values in the map.\n * @returns {Record<string, any>} - The built memory object with the applied values from the map.\n *\n * @example\n * const valuesMap = new Map();\n * valuesMap.set('a.b.c', 1);\n * valuesMap.set('x.y.z', '$delete');\n * const allMemory = { x: { y: { z: 2 } } };\n * const result = buildObject(valuesMap, allMemory);\n * // result is { a: { b: { c: 1 } }, x: { y: { z: '$delete' } } }\n * // allMemory is { a: { b: { c: 1 } }, x: { y: {} } }\n */\nexport function buildObject(valuesMap: Map<string, any>, allMemory: Record<string, any>): Record<string, any> {\n let memoryObj = {};\n for (let path of valuesMap.keys()) {\n const value = valuesMap.get(path);\n dset(memoryObj, path, value);\n if (value === \"$delete\") {\n dremove(allMemory, path);\n } else {\n dset(allMemory, path, value);\n }\n }\n return memoryObj;\n}"],"mappings":"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","names":["dset","isSignal","isSignal","isClass","dset","isClass"]}
1
+ {"version":3,"sources":["../src/decorators.ts","../../sync/src/utils.ts","../src/mock.ts","../src/server.ts","../../sync/src/core.ts","../../sync/src/load.ts","../src/utils.ts"],"sourcesContent":["export function Action(name: string, bodyValidation?) {\n return function (target: any, propertyKey: string) {\n if (!target.constructor._actionMetadata) {\n target.constructor._actionMetadata = new Map();\n }\n target.constructor._actionMetadata.set(name, {\n key: propertyKey,\n bodyValidation,\n });\n };\n}\n\nexport interface RoomOptions {\n path: string;\n maxUsers?: number;\n throttleStorage?: number;\n throttleSync?: number;\n hibernate?: boolean;\n}\n\nexport function Room(options: RoomOptions) {\n return function (target: any) {\n target.path = options.path;\n target.maxUsers = options.maxUsers;\n target.throttleStorage = options.throttleStorage;\n target.throttleSync = options.throttleSync;\n };\n}\n","/**\n * Checks if the given value is a function.\n *\n * @param {unknown} val - The value to check.\n * @returns {boolean} - True if the value is a function, false otherwise.\n * @example\n * isFunction(function() {}); // true\n * isFunction(() => {}); // true\n * isFunction(123); // false\n */\nexport function isFunction(val: unknown): boolean {\n return {}.toString.call(val) === \"[object Function]\";\n}\n\n/**\n * Checks if the given object is a class.\n *\n * @param {any} obj - The object to check.\n * @returns {boolean} - True if the object is a class, false otherwise.\n * @example\n * class MyClass {}\n * isClass(MyClass); // true\n * isClass(() => {}); // false\n */\nexport function isClass(obj: any): boolean {\n return (\n typeof obj === \"function\" &&\n obj.prototype &&\n obj.prototype.constructor === obj\n );\n}\n\n/**\n * Checks if the given item is an object.\n *\n * @param {any} item - The item to check.\n * @returns {boolean} - True if the item is an object, false otherwise.\n * @example\n * isObject({}); // true\n * isObject(null); // false\n * isObject([]); // false\n */\nexport const isObject = (item: any): boolean =>\n item && typeof item === \"object\" && !Array.isArray(item) && item !== null;\n\n/**\n * Checks if the given value is an instance of a class.\n *\n * @param {unknown} value - The value to check.\n * @returns {boolean} - True if the value is an instance of a class, false otherwise.\n * @example\n * class MyClass {}\n * const instance = new MyClass();\n * isInstanceOfClass(instance); // true\n * isInstanceOfClass({}); // false\n */\nexport function isInstanceOfClass(value: unknown): boolean {\n if (\n value === null ||\n typeof value !== \"object\" ||\n value === undefined ||\n Array.isArray(value)\n ) {\n return false;\n }\n return Object.getPrototypeOf(value) !== Object.prototype;\n}\n\nexport function generateShortUUID(): string {\n const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';\n let uuid = '';\n for (let i = 0; i < 8; i++) {\n const randomIndex = Math.floor(Math.random() * chars.length);\n uuid += chars[randomIndex];\n }\n return uuid;\n}","import { generateShortUUID } from \"../../sync/src/utils\";\n\nclass MockPartySocket {\n private events: Map<string, Function> = new Map();\n id = generateShortUUID()\n \n addEventListener(event, cb) {\n this.events.set(event, cb);\n }\n\n removeEventListener(event, cb) {\n this.events.delete(event);\n }\n\n _trigger(event, data) {\n this.events.get(event)?.(data);\n }\n}\n\nclass MockStorage {\n private storage: Map<string, any> = new Map();\n \n async get(key: string) {\n return this.storage.get(key);\n }\n \n async put(key: string, value: any) {\n this.storage.set(key, value);\n }\n \n async list() {\n return this.storage\n }\n}\n\nclass MockPartyRoom {\n private clients: Map<string, MockPartySocket> = new Map();\n storage = new MockStorage();\n\n constructor(public id?: string) {\n this.id = id || generateShortUUID()\n }\n\n connection(client) {\n const socket = new MockPartySocket();\n this.clients.set(socket.id, client);\n client.id = socket.id;\n }\n\n broadcast(data: any) {\n this.clients.forEach((client) => {\n client._trigger('message', data);\n });\n }\n\n clear() {\n this.clients.clear();\n }\n}\n\nexport class MockConnection {\n state: any = {};\n\n setState(value: any) {\n this.state = value;\n }\n}\n\nexport const ServerIo = MockPartyRoom;\nexport const ClientIo = MockPartySocket;\n","import { dset } from \"dset\";\nimport z from \"zod\";\nimport {\n createStatesSnapshot,\n getByPath,\n load,\n syncClass,\n} from \"../../sync/src\";\nimport { generateShortUUID } from \"../../sync/src/utils\";\nimport type * as Party from \"./types/party\";\nimport {\n awaitReturn,\n buildObject,\n extractParams,\n isClass,\n throttle,\n} from \"./utils\";\n\nconst Message = z.object({\n action: z.string(),\n value: z.any(),\n});\n\ntype CreateRoomOptions = {\n getMemoryAll?: boolean;\n};\n\n/**\n * @class Server\n * @implements {Party.Server}\n * @description Represents a server that manages rooms and connections for a multiplayer game or application.\n * \n * @example\n * ```typescript\n * import { Room, Server, ServerIo } from \"@yourpackage/room\";\n * \n * @Room({ path: \"game\" })\n * class GameRoom {\n * // Room implementation\n * }\n * \n * class MyServer extends Server {\n * rooms = [GameRoom];\n * }\n * \n * const server = new MyServer(new ServerIo(\"game\"));\n * server.onStart();\n * ```\n */\nexport class Server implements Party.Server {\n subRoom = null;\n rooms: any[] = [];\n\n /**\n * @constructor\n * @param {Party.Room} room - The room object representing the current game or application instance.\n * \n * @example\n * ```typescript\n * const server = new MyServer(new ServerIo(\"game\"));\n * ```\n */\n constructor(readonly room: Party.Room) {}\n\n /**\n * @readonly\n * @property {boolean} isHibernate - Indicates whether the server is in hibernate mode.\n * \n * @example\n * ```typescript\n * if (!server.isHibernate) {\n * console.log(\"Server is active\");\n * }\n * ```\n */\n get isHibernate(): boolean {\n return !!this[\"options\"]?.hibernate;\n }\n\n /**\n * @method onStart\n * @async\n * @description Initializes the server and creates the initial room if not in hibernate mode.\n * @returns {Promise<void>}\n * \n * @example\n * ```typescript\n * async function initServer() {\n * await server.onStart();\n * console.log(\"Server started\");\n * }\n * ```\n */\n\n async onStart() {\n // Only create a room if not in hibernate mode\n // This prevents unnecessary resource allocation for inactive rooms\n if (!this.isHibernate) {\n this.subRoom = await this.createRoom();\n }\n }\n\n /**\n * @method createRoom\n * @private\n * @async\n * @param {CreateRoomOptions} [options={}] - Options for creating the room.\n * @returns {Promise<Object>} The created room instance.\n * @throws {Error} If no matching room is found.\n * \n * @example\n * ```typescript\n * // This method is private and called internally\n * async function internalCreateRoom() {\n * const room = await this.createRoom({ getMemoryAll: true });\n * console.log(\"Room created:\", room);\n * }\n * ```\n */\n private async createRoom(options: CreateRoomOptions = {}) {\n let instance\n let init = true\n\n // Find the appropriate room based on the current room ID\n for (let room of this.rooms) {\n const params = extractParams(room.path, this.room.id);\n if (params) {\n instance = new room(this.room, params);\n break;\n }\n }\n\n if (!instance) {\n throw new Error(\"Room not found\");\n }\n\n // Load the room's memory from storage\n // This ensures persistence across server restarts\n const loadMemory = async () => {\n const root = await this.room.storage.get(\".\");\n const memory = await this.room.storage.list();\n const tmpObject: any = root || {};\n for (let [key, value] of memory) {\n if (key == \".\") {\n continue;\n }\n dset(tmpObject, key, value);\n }\n load(instance, tmpObject);\n };\n\n await loadMemory();\n\n instance.$memoryAll = {}\n\n // Sync callback: Broadcast changes to all clients\n const syncCb = (values) => {\n if (options.getMemoryAll) {\n buildObject(values, instance.$memoryAll);\n }\n if (init && this.isHibernate) {\n init = false;\n return;\n }\n const packet = buildObject(values, instance.$memoryAll);\n this.room.broadcast(\n JSON.stringify({\n type: \"sync\",\n value: packet,\n })\n );\n values.clear();\n }\n\n // Persist callback: Save changes to storage\n const persistCb = async (values) => {\n for (let path of values) {\n const _instance =\n path == \".\" ? instance : getByPath(instance, path);\n const itemValue = createStatesSnapshot(_instance);\n await this.room.storage.put(path, itemValue);\n }\n values.clear();\n }\n\n // Set up syncing and persistence with throttling to optimize performance\n syncClass(instance, {\n onSync: throttle(syncCb, instance[\"throttleSync\"] ?? 500),\n onPersist: throttle(persistCb, instance[\"throttleStorage\"] ?? 2000),\n });\n\n return instance\n }\n\n /**\n * @method getSubRoom\n * @private\n * @async\n * @param {Object} [options={}] - Options for getting the sub-room.\n * @returns {Promise<Object>} The sub-room instance.\n * \n * @example\n * ```typescript\n * // This method is private and called internally\n * async function internalGetSubRoom() {\n * const subRoom = await this.getSubRoom();\n * console.log(\"Sub-room retrieved:\", subRoom);\n * }\n * ```\n */\n private async getSubRoom(options = {}) {\n let subRoom\n if (this.isHibernate) {\n subRoom = await this.createRoom(options)\n }\n else {\n subRoom = this.subRoom\n }\n return subRoom\n }\n\n /**\n * @method getUsersProperty\n * @private\n * @param {Object} subRoom - The sub-room instance.\n * @returns {Object|null} The users property of the sub-room, or null if not found.\n * \n * @example\n * ```typescript\n * // This method is private and called internally\n * function internalGetUsers(subRoom) {\n * const users = this.getUsersProperty(subRoom);\n * console.log(\"Users:\", users);\n * }\n * ```\n */\n\n private getUsersProperty(subRoom) {\n const meta = subRoom.constructor[\"_propertyMetadata\"];\n const propId = meta?.get(\"users\");\n if (propId) {\n return subRoom[propId];\n }\n return null;\n }\n\n /**\n * @method onConnect\n * @async\n * @param {Party.Connection} conn - The connection object for the new user.\n * @param {Party.ConnectionContext} ctx - The context of the connection.\n * @description Handles a new user connection, creates a user object, and sends initial sync data.\n * @returns {Promise<void>}\n * \n * @example\n * ```typescript\n * server.onConnect = async (conn, ctx) => {\n * await server.onConnect(conn, ctx);\n * console.log(\"New user connected:\", conn.id);\n * };\n * ```\n */\n async onConnect(conn: Party.Connection, ctx: Party.ConnectionContext) {\n const subRoom = await this.getSubRoom({\n getMemoryAll: true,\n })\n // Generate a unique public ID for the user\n const publicId = generateShortUUID()\n let user = null;\n const signal = this.getUsersProperty(subRoom);\n if (signal) {\n const { classType } = signal.options;\n // Create a new user instance based on the defined class type\n user = isClass(classType) ? new classType() : classType(conn, ctx);\n signal()[publicId] = user;\n }\n // Call the room's onJoin method if it exists\n await awaitReturn(subRoom[\"onJoin\"]?.(user, conn, ctx));\n conn.setState({ publicId });\n // Send initial sync data to the new connection\n conn.send(\n JSON.stringify({\n type: \"sync\",\n value: {\n pId: publicId,\n ...subRoom.$memoryAll,\n },\n })\n );\n }\n\n /**\n * @method onMessage\n * @async\n * @param {string} message - The message received from a user.\n * @param {Party.Connection} sender - The connection object of the sender.\n * @description Processes incoming messages and triggers corresponding actions in the sub-room.\n * @returns {Promise<void>}\n * \n * @example\n * ```typescript\n * server.onMessage = async (message, sender) => {\n * await server.onMessage(message, sender);\n * console.log(\"Message processed from:\", sender.id);\n * };\n * ```\n */\n\n async onMessage(message: string, sender: Party.Connection) {\n let json\n try {\n json = JSON.parse(message)\n }\n catch (e) {\n return;\n }\n // Validate incoming messages\n const result = Message.safeParse(json);\n if (!result.success) {\n return;\n }\n const subRoom = await this.getSubRoom()\n const actions = subRoom.constructor[\"_actionMetadata\"];\n if (actions) {\n const signal = this.getUsersProperty(subRoom);\n const { publicId } = sender.state as any;\n const user = signal?.()[publicId];\n const actionName = actions.get(result.data.action);\n if (actionName) {\n // Validate action body if a validation schema is defined\n if (actionName.bodyValidation) {\n const bodyResult = actionName.bodyValidation.safeParse(\n result.data.value\n );\n if (!bodyResult.success) {\n return;\n }\n }\n // Execute the action\n await awaitReturn(\n subRoom[actionName.key](user, result.data.value, sender)\n );\n }\n }\n }\n\n /**\n * @method onClose\n * @async\n * @param {Party.Connection} conn - The connection object of the disconnecting user.\n * @description Handles user disconnection, removing them from the room and triggering the onLeave event.\n * @returns {Promise<void>}\n * \n * @example\n * ```typescript\n * server.onClose = async (conn) => {\n * await server.onClose(conn);\n * console.log(\"User disconnected:\", conn.id);\n * };\n * ```\n */\n async onClose(conn: Party.Connection) {\n const subRoom = await this.getSubRoom()\n const signal = this.getUsersProperty(subRoom);\n const { publicId } = conn.state as any;\n const user = signal?.()[publicId];\n // Call the room's onLeave method if it exists\n await awaitReturn(subRoom[\"onLeave\"]?.(user, conn));\n if (signal) {\n // Remove the user from the room\n delete signal()[publicId];\n }\n }\n}\n","import {\n ArraySubject,\n ObjectSubject,\n isSignal,\n type WritableSignal,\n} from \"@signe/reactive\";\nimport { isInstanceOfClass, isObject } from \"./utils\";\n\ninterface SyncOptions {\n onSync?: (value: Map<string, any>) => void;\n onPersist?: (value: Set<string>) => void;\n}\n\ninterface TypeOptions {\n syncToClient?: boolean;\n persist?: boolean;\n classType?: any;\n}\n\n/**\n * Synchronizes an instance by adding `$valuesChanges` methods for state management.\n *\n * This function initializes a cache for syncing and persisting values. It adds methods to the instance\n * to set values, mark values for persistence, and check and retrieve values from the cache.\n * Optionally, callbacks can be provided to handle synchronization and persistence events.\n *\n * @param {Record<string, any>} instance - The instance to be synchronized.\n * @param {SyncOptions} [options={}] - Optional synchronization options.\n * @param {Function} [options.onSync] - Callback function to be called on value sync with the current cache.\n * @param {Function} [options.onPersist] - Callback function to be called on value persistence with the current cache.\n *\n * @example\n * class TestClass {\n * @sync() count = signal(0);\n * @sync() text = signal('hello');\n * }\n * const instance = new TestClass();\n * syncClass(instance, {\n * onSync: (cache) => console.log('Sync cache:', cache),\n * onPersist: (cache) => console.log('Persist cache:', cache),\n * });\n */\nexport const syncClass = (instance: any, options: SyncOptions = {}) => {\n const cacheSync = new Map();\n const cachePersist = new Set<string>();\n instance.$valuesChanges = {\n set: (path: string, value: any) => {\n cacheSync.set(path, value);\n options.onSync?.(cacheSync);\n },\n setPersist: (path: string) => {\n if (path == \"\") path = \".\";\n cachePersist.add(path);\n options.onPersist?.(cachePersist);\n },\n has: (path: string) => {\n return cacheSync.has(path);\n },\n get: (path: string) => {\n return cacheSync.get(path);\n },\n };\n createSyncClass(instance);\n};\n\n/**\n * Creates a snapshot of the current state of an instance's signals.\n *\n * This function iterates over the signals stored in the instance's $snapshot property.\n * If a signal's value is not an object or array and the signal's persist option is true or undefined,\n * it adds the signal's value to the returned snapshot object.\n *\n * @param {Record<string, any>} instance - The instance containing the $snapshot map of signals.\n * @returns {Record<string, any>} - An object representing the persisted snapshot of the instance's state.\n *\n * @example\n * ```typescript\n * class TestClass {\n * @sync() count = signal(0);\n * @sync() text = signal('hello');\n * }\n * const instance = new TestClass();\n * syncClass(instance);\n * const snapshot = createStatesSnapshot(instance);\n * console.log(snapshot); // { count: 0, text: 'hello' }\n * ```\n */\nexport function createStatesSnapshot(instance: Record<string, any>): Record<string, any> {\n let persistObject: any = {};\n if (instance.$snapshot) {\n for (const key of instance.$snapshot.keys()) {\n const signal = instance.$snapshot.get(key);\n const persist = signal.options.persist ?? true;\n let value = signal();\n if (isObject(value) || Array.isArray(value)) {\n break;\n }\n if (persist) {\n persistObject[key] = value;\n }\n }\n }\n return persistObject;\n}\n\nexport function setMetadata(target: any, key: string, value: any) {\n const meta = target.constructor._propertyMetadata;\n const propId = meta?.get(key);\n if (propId) {\n if (isSignal(target[propId])) {\n target[propId].set(value);\n } else {\n target[propId] = value;\n }\n }\n}\n\nexport const createSyncClass = (\n currentClass: any,\n parentKey: any = null,\n parentClass = null,\n path = \"\"\n) => {\n currentClass.$path = path;\n if (parentClass) {\n currentClass.$valuesChanges = parentClass.$valuesChanges;\n }\n if (parentKey) {\n setMetadata(currentClass, \"id\", parentKey);\n }\n if (currentClass.$snapshot) {\n for (const key of currentClass.$snapshot.keys()) {\n const signal = currentClass.$snapshot.get(key);\n const syncToClient = signal.options.syncToClient ?? true;\n const persist = signal.options.persist ?? true;\n let value = signal();\n if (isObject(value) || Array.isArray(value)) {\n value = { ...value };\n }\n const newPath = (path ? path + \".\" : \"\") + key;\n if (syncToClient) {\n currentClass.$valuesChanges.set(newPath, value);\n }\n if (persist) {\n if (parentClass) currentClass.$valuesChanges.setPersist(path);\n }\n }\n }\n};\n\nexport const type = (\n _signal: any,\n path: string,\n options: TypeOptions = {},\n currentInstance: any\n): WritableSignal<any> => {\n const syncToClient = options.syncToClient ?? true;\n const persist = options.persist ?? true;\n let init = true;\n _signal.options = options;\n _signal.observable.subscribe((value) => {\n const check = currentInstance.$valuesChanges;\n\n function savePath(propPath, value) {\n if (syncToClient) check.set(propPath, value);\n if (persist) {\n check.setPersist(currentInstance.$path);\n }\n }\n\n if (init) {\n init = false;\n return;\n }\n if (currentInstance.$path !== undefined) {\n const propPath =\n (currentInstance.$path ? currentInstance.$path + \".\" : \"\") + path;\n if (_signal._subject instanceof ObjectSubject) {\n const newPath =\n (currentInstance.$path ? currentInstance.$path + \".\" : \"\") +\n path +\n \".\" +\n value.key;\n\n if (value.type == \"add\") {\n if (isInstanceOfClass(value.value)) {\n createSyncClass(value.value, value.key, currentInstance, newPath);\n } else {\n savePath(newPath, value.value);\n }\n } else if (value.type == \"update\") {\n if (isObject(value.value) || Array.isArray(value.value)) {\n createSyncClass(value.value, value.key, currentInstance, newPath);\n } else {\n savePath(newPath, value.value);\n }\n } else if (value.type == \"remove\") {\n savePath(newPath, \"$delete\");\n }\n } else if (_signal._subject instanceof ArraySubject) {\n const newPath = propPath + \".\" + value.index;\n const firstItem = value.items[0];\n if (value.type == \"add\") {\n if (isInstanceOfClass(firstItem)) {\n createSyncClass(firstItem, value.key, currentInstance, newPath);\n } else {\n savePath(newPath, firstItem);\n }\n } else if (value.type == \"update\") {\n if (isObject(firstItem) || Array.isArray(firstItem)) {\n createSyncClass(firstItem, value.key, currentInstance, newPath);\n } else {\n savePath(newPath, firstItem);\n }\n } else if (value.type == \"remove\") {\n savePath(newPath, \"$delete\");\n }\n } else {\n savePath(propPath, value);\n }\n }\n });\n\n if (!currentInstance.$snapshot) {\n currentInstance.$snapshot = new Map();\n }\n\n currentInstance.$snapshot.set(path, _signal);\n\n return _signal;\n};\n","import { isSignal } from \"@signe/reactive\";\nimport { setMetadata } from \"./core\";\nimport { isClass } from \"./utils\";\n\n/**\n * Loads values into the root instance by paths or from an object.\n * \n * @param {object} rootInstance - The instance into which values will be loaded.\n * @param {object} values - The values to load, either as paths or an object.\n * @param {boolean} [valueIsObject=false] - If true, `values` is treated as an object.\n * @example\n * // Using paths:\n * load(instance, { 'position.x': 10, 'position.y': 20 });\n * \n * // Using an object:\n * load(instance, { position: { x: 10, y: 20 } }, true);\n */\nexport function load(rootInstance: any, values: { [path: string]: any }): void;\nexport function load(\n rootInstance: any,\n values: object,\n valueIsObject: true\n): void;\nexport function load(\n rootInstance: any,\n values: { [path: string]: any } | object,\n valueIsObject?: boolean\n) {\n if (valueIsObject) {\n loadFromObject(rootInstance, values);\n } else {\n loadFromPaths(rootInstance, values);\n }\n}\n\n/**\n * Loads values into the root instance using paths.\n * \n * @param {object} rootInstance - The instance into which values will be loaded.\n * @param {object} values - The values to load, with keys as paths.\n * @example\n * loadFromPaths(instance, { 'position.x': 10, 'position.y': 20 });\n */\nfunction loadFromPaths(rootInstance: any, values: { [path: string]: any }) {\n for (const [path, value] of Object.entries(values)) {\n const parts = path.split(\".\");\n loadValue(rootInstance, parts, value);\n }\n}\n\n/**\n * Recursively loads values from an object into the root instance.\n * \n * @param {object} rootInstance - The instance into which values will be loaded.\n * @param {object} values - The values to load.\n * @param {string} [currentPath=\"\"] - The current path in the recursion.\n * @example\n * loadFromObject(instance, { position: { x: 10, y: 20 } });\n */\nfunction loadFromObject(\n rootInstance: any,\n values: object,\n currentPath: string = \"\"\n) {\n for (let key in values) {\n const value = values[key];\n const newPath = currentPath ? `${currentPath}.${key}` : key;\n if (typeof value === \"object\" && !Array.isArray(value) && value !== null) {\n loadFromObject(rootInstance, value, newPath);\n } else {\n const parts = newPath.split(\".\");\n loadValue(rootInstance, parts, value);\n }\n }\n}\n\n/**\n * Sets a value in the root instance by navigating through the path parts.\n * \n * @param {object} rootInstance - The instance into which the value will be set.\n * @param {string[]} parts - The parts of the path.\n * @param {any} value - The value to set.\n * @example\n * loadValue(instance, ['position', 'x'], 10);\n */\nfunction loadValue(rootInstance: any, parts: string[], value: any) {\n let current: any = rootInstance;\n\n for (let i = 0; i < parts.length; i++) {\n const part = parts[i];\n\n if (i === parts.length - 1) {\n if (value == '$delete') {\n if (isSignal(current)) {\n current = current();\n }\n Reflect.deleteProperty(current, part);\n }\n else if (current[part]?._subject) {\n current[part].set(value);\n }\n } else {\n if (isSignal(current)) {\n current = current();\n }\n const currentValue = current[part];\n if (currentValue === undefined) {\n const parentInstance = getByPath(\n rootInstance,\n parts.slice(0, i).join(\".\")\n );\n const classType = parentInstance?.options?.classType;\n if (classType) {\n current[part] = !isClass(classType) ? classType(part) : new classType();\n setMetadata(current[part], 'id', part)\n } else {\n current[part] = {};\n }\n }\n current = current[part];\n }\n }\n}\n\n/**\n * Retrieves a value from the root instance by a path.\n * \n * @param {object} root - The root instance.\n * @param {string} path - The path to the value.\n * @returns {any} - The value at the specified path.\n * @example\n * const value = getByPath(instance, 'position.x');\n */\nexport function getByPath(root: any, path: string) {\n const parts = path.split(\".\");\n let current = root;\n for (const part of parts) {\n if (isSignal(current)) {\n current = current();\n }\n if (current[part]) {\n current = current[part];\n } else {\n return undefined;\n }\n }\n return current;\n}\n","import { dset } from \"dset\";\n\n/**\n * Checks if a value is a Promise.\n *\n * @param {unknown} value - The value to check.\n * @returns {boolean} - Returns true if the value is a Promise, otherwise false.\n *\n * @example\n * isPromise(Promise.resolve()); // true\n * isPromise(42); // false\n */\nexport function isPromise(value: unknown): value is Promise<any> {\n return value instanceof Promise;\n}\n\n/**\n * Awaits the given value if it is a Promise, otherwise returns the value directly.\n *\n * @param {unknown} val - The value to await or return.\n * @returns {Promise<any>} - Returns a Promise that resolves to the value.\n *\n * @example\n * awaitReturn(Promise.resolve(42)); // 42\n * awaitReturn(42); // 42\n */\nexport async function awaitReturn(val: unknown): Promise<any> {\n return isPromise(val) ? await val : val;\n}\n\n/**\n * Checks if a value is a class.\n *\n * @param {unknown} obj - The value to check.\n * @returns {boolean} - Returns true if the value is a class, otherwise false.\n *\n * @example\n * class MyClass {}\n * isClass(MyClass); // true\n * isClass(() => {}); // false\n */\nexport function isClass(obj: unknown): boolean {\n return (\n typeof obj === \"function\" &&\n obj.prototype &&\n obj.prototype.constructor === obj\n );\n}\n\n\n/**\n * Creates a throttled function that only invokes the provided function at most once per every wait milliseconds.\n *\n * The throttled function comes with a cancel method to cancel delayed invocations.\n * If the throttled function is invoked more than once during the wait timeout,\n * it will call the provided function with the latest arguments.\n *\n * @template F - The type of the function to throttle.\n * @param {F} func - The function to throttle.\n * @param {number} wait - The number of milliseconds to throttle invocations to.\n * @returns {(...args: Parameters<F>) => void} - Returns the new throttled function.\n *\n * @example\n * const log = throttle((message) => console.log(message), 1000);\n * log(\"Hello\"); // Will log \"Hello\" immediately\n * log(\"World\"); // Will log \"World\" after 1 second, if no other calls to log() are made within the 1 second.\n */\nexport function throttle<F extends (...args: any[]) => any>(\n func: F,\n wait: number\n): (...args: Parameters<F>) => void {\n let timeout: ReturnType<typeof setTimeout> | null = null;\n let lastArgs: Parameters<F> | null = null;\n\n return function (...args: Parameters<F>) {\n if (!timeout) {\n func(...args);\n timeout = setTimeout(() => {\n if (lastArgs) {\n func(...lastArgs);\n lastArgs = null;\n }\n timeout = null;\n }, wait);\n } else {\n lastArgs = args;\n }\n };\n}\n\n/**\n * Extracts parameters from a given string based on a specified pattern.\n *\n * The pattern can include placeholders in the form of {paramName}, which will be\n * extracted from the input string if they match.\n *\n * @param {string} pattern - The pattern containing placeholders.\n * @param {string} str - The string to extract parameters from.\n * @returns {{ [key: string]: string } | null} - An object containing the extracted parameters,\n * or null if the string does not match the pattern.\n *\n * @example\n * // returns { id: '123' }\n * extractParams('game-{id}', 'game-123');\n *\n * @example\n * // returns { foo: 'abc', bar: 'xyz' }\n * extractParams('test-{foo}-{bar}', 'test-abc-xyz');\n *\n */\nexport function extractParams(\n pattern: string,\n str: string\n): { [key: string]: string } | null {\n // Replace placeholders in the pattern with named capture groups\n const regexPattern = pattern.replace(/{(\\w+)}/g, \"(?<$1>[\\\\w-]+)\");\n\n // Create a strict regular expression from the pattern\n const regex = new RegExp(`^${regexPattern}$`);\n const match = regex.exec(str);\n\n // If a match is found and groups are present, return the captured groups\n if (match && match.groups) {\n return match.groups;\n } else if (pattern === str) {\n // If the pattern exactly matches the string, return an empty object\n return {};\n } else {\n // Otherwise, return null\n return null;\n }\n}\n\n/**\n * Removes a property from an object based on a dot-separated key string or an array of keys.\n *\n * The function modifies the original object by deleting the specified property.\n * It safely handles dangerous keys like __proto__, constructor, and prototype.\n *\n * @param {Record<string, any>} obj - The object from which to remove the property.\n * @param {string | string[]} keys - The key(s) specifying the property to remove. Can be a dot-separated string or an array of strings.\n *\n * @example\n * const obj = { a: { b: { c: 3 } } };\n * dremove(obj, 'a.b.c');\n * // obj is now { a: { b: {} } }\n *\n * @example\n * const obj = { a: 1, b: 2 };\n * dremove(obj, 'a');\n * // obj is now { b: 2 }\n *\n * @example\n * const obj = { a: { b: { c: 3 } } };\n * dremove(obj, ['a', 'b', 'c']);\n * // obj is now { a: { b: {} } }\n */\nexport function dremove(\n obj: Record<string, any>,\n keys: string | string[]\n): void {\n // If keys is a string, convert it to an array using the \".\" separator\n if (typeof keys === \"string\") {\n keys = keys.split(\".\");\n }\n\n let i = 0;\n const l = keys.length;\n let t = obj;\n let k;\n\n while (i < l - 1) {\n k = keys[i++];\n if (k === \"__proto__\" || k === \"constructor\" || k === \"prototype\") return; // Avoid dangerous keys\n if (typeof t[k] !== \"object\" || t[k] === null) return; // If the object doesn't exist, stop\n t = t[k];\n }\n\n k = keys[i];\n if (\n t &&\n typeof t === \"object\" &&\n !(k === \"__proto__\" || k === \"constructor\" || k === \"prototype\")\n ) {\n delete t[k];\n }\n}\n\n/**\n * Builds an object from a map of values and updates the provided memory object.\n *\n * For each key-value pair in the map, this function sets the value at the given path in the `memoryObj`.\n * If the value is \"$delete\", it removes the corresponding path from `allMemory`.\n *\n * @param {Map<string, any>} valuesMap - A map where the keys are paths and the values are the values to set at those paths.\n * @param {Record<string, any>} allMemory - The object to update based on the values in the map.\n * @returns {Record<string, any>} - The built memory object with the applied values from the map.\n *\n * @example\n * const valuesMap = new Map();\n * valuesMap.set('a.b.c', 1);\n * valuesMap.set('x.y.z', '$delete');\n * const allMemory = { x: { y: { z: 2 } } };\n * const result = buildObject(valuesMap, allMemory);\n * // result is { a: { b: { c: 1 } }, x: { y: { z: '$delete' } } }\n * // allMemory is { a: { b: { c: 1 } }, x: { y: {} } }\n */\nexport function buildObject(valuesMap: Map<string, any>, allMemory: Record<string, any>): Record<string, any> {\n let memoryObj = {};\n for (let path of valuesMap.keys()) {\n const value = valuesMap.get(path);\n dset(memoryObj, path, value);\n if (value === \"$delete\") {\n dremove(allMemory, path);\n } else {\n dset(allMemory, path, value);\n }\n }\n return memoryObj;\n}"],"mappings":"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","names":["Action","name","bodyValidation","target","propertyKey","constructor","_actionMetadata","Map","set","key","Room","options","path","maxUsers","throttleStorage","throttleSync","isClass","obj","prototype","constructor","isObject","item","Array","isArray","generateShortUUID","chars","uuid","i","randomIndex","Math","floor","random","length","MockPartySocket","events","Map","id","generateShortUUID","addEventListener","event","cb","set","removeEventListener","delete","_trigger","data","get","MockStorage","storage","key","put","value","list","MockPartyRoom","clients","constructor","connection","client","socket","broadcast","forEach","clear","MockConnection","state","setState","ServerIo","ClientIo","dset","z","ArraySubject","ObjectSubject","isSignal","syncClass","instance","options","cacheSync","Map","cachePersist","Set","$valuesChanges","set","path","value","onSync","setPersist","add","onPersist","has","get","createSyncClass","createStatesSnapshot","persistObject","$snapshot","key","keys","signal","persist","isObject","Array","isArray","setMetadata","target","meta","constructor","_propertyMetadata","propId","isSignal","currentClass","parentKey","parentClass","$path","syncToClient","newPath","isSignal","load","rootInstance","values","valueIsObject","loadFromObject","loadFromPaths","path","value","Object","entries","parts","split","loadValue","currentPath","key","newPath","Array","isArray","current","i","length","part","isSignal","Reflect","deleteProperty","_subject","set","currentValue","undefined","parentInstance","getByPath","slice","join","classType","options","isClass","setMetadata","root","dset","isPromise","value","Promise","awaitReturn","val","isClass","obj","prototype","constructor","throttle","func","wait","timeout","lastArgs","args","setTimeout","extractParams","pattern","str","regexPattern","replace","regex","RegExp","match","exec","groups","dremove","keys","split","i","l","length","t","k","buildObject","valuesMap","allMemory","memoryObj","path","get","dset","Message","z","object","action","string","value","any","Server","subRoom","rooms","constructor","room","isHibernate","hibernate","onStart","createRoom","options","instance","init","params","extractParams","path","id","Error","loadMemory","root","storage","get","memory","list","tmpObject","key","dset","load","$memoryAll","syncCb","values","getMemoryAll","buildObject","packet","broadcast","JSON","stringify","type","clear","persistCb","_instance","getByPath","itemValue","createStatesSnapshot","put","syncClass","onSync","throttle","onPersist","getSubRoom","getUsersProperty","meta","propId","onConnect","conn","ctx","publicId","generateShortUUID","user","signal","classType","isClass","awaitReturn","setState","send","pId","onMessage","message","sender","json","parse","e","result","safeParse","success","actions","state","actionName","data","bodyValidation","bodyResult","onClose"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@signe/room",
3
- "version": "1.0.1",
3
+ "version": "1.0.3",
4
4
  "description": "",
5
5
  "main": "./dist/index.js",
6
6
  "keywords": [],
@@ -17,7 +17,7 @@
17
17
  "dset": "^3.1.3",
18
18
  "partysocket": "^1.0.1",
19
19
  "zod": "^3.23.8",
20
- "@signe/sync": "1.0.1"
20
+ "@signe/sync": "1.0.3"
21
21
  },
22
22
  "publishConfig": {
23
23
  "access": "public"
package/readme.md CHANGED
@@ -1,10 +1,31 @@
1
- # Example
1
+ # @signe/room
2
+
3
+ A real-time multiplayer room system for Signe applications, providing seamless state synchronization and user management.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @signe/room @signe/reactive @signe/sync
9
+ ```
10
+
11
+ ## Features
12
+
13
+ - 🔄 Automatic state synchronization
14
+ - 👥 Built-in user management
15
+ - 🎮 Action-based message handling
16
+ - 🔐 Authentication support
17
+ - 🎯 TypeScript support
18
+
19
+ ## Usage
20
+
21
+ Here's a complete example of how to create a multiplayer room:
2
22
 
3
23
  ```ts
4
24
  import { signal } from "@signe/reactive";
5
25
  import { Room, Server, action } from "@signe/room";
6
26
  import { id, sync, users } from "@signe/sync";
7
27
 
28
+ // Define a Player class to represent connected users
8
29
  export class Player {
9
30
  @id() id = signal("");
10
31
  @sync() x = signal(0);
@@ -17,38 +38,77 @@ export class Player {
17
38
  }
18
39
  }
19
40
 
41
+ // Define your room's state schema
20
42
  export class RoomSchema {
21
43
  @sync() count = signal(0);
22
44
  @users(Player) players = signal({});
23
45
  }
24
46
 
47
+ // Create your room with custom logic
25
48
  @Room({
26
- path: "chess-{id}",
27
- maxUsers: 2,
49
+ path: "chess-{id}", // Dynamic room path with parameters
50
+ maxUsers: 2, // Limit number of users per room
28
51
  })
29
52
  export class MyRoom extends RoomSchema {
30
- static onAuth() {}
53
+ // Authentication hook
54
+ static onAuth() {
55
+ // Add your authentication logic here
56
+ }
31
57
 
32
58
  constructor(readonly room, readonly params: { id: string }) {
33
59
  super();
34
60
  }
35
61
 
36
- onCreate() {}
37
-
38
- @action("move")
39
- move(player: Player, data: any) {
40
- player.x.set(data.x);
41
- player.y.set(data.y);
62
+ // Room lifecycle hooks
63
+ onCreate() {
64
+ // Called when the room is created
42
65
  }
43
66
 
44
67
  onJoin(player: Player) {
45
68
  console.log(player.id(), "joined");
69
+ // Handle player joining
70
+ }
71
+
72
+ onLeave() {
73
+ // Handle player leaving
46
74
  }
47
75
 
48
- onLeave() {}
76
+ // Custom actions
77
+ @action("move")
78
+ move(player: Player, data: any) {
79
+ player.x.set(data.x);
80
+ player.y.set(data.y);
81
+ }
49
82
  }
50
83
 
84
+ // Create your server with room definitions
51
85
  export default class MyServer extends Server {
52
86
  rooms = [MyRoom];
53
87
  }
54
- ```
88
+ ```
89
+
90
+ ## Decorators
91
+
92
+ - `@Room(options)`: Defines a room with configuration options
93
+ - `@sync()`: Marks a property for automatic synchronization
94
+ - `@id()`: Marks a property as the unique identifier
95
+ - `@users(PlayerClass)`: Creates a synchronized collection of users
96
+ - `@action(name)`: Defines a method as a callable action from clients
97
+
98
+ ## Lifecycle Hooks
99
+
100
+ - `onAuth()`: Called during authentication
101
+ - `onCreate()`: Called when the room is created
102
+ - `onJoin(player)`: Called when a player joins
103
+ - `onLeave()`: Called when a player leaves
104
+
105
+ ## Best Practices
106
+
107
+ 1. Always define proper types for your actions' data parameters
108
+ 2. Implement proper authentication in the `onAuth` hook
109
+ 3. Clean up resources in the `onLeave` hook
110
+ 4. Use TypeScript for better type safety and development experience
111
+
112
+ ## License
113
+
114
+ MIT