@signe/room 1.2.1 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +8 -15
- package/dist/index.js +140 -239
- package/dist/index.js.map +1 -1
- package/examples/game/app/client.tsx +0 -26
- package/examples/game/app/components/Counter.tsx +47 -9
- package/examples/game/package-lock.json +225 -0
- package/examples/game/package.json +1 -1
- package/examples/game/party/game.room.ts +13 -5
- package/examples/game/party/server.ts +1 -4
- package/examples/game/shared/room.schema.ts +9 -1
- package/package.json +2 -2
- package/src/decorators.ts +2 -2
- package/src/mock.ts +5 -1
- package/src/server.ts +173 -94
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Request as Request$1,
|
|
1
|
+
import { Request as Request$1, WebSocket, DurableObjectState, AnalyticsEngineDataset, DurableObjectStorage, VectorizeIndex, R2Bucket, KVNamespace } from '@cloudflare/workers-types';
|
|
2
2
|
import { z } from 'zod';
|
|
3
3
|
|
|
4
4
|
type AssetFetcher = {
|
|
@@ -47,16 +47,6 @@ type Context = {
|
|
|
47
47
|
bindings: CustomBindings;
|
|
48
48
|
};
|
|
49
49
|
type AI = Record<string, never>;
|
|
50
|
-
type Lobby = {
|
|
51
|
-
id: string;
|
|
52
|
-
env: Record<string, unknown>;
|
|
53
|
-
ai: AI;
|
|
54
|
-
parties: Context["parties"];
|
|
55
|
-
vectorize: Context["vectorize"];
|
|
56
|
-
analytics: AnalyticsEngineDataset;
|
|
57
|
-
assets: AssetFetcher;
|
|
58
|
-
bindings: CustomBindings;
|
|
59
|
-
};
|
|
60
50
|
type ImmutablePrimitive = undefined | null | boolean | string | number;
|
|
61
51
|
type Immutable<T> = T extends ImmutablePrimitive ? T : T extends Array<infer U> ? ImmutableArray<U> : T extends Map<infer K, infer V> ? ImmutableMap<K, V> : T extends Set<infer M> ? ImmutableSet<M> : ImmutableObject<T>;
|
|
62
52
|
type ImmutableArray<T> = ReadonlyArray<Immutable<T>>;
|
|
@@ -191,7 +181,7 @@ interface RoomOptions {
|
|
|
191
181
|
throttleSync?: number;
|
|
192
182
|
hibernate?: boolean;
|
|
193
183
|
guards?: RoomGuardFn[];
|
|
194
|
-
|
|
184
|
+
sessionExpiryTime?: number;
|
|
195
185
|
}
|
|
196
186
|
declare function Room(options: RoomOptions): (target: any) => void;
|
|
197
187
|
/**
|
|
@@ -227,6 +217,7 @@ declare class MockPartyRoom {
|
|
|
227
217
|
constructor(id?: string);
|
|
228
218
|
connection(client: any): void;
|
|
229
219
|
broadcast(data: any): void;
|
|
220
|
+
getConnections(): Map<string, MockPartySocket>;
|
|
230
221
|
clear(): void;
|
|
231
222
|
}
|
|
232
223
|
declare class MockConnection {
|
|
@@ -262,8 +253,6 @@ declare class Server implements Server$1 {
|
|
|
262
253
|
readonly room: Room$1;
|
|
263
254
|
subRoom: any;
|
|
264
255
|
rooms: any[];
|
|
265
|
-
private timeoutHandles;
|
|
266
|
-
static onBeforeConnect(request: Request, lobby: Lobby): Promise<Request>;
|
|
267
256
|
/**
|
|
268
257
|
* @constructor
|
|
269
258
|
* @param {Party.Room} room - The room object representing the current game or application instance.
|
|
@@ -301,13 +290,13 @@ declare class Server implements Server$1 {
|
|
|
301
290
|
* ```
|
|
302
291
|
*/
|
|
303
292
|
onStart(): Promise<void>;
|
|
293
|
+
private garbageCollector;
|
|
304
294
|
/**
|
|
305
295
|
* @method createRoom
|
|
306
296
|
* @private
|
|
307
297
|
* @async
|
|
308
298
|
* @param {CreateRoomOptions} [options={}] - Options for creating the room.
|
|
309
299
|
* @returns {Promise<Object>} The created room instance.
|
|
310
|
-
* @throws {Error} If no matching room is found.
|
|
311
300
|
*
|
|
312
301
|
* @example
|
|
313
302
|
* ```typescript
|
|
@@ -352,8 +341,10 @@ declare class Server implements Server$1 {
|
|
|
352
341
|
* ```
|
|
353
342
|
*/
|
|
354
343
|
private getUsersProperty;
|
|
344
|
+
private getUsersPropName;
|
|
355
345
|
private getSession;
|
|
356
346
|
private saveSession;
|
|
347
|
+
private updateSessionConnection;
|
|
357
348
|
private deleteSession;
|
|
358
349
|
/**
|
|
359
350
|
* @method onConnect
|
|
@@ -406,6 +397,8 @@ declare class Server implements Server$1 {
|
|
|
406
397
|
*/
|
|
407
398
|
onClose(conn: Connection): Promise<void>;
|
|
408
399
|
onAlarm(): Promise<void>;
|
|
400
|
+
onError(connection: Connection, error: Error): Promise<void>;
|
|
401
|
+
onRequest(req: Request): Promise<Response>;
|
|
409
402
|
}
|
|
410
403
|
|
|
411
404
|
export { Action, ClientIo, Guard, MockConnection, Room, RoomGuard, type RoomOptions, Server, ServerIo };
|
package/dist/index.js
CHANGED
|
@@ -20,7 +20,7 @@ function Room(options) {
|
|
|
20
20
|
target.maxUsers = options.maxUsers;
|
|
21
21
|
target.throttleStorage = options.throttleStorage;
|
|
22
22
|
target.throttleSync = options.throttleSync;
|
|
23
|
-
target.
|
|
23
|
+
target.sessionExpiryTime = options.sessionExpiryTime ?? 5 * 60 * 1e3;
|
|
24
24
|
if (options.guards) {
|
|
25
25
|
target["_roomGuards"] = options.guards;
|
|
26
26
|
}
|
|
@@ -44,11 +44,6 @@ function Guard(guards) {
|
|
|
44
44
|
__name(Guard, "Guard");
|
|
45
45
|
|
|
46
46
|
// ../sync/src/utils.ts
|
|
47
|
-
function isClass(obj) {
|
|
48
|
-
return typeof obj === "function" && obj.prototype && obj.prototype.constructor === obj;
|
|
49
|
-
}
|
|
50
|
-
__name(isClass, "isClass");
|
|
51
|
-
var isObject = /* @__PURE__ */ __name((item) => item && typeof item === "object" && !Array.isArray(item) && item !== null, "isObject");
|
|
52
47
|
function generateShortUUID() {
|
|
53
48
|
const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
|
54
49
|
let uuid = "";
|
|
@@ -81,7 +76,7 @@ var Storage = class {
|
|
|
81
76
|
};
|
|
82
77
|
|
|
83
78
|
// src/mock.ts
|
|
84
|
-
var MockPartySocket = class
|
|
79
|
+
var MockPartySocket = class {
|
|
85
80
|
static {
|
|
86
81
|
__name(this, "MockPartySocket");
|
|
87
82
|
}
|
|
@@ -120,6 +115,9 @@ var MockPartyRoom = class MockPartyRoom2 {
|
|
|
120
115
|
client._trigger("message", data);
|
|
121
116
|
});
|
|
122
117
|
}
|
|
118
|
+
getConnections() {
|
|
119
|
+
return this.clients;
|
|
120
|
+
}
|
|
123
121
|
clear() {
|
|
124
122
|
this.clients.clear();
|
|
125
123
|
}
|
|
@@ -139,172 +137,7 @@ var ClientIo = MockPartySocket;
|
|
|
139
137
|
// src/server.ts
|
|
140
138
|
import { dset as dset2 } from "dset";
|
|
141
139
|
import z from "zod";
|
|
142
|
-
|
|
143
|
-
// ../sync/src/core.ts
|
|
144
|
-
import { ArraySubject, ObjectSubject, isSignal } from "@signe/reactive";
|
|
145
|
-
var syncClass = /* @__PURE__ */ __name((instance, options = {}) => {
|
|
146
|
-
const cacheSync = /* @__PURE__ */ new Map();
|
|
147
|
-
const cachePersist = /* @__PURE__ */ new Set();
|
|
148
|
-
instance.$valuesChanges = {
|
|
149
|
-
set: /* @__PURE__ */ __name((path, value) => {
|
|
150
|
-
cacheSync.set(path, value);
|
|
151
|
-
options.onSync?.(cacheSync);
|
|
152
|
-
}, "set"),
|
|
153
|
-
setPersist: /* @__PURE__ */ __name((path) => {
|
|
154
|
-
if (path == "") path = ".";
|
|
155
|
-
cachePersist.add(path);
|
|
156
|
-
options.onPersist?.(cachePersist);
|
|
157
|
-
}, "setPersist"),
|
|
158
|
-
has: /* @__PURE__ */ __name((path) => {
|
|
159
|
-
return cacheSync.has(path);
|
|
160
|
-
}, "has"),
|
|
161
|
-
get: /* @__PURE__ */ __name((path) => {
|
|
162
|
-
return cacheSync.get(path);
|
|
163
|
-
}, "get")
|
|
164
|
-
};
|
|
165
|
-
createSyncClass(instance);
|
|
166
|
-
}, "syncClass");
|
|
167
|
-
function createStatesSnapshot(instance) {
|
|
168
|
-
let persistObject = {};
|
|
169
|
-
if (instance.$snapshot) {
|
|
170
|
-
for (const key of instance.$snapshot.keys()) {
|
|
171
|
-
const signal = instance.$snapshot.get(key);
|
|
172
|
-
const persist = signal.options.persist ?? true;
|
|
173
|
-
let value = signal();
|
|
174
|
-
if (isObject(value) || Array.isArray(value)) {
|
|
175
|
-
break;
|
|
176
|
-
}
|
|
177
|
-
if (persist) {
|
|
178
|
-
persistObject[key] = value;
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
return persistObject;
|
|
183
|
-
}
|
|
184
|
-
__name(createStatesSnapshot, "createStatesSnapshot");
|
|
185
|
-
function setMetadata(target, key, value) {
|
|
186
|
-
const meta = target.constructor._propertyMetadata;
|
|
187
|
-
const propId = meta?.get(key);
|
|
188
|
-
if (propId) {
|
|
189
|
-
if (isSignal(target[propId])) {
|
|
190
|
-
target[propId].set(value);
|
|
191
|
-
} else {
|
|
192
|
-
target[propId] = value;
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
__name(setMetadata, "setMetadata");
|
|
197
|
-
var createSyncClass = /* @__PURE__ */ __name((currentClass, parentKey = null, parentClass = null, path = "") => {
|
|
198
|
-
currentClass.$path = path;
|
|
199
|
-
if (parentClass) {
|
|
200
|
-
currentClass.$valuesChanges = parentClass.$valuesChanges;
|
|
201
|
-
}
|
|
202
|
-
if (parentKey) {
|
|
203
|
-
setMetadata(currentClass, "id", parentKey);
|
|
204
|
-
}
|
|
205
|
-
if (currentClass.$snapshot) {
|
|
206
|
-
for (const key of currentClass.$snapshot.keys()) {
|
|
207
|
-
const signal = currentClass.$snapshot.get(key);
|
|
208
|
-
const syncToClient = signal.options.syncToClient ?? true;
|
|
209
|
-
const persist = signal.options.persist ?? true;
|
|
210
|
-
let value = signal();
|
|
211
|
-
if (isObject(value) || Array.isArray(value)) {
|
|
212
|
-
value = {
|
|
213
|
-
...value
|
|
214
|
-
};
|
|
215
|
-
}
|
|
216
|
-
const newPath = (path ? path + "." : "") + key;
|
|
217
|
-
if (syncToClient) {
|
|
218
|
-
currentClass.$valuesChanges.set(newPath, value);
|
|
219
|
-
}
|
|
220
|
-
if (persist) {
|
|
221
|
-
if (parentClass) currentClass.$valuesChanges.setPersist(path);
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
}, "createSyncClass");
|
|
226
|
-
|
|
227
|
-
// ../sync/src/load.ts
|
|
228
|
-
import { isSignal as isSignal2 } from "@signe/reactive";
|
|
229
|
-
function load(rootInstance, values, valueIsObject) {
|
|
230
|
-
if (valueIsObject) {
|
|
231
|
-
loadFromObject(rootInstance, values);
|
|
232
|
-
} else {
|
|
233
|
-
loadFromPaths(rootInstance, values);
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
__name(load, "load");
|
|
237
|
-
function loadFromPaths(rootInstance, values) {
|
|
238
|
-
for (const [path, value] of Object.entries(values)) {
|
|
239
|
-
const parts = path.split(".");
|
|
240
|
-
loadValue(rootInstance, parts, value);
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
__name(loadFromPaths, "loadFromPaths");
|
|
244
|
-
function loadFromObject(rootInstance, values, currentPath = "") {
|
|
245
|
-
for (let key in values) {
|
|
246
|
-
const value = values[key];
|
|
247
|
-
const newPath = currentPath ? `${currentPath}.${key}` : key;
|
|
248
|
-
if (typeof value === "object" && !Array.isArray(value) && value !== null) {
|
|
249
|
-
loadFromObject(rootInstance, value, newPath);
|
|
250
|
-
} else {
|
|
251
|
-
const parts = newPath.split(".");
|
|
252
|
-
loadValue(rootInstance, parts, value);
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
__name(loadFromObject, "loadFromObject");
|
|
257
|
-
function loadValue(rootInstance, parts, value) {
|
|
258
|
-
let current = rootInstance;
|
|
259
|
-
for (let i = 0; i < parts.length; i++) {
|
|
260
|
-
const part = parts[i];
|
|
261
|
-
if (i === parts.length - 1) {
|
|
262
|
-
if (value == "$delete") {
|
|
263
|
-
if (isSignal2(current)) {
|
|
264
|
-
current = current();
|
|
265
|
-
}
|
|
266
|
-
Reflect.deleteProperty(current, part);
|
|
267
|
-
} else if (current[part]?._subject) {
|
|
268
|
-
current[part].set(value);
|
|
269
|
-
} else {
|
|
270
|
-
current[part] = value;
|
|
271
|
-
}
|
|
272
|
-
} else {
|
|
273
|
-
if (isSignal2(current)) {
|
|
274
|
-
current = current();
|
|
275
|
-
}
|
|
276
|
-
const currentValue = current[part];
|
|
277
|
-
if (currentValue === void 0) {
|
|
278
|
-
const parentInstance = getByPath(rootInstance, parts.slice(0, i).join("."));
|
|
279
|
-
const classType = parentInstance?.options?.classType;
|
|
280
|
-
if (classType) {
|
|
281
|
-
current[part] = !isClass(classType) ? classType(part) : new classType();
|
|
282
|
-
setMetadata(current[part], "id", part);
|
|
283
|
-
} else {
|
|
284
|
-
current[part] = {};
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
current = current[part];
|
|
288
|
-
}
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
__name(loadValue, "loadValue");
|
|
292
|
-
function getByPath(root, path) {
|
|
293
|
-
const parts = path.split(".");
|
|
294
|
-
let current = root;
|
|
295
|
-
for (const part of parts) {
|
|
296
|
-
if (isSignal2(current)) {
|
|
297
|
-
current = current();
|
|
298
|
-
}
|
|
299
|
-
if (current[part]) {
|
|
300
|
-
current = current[part];
|
|
301
|
-
} else {
|
|
302
|
-
return void 0;
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
|
-
return current;
|
|
306
|
-
}
|
|
307
|
-
__name(getByPath, "getByPath");
|
|
140
|
+
import { createStatesSnapshot, getByPath, load, syncClass, DELETE_TOKEN, generateShortUUID as generateShortUUID2 } from "@signe/sync";
|
|
308
141
|
|
|
309
142
|
// src/utils.ts
|
|
310
143
|
import { dset } from "dset";
|
|
@@ -316,10 +149,10 @@ async function awaitReturn(val) {
|
|
|
316
149
|
return isPromise(val) ? await val : val;
|
|
317
150
|
}
|
|
318
151
|
__name(awaitReturn, "awaitReturn");
|
|
319
|
-
function
|
|
152
|
+
function isClass(obj) {
|
|
320
153
|
return typeof obj === "function" && obj.prototype && obj.prototype.constructor === obj;
|
|
321
154
|
}
|
|
322
|
-
__name(
|
|
155
|
+
__name(isClass, "isClass");
|
|
323
156
|
function throttle(func, wait) {
|
|
324
157
|
let timeout = null;
|
|
325
158
|
let lastArgs = null;
|
|
@@ -399,12 +232,6 @@ var Server = class {
|
|
|
399
232
|
room;
|
|
400
233
|
subRoom;
|
|
401
234
|
rooms;
|
|
402
|
-
timeoutHandles;
|
|
403
|
-
static async onBeforeConnect(request, lobby) {
|
|
404
|
-
const token = new URL(request.url).searchParams.get("token") ?? "";
|
|
405
|
-
request.headers.set("X-User-ID", token);
|
|
406
|
-
return request;
|
|
407
|
-
}
|
|
408
235
|
/**
|
|
409
236
|
* @constructor
|
|
410
237
|
* @param {Party.Room} room - The room object representing the current game or application instance.
|
|
@@ -418,7 +245,6 @@ var Server = class {
|
|
|
418
245
|
this.room = room;
|
|
419
246
|
this.subRoom = null;
|
|
420
247
|
this.rooms = [];
|
|
421
|
-
this.timeoutHandles = /* @__PURE__ */ new Map();
|
|
422
248
|
}
|
|
423
249
|
/**
|
|
424
250
|
* @readonly
|
|
@@ -453,13 +279,51 @@ var Server = class {
|
|
|
453
279
|
this.subRoom = await this.createRoom();
|
|
454
280
|
}
|
|
455
281
|
}
|
|
282
|
+
async garbageCollector(options) {
|
|
283
|
+
const subRoom = await this.getSubRoom();
|
|
284
|
+
if (!subRoom) return;
|
|
285
|
+
const activeConnections = [
|
|
286
|
+
...this.room.getConnections()
|
|
287
|
+
];
|
|
288
|
+
const activePrivateIds = new Set(activeConnections.map((conn) => conn.id));
|
|
289
|
+
try {
|
|
290
|
+
const sessions = await this.room.storage.list();
|
|
291
|
+
const users = this.getUsersProperty(subRoom);
|
|
292
|
+
const usersPropName = this.getUsersPropName(subRoom);
|
|
293
|
+
const validPublicIds = /* @__PURE__ */ new Set();
|
|
294
|
+
const expiredPublicIds = /* @__PURE__ */ new Set();
|
|
295
|
+
const SESSION_EXPIRY_TIME = options.sessionExpiryTime;
|
|
296
|
+
const now = Date.now();
|
|
297
|
+
for (const [key, session] of sessions) {
|
|
298
|
+
if (!key.startsWith("session:")) continue;
|
|
299
|
+
const privateId = key.replace("session:", "");
|
|
300
|
+
const typedSession = session;
|
|
301
|
+
if (!activePrivateIds.has(privateId) && !typedSession.connected && now - typedSession.created > SESSION_EXPIRY_TIME) {
|
|
302
|
+
await this.deleteSession(privateId);
|
|
303
|
+
expiredPublicIds.add(typedSession.publicId);
|
|
304
|
+
} else if (typedSession && typedSession.publicId) {
|
|
305
|
+
validPublicIds.add(typedSession.publicId);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
if (users && usersPropName) {
|
|
309
|
+
const currentUsers = users();
|
|
310
|
+
for (const publicId in currentUsers) {
|
|
311
|
+
if (expiredPublicIds.has(publicId) && !validPublicIds.has(publicId)) {
|
|
312
|
+
delete currentUsers[publicId];
|
|
313
|
+
await this.room.storage.delete(`${usersPropName}.${publicId}`);
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
} catch (error) {
|
|
318
|
+
console.error("Error in garbage collector:", error);
|
|
319
|
+
}
|
|
320
|
+
}
|
|
456
321
|
/**
|
|
457
322
|
* @method createRoom
|
|
458
323
|
* @private
|
|
459
324
|
* @async
|
|
460
325
|
* @param {CreateRoomOptions} [options={}] - Options for creating the room.
|
|
461
326
|
* @returns {Promise<Object>} The created room instance.
|
|
462
|
-
* @throws {Error} If no matching room is found.
|
|
463
327
|
*
|
|
464
328
|
* @example
|
|
465
329
|
* ```typescript
|
|
@@ -473,6 +337,7 @@ var Server = class {
|
|
|
473
337
|
async createRoom(options = {}) {
|
|
474
338
|
let instance;
|
|
475
339
|
let init = true;
|
|
340
|
+
let initPersist = true;
|
|
476
341
|
for (let room of this.rooms) {
|
|
477
342
|
const params = extractParams(room.path, this.room.id);
|
|
478
343
|
if (params) {
|
|
@@ -481,21 +346,23 @@ var Server = class {
|
|
|
481
346
|
}
|
|
482
347
|
}
|
|
483
348
|
if (!instance) {
|
|
484
|
-
|
|
349
|
+
return null;
|
|
485
350
|
}
|
|
486
351
|
const loadMemory = /* @__PURE__ */ __name(async () => {
|
|
487
352
|
const root = await this.room.storage.get(".");
|
|
488
353
|
const memory = await this.room.storage.list();
|
|
489
354
|
const tmpObject = root || {};
|
|
490
355
|
for (let [key, value] of memory) {
|
|
356
|
+
if (key.startsWith("session:")) {
|
|
357
|
+
continue;
|
|
358
|
+
}
|
|
491
359
|
if (key == ".") {
|
|
492
360
|
continue;
|
|
493
361
|
}
|
|
494
362
|
dset2(tmpObject, key, value);
|
|
495
363
|
}
|
|
496
|
-
load(instance, tmpObject);
|
|
364
|
+
load(instance, tmpObject, true);
|
|
497
365
|
}, "loadMemory");
|
|
498
|
-
await loadMemory();
|
|
499
366
|
instance.$memoryAll = {};
|
|
500
367
|
const syncCb = /* @__PURE__ */ __name((values) => {
|
|
501
368
|
if (options.getMemoryAll) {
|
|
@@ -513,10 +380,18 @@ var Server = class {
|
|
|
513
380
|
values.clear();
|
|
514
381
|
}, "syncCb");
|
|
515
382
|
const persistCb = /* @__PURE__ */ __name(async (values) => {
|
|
516
|
-
|
|
383
|
+
if (initPersist) {
|
|
384
|
+
values.clear();
|
|
385
|
+
return;
|
|
386
|
+
}
|
|
387
|
+
for (let [path, value] of values) {
|
|
517
388
|
const _instance = path == "." ? instance : getByPath(instance, path);
|
|
518
389
|
const itemValue = createStatesSnapshot(_instance);
|
|
519
|
-
|
|
390
|
+
if (value == DELETE_TOKEN) {
|
|
391
|
+
await this.room.storage.delete(path);
|
|
392
|
+
} else {
|
|
393
|
+
await this.room.storage.put(path, itemValue);
|
|
394
|
+
}
|
|
520
395
|
}
|
|
521
396
|
values.clear();
|
|
522
397
|
}, "persistCb");
|
|
@@ -524,6 +399,8 @@ var Server = class {
|
|
|
524
399
|
onSync: throttle(syncCb, instance["throttleSync"] ?? 500),
|
|
525
400
|
onPersist: throttle(persistCb, instance["throttleStorage"] ?? 2e3)
|
|
526
401
|
});
|
|
402
|
+
await loadMemory();
|
|
403
|
+
initPersist = false;
|
|
527
404
|
return instance;
|
|
528
405
|
}
|
|
529
406
|
/**
|
|
@@ -574,6 +451,10 @@ var Server = class {
|
|
|
574
451
|
}
|
|
575
452
|
return null;
|
|
576
453
|
}
|
|
454
|
+
getUsersPropName(subRoom) {
|
|
455
|
+
const meta = subRoom.constructor["_propertyMetadata"];
|
|
456
|
+
return meta?.get("users");
|
|
457
|
+
}
|
|
577
458
|
async getSession(privateId) {
|
|
578
459
|
if (!privateId) return null;
|
|
579
460
|
try {
|
|
@@ -584,7 +465,21 @@ var Server = class {
|
|
|
584
465
|
}
|
|
585
466
|
}
|
|
586
467
|
async saveSession(privateId, data) {
|
|
587
|
-
|
|
468
|
+
const sessionData = {
|
|
469
|
+
...data,
|
|
470
|
+
created: data.created || Date.now(),
|
|
471
|
+
connected: data.connected !== void 0 ? data.connected : true
|
|
472
|
+
};
|
|
473
|
+
await this.room.storage.put(`session:${privateId}`, sessionData);
|
|
474
|
+
}
|
|
475
|
+
async updateSessionConnection(privateId, connected) {
|
|
476
|
+
const session = await this.getSession(privateId);
|
|
477
|
+
if (session) {
|
|
478
|
+
await this.saveSession(privateId, {
|
|
479
|
+
...session,
|
|
480
|
+
connected
|
|
481
|
+
});
|
|
482
|
+
}
|
|
588
483
|
}
|
|
589
484
|
async deleteSession(privateId) {
|
|
590
485
|
await this.room.storage.delete(`session:${privateId}`);
|
|
@@ -609,6 +504,14 @@ var Server = class {
|
|
|
609
504
|
const subRoom = await this.getSubRoom({
|
|
610
505
|
getMemoryAll: true
|
|
611
506
|
});
|
|
507
|
+
if (!subRoom) {
|
|
508
|
+
conn.close();
|
|
509
|
+
return;
|
|
510
|
+
}
|
|
511
|
+
const sessionExpiryTime = subRoom.constructor.sessionExpiryTime;
|
|
512
|
+
await this.garbageCollector({
|
|
513
|
+
sessionExpiryTime
|
|
514
|
+
});
|
|
612
515
|
const roomGuards = subRoom.constructor["_roomGuards"] || [];
|
|
613
516
|
for (const guard of roomGuards) {
|
|
614
517
|
const isAuthorized = await guard(conn, ctx);
|
|
@@ -617,34 +520,35 @@ var Server = class {
|
|
|
617
520
|
return;
|
|
618
521
|
}
|
|
619
522
|
}
|
|
620
|
-
const
|
|
621
|
-
const
|
|
622
|
-
const publicId = existingSession?.publicId || generateShortUUID();
|
|
623
|
-
const privateId = existingSession ? providedPrivateId : generateShortUUID();
|
|
523
|
+
const existingSession = await this.getSession(conn.id);
|
|
524
|
+
const publicId = existingSession?.publicId || generateShortUUID2();
|
|
624
525
|
let user = null;
|
|
625
526
|
const signal = this.getUsersProperty(subRoom);
|
|
527
|
+
const usersPropName = this.getUsersPropName(subRoom);
|
|
626
528
|
if (signal) {
|
|
627
529
|
const { classType } = signal.options;
|
|
628
530
|
if (!existingSession?.publicId) {
|
|
629
|
-
user =
|
|
531
|
+
user = isClass(classType) ? new classType() : classType(conn, ctx);
|
|
630
532
|
signal()[publicId] = user;
|
|
533
|
+
const snapshot = createStatesSnapshot(user);
|
|
534
|
+
this.room.storage.put(`${usersPropName}.${publicId}`, snapshot);
|
|
631
535
|
}
|
|
632
536
|
if (!existingSession) {
|
|
633
|
-
await this.saveSession(
|
|
537
|
+
await this.saveSession(conn.id, {
|
|
634
538
|
publicId
|
|
635
539
|
});
|
|
540
|
+
} else {
|
|
541
|
+
await this.updateSessionConnection(conn.id, true);
|
|
636
542
|
}
|
|
637
543
|
}
|
|
638
544
|
await awaitReturn(subRoom["onJoin"]?.(user, conn, ctx));
|
|
639
545
|
conn.setState({
|
|
640
|
-
publicId
|
|
641
|
-
privateId
|
|
546
|
+
publicId
|
|
642
547
|
});
|
|
643
548
|
conn.send(JSON.stringify({
|
|
644
549
|
type: "sync",
|
|
645
550
|
value: {
|
|
646
551
|
pId: publicId,
|
|
647
|
-
privateId,
|
|
648
552
|
...subRoom.$memoryAll
|
|
649
553
|
}
|
|
650
554
|
}));
|
|
@@ -725,60 +629,57 @@ var Server = class {
|
|
|
725
629
|
*/
|
|
726
630
|
async onClose(conn) {
|
|
727
631
|
const subRoom = await this.getSubRoom();
|
|
632
|
+
if (!subRoom) {
|
|
633
|
+
return;
|
|
634
|
+
}
|
|
728
635
|
const signal = this.getUsersProperty(subRoom);
|
|
729
636
|
if (!conn.state) {
|
|
730
637
|
return;
|
|
731
638
|
}
|
|
732
|
-
const
|
|
639
|
+
const privateId = conn.id;
|
|
640
|
+
const { publicId } = conn.state;
|
|
733
641
|
const user = signal?.()[publicId];
|
|
734
642
|
if (!user) return;
|
|
735
|
-
|
|
736
|
-
|
|
643
|
+
await awaitReturn(subRoom["onLeave"]?.(user, conn));
|
|
644
|
+
await this.updateSessionConnection(privateId, false);
|
|
645
|
+
this.room.broadcast(JSON.stringify({
|
|
646
|
+
type: "user_disconnected",
|
|
647
|
+
value: {
|
|
737
648
|
publicId
|
|
738
|
-
});
|
|
739
|
-
}
|
|
740
|
-
const existingTimeout = this.timeoutHandles.get(privateId);
|
|
741
|
-
if (existingTimeout) {
|
|
742
|
-
clearTimeout(existingTimeout);
|
|
743
|
-
this.timeoutHandles.delete(privateId);
|
|
744
|
-
}
|
|
745
|
-
const cleanup = /* @__PURE__ */ __name(async () => {
|
|
746
|
-
await awaitReturn(subRoom["onLeave"]?.(user, conn));
|
|
747
|
-
if (signal) {
|
|
748
|
-
delete signal()[publicId];
|
|
749
|
-
}
|
|
750
|
-
if (privateId) {
|
|
751
|
-
await this.deleteSession(privateId);
|
|
752
649
|
}
|
|
753
|
-
|
|
754
|
-
type: "user_disconnected",
|
|
755
|
-
value: {
|
|
756
|
-
publicId
|
|
757
|
-
}
|
|
758
|
-
}));
|
|
759
|
-
this.timeoutHandles.delete(privateId);
|
|
760
|
-
}, "cleanup");
|
|
761
|
-
const disconnectTimeout = subRoom.constructor.disconnectTimeout ?? 0;
|
|
762
|
-
if (disconnectTimeout > 0) {
|
|
763
|
-
if (user.status) {
|
|
764
|
-
user.status.set("offline");
|
|
765
|
-
}
|
|
766
|
-
this.room.broadcast(JSON.stringify({
|
|
767
|
-
type: "user_offline",
|
|
768
|
-
value: {
|
|
769
|
-
publicId
|
|
770
|
-
}
|
|
771
|
-
}));
|
|
772
|
-
const timeout = setTimeout(cleanup, disconnectTimeout);
|
|
773
|
-
this.timeoutHandles.set(privateId, timeout);
|
|
774
|
-
} else {
|
|
775
|
-
await cleanup();
|
|
776
|
-
}
|
|
650
|
+
}));
|
|
777
651
|
}
|
|
778
652
|
async onAlarm() {
|
|
779
653
|
const subRoom = await this.getSubRoom();
|
|
780
654
|
await awaitReturn(subRoom["onAlarm"]?.(subRoom));
|
|
781
655
|
}
|
|
656
|
+
async onError(connection, error) {
|
|
657
|
+
const subRoom = await this.getSubRoom();
|
|
658
|
+
await awaitReturn(subRoom["onError"]?.(connection, error));
|
|
659
|
+
}
|
|
660
|
+
async onRequest(req) {
|
|
661
|
+
const subRoom = await this.getSubRoom();
|
|
662
|
+
const res = /* @__PURE__ */ __name((body, status) => {
|
|
663
|
+
return new Response(JSON.stringify(body), {
|
|
664
|
+
status
|
|
665
|
+
});
|
|
666
|
+
}, "res");
|
|
667
|
+
if (!subRoom) {
|
|
668
|
+
return res({
|
|
669
|
+
error: "Not found"
|
|
670
|
+
}, 404);
|
|
671
|
+
}
|
|
672
|
+
const response = await awaitReturn(subRoom["onRequest"]?.(req, this.room));
|
|
673
|
+
if (!response) {
|
|
674
|
+
return res({
|
|
675
|
+
error: "Not found"
|
|
676
|
+
}, 404);
|
|
677
|
+
}
|
|
678
|
+
if (response instanceof Response) {
|
|
679
|
+
return response;
|
|
680
|
+
}
|
|
681
|
+
return res(response, 200);
|
|
682
|
+
}
|
|
782
683
|
};
|
|
783
684
|
export {
|
|
784
685
|
Action,
|