@pluv/io 3.2.2 → 4.0.1
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/.turbo/turbo-build.log +15 -17
- package/CHANGELOG.md +99 -0
- package/dist/index.d.mts +427 -423
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +1390 -1476
- package/dist/index.mjs.map +1 -0
- package/package.json +18 -14
- package/src/IORoom.ts +65 -17
- package/src/PluvIO.ts +4 -16
- package/src/PluvRouter.ts +3 -10
- package/src/PluvServer.ts +31 -22
- package/src/index.ts +1 -0
- package/src/types.ts +13 -2
- package/src/version.generated.ts +1 -1
- package/tsdown.config.ts +14 -0
- package/dist/index.d.ts +0 -539
- package/dist/index.js +0 -1575
package/dist/index.js
DELETED
|
@@ -1,1575 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __create = Object.create;
|
|
3
|
-
var __defProp = Object.defineProperty;
|
|
4
|
-
var __defProps = Object.defineProperties;
|
|
5
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
-
var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
|
|
7
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
8
|
-
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
|
9
|
-
var __getProtoOf = Object.getPrototypeOf;
|
|
10
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
11
|
-
var __propIsEnum = Object.prototype.propertyIsEnumerable;
|
|
12
|
-
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
13
|
-
var __spreadValues = (a, b) => {
|
|
14
|
-
for (var prop in b || (b = {}))
|
|
15
|
-
if (__hasOwnProp.call(b, prop))
|
|
16
|
-
__defNormalProp(a, prop, b[prop]);
|
|
17
|
-
if (__getOwnPropSymbols)
|
|
18
|
-
for (var prop of __getOwnPropSymbols(b)) {
|
|
19
|
-
if (__propIsEnum.call(b, prop))
|
|
20
|
-
__defNormalProp(a, prop, b[prop]);
|
|
21
|
-
}
|
|
22
|
-
return a;
|
|
23
|
-
};
|
|
24
|
-
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
|
|
25
|
-
var __objRest = (source, exclude) => {
|
|
26
|
-
var target = {};
|
|
27
|
-
for (var prop in source)
|
|
28
|
-
if (__hasOwnProp.call(source, prop) && exclude.indexOf(prop) < 0)
|
|
29
|
-
target[prop] = source[prop];
|
|
30
|
-
if (source != null && __getOwnPropSymbols)
|
|
31
|
-
for (var prop of __getOwnPropSymbols(source)) {
|
|
32
|
-
if (exclude.indexOf(prop) < 0 && __propIsEnum.call(source, prop))
|
|
33
|
-
target[prop] = source[prop];
|
|
34
|
-
}
|
|
35
|
-
return target;
|
|
36
|
-
};
|
|
37
|
-
var __export = (target, all) => {
|
|
38
|
-
for (var name in all)
|
|
39
|
-
__defProp(target, name, { get: all[name], enumerable: true });
|
|
40
|
-
};
|
|
41
|
-
var __copyProps = (to, from, except, desc) => {
|
|
42
|
-
if (from && typeof from === "object" || typeof from === "function") {
|
|
43
|
-
for (let key of __getOwnPropNames(from))
|
|
44
|
-
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
45
|
-
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
46
|
-
}
|
|
47
|
-
return to;
|
|
48
|
-
};
|
|
49
|
-
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
50
|
-
// If the importer is in node compatibility mode or this is not an ESM
|
|
51
|
-
// file that has been converted to a CommonJS file using a Babel-
|
|
52
|
-
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
53
|
-
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
54
|
-
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
55
|
-
mod
|
|
56
|
-
));
|
|
57
|
-
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
58
|
-
var __async = (__this, __arguments, generator) => {
|
|
59
|
-
return new Promise((resolve, reject) => {
|
|
60
|
-
var fulfilled = (value) => {
|
|
61
|
-
try {
|
|
62
|
-
step(generator.next(value));
|
|
63
|
-
} catch (e) {
|
|
64
|
-
reject(e);
|
|
65
|
-
}
|
|
66
|
-
};
|
|
67
|
-
var rejected = (value) => {
|
|
68
|
-
try {
|
|
69
|
-
step(generator.throw(value));
|
|
70
|
-
} catch (e) {
|
|
71
|
-
reject(e);
|
|
72
|
-
}
|
|
73
|
-
};
|
|
74
|
-
var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
|
|
75
|
-
step((generator = generator.apply(__this, __arguments)).next());
|
|
76
|
-
});
|
|
77
|
-
};
|
|
78
|
-
|
|
79
|
-
// src/index.ts
|
|
80
|
-
var index_exports = {};
|
|
81
|
-
__export(index_exports, {
|
|
82
|
-
AbstractPersistence: () => AbstractPersistence,
|
|
83
|
-
AbstractPlatform: () => AbstractPlatform,
|
|
84
|
-
AbstractPubSub: () => AbstractPubSub,
|
|
85
|
-
AbstractWebSocket: () => AbstractWebSocket,
|
|
86
|
-
PluvIO: () => PluvIO,
|
|
87
|
-
PluvProcedure: () => PluvProcedure,
|
|
88
|
-
PluvRouter: () => PluvRouter,
|
|
89
|
-
PluvServer: () => PluvServer,
|
|
90
|
-
authorize: () => authorize,
|
|
91
|
-
createIO: () => createIO
|
|
92
|
-
});
|
|
93
|
-
module.exports = __toCommonJS(index_exports);
|
|
94
|
-
|
|
95
|
-
// src/AbstractPersistence.ts
|
|
96
|
-
var AbstractPersistence = class {
|
|
97
|
-
};
|
|
98
|
-
|
|
99
|
-
// src/Persistence.ts
|
|
100
|
-
var Persistence = class extends AbstractPersistence {
|
|
101
|
-
constructor() {
|
|
102
|
-
super(...arguments);
|
|
103
|
-
this._storages = /* @__PURE__ */ new Map();
|
|
104
|
-
this._users = /* @__PURE__ */ new Map();
|
|
105
|
-
}
|
|
106
|
-
addUser(room, connectionId, user) {
|
|
107
|
-
var _a;
|
|
108
|
-
const users = (_a = this._users.get(room)) != null ? _a : /* @__PURE__ */ new Map();
|
|
109
|
-
this._users.set(room, users);
|
|
110
|
-
users.set(connectionId, user);
|
|
111
|
-
return Promise.resolve();
|
|
112
|
-
}
|
|
113
|
-
deleteStorageState(room) {
|
|
114
|
-
this._storages.delete(room);
|
|
115
|
-
return Promise.resolve();
|
|
116
|
-
}
|
|
117
|
-
deleteUser(room, connectionId) {
|
|
118
|
-
return __async(this, null, function* () {
|
|
119
|
-
const users = this._users.get(room);
|
|
120
|
-
if (!users) return;
|
|
121
|
-
users.delete(connectionId);
|
|
122
|
-
if (!users.size) {
|
|
123
|
-
yield this.deleteUsers(room);
|
|
124
|
-
yield this.deleteStorageState(room);
|
|
125
|
-
}
|
|
126
|
-
return Promise.resolve();
|
|
127
|
-
});
|
|
128
|
-
}
|
|
129
|
-
deleteUsers(room) {
|
|
130
|
-
this._users.delete(room);
|
|
131
|
-
return Promise.resolve();
|
|
132
|
-
}
|
|
133
|
-
getSize(room) {
|
|
134
|
-
var _a, _b;
|
|
135
|
-
return Promise.resolve((_b = (_a = this._users.get(room)) == null ? void 0 : _a.size) != null ? _b : 0);
|
|
136
|
-
}
|
|
137
|
-
getStorageState(room) {
|
|
138
|
-
var _a;
|
|
139
|
-
const storage = (_a = this._storages.get(room)) != null ? _a : null;
|
|
140
|
-
return Promise.resolve(storage);
|
|
141
|
-
}
|
|
142
|
-
getUser(room, connectionId) {
|
|
143
|
-
var _a;
|
|
144
|
-
const user = (_a = this._users.get(room)) == null ? void 0 : _a.get(connectionId);
|
|
145
|
-
return Promise.resolve(user != null ? user : null);
|
|
146
|
-
}
|
|
147
|
-
getUsers(room) {
|
|
148
|
-
return __async(this, null, function* () {
|
|
149
|
-
const users = this._users.get(room);
|
|
150
|
-
if (!users) return /* @__PURE__ */ new Map();
|
|
151
|
-
return users;
|
|
152
|
-
});
|
|
153
|
-
}
|
|
154
|
-
getUsersSize(room) {
|
|
155
|
-
return __async(this, null, function* () {
|
|
156
|
-
return this._users.size;
|
|
157
|
-
});
|
|
158
|
-
}
|
|
159
|
-
initialize(roomContext) {
|
|
160
|
-
return this;
|
|
161
|
-
}
|
|
162
|
-
setStorageState(room, state) {
|
|
163
|
-
this._storages.set(room, state);
|
|
164
|
-
return Promise.resolve();
|
|
165
|
-
}
|
|
166
|
-
};
|
|
167
|
-
|
|
168
|
-
// src/PubSub.ts
|
|
169
|
-
var import_wonka = require("wonka");
|
|
170
|
-
|
|
171
|
-
// src/AbstractPubSub.ts
|
|
172
|
-
var AbstractPubSub = class {
|
|
173
|
-
};
|
|
174
|
-
|
|
175
|
-
// src/PubSub.ts
|
|
176
|
-
var PubSub = class extends AbstractPubSub {
|
|
177
|
-
constructor() {
|
|
178
|
-
super(...arguments);
|
|
179
|
-
this._currentSubscriptionId = 0;
|
|
180
|
-
// Map of roomName to subjects
|
|
181
|
-
this._subjects = /* @__PURE__ */ new Map();
|
|
182
|
-
// Map of roomName to subscription ids
|
|
183
|
-
this._subscriptionsRefs = /* @__PURE__ */ new Map();
|
|
184
|
-
// Map of
|
|
185
|
-
this._unsubscriptions = /* @__PURE__ */ new Map();
|
|
186
|
-
}
|
|
187
|
-
publish(roomName, payload) {
|
|
188
|
-
return __async(this, null, function* () {
|
|
189
|
-
const subject = this._subjects.get(roomName);
|
|
190
|
-
if (!subject) return;
|
|
191
|
-
Promise.resolve(subject.next(payload));
|
|
192
|
-
});
|
|
193
|
-
}
|
|
194
|
-
subscribe(roomName, onMessage) {
|
|
195
|
-
var _a;
|
|
196
|
-
const id = this._currentSubscriptionId++;
|
|
197
|
-
const refs = (_a = this._subscriptionsRefs.get(roomName)) != null ? _a : [];
|
|
198
|
-
const subject = (0, import_wonka.makeSubject)();
|
|
199
|
-
this._subjects.set(roomName, subject);
|
|
200
|
-
this._subscriptionsRefs.set(roomName, [...refs, id]);
|
|
201
|
-
const unsubscribe = (0, import_wonka.subscribe)(onMessage)(subject.source).unsubscribe;
|
|
202
|
-
this._unsubscriptions.set(id, [roomName, unsubscribe]);
|
|
203
|
-
return Promise.resolve(id);
|
|
204
|
-
}
|
|
205
|
-
unsubscribe(subscriptionId) {
|
|
206
|
-
var _a;
|
|
207
|
-
const [roomName, unsubscribe] = (_a = this._unsubscriptions.get(subscriptionId)) != null ? _a : [];
|
|
208
|
-
if (!roomName || !unsubscribe) return;
|
|
209
|
-
const refs = this._subscriptionsRefs.get(roomName);
|
|
210
|
-
if (!refs) return;
|
|
211
|
-
if (refs.length === 1) {
|
|
212
|
-
this._subjects.delete(roomName);
|
|
213
|
-
this._subscriptionsRefs.delete(roomName);
|
|
214
|
-
this._unsubscriptions.delete(subscriptionId);
|
|
215
|
-
return;
|
|
216
|
-
}
|
|
217
|
-
const index = refs.indexOf(subscriptionId);
|
|
218
|
-
this._subscriptionsRefs.set(
|
|
219
|
-
roomName,
|
|
220
|
-
index === -1 ? refs : [...refs.slice(0, index), ...refs.slice(index + 1)]
|
|
221
|
-
);
|
|
222
|
-
unsubscribe();
|
|
223
|
-
this._unsubscriptions.delete(subscriptionId);
|
|
224
|
-
}
|
|
225
|
-
};
|
|
226
|
-
|
|
227
|
-
// src/AbstractPlatform.ts
|
|
228
|
-
var AbstractPlatform = class {
|
|
229
|
-
constructor(config = {}) {
|
|
230
|
-
this._initialized = false;
|
|
231
|
-
const { roomContext, persistence, pubSub } = config;
|
|
232
|
-
this._meta = config._meta;
|
|
233
|
-
if (!!roomContext) this._roomContext = roomContext;
|
|
234
|
-
this.persistence = persistence != null ? persistence : new Persistence();
|
|
235
|
-
this.pubSub = pubSub != null ? pubSub : new PubSub();
|
|
236
|
-
}
|
|
237
|
-
validateConfig(config) {
|
|
238
|
-
}
|
|
239
|
-
_initialize() {
|
|
240
|
-
if (this._initialized) throw new Error("Platform is already initialized");
|
|
241
|
-
this._initialized = true;
|
|
242
|
-
return this;
|
|
243
|
-
}
|
|
244
|
-
};
|
|
245
|
-
|
|
246
|
-
// src/AbstractWebSocket.ts
|
|
247
|
-
var AbstractWebSocket = class {
|
|
248
|
-
constructor(webSocket, config) {
|
|
249
|
-
/** The connection is not yet open. */
|
|
250
|
-
this.CONNECTING = 0;
|
|
251
|
-
/** The connection is open and ready to communicate. */
|
|
252
|
-
this.OPEN = 1;
|
|
253
|
-
/** The connection is in the process of closing. */
|
|
254
|
-
this.CLOSING = 2;
|
|
255
|
-
/** The connection is closed. */
|
|
256
|
-
this.CLOSED = 3;
|
|
257
|
-
const { persistence, platform, room } = config;
|
|
258
|
-
this.persistence = persistence;
|
|
259
|
-
this._platform = platform;
|
|
260
|
-
this.room = room;
|
|
261
|
-
this.webSocket = webSocket;
|
|
262
|
-
}
|
|
263
|
-
handleError(params) {
|
|
264
|
-
var _a, _b, _c;
|
|
265
|
-
const { error, message: _message, room, session } = params;
|
|
266
|
-
const message = error instanceof Error ? error.message : "Unexpected error";
|
|
267
|
-
const stack = error instanceof Error ? (_a = error.stack) != null ? _a : null : null;
|
|
268
|
-
const connectionId = (_b = session == null ? void 0 : session.id) != null ? _b : null;
|
|
269
|
-
return this.sendMessage({
|
|
270
|
-
connectionId,
|
|
271
|
-
data: { message, stack },
|
|
272
|
-
room,
|
|
273
|
-
type: "$error",
|
|
274
|
-
user: (_c = session == null ? void 0 : session.user) != null ? _c : null
|
|
275
|
-
});
|
|
276
|
-
}
|
|
277
|
-
sendMessage(data) {
|
|
278
|
-
return this.send(JSON.stringify(data));
|
|
279
|
-
}
|
|
280
|
-
};
|
|
281
|
-
|
|
282
|
-
// src/authorize.ts
|
|
283
|
-
var import_hkdf = __toESM(require("@panva/hkdf"));
|
|
284
|
-
var import_jose = require("jose");
|
|
285
|
-
var DEFAULT_MAX_AGE_MS = 6e4;
|
|
286
|
-
var getEncryptionKey = (secret) => __async(null, null, function* () {
|
|
287
|
-
return yield (0, import_hkdf.default)("sha256", secret, "", "Pluv.io Generated Encryption Key", 32);
|
|
288
|
-
});
|
|
289
|
-
var now = () => Date.now() / 1e3 | 0;
|
|
290
|
-
var authorize = (params) => {
|
|
291
|
-
const { platform, secret } = params;
|
|
292
|
-
const encode = (encodeParams) => __async(null, null, function* () {
|
|
293
|
-
const { maxAge = DEFAULT_MAX_AGE_MS, room, user } = encodeParams;
|
|
294
|
-
const encryptionSecret = yield getEncryptionKey(secret);
|
|
295
|
-
const token = yield new import_jose.EncryptJWT({
|
|
296
|
-
room,
|
|
297
|
-
sub: room ? `${room}|${user.id}` : user.id,
|
|
298
|
-
user
|
|
299
|
-
}).setProtectedHeader({ alg: "dir", enc: "A256GCM" }).setIssuedAt().setExpirationTime(now() + maxAge).setJti(platform.randomUUID()).encrypt(encryptionSecret);
|
|
300
|
-
return token.toString();
|
|
301
|
-
});
|
|
302
|
-
const decode = (jwt) => __async(null, null, function* () {
|
|
303
|
-
const encryptionSecret = yield getEncryptionKey(secret);
|
|
304
|
-
const { payload } = yield (0, import_jose.jwtDecrypt)(jwt, encryptionSecret, {
|
|
305
|
-
clockTolerance: 15
|
|
306
|
-
});
|
|
307
|
-
return payload != null ? payload : null;
|
|
308
|
-
});
|
|
309
|
-
return { decode, encode };
|
|
310
|
-
};
|
|
311
|
-
|
|
312
|
-
// src/PluvIO.ts
|
|
313
|
-
var import_crdt3 = require("@pluv/crdt");
|
|
314
|
-
|
|
315
|
-
// src/PluvProcedure.ts
|
|
316
|
-
var PluvProcedure = class _PluvProcedure {
|
|
317
|
-
constructor(config = {}) {
|
|
318
|
-
this._broadcast = null;
|
|
319
|
-
this._input = null;
|
|
320
|
-
this._self = null;
|
|
321
|
-
this._sync = null;
|
|
322
|
-
const { broadcast, input, self, sync } = config;
|
|
323
|
-
this._broadcast = broadcast != null ? broadcast : null;
|
|
324
|
-
this._input = input != null ? input : null;
|
|
325
|
-
this._self = self != null ? self : null;
|
|
326
|
-
this._sync = sync != null ? sync : null;
|
|
327
|
-
}
|
|
328
|
-
get config() {
|
|
329
|
-
var _a, _b, _c, _d, _e, _f, _g;
|
|
330
|
-
return {
|
|
331
|
-
broadcast: (_b = (_a = this._broadcast) == null ? void 0 : _a.bind(this)) != null ? _b : null,
|
|
332
|
-
input: (_c = this._input) != null ? _c : null,
|
|
333
|
-
resolver: this._resolver(),
|
|
334
|
-
self: (_e = (_d = this._self) == null ? void 0 : _d.bind(this)) != null ? _e : null,
|
|
335
|
-
sync: (_g = (_f = this._sync) == null ? void 0 : _f.bind(this)) != null ? _g : null
|
|
336
|
-
};
|
|
337
|
-
}
|
|
338
|
-
broadcast(resolver) {
|
|
339
|
-
const { broadcast } = this.config;
|
|
340
|
-
if (!!broadcast) throw new Error("Broadcast was already defined for this procedure");
|
|
341
|
-
return new _PluvProcedure(__spreadProps(__spreadValues({}, this.config), {
|
|
342
|
-
broadcast: resolver
|
|
343
|
-
}));
|
|
344
|
-
}
|
|
345
|
-
input(input) {
|
|
346
|
-
return new _PluvProcedure({
|
|
347
|
-
input
|
|
348
|
-
});
|
|
349
|
-
}
|
|
350
|
-
self(resolver) {
|
|
351
|
-
const { self } = this.config;
|
|
352
|
-
if (!!self) throw new Error("Self was already defined for this procedure");
|
|
353
|
-
return new _PluvProcedure(__spreadProps(__spreadValues({}, this.config), {
|
|
354
|
-
self: resolver
|
|
355
|
-
}));
|
|
356
|
-
}
|
|
357
|
-
sync(resolver) {
|
|
358
|
-
const { sync } = this.config;
|
|
359
|
-
if (!!sync) throw new Error("Sync was already defined for this procedure");
|
|
360
|
-
return new _PluvProcedure(__spreadProps(__spreadValues({}, this.config), {
|
|
361
|
-
sync: resolver
|
|
362
|
-
}));
|
|
363
|
-
}
|
|
364
|
-
_resolver() {
|
|
365
|
-
return (data, context) => {
|
|
366
|
-
var _a, _b, _c;
|
|
367
|
-
return __spreadValues(__spreadValues(__spreadValues({}, (_a = this._broadcast) == null ? void 0 : _a.call(this, data, context)), (_b = this._self) == null ? void 0 : _b.call(this, data, context)), (_c = this._sync) == null ? void 0 : _c.call(this, data, context));
|
|
368
|
-
};
|
|
369
|
-
}
|
|
370
|
-
};
|
|
371
|
-
|
|
372
|
-
// src/PluvRouter.ts
|
|
373
|
-
var PluvRouter = class _PluvRouter {
|
|
374
|
-
constructor(events) {
|
|
375
|
-
this._defs = { events: {} };
|
|
376
|
-
const invalidName = Object.keys(events).find((name) => !this._isValidEventName(name));
|
|
377
|
-
if (typeof invalidName === "string") {
|
|
378
|
-
throw new Error(
|
|
379
|
-
`Invalid event name. Event names must be formatted as valid JavaScript variable names: "${invalidName}"`
|
|
380
|
-
);
|
|
381
|
-
}
|
|
382
|
-
this._defs = { events };
|
|
383
|
-
}
|
|
384
|
-
static merge(...routers) {
|
|
385
|
-
const events = Object.assign(
|
|
386
|
-
/* @__PURE__ */ Object.create(null),
|
|
387
|
-
...routers.map((router) => router._defs.events)
|
|
388
|
-
);
|
|
389
|
-
return new _PluvRouter(events);
|
|
390
|
-
}
|
|
391
|
-
_isValidEventName(name) {
|
|
392
|
-
return /^[a-z_$][a-z0-9_$]*$/gi.test(name);
|
|
393
|
-
}
|
|
394
|
-
};
|
|
395
|
-
|
|
396
|
-
// src/PluvServer.ts
|
|
397
|
-
var import_crdt2 = require("@pluv/crdt");
|
|
398
|
-
var import_kleur2 = __toESM(require("kleur"));
|
|
399
|
-
|
|
400
|
-
// src/IORoom.ts
|
|
401
|
-
var import_crdt = require("@pluv/crdt");
|
|
402
|
-
var import_kleur = __toESM(require("kleur"));
|
|
403
|
-
|
|
404
|
-
// src/constants.ts
|
|
405
|
-
var GARBAGE_COLLECT_INTERVAL_MS = 6e4;
|
|
406
|
-
var MAX_PRESENCE_SIZE_BYTES = 512;
|
|
407
|
-
var MAX_STORAGE_SIZE_BYTES = 31457280;
|
|
408
|
-
var MAX_USER_ID_LENGTH = 128;
|
|
409
|
-
var MAX_USER_SIZE_BYTES = 512;
|
|
410
|
-
var PING_TIMEOUT_MS = 3e4;
|
|
411
|
-
|
|
412
|
-
// src/utils/oneLine.ts
|
|
413
|
-
var oneLine = (strings, ...values) => {
|
|
414
|
-
return strings.map((str) => str.replace(/\s+/g, " ")).reduce((acc, str, i) => acc + str + (i < values.length ? values[i] : ""), "").replace(/\s+/g, " ").trim();
|
|
415
|
-
};
|
|
416
|
-
|
|
417
|
-
// src/utils/pickBy.ts
|
|
418
|
-
var pickBy = (obj, by) => {
|
|
419
|
-
const result = {};
|
|
420
|
-
const keys = Object.keys(obj);
|
|
421
|
-
for (let i = 0; i < keys.length; ++i) {
|
|
422
|
-
const key = keys[i];
|
|
423
|
-
const value = obj[key];
|
|
424
|
-
if (by(value)) result[key] = value;
|
|
425
|
-
}
|
|
426
|
-
return result;
|
|
427
|
-
};
|
|
428
|
-
|
|
429
|
-
// src/IORoom.ts
|
|
430
|
-
var IORoom = class {
|
|
431
|
-
constructor(id, config) {
|
|
432
|
-
this._lastGarbageCollectMs = -1 * (GARBAGE_COLLECT_INTERVAL_MS + 1);
|
|
433
|
-
this._uninitialize = null;
|
|
434
|
-
this._authorize = null;
|
|
435
|
-
this._sessions = /* @__PURE__ */ new Map();
|
|
436
|
-
this._userSessionss = /* @__PURE__ */ new Map();
|
|
437
|
-
const {
|
|
438
|
-
_meta,
|
|
439
|
-
authorize: authorize2,
|
|
440
|
-
context,
|
|
441
|
-
crdt = import_crdt.noop,
|
|
442
|
-
debug,
|
|
443
|
-
getInitialStorage,
|
|
444
|
-
onDestroy,
|
|
445
|
-
onMessage,
|
|
446
|
-
onUserConnected,
|
|
447
|
-
onUserDisconnected,
|
|
448
|
-
platform,
|
|
449
|
-
roomContext,
|
|
450
|
-
router
|
|
451
|
-
} = config;
|
|
452
|
-
this.id = id;
|
|
453
|
-
this._context = context;
|
|
454
|
-
this._crdt = crdt;
|
|
455
|
-
this._debug = debug;
|
|
456
|
-
this._docFactory = crdt.doc(() => ({}));
|
|
457
|
-
this._getInitialStorage = getInitialStorage;
|
|
458
|
-
this._roomContext = roomContext;
|
|
459
|
-
this._router = router;
|
|
460
|
-
this._platform = platform.initialize(__spreadProps(__spreadValues({}, !!_meta ? { _meta } : {}), { roomContext }));
|
|
461
|
-
this._listeners = {
|
|
462
|
-
onDestroy: (event) => onDestroy == null ? void 0 : onDestroy(event),
|
|
463
|
-
onMessage: (event) => onMessage == null ? void 0 : onMessage(event),
|
|
464
|
-
onUserConnected: (event) => onUserConnected == null ? void 0 : onUserConnected(event),
|
|
465
|
-
onUserDisconnected: (event) => onUserDisconnected == null ? void 0 : onUserDisconnected(event)
|
|
466
|
-
};
|
|
467
|
-
if (authorize2) this._authorize = authorize2;
|
|
468
|
-
const webSockets = this._platform.getWebSockets();
|
|
469
|
-
webSockets.forEach((webSocket) => {
|
|
470
|
-
var _a;
|
|
471
|
-
const deserialized = this._platform.getSerializedState(webSocket);
|
|
472
|
-
const sessionId = this._platform.getSessionId(webSocket);
|
|
473
|
-
if (!deserialized) return;
|
|
474
|
-
if (typeof sessionId !== "string") return;
|
|
475
|
-
const pluvWs = this._platform.convertWebSocket(webSocket, { room: this.id });
|
|
476
|
-
this._sessions.set(sessionId, pluvWs);
|
|
477
|
-
const userId = (_a = pluvWs.session.user) == null ? void 0 : _a.id;
|
|
478
|
-
if (!!userId) this._addUserSession(userId, sessionId);
|
|
479
|
-
});
|
|
480
|
-
const { doc, uninitialize } = this._initialize();
|
|
481
|
-
this._doc = doc;
|
|
482
|
-
this._uninitialize = uninitialize;
|
|
483
|
-
}
|
|
484
|
-
/**
|
|
485
|
-
* @ignore
|
|
486
|
-
* @readonly
|
|
487
|
-
* @deprecated Internal use only. Changes to this will never be marked as breaking.
|
|
488
|
-
*/
|
|
489
|
-
get _defs() {
|
|
490
|
-
return {
|
|
491
|
-
authorize: this._authorize,
|
|
492
|
-
context: this._context,
|
|
493
|
-
crdt: this._crdt,
|
|
494
|
-
events: this._router._defs.events,
|
|
495
|
-
platform: this._platform
|
|
496
|
-
};
|
|
497
|
-
}
|
|
498
|
-
get broadcast() {
|
|
499
|
-
const _broadcast = (event, data) => {
|
|
500
|
-
const message = { type: event, data };
|
|
501
|
-
return Promise.resolve(this._broadcast({ message }));
|
|
502
|
-
};
|
|
503
|
-
return new Proxy(_broadcast, {
|
|
504
|
-
get(fn, prop) {
|
|
505
|
-
return (data) => fn(prop, data);
|
|
506
|
-
}
|
|
507
|
-
});
|
|
508
|
-
}
|
|
509
|
-
get _initialized() {
|
|
510
|
-
var _a;
|
|
511
|
-
return Promise.resolve(!!((_a = this._uninitialize) == null ? void 0 : _a.then((result) => !!result)));
|
|
512
|
-
}
|
|
513
|
-
/**
|
|
514
|
-
* @description Closes a connection with the specified sessionId
|
|
515
|
-
* @param sessionId The session id of the connection to close
|
|
516
|
-
*/
|
|
517
|
-
evict(sessionId) {
|
|
518
|
-
return __async(this, null, function* () {
|
|
519
|
-
const session = this._sessions.get(sessionId);
|
|
520
|
-
if (session) session.state.quit = true;
|
|
521
|
-
yield this._emitQuitters();
|
|
522
|
-
});
|
|
523
|
-
}
|
|
524
|
-
/**
|
|
525
|
-
* @description Closes all connections to this room, effectively destroying
|
|
526
|
-
* the room
|
|
527
|
-
*/
|
|
528
|
-
evictAll() {
|
|
529
|
-
return __async(this, null, function* () {
|
|
530
|
-
const sessions = Array.from(this._sessions.values());
|
|
531
|
-
sessions.forEach((session) => {
|
|
532
|
-
session.state.quit = true;
|
|
533
|
-
});
|
|
534
|
-
yield this._emitQuitters();
|
|
535
|
-
});
|
|
536
|
-
}
|
|
537
|
-
/**
|
|
538
|
-
* @description The IORoom will garbage collect occasionally as connections ping/pong the
|
|
539
|
-
* server
|
|
540
|
-
*/
|
|
541
|
-
garbageCollect() {
|
|
542
|
-
return __async(this, null, function* () {
|
|
543
|
-
this._lastGarbageCollectMs = Date.now();
|
|
544
|
-
this._emitQuitters();
|
|
545
|
-
this._emitSyncState();
|
|
546
|
-
});
|
|
547
|
-
}
|
|
548
|
-
getSize() {
|
|
549
|
-
const currentTime = (/* @__PURE__ */ new Date()).getTime();
|
|
550
|
-
return Array.from(this._sessions.values()).reduce((count, pluvWs) => {
|
|
551
|
-
var _a;
|
|
552
|
-
if (pluvWs.state.quit) return count;
|
|
553
|
-
const pingTime = (_a = this._platform.getLastPing(pluvWs)) != null ? _a : pluvWs.state.timers.ping;
|
|
554
|
-
return currentTime - pingTime > PING_TIMEOUT_MS ? count : count + 1;
|
|
555
|
-
}, 0);
|
|
556
|
-
}
|
|
557
|
-
onClose(webSocket) {
|
|
558
|
-
this._ensureDetached();
|
|
559
|
-
const wsSession = this._getAbstractWs(webSocket);
|
|
560
|
-
if (!wsSession) return () => __async(this, null, function* () {
|
|
561
|
-
return void 0;
|
|
562
|
-
});
|
|
563
|
-
return this._onClose(wsSession);
|
|
564
|
-
}
|
|
565
|
-
onError(webSocket) {
|
|
566
|
-
this._ensureDetached();
|
|
567
|
-
const wsSession = this._getAbstractWs(webSocket);
|
|
568
|
-
if (!wsSession) return () => __async(this, null, function* () {
|
|
569
|
-
return void 0;
|
|
570
|
-
});
|
|
571
|
-
return this._onClose(wsSession);
|
|
572
|
-
}
|
|
573
|
-
onMessage(webSocket) {
|
|
574
|
-
this._ensureDetached();
|
|
575
|
-
const wsSession = this._getAbstractWs(webSocket);
|
|
576
|
-
if (!wsSession) return () => __async(this, null, function* () {
|
|
577
|
-
return void 0;
|
|
578
|
-
});
|
|
579
|
-
return this._onMessage(wsSession);
|
|
580
|
-
}
|
|
581
|
-
register(webSocket, ...options) {
|
|
582
|
-
return __async(this, null, function* () {
|
|
583
|
-
var _a, _b;
|
|
584
|
-
const _options = (_a = options[0]) != null ? _a : {};
|
|
585
|
-
const token = (_b = _options.token) != null ? _b : null;
|
|
586
|
-
const sessionId = this._platform.getSessionId(webSocket);
|
|
587
|
-
const sessionExists = typeof sessionId === "string" && this._sessions.has(sessionId);
|
|
588
|
-
if (sessionExists) return;
|
|
589
|
-
if (!(yield this._initialized)) {
|
|
590
|
-
this._initialize();
|
|
591
|
-
yield this._initialized;
|
|
592
|
-
}
|
|
593
|
-
const user = yield this._getAuthorizedUser(token, _options);
|
|
594
|
-
const ioAuthorize = this._getIOAuthorize(_options);
|
|
595
|
-
const isUnauthorized = !!ioAuthorize && !user;
|
|
596
|
-
const pluvWs = this._platform.convertWebSocket(webSocket, { room: this.id });
|
|
597
|
-
if (isUnauthorized) {
|
|
598
|
-
this._logDebug(`${import_kleur.default.blue("Authorization failed for connection")}`);
|
|
599
|
-
pluvWs.handleError({ error: new Error("Not authorized"), room: this.id });
|
|
600
|
-
pluvWs.close(3e3, "WebSocket unauthorized.");
|
|
601
|
-
return;
|
|
602
|
-
}
|
|
603
|
-
if (!!user) {
|
|
604
|
-
const latest = this._getLatestPresence(user.id);
|
|
605
|
-
const prevState = pluvWs.state;
|
|
606
|
-
pluvWs.user = user;
|
|
607
|
-
this._platform.setSerializedState(pluvWs, __spreadProps(__spreadValues({}, prevState), {
|
|
608
|
-
presence: latest.presence,
|
|
609
|
-
timers: __spreadProps(__spreadValues({}, prevState.timers), { presence: latest.timer })
|
|
610
|
-
}));
|
|
611
|
-
this._addUserSession(user.id, pluvWs.sessionId);
|
|
612
|
-
}
|
|
613
|
-
this._logDebug(
|
|
614
|
-
`${import_kleur.default.blue(`Registering connection for room ${this.id}:`)} ${pluvWs.sessionId}`
|
|
615
|
-
);
|
|
616
|
-
yield this._platform.acceptWebSocket(pluvWs);
|
|
617
|
-
this._sessions.set(pluvWs.sessionId, pluvWs);
|
|
618
|
-
yield this._platform.persistence.addUser(this.id, pluvWs.sessionId, user != null ? user : {});
|
|
619
|
-
if (this._platform._config.registrationMode === "attached") {
|
|
620
|
-
const onClose = this._onClose(pluvWs).bind(this);
|
|
621
|
-
const onMessage = this._onMessage(pluvWs).bind(this);
|
|
622
|
-
pluvWs.addEventListener("close", onClose);
|
|
623
|
-
pluvWs.addEventListener("error", onClose);
|
|
624
|
-
pluvWs.addEventListener("message", onMessage);
|
|
625
|
-
}
|
|
626
|
-
yield this._emitRegistered(pluvWs);
|
|
627
|
-
const size = this.getSize();
|
|
628
|
-
this._logDebug(oneLine`
|
|
629
|
-
${import_kleur.default.blue(`Registered connection for room ${this.id}:`)}
|
|
630
|
-
${pluvWs.sessionId}
|
|
631
|
-
`);
|
|
632
|
-
this._logDebug(`${import_kleur.default.blue(`Room ${this.id} size:`)} ${size}`);
|
|
633
|
-
});
|
|
634
|
-
}
|
|
635
|
-
_addUserSession(userId, sessionId) {
|
|
636
|
-
var _a;
|
|
637
|
-
const set = (_a = this._userSessionss.get(userId)) != null ? _a : /* @__PURE__ */ new Set();
|
|
638
|
-
const updated = set.add(sessionId);
|
|
639
|
-
this._userSessionss.set(userId, updated);
|
|
640
|
-
return updated;
|
|
641
|
-
}
|
|
642
|
-
_broadcast(params) {
|
|
643
|
-
return __async(this, null, function* () {
|
|
644
|
-
var _a, _b, _c;
|
|
645
|
-
const { message, senderId } = params;
|
|
646
|
-
const sender = senderId ? (_a = this._sessions.get(senderId)) != null ? _a : null : null;
|
|
647
|
-
const session = (_b = sender == null ? void 0 : sender.session) != null ? _b : null;
|
|
648
|
-
const user = (_c = session == null ? void 0 : session.user) != null ? _c : null;
|
|
649
|
-
if (typeof senderId !== "string") return;
|
|
650
|
-
this._platform.pubSub.publish(this.id, __spreadValues({
|
|
651
|
-
connectionId: senderId != null ? senderId : null,
|
|
652
|
-
room: this.id,
|
|
653
|
-
user
|
|
654
|
-
}, message));
|
|
655
|
-
});
|
|
656
|
-
}
|
|
657
|
-
_closeWebSockets(webSockets) {
|
|
658
|
-
return __async(this, null, function* () {
|
|
659
|
-
const closeWebSocket = (webSocket) => __async(this, null, function* () {
|
|
660
|
-
webSocket.state = __spreadProps(__spreadValues({}, webSocket.state), { quit: true });
|
|
661
|
-
const sessionId = webSocket.sessionId;
|
|
662
|
-
this._logDebug(
|
|
663
|
-
`${import_kleur.default.blue(`Unregistering connection for room ${this.id}:`)} ${sessionId}`
|
|
664
|
-
);
|
|
665
|
-
this._sessions.delete(sessionId);
|
|
666
|
-
yield this._platform.persistence.deleteUser(this.id, sessionId).catch(() => null);
|
|
667
|
-
yield this._broadcast({
|
|
668
|
-
message: { type: "$exit", data: { sessionId } },
|
|
669
|
-
senderId: sessionId
|
|
670
|
-
});
|
|
671
|
-
const session = webSocket.session;
|
|
672
|
-
const user = session.user;
|
|
673
|
-
if (!!user) this._removeUserSession(user.id, sessionId);
|
|
674
|
-
try {
|
|
675
|
-
const [doc, context] = yield Promise.all([this._doc, this._getContext()]);
|
|
676
|
-
const encodedState = doc.getEncodedState();
|
|
677
|
-
yield Promise.resolve(
|
|
678
|
-
this._listeners.onUserDisconnected({
|
|
679
|
-
context,
|
|
680
|
-
encodedState,
|
|
681
|
-
platform: this._platform,
|
|
682
|
-
room: this.id,
|
|
683
|
-
user
|
|
684
|
-
})
|
|
685
|
-
);
|
|
686
|
-
} catch (error) {
|
|
687
|
-
console.error(error);
|
|
688
|
-
}
|
|
689
|
-
this._logDebug(
|
|
690
|
-
`${import_kleur.default.blue(`Unregistered connection for room ${this.id}:`)} ${webSocket.sessionId}`
|
|
691
|
-
);
|
|
692
|
-
});
|
|
693
|
-
const promises = webSockets.map((webSocket) => __async(this, null, function* () {
|
|
694
|
-
yield closeWebSocket(webSocket).catch(() => null);
|
|
695
|
-
}));
|
|
696
|
-
yield Promise.all(promises).catch(() => null);
|
|
697
|
-
const size = this.getSize();
|
|
698
|
-
this._logDebug(`${import_kleur.default.blue(`Room ${this.id} size:`)} ${size}`);
|
|
699
|
-
if (!!size) return;
|
|
700
|
-
if (!this._uninitialize) return;
|
|
701
|
-
const uninitialize = (yield this._uninitialize).bind(this);
|
|
702
|
-
yield uninitialize();
|
|
703
|
-
});
|
|
704
|
-
}
|
|
705
|
-
_emitQuitters() {
|
|
706
|
-
return __async(this, null, function* () {
|
|
707
|
-
const currentTime = (/* @__PURE__ */ new Date()).getTime();
|
|
708
|
-
const quitters = Array.from(this._sessions.values()).filter((pluvWs) => {
|
|
709
|
-
return pluvWs.state.quit || currentTime - pluvWs.state.timers.ping > PING_TIMEOUT_MS;
|
|
710
|
-
});
|
|
711
|
-
this._closeWebSockets(quitters);
|
|
712
|
-
});
|
|
713
|
-
}
|
|
714
|
-
_emitRegistered(pluvWs) {
|
|
715
|
-
return __async(this, null, function* () {
|
|
716
|
-
const session = pluvWs.session;
|
|
717
|
-
const sessionId = session.id;
|
|
718
|
-
const presence = session.presence;
|
|
719
|
-
const user = session.user;
|
|
720
|
-
const [doc, context] = yield Promise.all([this._doc, this._getContext()]);
|
|
721
|
-
const encodedState = doc.isEmpty() ? null : doc.getEncodedState();
|
|
722
|
-
yield this._sendSelfMessage(
|
|
723
|
-
{
|
|
724
|
-
type: "$registered",
|
|
725
|
-
data: {
|
|
726
|
-
presence,
|
|
727
|
-
sessionId,
|
|
728
|
-
state: encodedState,
|
|
729
|
-
timers: { presence: session.timers.presence }
|
|
730
|
-
}
|
|
731
|
-
},
|
|
732
|
-
{ sessionId, user }
|
|
733
|
-
);
|
|
734
|
-
try {
|
|
735
|
-
yield Promise.resolve(
|
|
736
|
-
this._listeners.onUserConnected({
|
|
737
|
-
context,
|
|
738
|
-
encodedState,
|
|
739
|
-
platform: this._platform,
|
|
740
|
-
room: this.id,
|
|
741
|
-
user,
|
|
742
|
-
webSocket: pluvWs.webSocket
|
|
743
|
-
})
|
|
744
|
-
);
|
|
745
|
-
} catch (err) {
|
|
746
|
-
console.error(err);
|
|
747
|
-
}
|
|
748
|
-
});
|
|
749
|
-
}
|
|
750
|
-
_emitSyncState() {
|
|
751
|
-
return __async(this, null, function* () {
|
|
752
|
-
const connectionIds = yield this._platform.persistence.getUsers(this.id).then((map) => Object.keys(map));
|
|
753
|
-
yield this._broadcast({
|
|
754
|
-
message: {
|
|
755
|
-
type: "$syncStateReceived",
|
|
756
|
-
data: { connectionIds }
|
|
757
|
-
}
|
|
758
|
-
});
|
|
759
|
-
});
|
|
760
|
-
}
|
|
761
|
-
_ensureDetached() {
|
|
762
|
-
if (this._platform._config.registrationMode === "detached") return;
|
|
763
|
-
throw new Error("Platform must use detached mode");
|
|
764
|
-
}
|
|
765
|
-
_getAbstractWs(webSocket) {
|
|
766
|
-
var _a, _b;
|
|
767
|
-
if (webSocket instanceof AbstractWebSocket) return webSocket;
|
|
768
|
-
const sessionId = this._platform.getSessionId(webSocket);
|
|
769
|
-
if (typeof sessionId === "string") {
|
|
770
|
-
const session = (_a = this._sessions.get(sessionId)) != null ? _a : null;
|
|
771
|
-
if (session) return session;
|
|
772
|
-
}
|
|
773
|
-
const sessions = Array.from(this._sessions.values());
|
|
774
|
-
return (_b = sessions.find((pluvWs) => pluvWs.webSocket === webSocket)) != null ? _b : null;
|
|
775
|
-
}
|
|
776
|
-
/**
|
|
777
|
-
* TODO
|
|
778
|
-
* @description Revisit any cast types
|
|
779
|
-
* @date July 17, 2025
|
|
780
|
-
*/
|
|
781
|
-
_getAuthorizedUser(token, options) {
|
|
782
|
-
return __async(this, null, function* () {
|
|
783
|
-
var _a;
|
|
784
|
-
const ioAuthorize = this._getIOAuthorize(options);
|
|
785
|
-
if (!ioAuthorize) return null;
|
|
786
|
-
if (!token) return null;
|
|
787
|
-
if (!ioAuthorize.secret)
|
|
788
|
-
throw new Error("`authorize` was specified without a valid secret");
|
|
789
|
-
const payload = yield authorize({
|
|
790
|
-
platform: this._platform,
|
|
791
|
-
secret: ioAuthorize.secret
|
|
792
|
-
}).decode(token);
|
|
793
|
-
if (!payload) {
|
|
794
|
-
this._logDebug(import_kleur.default.blue("Could not decode token:"));
|
|
795
|
-
this._logDebug(token);
|
|
796
|
-
return null;
|
|
797
|
-
}
|
|
798
|
-
if (payload.room !== this.id) {
|
|
799
|
-
this._logDebug(import_kleur.default.blue(`Token is not authorized for room ${this.id}:`));
|
|
800
|
-
this._logDebug(import_kleur.default.blue("Received:"), payload.room);
|
|
801
|
-
this._logDebug(token);
|
|
802
|
-
return null;
|
|
803
|
-
}
|
|
804
|
-
try {
|
|
805
|
-
return (_a = ioAuthorize.user.parse(payload.user)) != null ? _a : null;
|
|
806
|
-
} catch (e) {
|
|
807
|
-
this._logDebug(`${import_kleur.default.blue("Token fails validation:")} ${token}`);
|
|
808
|
-
return null;
|
|
809
|
-
}
|
|
810
|
-
});
|
|
811
|
-
}
|
|
812
|
-
_getContext() {
|
|
813
|
-
return __async(this, null, function* () {
|
|
814
|
-
return typeof this._context === "function" ? yield Promise.resolve(this._context(this._roomContext)) : yield Promise.resolve(this._context);
|
|
815
|
-
});
|
|
816
|
-
}
|
|
817
|
-
_getInitialDoc() {
|
|
818
|
-
return __async(this, null, function* () {
|
|
819
|
-
const doc = this._docFactory.getEmpty();
|
|
820
|
-
const [encodedState, context] = yield Promise.all([
|
|
821
|
-
this._platform.persistence.getStorageState(this.id),
|
|
822
|
-
this._getContext()
|
|
823
|
-
]);
|
|
824
|
-
if (!encodedState) {
|
|
825
|
-
const loadedState = yield this._getInitialStorage({
|
|
826
|
-
context,
|
|
827
|
-
room: this.id
|
|
828
|
-
});
|
|
829
|
-
if (!!loadedState && !this._docFactory.isEmpty(loadedState)) {
|
|
830
|
-
doc.applyEncodedState({ update: loadedState });
|
|
831
|
-
}
|
|
832
|
-
}
|
|
833
|
-
if (typeof encodedState === "string") doc.applyEncodedState({ update: encodedState });
|
|
834
|
-
return doc;
|
|
835
|
-
});
|
|
836
|
-
}
|
|
837
|
-
_getIOAuthorize(options) {
|
|
838
|
-
if (typeof this._authorize === "function") return this._authorize(options);
|
|
839
|
-
return this._authorize;
|
|
840
|
-
}
|
|
841
|
-
_getLatestPresence(userId) {
|
|
842
|
-
var _a, _b;
|
|
843
|
-
if (!this._authorize) return { timer: null, presence: null };
|
|
844
|
-
const sessionIds = Array.from((_b = (_a = this._userSessionss.get(userId)) == null ? void 0 : _a.values()) != null ? _b : []);
|
|
845
|
-
if (!sessionIds.length) return { timer: null, presence: null };
|
|
846
|
-
return sessionIds.reduce(
|
|
847
|
-
(state, sessionId) => {
|
|
848
|
-
var _a2, _b2;
|
|
849
|
-
const pluvWs = (_a2 = this._sessions.get(sessionId)) != null ? _a2 : null;
|
|
850
|
-
if (!pluvWs) return state;
|
|
851
|
-
const session = pluvWs.session;
|
|
852
|
-
const presence = session.presence;
|
|
853
|
-
const timer = session.timers.presence;
|
|
854
|
-
if (((_b2 = session.user) == null ? void 0 : _b2.id) !== userId) return state;
|
|
855
|
-
if (typeof state.timer !== "number") return { presence, timer };
|
|
856
|
-
if (typeof timer !== "number") return state;
|
|
857
|
-
return timer > state.timer ? { presence, timer } : state;
|
|
858
|
-
},
|
|
859
|
-
{ presence: null, timer: null }
|
|
860
|
-
);
|
|
861
|
-
}
|
|
862
|
-
_getProcedure(message) {
|
|
863
|
-
var _a;
|
|
864
|
-
return (_a = this._router._defs.events[message.type]) != null ? _a : null;
|
|
865
|
-
}
|
|
866
|
-
_getProcedureInputs(message) {
|
|
867
|
-
const procedure = this._getProcedure(message);
|
|
868
|
-
if (!procedure) return message.data;
|
|
869
|
-
return procedure.config.input ? procedure.config.input.parse(message.data) : message.data;
|
|
870
|
-
}
|
|
871
|
-
_getSession(webSocket) {
|
|
872
|
-
const pluvWs = this._getAbstractWs(webSocket);
|
|
873
|
-
if (!pluvWs) throw new Error("Session could not be found");
|
|
874
|
-
return pluvWs.session;
|
|
875
|
-
}
|
|
876
|
-
_getSessions() {
|
|
877
|
-
const sessions = Array.from(this._sessions.values()).map((pluvWs) => pluvWs.session);
|
|
878
|
-
return sessions;
|
|
879
|
-
}
|
|
880
|
-
_initialize() {
|
|
881
|
-
const promise = (() => __async(this, null, function* () {
|
|
882
|
-
this._logDebug(`${import_kleur.default.blue(`Initializing room: ${this.id}`)}`);
|
|
883
|
-
if (!!this._uninitialize) {
|
|
884
|
-
const uninitialize2 = (yield this._uninitialize).bind(this);
|
|
885
|
-
yield uninitialize2();
|
|
886
|
-
this._uninitialize = null;
|
|
887
|
-
}
|
|
888
|
-
const pubSubId = yield this._platform.pubSub.subscribe(
|
|
889
|
-
this.id,
|
|
890
|
-
(_a) => __async(this, null, function* () {
|
|
891
|
-
var _b = _a, { options = {} } = _b, message = __objRest(_b, ["options"]);
|
|
892
|
-
yield promise;
|
|
893
|
-
const sender = {
|
|
894
|
-
sessionId: message.connectionId,
|
|
895
|
-
user: message.user
|
|
896
|
-
};
|
|
897
|
-
switch (options.type) {
|
|
898
|
-
case "self": {
|
|
899
|
-
yield this._sendSelfMessage(message, sender);
|
|
900
|
-
return;
|
|
901
|
-
}
|
|
902
|
-
case "sync": {
|
|
903
|
-
yield this._sendSyncMessage(message, sender);
|
|
904
|
-
return;
|
|
905
|
-
}
|
|
906
|
-
case "broadcast":
|
|
907
|
-
default: {
|
|
908
|
-
const sessionIds = options.sessionIds;
|
|
909
|
-
yield this._sendBroadcastMessage(message, sender, sessionIds);
|
|
910
|
-
}
|
|
911
|
-
}
|
|
912
|
-
})
|
|
913
|
-
);
|
|
914
|
-
const doc = yield this._getInitialDoc();
|
|
915
|
-
const uninitialize = () => __async(this, null, function* () {
|
|
916
|
-
this._platform.pubSub.unsubscribe(pubSubId);
|
|
917
|
-
const [doc2, context] = yield Promise.all([this._doc, this._getContext()]);
|
|
918
|
-
const encodedState = doc2.getEncodedState();
|
|
919
|
-
doc2.destroy();
|
|
920
|
-
this._doc = Promise.resolve(this._docFactory.getEmpty());
|
|
921
|
-
yield Promise.resolve(
|
|
922
|
-
this._listeners.onDestroy(__spreadProps(__spreadValues({}, "_meta" in this._platform && !!this._platform._meta ? { _meta: this._platform._meta } : {}), {
|
|
923
|
-
context,
|
|
924
|
-
encodedState,
|
|
925
|
-
platform: this._platform,
|
|
926
|
-
room: this.id
|
|
927
|
-
}))
|
|
928
|
-
);
|
|
929
|
-
this._uninitialize = null;
|
|
930
|
-
});
|
|
931
|
-
return { doc, uninitialize };
|
|
932
|
-
}))();
|
|
933
|
-
this._doc = promise.then((result) => result.doc);
|
|
934
|
-
this._uninitialize = promise.then((result) => result.uninitialize);
|
|
935
|
-
return { doc: this._doc, uninitialize: this._uninitialize };
|
|
936
|
-
}
|
|
937
|
-
_logDebug(...data) {
|
|
938
|
-
if (this._debug) console.log(...data);
|
|
939
|
-
}
|
|
940
|
-
_onClose(webSocket) {
|
|
941
|
-
return () => __async(this, null, function* () {
|
|
942
|
-
if (!(yield this._initialized)) return;
|
|
943
|
-
yield this._closeWebSockets([webSocket]);
|
|
944
|
-
});
|
|
945
|
-
}
|
|
946
|
-
_onMessage(webSocket) {
|
|
947
|
-
return (event) => __async(this, null, function* () {
|
|
948
|
-
var _a, _b, _c, _d, _e, _f;
|
|
949
|
-
if (!(yield this._initialized)) return;
|
|
950
|
-
const pluvWs = this._getAbstractWs(webSocket);
|
|
951
|
-
if (!pluvWs) throw new Error("Could not get session");
|
|
952
|
-
const session = this._getSession(pluvWs);
|
|
953
|
-
const sessions = this._getSessions();
|
|
954
|
-
const setPresence = (params) => {
|
|
955
|
-
this._setPresence.bind(this)(params);
|
|
956
|
-
};
|
|
957
|
-
const [doc, context] = yield Promise.all([this._doc, this._getContext()]);
|
|
958
|
-
const eventContext = {
|
|
959
|
-
context,
|
|
960
|
-
doc,
|
|
961
|
-
garbageCollect: () => __async(this, null, function* () {
|
|
962
|
-
yield this.garbageCollect();
|
|
963
|
-
}),
|
|
964
|
-
platform: this._platform,
|
|
965
|
-
get presence() {
|
|
966
|
-
return session.presence;
|
|
967
|
-
},
|
|
968
|
-
set presence(presence) {
|
|
969
|
-
var _a2;
|
|
970
|
-
const sessionId2 = (_a2 = this.session) == null ? void 0 : _a2.id;
|
|
971
|
-
if (!sessionId2) return;
|
|
972
|
-
setPresence({ presence, sessionId: sessionId2, timer: this.time });
|
|
973
|
-
},
|
|
974
|
-
room: this.id,
|
|
975
|
-
session,
|
|
976
|
-
sessions,
|
|
977
|
-
time: (/* @__PURE__ */ new Date()).getTime()
|
|
978
|
-
};
|
|
979
|
-
if (pluvWs.state.quit) {
|
|
980
|
-
yield this._closeWebSockets([pluvWs]).catch(() => null);
|
|
981
|
-
pluvWs.close(1011, "WebSocket broken.");
|
|
982
|
-
return;
|
|
983
|
-
}
|
|
984
|
-
const message = this._parseMessage(event);
|
|
985
|
-
if (!message) return;
|
|
986
|
-
const procedure = this._getProcedure(message);
|
|
987
|
-
this._listeners.onMessage(__spreadProps(__spreadValues({}, "_meta" in this._platform && !!this._platform._meta ? { _meta: this._platform._meta } : {}), {
|
|
988
|
-
context,
|
|
989
|
-
encodedState: doc.getEncodedState(),
|
|
990
|
-
message,
|
|
991
|
-
platform: this._platform,
|
|
992
|
-
room: this.id,
|
|
993
|
-
user: session.user,
|
|
994
|
-
webSocket: session.webSocket.webSocket
|
|
995
|
-
}));
|
|
996
|
-
const sessionId = session.id;
|
|
997
|
-
const user = session.user;
|
|
998
|
-
if (!procedure) {
|
|
999
|
-
yield this._broadcast({
|
|
1000
|
-
message,
|
|
1001
|
-
senderId: sessionId
|
|
1002
|
-
});
|
|
1003
|
-
return;
|
|
1004
|
-
}
|
|
1005
|
-
let inputs;
|
|
1006
|
-
try {
|
|
1007
|
-
inputs = this._getProcedureInputs(message);
|
|
1008
|
-
} catch (error) {
|
|
1009
|
-
pluvWs.handleError({
|
|
1010
|
-
error,
|
|
1011
|
-
message: "Invalid input",
|
|
1012
|
-
room: this.id,
|
|
1013
|
-
session
|
|
1014
|
-
});
|
|
1015
|
-
return;
|
|
1016
|
-
}
|
|
1017
|
-
yield Promise.all([
|
|
1018
|
-
(_b = (_a = procedure.config).broadcast) == null ? void 0 : _b.call(_a, inputs, eventContext),
|
|
1019
|
-
(_d = (_c = procedure.config).self) == null ? void 0 : _d.call(_c, inputs, eventContext),
|
|
1020
|
-
(_f = (_e = procedure.config).sync) == null ? void 0 : _f.call(_e, inputs, eventContext)
|
|
1021
|
-
]).then((_0) => __async(this, [_0], function* ([broadcast, self, sync]) {
|
|
1022
|
-
const handleBroadcast = () => __async(this, null, function* () {
|
|
1023
|
-
if (!broadcast) return;
|
|
1024
|
-
const messages = Object.entries(broadcast).map((_02) => __async(this, [_02], function* ([type, data]) {
|
|
1025
|
-
yield this._broadcast({
|
|
1026
|
-
message: { data, type },
|
|
1027
|
-
senderId: sessionId
|
|
1028
|
-
});
|
|
1029
|
-
}));
|
|
1030
|
-
yield Promise.all(messages);
|
|
1031
|
-
});
|
|
1032
|
-
const handleSelf = () => __async(this, null, function* () {
|
|
1033
|
-
if (!self) return;
|
|
1034
|
-
const messages = Object.entries(self).map((_02) => __async(this, [_02], function* ([type, data]) {
|
|
1035
|
-
yield this._sendSelfMessage({ data, type }, { sessionId, user });
|
|
1036
|
-
}));
|
|
1037
|
-
yield Promise.all(messages);
|
|
1038
|
-
});
|
|
1039
|
-
const handleSync = () => __async(this, null, function* () {
|
|
1040
|
-
if (!sync) return;
|
|
1041
|
-
yield this._platform.pubSub.publish(this.id, __spreadValues({
|
|
1042
|
-
connectionId: session.id,
|
|
1043
|
-
options: { type: "sync" },
|
|
1044
|
-
room: this.id,
|
|
1045
|
-
user
|
|
1046
|
-
}, message));
|
|
1047
|
-
const messages = Object.entries(sync).map((_02) => __async(this, [_02], function* ([type, data]) {
|
|
1048
|
-
yield this._sendSelfMessage({ data, type }, { sessionId, user });
|
|
1049
|
-
}));
|
|
1050
|
-
yield Promise.all(messages);
|
|
1051
|
-
});
|
|
1052
|
-
yield Promise.all([handleBroadcast(), handleSelf(), handleSync()]);
|
|
1053
|
-
}));
|
|
1054
|
-
});
|
|
1055
|
-
}
|
|
1056
|
-
_parseMessage(message) {
|
|
1057
|
-
try {
|
|
1058
|
-
const parsed = this._platform.parseData(message.data);
|
|
1059
|
-
if (typeof parsed !== "object") return null;
|
|
1060
|
-
if (typeof parsed.type !== "string") return null;
|
|
1061
|
-
if (typeof parsed.data !== "object") return null;
|
|
1062
|
-
return parsed;
|
|
1063
|
-
} catch (e) {
|
|
1064
|
-
return null;
|
|
1065
|
-
}
|
|
1066
|
-
}
|
|
1067
|
-
_setPresence(params) {
|
|
1068
|
-
var _a, _b;
|
|
1069
|
-
const { presence, sessionId, timer: _timer } = params;
|
|
1070
|
-
const timer = _timer != null ? _timer : (/* @__PURE__ */ new Date()).getTime();
|
|
1071
|
-
const pluvWs = (_a = this._sessions.get(sessionId)) != null ? _a : null;
|
|
1072
|
-
if (!pluvWs) return;
|
|
1073
|
-
const wsSession = pluvWs.session;
|
|
1074
|
-
const user = wsSession.user;
|
|
1075
|
-
const sessionIds = user ? /* @__PURE__ */ new Set([...(_b = this._userSessionss.get(user.id)) != null ? _b : [], sessionId]) : /* @__PURE__ */ new Set([sessionId]);
|
|
1076
|
-
sessionIds.forEach((sId) => {
|
|
1077
|
-
const pWs = this._sessions.get(sId);
|
|
1078
|
-
const session = pWs == null ? void 0 : pWs.session;
|
|
1079
|
-
if (!session) return;
|
|
1080
|
-
const prevState = session.webSocket.state;
|
|
1081
|
-
this._platform.setSerializedState(session.webSocket, __spreadProps(__spreadValues({}, prevState), {
|
|
1082
|
-
presence,
|
|
1083
|
-
timers: __spreadProps(__spreadValues({}, prevState.timers), {
|
|
1084
|
-
presence: timer
|
|
1085
|
-
})
|
|
1086
|
-
}));
|
|
1087
|
-
});
|
|
1088
|
-
}
|
|
1089
|
-
_removeUserSession(userId, sessionId) {
|
|
1090
|
-
const set = this._userSessionss.get(userId);
|
|
1091
|
-
if (!set) return null;
|
|
1092
|
-
set.delete(sessionId);
|
|
1093
|
-
if (!!set.size) return set;
|
|
1094
|
-
this._userSessionss.delete(userId);
|
|
1095
|
-
return set;
|
|
1096
|
-
}
|
|
1097
|
-
_sendMessage(pluvWs, message) {
|
|
1098
|
-
return __async(this, null, function* () {
|
|
1099
|
-
if (!(yield this._initialized)) return;
|
|
1100
|
-
yield Promise.resolve(pluvWs.sendMessage(message));
|
|
1101
|
-
});
|
|
1102
|
-
}
|
|
1103
|
-
_sendBroadcastMessage(message, sender, sessionIds) {
|
|
1104
|
-
return __async(this, null, function* () {
|
|
1105
|
-
var _a, _b;
|
|
1106
|
-
const { data, type } = message;
|
|
1107
|
-
const connectionId = (_a = sender == null ? void 0 : sender.sessionId) != null ? _a : null;
|
|
1108
|
-
const room = this.id;
|
|
1109
|
-
if (typeof connectionId !== "string") return;
|
|
1110
|
-
const webSockets = (_b = sessionIds == null ? void 0 : sessionIds.reduce((dict, id) => {
|
|
1111
|
-
const pluvWs = this._sessions.get(id);
|
|
1112
|
-
return pluvWs ? dict.set(id, pluvWs) : dict;
|
|
1113
|
-
}, /* @__PURE__ */ new Map())) != null ? _b : this._sessions;
|
|
1114
|
-
const promises = Array.from(webSockets.values()).map((pluvWs) => __async(this, null, function* () {
|
|
1115
|
-
var _a2;
|
|
1116
|
-
const session = pluvWs.session;
|
|
1117
|
-
const user = (_a2 = session.user) != null ? _a2 : null;
|
|
1118
|
-
this._sendMessage(pluvWs, { connectionId, data, room, type, user });
|
|
1119
|
-
}));
|
|
1120
|
-
yield Promise.all(promises);
|
|
1121
|
-
});
|
|
1122
|
-
}
|
|
1123
|
-
_sendSelfMessage(message, sender) {
|
|
1124
|
-
return __async(this, null, function* () {
|
|
1125
|
-
var _a, _b;
|
|
1126
|
-
const senderId = (_a = sender == null ? void 0 : sender.sessionId) != null ? _a : null;
|
|
1127
|
-
if (typeof senderId !== "string") return;
|
|
1128
|
-
const pluvWs = this._sessions.get(senderId);
|
|
1129
|
-
if (!pluvWs) return;
|
|
1130
|
-
if (message.type === "$pong") {
|
|
1131
|
-
const elapsedMs = Date.now() - this._lastGarbageCollectMs;
|
|
1132
|
-
if (elapsedMs > GARBAGE_COLLECT_INTERVAL_MS) {
|
|
1133
|
-
yield this.garbageCollect();
|
|
1134
|
-
}
|
|
1135
|
-
}
|
|
1136
|
-
const session = pluvWs.session;
|
|
1137
|
-
const user = (_b = session.user) != null ? _b : null;
|
|
1138
|
-
this._sendMessage(pluvWs, __spreadValues({
|
|
1139
|
-
connectionId: senderId,
|
|
1140
|
-
room: this.id,
|
|
1141
|
-
user
|
|
1142
|
-
}, message));
|
|
1143
|
-
});
|
|
1144
|
-
}
|
|
1145
|
-
_sendSyncMessage(message, sender) {
|
|
1146
|
-
return __async(this, null, function* () {
|
|
1147
|
-
var _a;
|
|
1148
|
-
if (!sender) return;
|
|
1149
|
-
const connectionId = sender.sessionId;
|
|
1150
|
-
if (typeof connectionId !== "string") return;
|
|
1151
|
-
const [doc, context] = yield Promise.all([this._doc, this._getContext()]);
|
|
1152
|
-
const resolverCtx = {
|
|
1153
|
-
context,
|
|
1154
|
-
doc,
|
|
1155
|
-
garbageCollect: () => __async(this, null, function* () {
|
|
1156
|
-
yield this.garbageCollect();
|
|
1157
|
-
}),
|
|
1158
|
-
platform: this._platform,
|
|
1159
|
-
get presence() {
|
|
1160
|
-
return null;
|
|
1161
|
-
},
|
|
1162
|
-
set presence(presence) {
|
|
1163
|
-
return;
|
|
1164
|
-
},
|
|
1165
|
-
room: this.id,
|
|
1166
|
-
session: null,
|
|
1167
|
-
sessions: this._getSessions(),
|
|
1168
|
-
time: (/* @__PURE__ */ new Date()).getTime()
|
|
1169
|
-
};
|
|
1170
|
-
const resolver = (_a = this._getProcedure(message)) == null ? void 0 : _a.config.sync;
|
|
1171
|
-
if (!resolver) return;
|
|
1172
|
-
let inputs;
|
|
1173
|
-
try {
|
|
1174
|
-
inputs = this._getProcedureInputs(message);
|
|
1175
|
-
} catch (e) {
|
|
1176
|
-
return;
|
|
1177
|
-
}
|
|
1178
|
-
Promise.resolve(resolver(inputs, resolverCtx)).then((output) => {
|
|
1179
|
-
if (!output) return;
|
|
1180
|
-
Object.keys(output).forEach((type) => {
|
|
1181
|
-
var _a2, _b;
|
|
1182
|
-
const data = (_a2 = output[type]) != null ? _a2 : {};
|
|
1183
|
-
this._platform.pubSub.publish(this.id, {
|
|
1184
|
-
connectionId,
|
|
1185
|
-
data,
|
|
1186
|
-
options: { type: "self" },
|
|
1187
|
-
room: this.id,
|
|
1188
|
-
type,
|
|
1189
|
-
user: (_b = sender.user) != null ? _b : null
|
|
1190
|
-
});
|
|
1191
|
-
});
|
|
1192
|
-
});
|
|
1193
|
-
});
|
|
1194
|
-
}
|
|
1195
|
-
};
|
|
1196
|
-
|
|
1197
|
-
// src/version.generated.ts
|
|
1198
|
-
var version = "3.2.2";
|
|
1199
|
-
|
|
1200
|
-
// src/PluvServer.ts
|
|
1201
|
-
var PluvServer = class {
|
|
1202
|
-
constructor(options) {
|
|
1203
|
-
this.version = version;
|
|
1204
|
-
this._getInitialStorage = (...args) => {
|
|
1205
|
-
var _a;
|
|
1206
|
-
const getInitialStorage = typeof this._config.getInitialStorage !== "string" ? (_a = this._config.getInitialStorage) != null ? _a : () => null : () => null;
|
|
1207
|
-
return getInitialStorage(...args);
|
|
1208
|
-
};
|
|
1209
|
-
this._config = __spreadValues({
|
|
1210
|
-
crdt: import_crdt2.noop,
|
|
1211
|
-
debug: false,
|
|
1212
|
-
router: new PluvRouter({})
|
|
1213
|
-
}, options);
|
|
1214
|
-
const {
|
|
1215
|
-
onRoomDeleted,
|
|
1216
|
-
onRoomMessage,
|
|
1217
|
-
onStorageUpdated,
|
|
1218
|
-
onUserConnected,
|
|
1219
|
-
onUserDisconnected
|
|
1220
|
-
} = options;
|
|
1221
|
-
this._docFactory = this._config.crdt.doc(() => ({}));
|
|
1222
|
-
this._listeners = {
|
|
1223
|
-
onRoomDeleted: (event) => onRoomDeleted == null ? void 0 : onRoomDeleted(event),
|
|
1224
|
-
onRoomMessage: (event) => onRoomMessage == null ? void 0 : onRoomMessage(event),
|
|
1225
|
-
onStorageUpdated: (event) => onStorageUpdated == null ? void 0 : onStorageUpdated(event),
|
|
1226
|
-
onUserConnected: (event) => onUserConnected == null ? void 0 : onUserConnected(event),
|
|
1227
|
-
onUserDisconnected: (event) => onUserDisconnected == null ? void 0 : onUserDisconnected(event)
|
|
1228
|
-
};
|
|
1229
|
-
}
|
|
1230
|
-
get fetch() {
|
|
1231
|
-
return (...args) => {
|
|
1232
|
-
const platform = this._config.platform();
|
|
1233
|
-
platform.validateConfig(this._config);
|
|
1234
|
-
if (!platform._fetch) {
|
|
1235
|
-
throw new Error(`\`${platform._name}\` does not support \`fetch\``);
|
|
1236
|
-
}
|
|
1237
|
-
return platform._fetch(...args);
|
|
1238
|
-
};
|
|
1239
|
-
}
|
|
1240
|
-
/**
|
|
1241
|
-
* @ignore
|
|
1242
|
-
* @readonly
|
|
1243
|
-
* @deprecated Internal use only. Changes to this will never be marked as breaking.
|
|
1244
|
-
*/
|
|
1245
|
-
get _defs() {
|
|
1246
|
-
return {
|
|
1247
|
-
authorize: this._config.authorize,
|
|
1248
|
-
context: this._config.context,
|
|
1249
|
-
crdt: this._config.crdt,
|
|
1250
|
-
events: this._router._defs.events,
|
|
1251
|
-
platform: this._config.platform()
|
|
1252
|
-
};
|
|
1253
|
-
}
|
|
1254
|
-
get _baseRouter() {
|
|
1255
|
-
const listeners = this._getListeners();
|
|
1256
|
-
return new PluvRouter({
|
|
1257
|
-
$getOthers: this._procedure.sync((data, { room, session, sessions }) => {
|
|
1258
|
-
const currentTime = Date.now();
|
|
1259
|
-
const others = sessions.filter((wsSession) => {
|
|
1260
|
-
if (wsSession.id === (session == null ? void 0 : session.id)) return false;
|
|
1261
|
-
if (wsSession.quit) return false;
|
|
1262
|
-
if (currentTime - wsSession.timers.ping > PING_TIMEOUT_MS) return false;
|
|
1263
|
-
return true;
|
|
1264
|
-
}).reduce(
|
|
1265
|
-
(acc, { id, presence, timers, user }) => __spreadProps(__spreadValues({}, acc), {
|
|
1266
|
-
[id]: {
|
|
1267
|
-
connectionId: id,
|
|
1268
|
-
presence,
|
|
1269
|
-
room,
|
|
1270
|
-
timers: { presence: timers.presence },
|
|
1271
|
-
user
|
|
1272
|
-
}
|
|
1273
|
-
}),
|
|
1274
|
-
{}
|
|
1275
|
-
);
|
|
1276
|
-
return { $othersReceived: { others } };
|
|
1277
|
-
}),
|
|
1278
|
-
$initializeSession: this._procedure.broadcast((data, { session }) => {
|
|
1279
|
-
const presence = data == null ? void 0 : data.presence;
|
|
1280
|
-
const timers = session.timers;
|
|
1281
|
-
if (!session) return {};
|
|
1282
|
-
session.presence = presence;
|
|
1283
|
-
return {
|
|
1284
|
-
$userJoined: {
|
|
1285
|
-
connectionId: session.id,
|
|
1286
|
-
user: session.user,
|
|
1287
|
-
presence,
|
|
1288
|
-
timers: { presence: timers.presence }
|
|
1289
|
-
}
|
|
1290
|
-
};
|
|
1291
|
-
}).self((_0, _1) => __async(this, [_0, _1], function* (data, { context, doc, platform, room, session }) {
|
|
1292
|
-
const oldState = yield platform.persistence.getStorageState(room);
|
|
1293
|
-
const update = data == null ? void 0 : data.update;
|
|
1294
|
-
if (!oldState) {
|
|
1295
|
-
const loadedState = yield this._getInitialStorage({ context, room });
|
|
1296
|
-
if (!!loadedState && !this._docFactory.isEmpty(loadedState)) {
|
|
1297
|
-
doc.applyEncodedState({ update: loadedState });
|
|
1298
|
-
}
|
|
1299
|
-
}
|
|
1300
|
-
if (!doc.isEmpty()) {
|
|
1301
|
-
const encodedState2 = doc.getEncodedState();
|
|
1302
|
-
return {
|
|
1303
|
-
$storageReceived: { changeKind: "unchanged", state: encodedState2 }
|
|
1304
|
-
};
|
|
1305
|
-
}
|
|
1306
|
-
if (!!update) {
|
|
1307
|
-
const encodedState2 = doc.applyEncodedState({ update }).getEncodedState();
|
|
1308
|
-
const storageSize = new TextEncoder().encode(encodedState2).length;
|
|
1309
|
-
if (!!this._config.limits.storageMaxSize && storageSize > this._config.limits.storageMaxSize) {
|
|
1310
|
-
throw new Error("Storage has exceeded the size limit");
|
|
1311
|
-
}
|
|
1312
|
-
yield platform.persistence.setStorageState(room, encodedState2).catch((error) => {
|
|
1313
|
-
this._logDebug(error);
|
|
1314
|
-
});
|
|
1315
|
-
listeners.onStorageUpdated({
|
|
1316
|
-
context,
|
|
1317
|
-
encodedState: encodedState2,
|
|
1318
|
-
platform,
|
|
1319
|
-
room,
|
|
1320
|
-
user: session == null ? void 0 : session.user,
|
|
1321
|
-
webSocket: session == null ? void 0 : session.webSocket.webSocket
|
|
1322
|
-
});
|
|
1323
|
-
return {
|
|
1324
|
-
$storageReceived: { changeKind: "initialized", state: encodedState2 }
|
|
1325
|
-
};
|
|
1326
|
-
}
|
|
1327
|
-
const encodedState = doc.getEncodedState();
|
|
1328
|
-
return { $storageReceived: { changeKind: "empty", state: encodedState } };
|
|
1329
|
-
})),
|
|
1330
|
-
$ping: this._procedure.self((data, { platform, session }) => {
|
|
1331
|
-
if (!session) return {};
|
|
1332
|
-
const currentTime = (/* @__PURE__ */ new Date()).getTime();
|
|
1333
|
-
const prevState = session.webSocket.state;
|
|
1334
|
-
platform.setSerializedState(session.webSocket, __spreadProps(__spreadValues({}, prevState), {
|
|
1335
|
-
timers: __spreadProps(__spreadValues({}, prevState.timers), {
|
|
1336
|
-
ping: currentTime
|
|
1337
|
-
})
|
|
1338
|
-
}));
|
|
1339
|
-
return { $pong: {} };
|
|
1340
|
-
}),
|
|
1341
|
-
$updatePresence: this._procedure.broadcast((data, context) => {
|
|
1342
|
-
const presence = data == null ? void 0 : data.presence;
|
|
1343
|
-
const { session } = context;
|
|
1344
|
-
if (!session) return {};
|
|
1345
|
-
const cleanedPatch = pickBy(
|
|
1346
|
-
presence != null ? presence : {},
|
|
1347
|
-
(value) => typeof value !== "undefined"
|
|
1348
|
-
);
|
|
1349
|
-
const updated = Object.assign(/* @__PURE__ */ Object.create(null), session.presence, cleanedPatch);
|
|
1350
|
-
const bytes = new TextEncoder().encode(JSON.stringify(updated)).length;
|
|
1351
|
-
if (!!this._config.limits.presenceMaxSize && bytes > this._config.limits.presenceMaxSize) {
|
|
1352
|
-
throw new Error(oneLine`
|
|
1353
|
-
Large presence. Presence must be at most
|
|
1354
|
-
${this._config.limits.presenceMaxSize.toLocaleString()} bytes.
|
|
1355
|
-
Current size: ${bytes.toLocaleString()} bytes
|
|
1356
|
-
`);
|
|
1357
|
-
}
|
|
1358
|
-
context.presence = updated;
|
|
1359
|
-
return {
|
|
1360
|
-
$presenceUpdated: {
|
|
1361
|
-
presence: updated,
|
|
1362
|
-
timers: { presence: session.timers.presence }
|
|
1363
|
-
}
|
|
1364
|
-
};
|
|
1365
|
-
}),
|
|
1366
|
-
$updateStorage: this._procedure.broadcast((data, { context, doc, platform, room }) => {
|
|
1367
|
-
var _a;
|
|
1368
|
-
const origin = data == null ? void 0 : data.origin;
|
|
1369
|
-
const update = (_a = data == null ? void 0 : data.update) != null ? _a : null;
|
|
1370
|
-
if (origin === "$initialized" && Object.keys(doc.toJson()).length) return {};
|
|
1371
|
-
const updated = update === null ? doc : doc.applyEncodedState({ update });
|
|
1372
|
-
const encodedState = updated.getEncodedState();
|
|
1373
|
-
const storageSize = new TextEncoder().encode(encodedState).length;
|
|
1374
|
-
if (!!this._config.limits.storageMaxSize && storageSize > this._config.limits.storageMaxSize) {
|
|
1375
|
-
throw new Error(oneLine`
|
|
1376
|
-
Large Storage. Storage must be at most
|
|
1377
|
-
${this._config.limits.storageMaxSize.toLocaleString()} bytes.
|
|
1378
|
-
Current size: ${storageSize.toLocaleString()} bytes
|
|
1379
|
-
`);
|
|
1380
|
-
}
|
|
1381
|
-
platform.persistence.setStorageState(room, encodedState).then(() => {
|
|
1382
|
-
listeners.onStorageUpdated({
|
|
1383
|
-
context,
|
|
1384
|
-
encodedState,
|
|
1385
|
-
platform,
|
|
1386
|
-
room
|
|
1387
|
-
});
|
|
1388
|
-
});
|
|
1389
|
-
return { $storageUpdated: { state: encodedState } };
|
|
1390
|
-
})
|
|
1391
|
-
});
|
|
1392
|
-
}
|
|
1393
|
-
get _procedure() {
|
|
1394
|
-
return new PluvProcedure();
|
|
1395
|
-
}
|
|
1396
|
-
get _router() {
|
|
1397
|
-
return this._config.router ? PluvRouter.merge(this._baseRouter, this._config.router) : this._baseRouter;
|
|
1398
|
-
}
|
|
1399
|
-
createRoom(room, ...options) {
|
|
1400
|
-
var _a, _c;
|
|
1401
|
-
const _b = (_a = options[0]) != null ? _a : {}, { _meta, debug, onDestroy, onMessage } = _b, platformRoomContext = __objRest(_b, ["_meta", "debug", "onDestroy", "onMessage"]);
|
|
1402
|
-
const platform = this._config.platform();
|
|
1403
|
-
platform.validateConfig(this._config);
|
|
1404
|
-
if (platform._config.handleMode !== "io") {
|
|
1405
|
-
throw new Error(`\`createRoom\` is unsupported for \`${platform._name}\``);
|
|
1406
|
-
}
|
|
1407
|
-
if (!/^[a-z0-9](?:[a-z0-9-_]*[a-z0-9])?$/i.test(room))
|
|
1408
|
-
throw new Error("Unsupported room name");
|
|
1409
|
-
const roomContext = platformRoomContext;
|
|
1410
|
-
const listeners = this._getListeners();
|
|
1411
|
-
const logDebug = this._logDebug.bind(this);
|
|
1412
|
-
const newRoom = new IORoom(room, __spreadProps(__spreadValues({}, !!_meta ? { _meta } : {}), {
|
|
1413
|
-
authorize: (_c = this._config.authorize) != null ? _c : void 0,
|
|
1414
|
-
context: this._config.context,
|
|
1415
|
-
crdt: this._config.crdt,
|
|
1416
|
-
debug: debug != null ? debug : this._config.debug,
|
|
1417
|
-
getInitialStorage: this._getInitialStorage,
|
|
1418
|
-
onDestroy(event) {
|
|
1419
|
-
return __async(this, null, function* () {
|
|
1420
|
-
logDebug(`${import_kleur2.default.blue("Deleting empty room:")} ${room}`);
|
|
1421
|
-
yield Promise.resolve(onDestroy == null ? void 0 : onDestroy(event));
|
|
1422
|
-
yield Promise.resolve(listeners.onRoomDeleted(event));
|
|
1423
|
-
yield event.platform.persistence.deleteStorageState(room);
|
|
1424
|
-
logDebug(`${import_kleur2.default.blue("Deleted room:")} ${room}`);
|
|
1425
|
-
});
|
|
1426
|
-
},
|
|
1427
|
-
onMessage(event) {
|
|
1428
|
-
return __async(this, null, function* () {
|
|
1429
|
-
yield Promise.resolve(onMessage == null ? void 0 : onMessage(event));
|
|
1430
|
-
yield Promise.resolve(listeners.onRoomMessage(event));
|
|
1431
|
-
});
|
|
1432
|
-
},
|
|
1433
|
-
onUserConnected(event) {
|
|
1434
|
-
return __async(this, null, function* () {
|
|
1435
|
-
yield Promise.resolve(listeners.onUserConnected(event));
|
|
1436
|
-
});
|
|
1437
|
-
},
|
|
1438
|
-
onUserDisconnected(event) {
|
|
1439
|
-
return __async(this, null, function* () {
|
|
1440
|
-
yield Promise.resolve(listeners.onUserDisconnected(event));
|
|
1441
|
-
});
|
|
1442
|
-
},
|
|
1443
|
-
platform,
|
|
1444
|
-
roomContext,
|
|
1445
|
-
router: this._router
|
|
1446
|
-
}));
|
|
1447
|
-
this._logDebug(`${import_kleur2.default.blue("Created room:")} ${room}`);
|
|
1448
|
-
return newRoom;
|
|
1449
|
-
}
|
|
1450
|
-
createToken(params) {
|
|
1451
|
-
return __async(this, null, function* () {
|
|
1452
|
-
return yield this._config.io.createToken(params);
|
|
1453
|
-
});
|
|
1454
|
-
}
|
|
1455
|
-
_getListeners() {
|
|
1456
|
-
return this._listeners;
|
|
1457
|
-
}
|
|
1458
|
-
_logDebug(...data) {
|
|
1459
|
-
if (this._config.debug) console.log(...data);
|
|
1460
|
-
}
|
|
1461
|
-
};
|
|
1462
|
-
|
|
1463
|
-
// src/PluvIO.ts
|
|
1464
|
-
var PluvIO = class {
|
|
1465
|
-
constructor(options) {
|
|
1466
|
-
this.version = version;
|
|
1467
|
-
this._authorize = null;
|
|
1468
|
-
this._context = {};
|
|
1469
|
-
const { authorize: authorize2, context, crdt = import_crdt3.noop, debug = false, limits, platform } = options;
|
|
1470
|
-
this._crdt = crdt;
|
|
1471
|
-
this._debug = debug;
|
|
1472
|
-
this._limits = __spreadValues({
|
|
1473
|
-
presenceMaxSize: MAX_PRESENCE_SIZE_BYTES,
|
|
1474
|
-
storageMaxSize: MAX_STORAGE_SIZE_BYTES,
|
|
1475
|
-
userIdMaxLength: MAX_USER_ID_LENGTH,
|
|
1476
|
-
userMaxSize: MAX_USER_SIZE_BYTES
|
|
1477
|
-
}, limits);
|
|
1478
|
-
this._platform = platform;
|
|
1479
|
-
if (authorize2) this._authorize = authorize2;
|
|
1480
|
-
if (context) this._context = context;
|
|
1481
|
-
}
|
|
1482
|
-
get procedure() {
|
|
1483
|
-
return new PluvProcedure();
|
|
1484
|
-
}
|
|
1485
|
-
createToken(params) {
|
|
1486
|
-
return __async(this, null, function* () {
|
|
1487
|
-
var _a;
|
|
1488
|
-
if (!this._authorize) {
|
|
1489
|
-
throw new Error("IO does not specify authorize during initialization.");
|
|
1490
|
-
}
|
|
1491
|
-
const ioAuthorize = this._getIOAuthorize(params);
|
|
1492
|
-
const user = params.user;
|
|
1493
|
-
const parsed = !!ioAuthorize ? ioAuthorize.user.parse(user) : user;
|
|
1494
|
-
if (!!this._limits.userIdMaxLength && user.id.length > this._limits.userIdMaxLength) {
|
|
1495
|
-
throw new Error(oneLine`
|
|
1496
|
-
createToken was called with a long user id. User ID must be at
|
|
1497
|
-
most 128 characters. Current length: ${user.id.length.toLocaleString()}
|
|
1498
|
-
`);
|
|
1499
|
-
}
|
|
1500
|
-
const bytes = new TextEncoder().encode(JSON.stringify(parsed)).length;
|
|
1501
|
-
if (!!this._limits.userMaxSize && bytes > this._limits.userMaxSize) {
|
|
1502
|
-
throw new Error(oneLine`
|
|
1503
|
-
createToken called with large payload. User must be at most 512
|
|
1504
|
-
bytes. Current size: ${bytes.toLocaleString()}
|
|
1505
|
-
`);
|
|
1506
|
-
}
|
|
1507
|
-
const platform = this._platform();
|
|
1508
|
-
if (platform._createToken) {
|
|
1509
|
-
return yield platform._createToken(__spreadProps(__spreadValues({}, params), { authorize: ioAuthorize }));
|
|
1510
|
-
}
|
|
1511
|
-
const secret = (_a = ioAuthorize == null ? void 0 : ioAuthorize.secret) != null ? _a : null;
|
|
1512
|
-
if (!secret) throw new Error("`authorize` was specified without a valid secret");
|
|
1513
|
-
return yield authorize({ platform, secret }).encode(
|
|
1514
|
-
params
|
|
1515
|
-
);
|
|
1516
|
-
});
|
|
1517
|
-
}
|
|
1518
|
-
mergeRouters(...routers) {
|
|
1519
|
-
return PluvRouter.merge(...routers);
|
|
1520
|
-
}
|
|
1521
|
-
router(events) {
|
|
1522
|
-
const invalidName = Object.keys(events).find((name) => name.includes("$"));
|
|
1523
|
-
if (typeof invalidName === "string") {
|
|
1524
|
-
throw new Error(oneLine`
|
|
1525
|
-
Invalid event name. Event names must not contain $: "${invalidName}"
|
|
1526
|
-
`);
|
|
1527
|
-
}
|
|
1528
|
-
return new PluvRouter(events);
|
|
1529
|
-
}
|
|
1530
|
-
server(...config) {
|
|
1531
|
-
var _a, _b;
|
|
1532
|
-
const serverConfig = (_a = config[0]) != null ? _a : {};
|
|
1533
|
-
return new PluvServer(__spreadProps(__spreadValues({}, serverConfig), {
|
|
1534
|
-
authorize: (_b = this._authorize) != null ? _b : void 0,
|
|
1535
|
-
context: this._context,
|
|
1536
|
-
crdt: this._crdt,
|
|
1537
|
-
debug: this._debug,
|
|
1538
|
-
io: this,
|
|
1539
|
-
limits: this._limits,
|
|
1540
|
-
platform: this._platform
|
|
1541
|
-
}));
|
|
1542
|
-
}
|
|
1543
|
-
_getIOAuthorize(options) {
|
|
1544
|
-
if (typeof this._authorize === "function") {
|
|
1545
|
-
return this._authorize(options);
|
|
1546
|
-
}
|
|
1547
|
-
return this._authorize;
|
|
1548
|
-
}
|
|
1549
|
-
};
|
|
1550
|
-
|
|
1551
|
-
// src/createIO.ts
|
|
1552
|
-
var createIO = (params) => {
|
|
1553
|
-
const { authorize: authorize2, context, crdt, debug, limits, platform } = params;
|
|
1554
|
-
return new PluvIO({
|
|
1555
|
-
authorize: authorize2,
|
|
1556
|
-
context,
|
|
1557
|
-
crdt,
|
|
1558
|
-
debug,
|
|
1559
|
-
limits,
|
|
1560
|
-
platform
|
|
1561
|
-
});
|
|
1562
|
-
};
|
|
1563
|
-
// Annotate the CommonJS export names for ESM import in node:
|
|
1564
|
-
0 && (module.exports = {
|
|
1565
|
-
AbstractPersistence,
|
|
1566
|
-
AbstractPlatform,
|
|
1567
|
-
AbstractPubSub,
|
|
1568
|
-
AbstractWebSocket,
|
|
1569
|
-
PluvIO,
|
|
1570
|
-
PluvProcedure,
|
|
1571
|
-
PluvRouter,
|
|
1572
|
-
PluvServer,
|
|
1573
|
-
authorize,
|
|
1574
|
-
createIO
|
|
1575
|
-
});
|