@pluv/client 3.2.1 → 4.0.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/.turbo/turbo-build.log +14 -16
- package/CHANGELOG.md +28 -0
- package/dist/index.d.mts +286 -284
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +1740 -1865
- package/dist/index.mjs.map +1 -0
- package/package.json +17 -13
- package/src/infer.ts +4 -2
- package/tsdown.config.ts +14 -0
- package/dist/index.d.ts +0 -354
- package/dist/index.js +0 -1944
package/dist/index.js
DELETED
|
@@ -1,1944 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __defProp = Object.defineProperty;
|
|
3
|
-
var __defProps = Object.defineProperties;
|
|
4
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
-
var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
|
|
6
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
-
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
|
8
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
-
var __propIsEnum = Object.prototype.propertyIsEnumerable;
|
|
10
|
-
var __typeError = (msg) => {
|
|
11
|
-
throw TypeError(msg);
|
|
12
|
-
};
|
|
13
|
-
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
14
|
-
var __spreadValues = (a, b) => {
|
|
15
|
-
for (var prop in b || (b = {}))
|
|
16
|
-
if (__hasOwnProp.call(b, prop))
|
|
17
|
-
__defNormalProp(a, prop, b[prop]);
|
|
18
|
-
if (__getOwnPropSymbols)
|
|
19
|
-
for (var prop of __getOwnPropSymbols(b)) {
|
|
20
|
-
if (__propIsEnum.call(b, prop))
|
|
21
|
-
__defNormalProp(a, prop, b[prop]);
|
|
22
|
-
}
|
|
23
|
-
return a;
|
|
24
|
-
};
|
|
25
|
-
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
|
|
26
|
-
var __export = (target, all) => {
|
|
27
|
-
for (var name in all)
|
|
28
|
-
__defProp(target, name, { get: all[name], enumerable: true });
|
|
29
|
-
};
|
|
30
|
-
var __copyProps = (to, from, except, desc) => {
|
|
31
|
-
if (from && typeof from === "object" || typeof from === "function") {
|
|
32
|
-
for (let key of __getOwnPropNames(from))
|
|
33
|
-
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
34
|
-
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
35
|
-
}
|
|
36
|
-
return to;
|
|
37
|
-
};
|
|
38
|
-
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
39
|
-
var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
|
|
40
|
-
var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
|
|
41
|
-
var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
|
|
42
|
-
var __async = (__this, __arguments, generator) => {
|
|
43
|
-
return new Promise((resolve, reject) => {
|
|
44
|
-
var fulfilled = (value) => {
|
|
45
|
-
try {
|
|
46
|
-
step(generator.next(value));
|
|
47
|
-
} catch (e) {
|
|
48
|
-
reject(e);
|
|
49
|
-
}
|
|
50
|
-
};
|
|
51
|
-
var rejected = (value) => {
|
|
52
|
-
try {
|
|
53
|
-
step(generator.throw(value));
|
|
54
|
-
} catch (e) {
|
|
55
|
-
reject(e);
|
|
56
|
-
}
|
|
57
|
-
};
|
|
58
|
-
var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
|
|
59
|
-
step((generator = generator.apply(__this, __arguments)).next());
|
|
60
|
-
});
|
|
61
|
-
};
|
|
62
|
-
|
|
63
|
-
// src/index.ts
|
|
64
|
-
var index_exports = {};
|
|
65
|
-
__export(index_exports, {
|
|
66
|
-
AbstractStorageStore: () => AbstractStorageStore,
|
|
67
|
-
ConnectionState: () => import_types3.ConnectionState,
|
|
68
|
-
MockedRoom: () => MockedRoom,
|
|
69
|
-
PluvClient: () => PluvClient,
|
|
70
|
-
PluvRoom: () => PluvRoom,
|
|
71
|
-
createClient: () => createClient,
|
|
72
|
-
infer: () => infer,
|
|
73
|
-
register: () => register
|
|
74
|
-
});
|
|
75
|
-
module.exports = __toCommonJS(index_exports);
|
|
76
|
-
var import_types3 = require("@pluv/types");
|
|
77
|
-
|
|
78
|
-
// src/AbstractStorageStore.ts
|
|
79
|
-
var AbstractStorageStore = class {
|
|
80
|
-
constructor(room) {
|
|
81
|
-
this.room = room;
|
|
82
|
-
}
|
|
83
|
-
};
|
|
84
|
-
|
|
85
|
-
// src/constants.ts
|
|
86
|
-
var MAX_PRESENCE_SIZE_BYTES = 512;
|
|
87
|
-
var PLUV_PRESENCE_META_KEY = "__pluv";
|
|
88
|
-
|
|
89
|
-
// src/PluvProcedure.ts
|
|
90
|
-
var PluvProcedure = class _PluvProcedure {
|
|
91
|
-
constructor(config = {}) {
|
|
92
|
-
this._broadcast = null;
|
|
93
|
-
this._input = null;
|
|
94
|
-
const { broadcast, input } = config;
|
|
95
|
-
this._broadcast = broadcast != null ? broadcast : null;
|
|
96
|
-
this._input = input != null ? input : null;
|
|
97
|
-
}
|
|
98
|
-
get config() {
|
|
99
|
-
var _a, _b, _c;
|
|
100
|
-
return {
|
|
101
|
-
broadcast: (_b = (_a = this._broadcast) == null ? void 0 : _a.bind(this)) != null ? _b : null,
|
|
102
|
-
input: (_c = this._input) != null ? _c : null,
|
|
103
|
-
resolver: this._resolver()
|
|
104
|
-
};
|
|
105
|
-
}
|
|
106
|
-
broadcast(resolver) {
|
|
107
|
-
const { broadcast } = this.config;
|
|
108
|
-
if (!!broadcast) throw new Error("Broadcast was already defined for this procedure");
|
|
109
|
-
return new _PluvProcedure(__spreadProps(__spreadValues({}, this.config), {
|
|
110
|
-
broadcast: resolver
|
|
111
|
-
}));
|
|
112
|
-
}
|
|
113
|
-
input(input) {
|
|
114
|
-
return new _PluvProcedure({ input });
|
|
115
|
-
}
|
|
116
|
-
_resolver() {
|
|
117
|
-
return (data, context) => {
|
|
118
|
-
var _a;
|
|
119
|
-
return (_a = this._broadcast) == null ? void 0 : _a.call(this, data, context);
|
|
120
|
-
};
|
|
121
|
-
}
|
|
122
|
-
};
|
|
123
|
-
|
|
124
|
-
// src/PluvRoom.ts
|
|
125
|
-
var import_types = require("@pluv/types");
|
|
126
|
-
|
|
127
|
-
// src/CrdtManager.ts
|
|
128
|
-
var import_crdt = require("@pluv/crdt");
|
|
129
|
-
var CrdtManager = class {
|
|
130
|
-
constructor(options) {
|
|
131
|
-
this.initialized = false;
|
|
132
|
-
const { initialStorage = import_crdt.noop.doc({}) } = options;
|
|
133
|
-
this._docFactory = initialStorage;
|
|
134
|
-
this.doc = this._docFactory.getEmpty();
|
|
135
|
-
}
|
|
136
|
-
/**
|
|
137
|
-
* !HACK
|
|
138
|
-
* @description If this is called before the doc is initialized. Omit the
|
|
139
|
-
* update. This unfortunately means updates can be dropped entirely
|
|
140
|
-
* @date June 13, 2023
|
|
141
|
-
*/
|
|
142
|
-
applyUpdate(params) {
|
|
143
|
-
const { origin, update } = params;
|
|
144
|
-
if (!this.initialized) return this;
|
|
145
|
-
const updates = typeof update === "string" ? [update] : update;
|
|
146
|
-
this._applyDocUpdates(this.doc, updates, origin);
|
|
147
|
-
return this;
|
|
148
|
-
}
|
|
149
|
-
destroy() {
|
|
150
|
-
this.initialized = false;
|
|
151
|
-
this.doc.destroy();
|
|
152
|
-
this.doc = this._docFactory.getEmpty();
|
|
153
|
-
}
|
|
154
|
-
get(key) {
|
|
155
|
-
return this.doc.get(key);
|
|
156
|
-
}
|
|
157
|
-
getInitialState() {
|
|
158
|
-
return this._docFactory.getInitialized().getEncodedState();
|
|
159
|
-
}
|
|
160
|
-
initialize(params) {
|
|
161
|
-
const { origin, update } = params;
|
|
162
|
-
if (this.initialized) {
|
|
163
|
-
console.warn("Attempted to initialize storage multiple times");
|
|
164
|
-
return this;
|
|
165
|
-
}
|
|
166
|
-
if (!this.doc.isEmpty()) {
|
|
167
|
-
console.warn("Attempted to initialize over a non-empty storage");
|
|
168
|
-
return this;
|
|
169
|
-
}
|
|
170
|
-
const updates = typeof update === "string" ? [update] : update;
|
|
171
|
-
if (!updates.length) return this;
|
|
172
|
-
const initialized = this._docFactory.getInitialized();
|
|
173
|
-
const reference = initialized.get();
|
|
174
|
-
this.doc = this._applyDocUpdates(this.doc, updates, origin).rebuildStorage(reference);
|
|
175
|
-
this.initialized = true;
|
|
176
|
-
initialized.destroy();
|
|
177
|
-
return this;
|
|
178
|
-
}
|
|
179
|
-
resolveEncodedState(updates) {
|
|
180
|
-
return this._docFactory.resolveEncodedState(updates);
|
|
181
|
-
}
|
|
182
|
-
_applyDocUpdates(doc, updates, origin) {
|
|
183
|
-
doc.transact(() => {
|
|
184
|
-
doc.batchApplyEncodedState({ origin, updates });
|
|
185
|
-
}, origin);
|
|
186
|
-
return doc;
|
|
187
|
-
}
|
|
188
|
-
};
|
|
189
|
-
|
|
190
|
-
// src/CrdtNotifier.ts
|
|
191
|
-
var import_wonka = require("wonka");
|
|
192
|
-
var CrdtNotifier = class {
|
|
193
|
-
constructor() {
|
|
194
|
-
this.rootSubject = (0, import_wonka.makeSubject)();
|
|
195
|
-
this.subjects = /* @__PURE__ */ new Map();
|
|
196
|
-
}
|
|
197
|
-
subject(key) {
|
|
198
|
-
const subject = this.subjects.get(key);
|
|
199
|
-
if (subject) {
|
|
200
|
-
return subject;
|
|
201
|
-
}
|
|
202
|
-
const newSubject = (0, import_wonka.makeSubject)();
|
|
203
|
-
this.subjects.set(key, newSubject);
|
|
204
|
-
return newSubject;
|
|
205
|
-
}
|
|
206
|
-
subcribeRoot(callback) {
|
|
207
|
-
return (0, import_wonka.subscribe)(callback)(this.rootSubject.source).unsubscribe;
|
|
208
|
-
}
|
|
209
|
-
subscribe(key, callback) {
|
|
210
|
-
const subject = this.subject(key);
|
|
211
|
-
return (0, import_wonka.subscribe)(callback)(subject.source).unsubscribe;
|
|
212
|
-
}
|
|
213
|
-
};
|
|
214
|
-
|
|
215
|
-
// src/EventNotifier.ts
|
|
216
|
-
var import_wonka2 = require("wonka");
|
|
217
|
-
var EventNotifier = class {
|
|
218
|
-
constructor() {
|
|
219
|
-
this.subjects = /* @__PURE__ */ new Map();
|
|
220
|
-
}
|
|
221
|
-
subject(name) {
|
|
222
|
-
const subject = this.subjects.get(name);
|
|
223
|
-
if (subject) return subject;
|
|
224
|
-
const newSubject = (0, import_wonka2.makeSubject)();
|
|
225
|
-
this.subjects.set(name, newSubject);
|
|
226
|
-
return newSubject;
|
|
227
|
-
}
|
|
228
|
-
subscribe(name, callback) {
|
|
229
|
-
const subject = this.subject(name);
|
|
230
|
-
return (0, import_wonka2.subscribe)(callback)(subject.source).unsubscribe;
|
|
231
|
-
}
|
|
232
|
-
};
|
|
233
|
-
|
|
234
|
-
// src/ListenerManager.ts
|
|
235
|
-
var import_wonka3 = require("wonka");
|
|
236
|
-
var ListenerManager = class {
|
|
237
|
-
constructor() {
|
|
238
|
-
this.subjects = {
|
|
239
|
-
close: (0, import_wonka3.makeSubject)(),
|
|
240
|
-
error: (0, import_wonka3.makeSubject)(),
|
|
241
|
-
message: (0, import_wonka3.makeSubject)(),
|
|
242
|
-
open: (0, import_wonka3.makeSubject)()
|
|
243
|
-
};
|
|
244
|
-
}
|
|
245
|
-
subscribe(kind, callback) {
|
|
246
|
-
var _a;
|
|
247
|
-
const source = (_a = this.subjects[kind]) == null ? void 0 : _a.source;
|
|
248
|
-
if (!source) return () => void 0;
|
|
249
|
-
return (0, import_wonka3.subscribe)(callback)(source).unsubscribe;
|
|
250
|
-
}
|
|
251
|
-
};
|
|
252
|
-
|
|
253
|
-
// src/PluvRouter.ts
|
|
254
|
-
var PluvRouter = class _PluvRouter {
|
|
255
|
-
constructor(events) {
|
|
256
|
-
this._defs = { events: {} };
|
|
257
|
-
const invalidName = Object.keys(events).find((name) => !this._isValidEventName(name));
|
|
258
|
-
if (typeof invalidName === "string") {
|
|
259
|
-
throw new Error(
|
|
260
|
-
`Invalid event name. Event names must be formatted as valid JavaScript variable names: "${invalidName}"`
|
|
261
|
-
);
|
|
262
|
-
}
|
|
263
|
-
this._defs = { events };
|
|
264
|
-
}
|
|
265
|
-
static merge(...routers) {
|
|
266
|
-
const events = Object.assign(
|
|
267
|
-
/* @__PURE__ */ Object.create(null),
|
|
268
|
-
...routers.map((router) => router._defs.events)
|
|
269
|
-
);
|
|
270
|
-
if (Object.keys(events).some((key) => key.startsWith("$"))) {
|
|
271
|
-
throw new Error('Procedures may not start with "$"');
|
|
272
|
-
}
|
|
273
|
-
return new _PluvRouter(events);
|
|
274
|
-
}
|
|
275
|
-
_isValidEventName(name) {
|
|
276
|
-
return /^[a-z_$][a-z0-9_$]*$/gi.test(name);
|
|
277
|
-
}
|
|
278
|
-
};
|
|
279
|
-
|
|
280
|
-
// src/StateNotifier.ts
|
|
281
|
-
var import_wonka4 = require("wonka");
|
|
282
|
-
var StateNotifier = class {
|
|
283
|
-
constructor() {
|
|
284
|
-
this.subjects = {
|
|
285
|
-
connection: (0, import_wonka4.makeSubject)(),
|
|
286
|
-
"my-presence": (0, import_wonka4.makeSubject)(),
|
|
287
|
-
myself: (0, import_wonka4.makeSubject)(),
|
|
288
|
-
others: (0, import_wonka4.makeSubject)(),
|
|
289
|
-
"storage-loaded": (0, import_wonka4.makeSubject)()
|
|
290
|
-
};
|
|
291
|
-
}
|
|
292
|
-
subscribe(name, callback) {
|
|
293
|
-
var _a;
|
|
294
|
-
const source = (_a = this.subjects[name]) == null ? void 0 : _a.source;
|
|
295
|
-
if (!source) throw new Error(`Could not subscribe to source: ${name}`);
|
|
296
|
-
return (0, import_wonka4.subscribe)(callback)(source).unsubscribe;
|
|
297
|
-
}
|
|
298
|
-
};
|
|
299
|
-
|
|
300
|
-
// src/StorageStore.ts
|
|
301
|
-
var StorageStore = class extends AbstractStorageStore {
|
|
302
|
-
addUpdate() {
|
|
303
|
-
return Promise.resolve();
|
|
304
|
-
}
|
|
305
|
-
applyUpdates() {
|
|
306
|
-
return Promise.resolve();
|
|
307
|
-
}
|
|
308
|
-
destroy() {
|
|
309
|
-
return Promise.resolve();
|
|
310
|
-
}
|
|
311
|
-
flatten() {
|
|
312
|
-
return Promise.resolve();
|
|
313
|
-
}
|
|
314
|
-
getShouldFlatten() {
|
|
315
|
-
return Promise.resolve(false);
|
|
316
|
-
}
|
|
317
|
-
getUpdates() {
|
|
318
|
-
return Promise.resolve([]);
|
|
319
|
-
}
|
|
320
|
-
initialize() {
|
|
321
|
-
return Promise.resolve();
|
|
322
|
-
}
|
|
323
|
-
};
|
|
324
|
-
|
|
325
|
-
// src/utils/debounce.ts
|
|
326
|
-
var debounce = (fn, options) => {
|
|
327
|
-
const { wait, immediate = false } = options != null ? options : {};
|
|
328
|
-
let prevResult;
|
|
329
|
-
let timeout = null;
|
|
330
|
-
return (...args) => {
|
|
331
|
-
if (typeof timeout === "number") clearTimeout(timeout);
|
|
332
|
-
timeout = setTimeout(() => {
|
|
333
|
-
timeout = null;
|
|
334
|
-
if (!immediate) prevResult = fn(args);
|
|
335
|
-
}, wait);
|
|
336
|
-
if (immediate && !timeout) {
|
|
337
|
-
fn(args);
|
|
338
|
-
return;
|
|
339
|
-
}
|
|
340
|
-
return prevResult;
|
|
341
|
-
};
|
|
342
|
-
};
|
|
343
|
-
|
|
344
|
-
// src/utils/pickBy.ts
|
|
345
|
-
var pickBy = (obj, by) => {
|
|
346
|
-
const result = {};
|
|
347
|
-
const keys = Object.keys(obj);
|
|
348
|
-
for (let i = 0; i < keys.length; ++i) {
|
|
349
|
-
const key = keys[i];
|
|
350
|
-
const value = obj[key];
|
|
351
|
-
if (by(value)) result[key] = value;
|
|
352
|
-
}
|
|
353
|
-
return result;
|
|
354
|
-
};
|
|
355
|
-
|
|
356
|
-
// src/UsersManager.ts
|
|
357
|
-
var UsersManager = class {
|
|
358
|
-
constructor(config) {
|
|
359
|
-
this._idMap = {
|
|
360
|
-
fromConnectionId: /* @__PURE__ */ new Map(),
|
|
361
|
-
fromClientId: /* @__PURE__ */ new Map()
|
|
362
|
-
};
|
|
363
|
-
/**
|
|
364
|
-
* @description This is only set when the user is connected.
|
|
365
|
-
*/
|
|
366
|
-
this._myself = null;
|
|
367
|
-
this._others = /* @__PURE__ */ new Map();
|
|
368
|
-
this._presence = null;
|
|
369
|
-
const { initialPresence, limits, presence = null } = config;
|
|
370
|
-
this.initialPresence = initialPresence;
|
|
371
|
-
this._limits = limits;
|
|
372
|
-
this._myPresence = initialPresence;
|
|
373
|
-
this._presence = presence;
|
|
374
|
-
}
|
|
375
|
-
get myPresence() {
|
|
376
|
-
return this._myPresence;
|
|
377
|
-
}
|
|
378
|
-
get myself() {
|
|
379
|
-
return this._myself;
|
|
380
|
-
}
|
|
381
|
-
addConnection(userInfo) {
|
|
382
|
-
var _a;
|
|
383
|
-
const cleaned = pickBy((_a = userInfo.presence) != null ? _a : {}, (value) => typeof value !== "undefined");
|
|
384
|
-
const presence = __spreadValues(__spreadValues({}, this.initialPresence), cleaned);
|
|
385
|
-
const data = __spreadProps(__spreadValues({}, userInfo), { presence });
|
|
386
|
-
const myClientId = !!this._myself ? this.getClientId(this._myself) : null;
|
|
387
|
-
const clientId = this.getClientId(data);
|
|
388
|
-
const remaining = this._setConnectionId(userInfo.connectionId, clientId).size;
|
|
389
|
-
if (myClientId === clientId) {
|
|
390
|
-
const result2 = {
|
|
391
|
-
clientId,
|
|
392
|
-
data,
|
|
393
|
-
isMyself: true,
|
|
394
|
-
remaining
|
|
395
|
-
};
|
|
396
|
-
return result2;
|
|
397
|
-
}
|
|
398
|
-
const other = this._others.get(clientId);
|
|
399
|
-
if (!other) this._others.set(clientId, data);
|
|
400
|
-
const result = {
|
|
401
|
-
clientId,
|
|
402
|
-
data,
|
|
403
|
-
isMyself: false,
|
|
404
|
-
remaining
|
|
405
|
-
};
|
|
406
|
-
return result;
|
|
407
|
-
}
|
|
408
|
-
clearConnections() {
|
|
409
|
-
this.removeMyself();
|
|
410
|
-
this._others.clear();
|
|
411
|
-
this._idMap.fromClientId.clear();
|
|
412
|
-
this._idMap.fromConnectionId.clear();
|
|
413
|
-
}
|
|
414
|
-
deleteConnection(connectionId) {
|
|
415
|
-
var _a, _b, _c;
|
|
416
|
-
const clientId = this.getClientId(connectionId);
|
|
417
|
-
this._deleteConnectionId(connectionId);
|
|
418
|
-
if (!clientId) return null;
|
|
419
|
-
const userInfo = (_a = this._others.get(clientId)) != null ? _a : null;
|
|
420
|
-
if (!userInfo) return null;
|
|
421
|
-
const remaining = (_c = (_b = this._getConnectionIds(clientId)) == null ? void 0 : _b.size) != null ? _c : 0;
|
|
422
|
-
const result = {
|
|
423
|
-
clientId,
|
|
424
|
-
remaining,
|
|
425
|
-
data: userInfo
|
|
426
|
-
};
|
|
427
|
-
if (!remaining) this._others.delete(clientId);
|
|
428
|
-
return result;
|
|
429
|
-
}
|
|
430
|
-
getClientId(input) {
|
|
431
|
-
var _a, _b, _c, _d;
|
|
432
|
-
if (typeof input !== "string") {
|
|
433
|
-
const userInfo = input;
|
|
434
|
-
const connectionId2 = userInfo.connectionId;
|
|
435
|
-
const userId = (_b = (_a = userInfo.user) == null ? void 0 : _a.id) != null ? _b : null;
|
|
436
|
-
return userId != null ? userId : connectionId2;
|
|
437
|
-
}
|
|
438
|
-
const connectionId = input;
|
|
439
|
-
if (((_c = this._myself) == null ? void 0 : _c.connectionId) === connectionId) {
|
|
440
|
-
return this.getClientId(this._myself);
|
|
441
|
-
}
|
|
442
|
-
const clientId = (_d = this._idMap.fromConnectionId.get(connectionId)) != null ? _d : null;
|
|
443
|
-
return clientId;
|
|
444
|
-
}
|
|
445
|
-
getOther(connectionId) {
|
|
446
|
-
var _a;
|
|
447
|
-
const clientId = this.getClientId(connectionId);
|
|
448
|
-
if (!clientId) return null;
|
|
449
|
-
return (_a = this._others.get(clientId)) != null ? _a : null;
|
|
450
|
-
}
|
|
451
|
-
getOthers() {
|
|
452
|
-
return Array.from(this._others.values());
|
|
453
|
-
}
|
|
454
|
-
patchPresence(connectionId, patch) {
|
|
455
|
-
var _a;
|
|
456
|
-
const clientId = this.getClientId(connectionId);
|
|
457
|
-
const myClientId = !!this._myself ? this.getClientId(this._myself) : null;
|
|
458
|
-
if (!clientId) return null;
|
|
459
|
-
if (myClientId === clientId) return this.updateMyPresence(patch);
|
|
460
|
-
const other = this._others.get(clientId);
|
|
461
|
-
if (!other) return null;
|
|
462
|
-
const cleanedPatch = pickBy(patch != null ? patch : {}, (value) => typeof value !== "undefined");
|
|
463
|
-
const cleanedPresence = pickBy(
|
|
464
|
-
(_a = other.presence) != null ? _a : {},
|
|
465
|
-
(value) => typeof value !== "undefined"
|
|
466
|
-
);
|
|
467
|
-
const presence = __spreadValues(__spreadValues(__spreadValues({}, this.initialPresence), cleanedPresence), cleanedPatch);
|
|
468
|
-
const validated = this._presence ? this._presence.parse(presence) : presence;
|
|
469
|
-
if (!!presence[PLUV_PRESENCE_META_KEY]) {
|
|
470
|
-
validated[PLUV_PRESENCE_META_KEY] = presence[PLUV_PRESENCE_META_KEY];
|
|
471
|
-
}
|
|
472
|
-
this._others.set(clientId, __spreadProps(__spreadValues({}, other), { presence: validated }));
|
|
473
|
-
return validated;
|
|
474
|
-
}
|
|
475
|
-
removeMyself() {
|
|
476
|
-
if (!this._myself) return;
|
|
477
|
-
const connectionId = this._myself.connectionId;
|
|
478
|
-
this._deleteConnectionId(connectionId);
|
|
479
|
-
this._myself = null;
|
|
480
|
-
}
|
|
481
|
-
setMyself(userInfo) {
|
|
482
|
-
var _a;
|
|
483
|
-
const connectionId = userInfo.connectionId;
|
|
484
|
-
const presence = (_a = userInfo.presence) != null ? _a : this.initialPresence;
|
|
485
|
-
this._myself = __spreadProps(__spreadValues({}, userInfo), { presence });
|
|
486
|
-
this._myPresence = presence;
|
|
487
|
-
const clientId = this.getClientId(this._myself);
|
|
488
|
-
this._setConnectionId(connectionId, clientId);
|
|
489
|
-
}
|
|
490
|
-
setPresence(connectionId, presence) {
|
|
491
|
-
const clientId = this.getClientId(connectionId);
|
|
492
|
-
const myClientId = !!this._myself ? this.getClientId(this._myself) : null;
|
|
493
|
-
if (!clientId) return;
|
|
494
|
-
if (myClientId === clientId) {
|
|
495
|
-
if (!this._myself) return;
|
|
496
|
-
this._myself.presence = presence;
|
|
497
|
-
this._myPresence = presence;
|
|
498
|
-
return;
|
|
499
|
-
}
|
|
500
|
-
const other = this._others.get(clientId);
|
|
501
|
-
if (!other) return;
|
|
502
|
-
this._others.set(clientId, __spreadProps(__spreadValues({}, other), { presence }));
|
|
503
|
-
}
|
|
504
|
-
/**
|
|
505
|
-
* @description This method need not care about being connected. This is because a user should
|
|
506
|
-
* be able to view and update their own presence without being online
|
|
507
|
-
* @date April 20, 2025
|
|
508
|
-
*/
|
|
509
|
-
updateMyPresence(patch) {
|
|
510
|
-
const cleanedPatch = pickBy(patch, (value) => typeof value !== "undefined");
|
|
511
|
-
const cleanedPresence = pickBy(this._myPresence, (value) => typeof value !== "undefined");
|
|
512
|
-
const updated = __spreadValues(__spreadValues(__spreadValues({}, this.initialPresence), cleanedPresence), cleanedPatch);
|
|
513
|
-
const bytes = new TextEncoder().encode(JSON.stringify(updated)).length;
|
|
514
|
-
if (!!this._limits.presenceMaxSize && bytes > this._limits.presenceMaxSize) {
|
|
515
|
-
throw new Error(
|
|
516
|
-
`Large presence. Presence must be at most 512 bytes. Current size: ${bytes.toLocaleString()}`
|
|
517
|
-
);
|
|
518
|
-
}
|
|
519
|
-
this._myPresence = updated;
|
|
520
|
-
if (!this._myself) return updated;
|
|
521
|
-
this._myself.presence = updated;
|
|
522
|
-
return updated;
|
|
523
|
-
}
|
|
524
|
-
_deleteConnectionId(connectionId) {
|
|
525
|
-
var _a;
|
|
526
|
-
const clientId = this.getClientId(connectionId);
|
|
527
|
-
this._idMap.fromConnectionId.delete(connectionId);
|
|
528
|
-
if (!clientId) return;
|
|
529
|
-
const set = (_a = this._idMap.fromClientId.get(clientId)) != null ? _a : null;
|
|
530
|
-
if (!set) return;
|
|
531
|
-
set.delete(connectionId);
|
|
532
|
-
if (!!set.size) return;
|
|
533
|
-
this._idMap.fromClientId.delete(clientId);
|
|
534
|
-
}
|
|
535
|
-
_getConnectionIds(clientId) {
|
|
536
|
-
const set = this._idMap.fromClientId.get(clientId);
|
|
537
|
-
return set != null ? set : null;
|
|
538
|
-
}
|
|
539
|
-
_setConnectionId(connectionId, clientId) {
|
|
540
|
-
var _a;
|
|
541
|
-
const set = (_a = this._idMap.fromClientId.get(clientId)) != null ? _a : /* @__PURE__ */ new Set();
|
|
542
|
-
const updated = set.add(connectionId);
|
|
543
|
-
this._idMap.fromConnectionId.set(connectionId, clientId);
|
|
544
|
-
this._idMap.fromClientId.set(clientId, updated);
|
|
545
|
-
return updated;
|
|
546
|
-
}
|
|
547
|
-
};
|
|
548
|
-
|
|
549
|
-
// src/UsersNotifier.ts
|
|
550
|
-
var import_wonka5 = require("wonka");
|
|
551
|
-
var UsersNotifier = class {
|
|
552
|
-
constructor() {
|
|
553
|
-
this.others = (0, import_wonka5.makeSubject)();
|
|
554
|
-
this._otherSubjects = /* @__PURE__ */ new Map();
|
|
555
|
-
}
|
|
556
|
-
clear() {
|
|
557
|
-
this._otherSubjects.forEach((subject) => {
|
|
558
|
-
subject.next(null);
|
|
559
|
-
});
|
|
560
|
-
this._otherSubjects.clear();
|
|
561
|
-
}
|
|
562
|
-
delete(clientId) {
|
|
563
|
-
this.other(clientId).next(null);
|
|
564
|
-
this._otherSubjects.delete(clientId);
|
|
565
|
-
}
|
|
566
|
-
other(clientId) {
|
|
567
|
-
const subject = this._otherSubjects.get(clientId);
|
|
568
|
-
if (subject) return subject;
|
|
569
|
-
const newSubject = (0, import_wonka5.makeSubject)();
|
|
570
|
-
this._otherSubjects.set(clientId, newSubject);
|
|
571
|
-
return newSubject;
|
|
572
|
-
}
|
|
573
|
-
subscribeOther(clientId, callback) {
|
|
574
|
-
const source = this.other(clientId).source;
|
|
575
|
-
const subscription = (0, import_wonka5.subscribe)(callback)(source);
|
|
576
|
-
return subscription.unsubscribe;
|
|
577
|
-
}
|
|
578
|
-
subscribeOthers(callback) {
|
|
579
|
-
const subscription = (0, import_wonka5.subscribe)(
|
|
580
|
-
({ others, event }) => callback(others, event)
|
|
581
|
-
)(this.others.source);
|
|
582
|
-
return subscription.unsubscribe;
|
|
583
|
-
}
|
|
584
|
-
};
|
|
585
|
-
|
|
586
|
-
// src/PluvRoom.ts
|
|
587
|
-
var ADD_TO_STORAGE_STATE_DEBOUNCE_MS = 1e3;
|
|
588
|
-
var HEARTBEAT_INTERVAL_MS = 1e4;
|
|
589
|
-
var PONG_TIMEOUT_MS = 2e3;
|
|
590
|
-
var RECONNECT_TIMEOUT_MS = 5e3;
|
|
591
|
-
var MIN_RECONNECT_TIMEOUT_MS = 1e3;
|
|
592
|
-
var MAX_RECONNECT_TIMEOUT_MS = 6e4;
|
|
593
|
-
var ORIGIN_INITIALIZED = "$initialized";
|
|
594
|
-
var ORIGIN_STORAGE_UPDATED = "$storageUpdated";
|
|
595
|
-
var DEFAULT_PLUV_CLIENT_ADDON = (input) => ({
|
|
596
|
-
storage: new StorageStore(input.room.id)
|
|
597
|
-
});
|
|
598
|
-
var __storage;
|
|
599
|
-
var PluvRoom = class {
|
|
600
|
-
constructor(room, options) {
|
|
601
|
-
this._crdtNotifier = new CrdtNotifier();
|
|
602
|
-
this._eventNotifier = new EventNotifier();
|
|
603
|
-
this._intervals = {
|
|
604
|
-
heartbeat: null
|
|
605
|
-
};
|
|
606
|
-
this._listenerManager = new ListenerManager();
|
|
607
|
-
this._publicKey = null;
|
|
608
|
-
this._stateNotifier = new StateNotifier();
|
|
609
|
-
this._subscriptions = {
|
|
610
|
-
observeCrdt: null
|
|
611
|
-
};
|
|
612
|
-
this._timeouts = {
|
|
613
|
-
pong: null,
|
|
614
|
-
reconnect: null
|
|
615
|
-
};
|
|
616
|
-
this._usersNotifier = new UsersNotifier();
|
|
617
|
-
this._lastMetadata = null;
|
|
618
|
-
this._state = {
|
|
619
|
-
authorization: {
|
|
620
|
-
token: null,
|
|
621
|
-
user: null
|
|
622
|
-
},
|
|
623
|
-
connection: {
|
|
624
|
-
attempts: 0,
|
|
625
|
-
id: null,
|
|
626
|
-
state: import_types.ConnectionState.Untouched
|
|
627
|
-
},
|
|
628
|
-
storage: {
|
|
629
|
-
state: import_types.StorageState.Unavailable
|
|
630
|
-
},
|
|
631
|
-
webSocket: null
|
|
632
|
-
};
|
|
633
|
-
this._windowListeners = null;
|
|
634
|
-
this._wsListeners = null;
|
|
635
|
-
this.broadcast = new Proxy(
|
|
636
|
-
(event, data) => __async(this, null, function* () {
|
|
637
|
-
if (!this._state.webSocket) return;
|
|
638
|
-
if (this._state.connection.state !== import_types.ConnectionState.Open) return;
|
|
639
|
-
const type = event.toString();
|
|
640
|
-
const procedure = this._router._defs.events[type];
|
|
641
|
-
if (!(procedure == null ? void 0 : procedure.config.broadcast)) {
|
|
642
|
-
this._sendMessage({ data, type });
|
|
643
|
-
return;
|
|
644
|
-
}
|
|
645
|
-
const myself = this._usersManager.myself;
|
|
646
|
-
if (!myself) return;
|
|
647
|
-
const parsed = procedure.config.input ? procedure.config.input.parse(data) : data;
|
|
648
|
-
const context = {
|
|
649
|
-
doc: this._crdtManager.doc,
|
|
650
|
-
others: this._usersManager.getOthers(),
|
|
651
|
-
room: this.id,
|
|
652
|
-
user: myself
|
|
653
|
-
};
|
|
654
|
-
const output = yield procedure.config.broadcast(parsed, context);
|
|
655
|
-
Object.entries(output).forEach(([_type, _data]) => {
|
|
656
|
-
this._sendMessage({ data: _data, type: _type });
|
|
657
|
-
});
|
|
658
|
-
}),
|
|
659
|
-
{
|
|
660
|
-
get(fn, prop) {
|
|
661
|
-
return (data) => __async(null, null, function* () {
|
|
662
|
-
return yield fn(prop, data);
|
|
663
|
-
});
|
|
664
|
-
}
|
|
665
|
-
}
|
|
666
|
-
);
|
|
667
|
-
this.canRedo = () => {
|
|
668
|
-
return !!this._crdtManager.doc.canRedo();
|
|
669
|
-
};
|
|
670
|
-
this.canUndo = () => {
|
|
671
|
-
return !!this._crdtManager.doc.canUndo();
|
|
672
|
-
};
|
|
673
|
-
this.getConnection = () => {
|
|
674
|
-
return Object.freeze(JSON.parse(JSON.stringify(this._state.connection)));
|
|
675
|
-
};
|
|
676
|
-
this.getDoc = () => {
|
|
677
|
-
return this._crdtManager.doc;
|
|
678
|
-
};
|
|
679
|
-
this.getMyPresence = () => {
|
|
680
|
-
return this._usersManager.myPresence;
|
|
681
|
-
};
|
|
682
|
-
this.getMyself = () => {
|
|
683
|
-
return this._usersManager.myself;
|
|
684
|
-
};
|
|
685
|
-
this.getOther = (connectionId) => {
|
|
686
|
-
return this._usersManager.getOther(connectionId);
|
|
687
|
-
};
|
|
688
|
-
this.getOthers = () => {
|
|
689
|
-
return this._usersManager.getOthers();
|
|
690
|
-
};
|
|
691
|
-
this.getStorage = (type) => {
|
|
692
|
-
const sharedType = this._crdtManager.get(type);
|
|
693
|
-
if (typeof sharedType === "undefined") return null;
|
|
694
|
-
return sharedType;
|
|
695
|
-
};
|
|
696
|
-
this.getStorageLoaded = () => {
|
|
697
|
-
return this._state.storage.state === import_types.StorageState.Offline || this._state.storage.state === import_types.StorageState.Synced;
|
|
698
|
-
};
|
|
699
|
-
this.redo = () => {
|
|
700
|
-
this._crdtManager.doc.redo();
|
|
701
|
-
};
|
|
702
|
-
this.subscribe = new Proxy(
|
|
703
|
-
(name, callback) => this._stateNotifier.subscribe(name, callback),
|
|
704
|
-
{
|
|
705
|
-
get: (fn, prop) => {
|
|
706
|
-
if (prop === "connection") {
|
|
707
|
-
return (callback) => {
|
|
708
|
-
return fn("connection", callback);
|
|
709
|
-
};
|
|
710
|
-
}
|
|
711
|
-
if (prop === "myPresence") {
|
|
712
|
-
return (callback) => {
|
|
713
|
-
return fn("my-presence", callback);
|
|
714
|
-
};
|
|
715
|
-
}
|
|
716
|
-
if (prop === "myself") {
|
|
717
|
-
return (callback) => {
|
|
718
|
-
return fn("myself", callback);
|
|
719
|
-
};
|
|
720
|
-
}
|
|
721
|
-
if (prop === "others") {
|
|
722
|
-
return (callback) => {
|
|
723
|
-
return this._usersNotifier.subscribeOthers(callback);
|
|
724
|
-
};
|
|
725
|
-
}
|
|
726
|
-
if (prop === "storageLoaded") {
|
|
727
|
-
return (callback) => {
|
|
728
|
-
return fn("storage-loaded", callback);
|
|
729
|
-
};
|
|
730
|
-
}
|
|
731
|
-
if (prop === "event") return this._event;
|
|
732
|
-
if (prop === "other") return this._other;
|
|
733
|
-
if (prop === "storage") return __privateGet(this, __storage);
|
|
734
|
-
}
|
|
735
|
-
}
|
|
736
|
-
);
|
|
737
|
-
this.transact = (fn, origin) => {
|
|
738
|
-
const _origin = origin != null ? origin : this._state.connection.id;
|
|
739
|
-
if (typeof _origin !== "string") return;
|
|
740
|
-
this._crdtManager.doc.transact(() => {
|
|
741
|
-
const storage = this._crdtManager.doc.get();
|
|
742
|
-
fn(storage);
|
|
743
|
-
}, _origin);
|
|
744
|
-
};
|
|
745
|
-
this.undo = () => {
|
|
746
|
-
this._crdtManager.doc.undo();
|
|
747
|
-
};
|
|
748
|
-
this.updateMyPresence = (presence) => {
|
|
749
|
-
var _a;
|
|
750
|
-
const newPresence = typeof presence === "function" ? presence(this.getMyPresence()) : presence;
|
|
751
|
-
this._usersManager.updateMyPresence(newPresence);
|
|
752
|
-
const myPresence = this._usersManager.myPresence;
|
|
753
|
-
const myself = (_a = this._usersManager.myself) != null ? _a : null;
|
|
754
|
-
this._stateNotifier.subjects["my-presence"].next(myPresence);
|
|
755
|
-
if (!!myself) this._stateNotifier.subjects.myself.next(myself);
|
|
756
|
-
this.broadcast(
|
|
757
|
-
"$updatePresence",
|
|
758
|
-
{ presence: newPresence }
|
|
759
|
-
);
|
|
760
|
-
};
|
|
761
|
-
this._event = new Proxy(
|
|
762
|
-
(event, callback) => this._eventNotifier.subscribe(event, callback),
|
|
763
|
-
{
|
|
764
|
-
get(fn, prop) {
|
|
765
|
-
return (callback) => fn(prop, callback);
|
|
766
|
-
}
|
|
767
|
-
}
|
|
768
|
-
);
|
|
769
|
-
this._flattenStorageStore = debounce(
|
|
770
|
-
() => __async(this, null, function* () {
|
|
771
|
-
const shouldFlatten = yield this._storageStore.getShouldFlatten();
|
|
772
|
-
if (!shouldFlatten) return;
|
|
773
|
-
const updates = yield this._storageStore.getUpdates();
|
|
774
|
-
this._crdtManager.applyUpdate({ update: updates, origin: this });
|
|
775
|
-
const encodedState = this._crdtManager.doc.getEncodedState();
|
|
776
|
-
yield this._storageStore.flatten(encodedState);
|
|
777
|
-
}),
|
|
778
|
-
{ wait: ADD_TO_STORAGE_STATE_DEBOUNCE_MS }
|
|
779
|
-
);
|
|
780
|
-
this._getAddon = (addons) => {
|
|
781
|
-
return addons.reduce(
|
|
782
|
-
(acc, addon) => () => __spreadValues(__spreadValues({}, acc({ room: this })), addon({ room: this })),
|
|
783
|
-
DEFAULT_PLUV_CLIENT_ADDON
|
|
784
|
-
);
|
|
785
|
-
};
|
|
786
|
-
this._other = (connectionId, callback) => {
|
|
787
|
-
const clientId = this._usersManager.getClientId(connectionId);
|
|
788
|
-
if (!clientId) return () => void 0;
|
|
789
|
-
return this._usersNotifier.subscribeOther(clientId, callback);
|
|
790
|
-
};
|
|
791
|
-
__privateAdd(this, __storage, new Proxy(
|
|
792
|
-
(key, callback) => this._crdtNotifier.subscribe(key, callback),
|
|
793
|
-
{
|
|
794
|
-
get: (fn, prop) => {
|
|
795
|
-
if (!!prop) {
|
|
796
|
-
return (callback) => {
|
|
797
|
-
return this._crdtNotifier.subcribeRoot(callback);
|
|
798
|
-
};
|
|
799
|
-
}
|
|
800
|
-
return (callback) => {
|
|
801
|
-
return fn(prop, callback);
|
|
802
|
-
};
|
|
803
|
-
}
|
|
804
|
-
}
|
|
805
|
-
));
|
|
806
|
-
const {
|
|
807
|
-
addons = [],
|
|
808
|
-
authEndpoint,
|
|
809
|
-
debug = false,
|
|
810
|
-
initialPresence,
|
|
811
|
-
initialStorage,
|
|
812
|
-
limits,
|
|
813
|
-
metadata,
|
|
814
|
-
onAuthorizationFail,
|
|
815
|
-
presence,
|
|
816
|
-
publicKey,
|
|
817
|
-
reconnectTimeoutMs = RECONNECT_TIMEOUT_MS,
|
|
818
|
-
router,
|
|
819
|
-
wsEndpoint
|
|
820
|
-
} = options;
|
|
821
|
-
this.id = room;
|
|
822
|
-
this.metadata = metadata;
|
|
823
|
-
const addon = this._getAddon(addons);
|
|
824
|
-
const { storage } = __spreadValues(__spreadValues({}, DEFAULT_PLUV_CLIENT_ADDON({ room: this })), addon({ room: this }));
|
|
825
|
-
this._storageStore = storage;
|
|
826
|
-
this._debug = debug;
|
|
827
|
-
this._endpoints = { authEndpoint, wsEndpoint };
|
|
828
|
-
this._limits = limits;
|
|
829
|
-
this._reconnectTimeoutMs = reconnectTimeoutMs;
|
|
830
|
-
if (!!publicKey) this._publicKey = publicKey;
|
|
831
|
-
this._listeners = {
|
|
832
|
-
onAuthorizationFail: (error) => {
|
|
833
|
-
onAuthorizationFail == null ? void 0 : onAuthorizationFail(error);
|
|
834
|
-
}
|
|
835
|
-
};
|
|
836
|
-
this._router = router != null ? router : new PluvRouter({});
|
|
837
|
-
this._usersManager = new UsersManager({
|
|
838
|
-
initialPresence,
|
|
839
|
-
limits: this._limits,
|
|
840
|
-
presence
|
|
841
|
-
});
|
|
842
|
-
this._crdtManager = new CrdtManager({ initialStorage });
|
|
843
|
-
}
|
|
844
|
-
get webSocket() {
|
|
845
|
-
return this._state.webSocket;
|
|
846
|
-
}
|
|
847
|
-
addEventListener(kind, handler) {
|
|
848
|
-
return this._listenerManager.subscribe(kind, handler);
|
|
849
|
-
}
|
|
850
|
-
connect(...args) {
|
|
851
|
-
return __async(this, null, function* () {
|
|
852
|
-
var _a;
|
|
853
|
-
const params = (_a = args[0]) != null ? _a : {};
|
|
854
|
-
const metadata = params.metadata;
|
|
855
|
-
this._setMetadata(metadata);
|
|
856
|
-
const canConnect = [
|
|
857
|
-
import_types.ConnectionState.Closed,
|
|
858
|
-
import_types.ConnectionState.Unavailable,
|
|
859
|
-
import_types.ConnectionState.Untouched
|
|
860
|
-
].some((state) => this._state.connection.state === state);
|
|
861
|
-
if (!canConnect) return;
|
|
862
|
-
this._clearTimeout(this._timeouts.reconnect);
|
|
863
|
-
this._updateState((oldState) => {
|
|
864
|
-
oldState.connection.state = import_types.ConnectionState.Connecting;
|
|
865
|
-
return oldState;
|
|
866
|
-
});
|
|
867
|
-
yield this._storageStore.initialize();
|
|
868
|
-
const wsEndpoint = this._getWsEndpoint(this.id, params);
|
|
869
|
-
const url = new URL(wsEndpoint);
|
|
870
|
-
let authToken = null;
|
|
871
|
-
let webSocket;
|
|
872
|
-
try {
|
|
873
|
-
const publicKey = this._getPublicKey(params);
|
|
874
|
-
authToken = yield this._getAuthorization(this.id, params);
|
|
875
|
-
if (authToken) url.searchParams.set("token", encodeURIComponent(authToken));
|
|
876
|
-
if (publicKey) url.searchParams.set("public_key", encodeURIComponent(publicKey));
|
|
877
|
-
webSocket = new WebSocket(url.toString());
|
|
878
|
-
} catch (err) {
|
|
879
|
-
this._updateState((oldState) => {
|
|
880
|
-
oldState.connection.attempts += 1;
|
|
881
|
-
return oldState;
|
|
882
|
-
});
|
|
883
|
-
yield this._storageStore.destroy();
|
|
884
|
-
this._pollReconnect();
|
|
885
|
-
yield Promise.resolve(this._onAuthorizationFail(err));
|
|
886
|
-
return;
|
|
887
|
-
}
|
|
888
|
-
this._updateState((oldState) => {
|
|
889
|
-
oldState.connection.attempts = 0;
|
|
890
|
-
oldState.authorization.token = authToken;
|
|
891
|
-
oldState.storage.state = import_types.StorageState.Loading;
|
|
892
|
-
oldState.webSocket = webSocket;
|
|
893
|
-
return oldState;
|
|
894
|
-
});
|
|
895
|
-
this._attachWindowListeners();
|
|
896
|
-
this._attachWsListeners();
|
|
897
|
-
});
|
|
898
|
-
}
|
|
899
|
-
disconnect() {
|
|
900
|
-
return __async(this, null, function* () {
|
|
901
|
-
if (!this._state.webSocket) return;
|
|
902
|
-
const canDisconnect = [import_types.ConnectionState.Connecting, import_types.ConnectionState.Open].some(
|
|
903
|
-
(state) => this._state.connection.state === state
|
|
904
|
-
);
|
|
905
|
-
if (!canDisconnect) return;
|
|
906
|
-
this._closeWs();
|
|
907
|
-
this._clearInterval(this._intervals.heartbeat);
|
|
908
|
-
this._clearTimeout(this._timeouts.reconnect);
|
|
909
|
-
this._updateState((oldState) => {
|
|
910
|
-
oldState.authorization.token = null;
|
|
911
|
-
oldState.connection.state = import_types.ConnectionState.Closed;
|
|
912
|
-
oldState.storage.state = oldState.storage.state === import_types.StorageState.Synced ? import_types.StorageState.Offline : import_types.StorageState.Unavailable;
|
|
913
|
-
oldState.webSocket = null;
|
|
914
|
-
return oldState;
|
|
915
|
-
});
|
|
916
|
-
yield this._storageStore.destroy();
|
|
917
|
-
this._crdtManager.destroy();
|
|
918
|
-
this._stateNotifier.subjects["my-presence"].next(null);
|
|
919
|
-
});
|
|
920
|
-
}
|
|
921
|
-
getStorageJson(type) {
|
|
922
|
-
if (this._state.connection.id === null) return null;
|
|
923
|
-
if (typeof type === "undefined") return this._crdtManager.doc.toJson();
|
|
924
|
-
const sharedType = this._crdtManager.get(type);
|
|
925
|
-
if (typeof sharedType === "undefined") return null;
|
|
926
|
-
return this._crdtManager.doc.toJson(type);
|
|
927
|
-
}
|
|
928
|
-
_addToStorageStore(update) {
|
|
929
|
-
return __async(this, null, function* () {
|
|
930
|
-
yield this._storageStore.addUpdate(update);
|
|
931
|
-
this._flattenStorageStore();
|
|
932
|
-
});
|
|
933
|
-
}
|
|
934
|
-
_attachWindowListeners() {
|
|
935
|
-
if (typeof window === "undefined") return;
|
|
936
|
-
if (typeof document === "undefined") return;
|
|
937
|
-
this._windowListeners = this._windowListeners || {
|
|
938
|
-
onNavigatorOffline: this._onNavigatorOffline.bind(this),
|
|
939
|
-
onNavigatorOnline: this._onNavigatorOnline.bind(this),
|
|
940
|
-
onVisibilityChange: this._onVisibilityChange.bind(this),
|
|
941
|
-
onWindowFocus: this._onWindowFocus.bind(this)
|
|
942
|
-
};
|
|
943
|
-
window.addEventListener("offline", this._windowListeners.onNavigatorOffline);
|
|
944
|
-
window.addEventListener("online", this._windowListeners.onNavigatorOnline);
|
|
945
|
-
document.addEventListener("visibilitychange", this._windowListeners.onVisibilityChange);
|
|
946
|
-
window.addEventListener("focus", this._windowListeners.onWindowFocus);
|
|
947
|
-
}
|
|
948
|
-
_attachWsListeners() {
|
|
949
|
-
if (!this._state.webSocket) return;
|
|
950
|
-
this._wsListeners = this._wsListeners || {
|
|
951
|
-
onClose: this._onClose.bind(this),
|
|
952
|
-
onError: this._onError.bind(this),
|
|
953
|
-
onMessage: this._onMessage.bind(this),
|
|
954
|
-
onOpen: this._onOpen.bind(this)
|
|
955
|
-
};
|
|
956
|
-
this._state.webSocket.addEventListener("close", this._wsListeners.onClose);
|
|
957
|
-
this._state.webSocket.addEventListener("error", this._wsListeners.onError);
|
|
958
|
-
this._state.webSocket.addEventListener("message", this._wsListeners.onMessage);
|
|
959
|
-
this._state.webSocket.addEventListener("open", this._wsListeners.onOpen);
|
|
960
|
-
}
|
|
961
|
-
_clearInterval(interval) {
|
|
962
|
-
if (Number.isNaN(interval)) return;
|
|
963
|
-
clearInterval(interval);
|
|
964
|
-
}
|
|
965
|
-
_clearTimeout(timeout) {
|
|
966
|
-
if (Number.isNaN(timeout)) return;
|
|
967
|
-
clearTimeout(timeout);
|
|
968
|
-
}
|
|
969
|
-
_closeWs() {
|
|
970
|
-
var _a, _b, _c;
|
|
971
|
-
(_b = (_a = this._subscriptions).observeCrdt) == null ? void 0 : _b.call(_a);
|
|
972
|
-
this._clearInterval(this._intervals.heartbeat);
|
|
973
|
-
this._clearTimeout(this._timeouts.pong);
|
|
974
|
-
this._detachWindowListeners();
|
|
975
|
-
this._usersManager.removeMyself();
|
|
976
|
-
this._usersManager.clearConnections();
|
|
977
|
-
this._usersNotifier.clear();
|
|
978
|
-
this._stateNotifier.subjects.myself.next(null);
|
|
979
|
-
this._stateNotifier.subjects.others.next([]);
|
|
980
|
-
this._usersNotifier.others.next({ others: [], event: { kind: "clear" } });
|
|
981
|
-
(_c = this._state.webSocket) == null ? void 0 : _c.close();
|
|
982
|
-
this._detachWsListeners();
|
|
983
|
-
}
|
|
984
|
-
_detachWindowListeners() {
|
|
985
|
-
if (!this._windowListeners) return;
|
|
986
|
-
if (typeof window === "undefined") return;
|
|
987
|
-
if (typeof document === "undefined") return;
|
|
988
|
-
window.removeEventListener("offline", this._windowListeners.onNavigatorOffline);
|
|
989
|
-
window.removeEventListener("online", this._windowListeners.onNavigatorOnline);
|
|
990
|
-
document.removeEventListener("visibilitychange", this._windowListeners.onVisibilityChange);
|
|
991
|
-
window.removeEventListener("focus", this._windowListeners.onWindowFocus);
|
|
992
|
-
this._windowListeners = null;
|
|
993
|
-
}
|
|
994
|
-
_detachWsListeners() {
|
|
995
|
-
if (!this._state.webSocket || !this._wsListeners) return;
|
|
996
|
-
this._state.webSocket.removeEventListener("close", this._wsListeners.onClose);
|
|
997
|
-
this._state.webSocket.removeEventListener("error", this._wsListeners.onError);
|
|
998
|
-
this._state.webSocket.removeEventListener("message", this._wsListeners.onMessage);
|
|
999
|
-
this._state.webSocket.removeEventListener("open", this._wsListeners.onOpen);
|
|
1000
|
-
this._wsListeners = null;
|
|
1001
|
-
}
|
|
1002
|
-
_emitSharedTypes() {
|
|
1003
|
-
const sharedTypes = this._crdtManager.doc.get();
|
|
1004
|
-
const storageRoot = Object.keys(sharedTypes).reduce(
|
|
1005
|
-
(acc, prop) => {
|
|
1006
|
-
const serialized = this._crdtManager.doc.toJson(prop);
|
|
1007
|
-
this._crdtNotifier.subject(prop).next(serialized);
|
|
1008
|
-
return __spreadProps(__spreadValues({}, acc), { [prop]: serialized });
|
|
1009
|
-
},
|
|
1010
|
-
{}
|
|
1011
|
-
);
|
|
1012
|
-
this._crdtNotifier.rootSubject.next(storageRoot);
|
|
1013
|
-
}
|
|
1014
|
-
_getAuthFetchOptions(room, params) {
|
|
1015
|
-
const metadata = params.metadata;
|
|
1016
|
-
if (typeof this._endpoints.authEndpoint === "undefined") return null;
|
|
1017
|
-
if (this._endpoints.authEndpoint === true) {
|
|
1018
|
-
return {
|
|
1019
|
-
url: `/api/pluv/authorize?room=${room}`,
|
|
1020
|
-
options: {}
|
|
1021
|
-
};
|
|
1022
|
-
}
|
|
1023
|
-
if (typeof this._endpoints.authEndpoint === "string") {
|
|
1024
|
-
return {
|
|
1025
|
-
url: this._endpoints.authEndpoint,
|
|
1026
|
-
options: {}
|
|
1027
|
-
};
|
|
1028
|
-
}
|
|
1029
|
-
const result = this._endpoints.authEndpoint({ metadata, room });
|
|
1030
|
-
return typeof result === "string" ? { url: result, options: {} } : result;
|
|
1031
|
-
}
|
|
1032
|
-
_getAuthorization(room, params) {
|
|
1033
|
-
return __async(this, null, function* () {
|
|
1034
|
-
const fetchOptions = this._getAuthFetchOptions(room, params);
|
|
1035
|
-
if (!fetchOptions) return null;
|
|
1036
|
-
const { url, options } = fetchOptions;
|
|
1037
|
-
const res = yield fetch(url, options);
|
|
1038
|
-
if (!res.ok || res.status !== 200) throw new Error("Room is unauthorized");
|
|
1039
|
-
try {
|
|
1040
|
-
return yield res.text().then((text) => text.trim());
|
|
1041
|
-
} catch (err) {
|
|
1042
|
-
throw new Error(err instanceof Error ? err.message : "Room is unauthorized");
|
|
1043
|
-
}
|
|
1044
|
-
});
|
|
1045
|
-
}
|
|
1046
|
-
_getPublicKey(params) {
|
|
1047
|
-
const metadata = params.metadata;
|
|
1048
|
-
if (!this._publicKey) return null;
|
|
1049
|
-
if (typeof this._publicKey === "string") return this._publicKey;
|
|
1050
|
-
return this._publicKey({ metadata });
|
|
1051
|
-
}
|
|
1052
|
-
_getWsEndpoint(room, params) {
|
|
1053
|
-
const metadata = params.metadata;
|
|
1054
|
-
switch (typeof this._endpoints.wsEndpoint) {
|
|
1055
|
-
case "undefined":
|
|
1056
|
-
return !!this._getPublicKey(params) ? `wss://rooms.pluv.io/api/room/${room}` : `/api/pluv/room/${room}`;
|
|
1057
|
-
case "string":
|
|
1058
|
-
return this._endpoints.wsEndpoint;
|
|
1059
|
-
default:
|
|
1060
|
-
return this._endpoints.wsEndpoint({ metadata, room });
|
|
1061
|
-
}
|
|
1062
|
-
}
|
|
1063
|
-
_handleExit(message) {
|
|
1064
|
-
const { connectionId } = message;
|
|
1065
|
-
if (!connectionId) return;
|
|
1066
|
-
if (!this._state.webSocket) throw new Error("Could not find WebSocket");
|
|
1067
|
-
const clientId = this._usersManager.getClientId(connectionId);
|
|
1068
|
-
const deleted = this._usersManager.deleteConnection(connectionId);
|
|
1069
|
-
const others = this._usersManager.getOthers();
|
|
1070
|
-
this._stateNotifier.subjects.others.next(others);
|
|
1071
|
-
if (!deleted) {
|
|
1072
|
-
console.warn("Could not identify exited connection");
|
|
1073
|
-
return;
|
|
1074
|
-
}
|
|
1075
|
-
const { data: user, remaining } = deleted;
|
|
1076
|
-
this._usersNotifier.others.next({
|
|
1077
|
-
others,
|
|
1078
|
-
event: { kind: "leave", user }
|
|
1079
|
-
});
|
|
1080
|
-
if (!remaining && !!clientId) this._usersNotifier.delete(clientId);
|
|
1081
|
-
}
|
|
1082
|
-
_handlePresenceUpdatedMessage(message) {
|
|
1083
|
-
var _a;
|
|
1084
|
-
const { connectionId } = message;
|
|
1085
|
-
if (!connectionId) return;
|
|
1086
|
-
if (!this._state.webSocket) throw new Error("Could not find WebSocket");
|
|
1087
|
-
const data = message.data;
|
|
1088
|
-
const myself = (_a = this._usersManager.myself) != null ? _a : null;
|
|
1089
|
-
if ((myself == null ? void 0 : myself.connectionId) === connectionId) return;
|
|
1090
|
-
const updated = this._usersManager.patchPresence(connectionId, data.presence);
|
|
1091
|
-
const myClientId = !!myself ? this._usersManager.getClientId(myself) : null;
|
|
1092
|
-
const clientId = this._usersManager.getClientId(connectionId);
|
|
1093
|
-
if (!!clientId && myClientId === clientId) {
|
|
1094
|
-
this._stateNotifier.subjects["my-presence"].next(updated);
|
|
1095
|
-
this._stateNotifier.subjects.myself.next(this._usersManager.myself);
|
|
1096
|
-
return;
|
|
1097
|
-
}
|
|
1098
|
-
if (!!clientId) {
|
|
1099
|
-
const other2 = this._usersManager.getOther(connectionId);
|
|
1100
|
-
const others2 = this._usersManager.getOthers();
|
|
1101
|
-
this._usersNotifier.other(clientId).next(other2);
|
|
1102
|
-
this._stateNotifier.subjects.others.next(others2);
|
|
1103
|
-
if (!!other2) {
|
|
1104
|
-
this._usersNotifier.others.next({
|
|
1105
|
-
others: others2,
|
|
1106
|
-
event: { kind: "update", user: other2 }
|
|
1107
|
-
});
|
|
1108
|
-
}
|
|
1109
|
-
return;
|
|
1110
|
-
}
|
|
1111
|
-
const added = this._usersManager.addConnection({
|
|
1112
|
-
connectionId,
|
|
1113
|
-
presence: data.presence,
|
|
1114
|
-
user: message.user
|
|
1115
|
-
});
|
|
1116
|
-
const other = this._usersManager.getOther(connectionId);
|
|
1117
|
-
const others = this._usersManager.getOthers();
|
|
1118
|
-
this._usersNotifier.other(added.clientId).next(other);
|
|
1119
|
-
this._stateNotifier.subjects.others.next(others);
|
|
1120
|
-
this._usersNotifier.others.next({
|
|
1121
|
-
others,
|
|
1122
|
-
event: { kind: "update", user: added.data }
|
|
1123
|
-
});
|
|
1124
|
-
}
|
|
1125
|
-
_handleReceiveOthers(message) {
|
|
1126
|
-
if (!message.connectionId) return;
|
|
1127
|
-
if (!this._state.webSocket) throw new Error("Could not find WebSocket");
|
|
1128
|
-
const data = message.data;
|
|
1129
|
-
Object.keys(data.others).forEach((connectionId) => {
|
|
1130
|
-
const { presence, user } = data.others[connectionId];
|
|
1131
|
-
const result = this._usersManager.addConnection({
|
|
1132
|
-
connectionId,
|
|
1133
|
-
presence,
|
|
1134
|
-
user
|
|
1135
|
-
});
|
|
1136
|
-
if (!!presence) this._usersManager.patchPresence(connectionId, presence);
|
|
1137
|
-
if (result.isMyself) return;
|
|
1138
|
-
const clientId = result.clientId;
|
|
1139
|
-
const other = this._usersManager.getOther(connectionId);
|
|
1140
|
-
this._usersNotifier.other(clientId).next(other);
|
|
1141
|
-
});
|
|
1142
|
-
const others = this._usersManager.getOthers();
|
|
1143
|
-
this._stateNotifier.subjects.others.next(others);
|
|
1144
|
-
this._usersNotifier.others.next({
|
|
1145
|
-
others,
|
|
1146
|
-
event: { kind: "sync", users: others }
|
|
1147
|
-
});
|
|
1148
|
-
}
|
|
1149
|
-
_handleRegisteredMessage(message) {
|
|
1150
|
-
return __async(this, null, function* () {
|
|
1151
|
-
var _a, _b;
|
|
1152
|
-
const { connectionId } = message;
|
|
1153
|
-
const user = message.user;
|
|
1154
|
-
if (!connectionId) return;
|
|
1155
|
-
if (!this._state.webSocket) throw new Error("Could not find WebSocket");
|
|
1156
|
-
const data = message.data;
|
|
1157
|
-
const state = data.state;
|
|
1158
|
-
this._updateState((oldState) => {
|
|
1159
|
-
oldState.connection.id = connectionId;
|
|
1160
|
-
oldState.authorization.user = user != null ? user : null;
|
|
1161
|
-
return oldState;
|
|
1162
|
-
});
|
|
1163
|
-
const userInfo = {
|
|
1164
|
-
connectionId,
|
|
1165
|
-
presence: (_a = data.presence) != null ? _a : void 0,
|
|
1166
|
-
user
|
|
1167
|
-
};
|
|
1168
|
-
this._usersManager.setMyself(userInfo);
|
|
1169
|
-
const presence = this._usersManager.myPresence;
|
|
1170
|
-
const myself = (_b = this._usersManager.myself) != null ? _b : null;
|
|
1171
|
-
this._stateNotifier.subjects["my-presence"].next(presence);
|
|
1172
|
-
this._stateNotifier.subjects.myself.next(myself);
|
|
1173
|
-
const update = yield (() => __async(this, null, function* () {
|
|
1174
|
-
if (!!state) {
|
|
1175
|
-
this._logDebug("Retrieving initial state");
|
|
1176
|
-
return this._crdtManager.getInitialState();
|
|
1177
|
-
}
|
|
1178
|
-
const resolved = this._crdtManager.resolveEncodedState(
|
|
1179
|
-
yield this._storageStore.getUpdates()
|
|
1180
|
-
);
|
|
1181
|
-
if (!!resolved) {
|
|
1182
|
-
this._logDebug("Retrieving storage store state");
|
|
1183
|
-
return resolved;
|
|
1184
|
-
}
|
|
1185
|
-
this._logDebug("Retrieving initial state");
|
|
1186
|
-
return this._crdtManager.getInitialState();
|
|
1187
|
-
}))();
|
|
1188
|
-
this._sendMessage({
|
|
1189
|
-
type: "$initializeSession",
|
|
1190
|
-
data: { presence, update }
|
|
1191
|
-
});
|
|
1192
|
-
});
|
|
1193
|
-
}
|
|
1194
|
-
_handleStorageReceivedMessage(message) {
|
|
1195
|
-
return __async(this, null, function* () {
|
|
1196
|
-
const { connectionId } = message;
|
|
1197
|
-
if (!connectionId) return;
|
|
1198
|
-
if (!this._state.webSocket) throw new Error("Could not find WebSocket");
|
|
1199
|
-
const data = message.data;
|
|
1200
|
-
const changeKind = data.changeKind;
|
|
1201
|
-
const state = data.state;
|
|
1202
|
-
const updates = yield this._storageStore.getUpdates();
|
|
1203
|
-
const origin = ORIGIN_INITIALIZED;
|
|
1204
|
-
const updateStorage = (update) => {
|
|
1205
|
-
return this._crdtManager.initialized ? this._crdtManager.applyUpdate({ update, origin }) : this._crdtManager.initialize({ update, origin });
|
|
1206
|
-
};
|
|
1207
|
-
if (changeKind === "empty") {
|
|
1208
|
-
this._crdtManager.destroy();
|
|
1209
|
-
this._crdtManager.initialize({ origin, update: updates });
|
|
1210
|
-
} else if (changeKind === "initialized") {
|
|
1211
|
-
if (!!updates.length) updateStorage(updates);
|
|
1212
|
-
else {
|
|
1213
|
-
const encodedState2 = updateStorage(state).doc.getEncodedState();
|
|
1214
|
-
this._addToStorageStore(encodedState2);
|
|
1215
|
-
}
|
|
1216
|
-
} else {
|
|
1217
|
-
const encodedState2 = updateStorage(state).doc.getEncodedState();
|
|
1218
|
-
if (!!updates.length) this._crdtManager.applyUpdate({ update: updates, origin });
|
|
1219
|
-
yield this._addToStorageStore(encodedState2);
|
|
1220
|
-
}
|
|
1221
|
-
const encodedState = this._crdtManager.doc.getEncodedState();
|
|
1222
|
-
this._emitSharedTypes();
|
|
1223
|
-
this._observeCrdt();
|
|
1224
|
-
this._updateState((oldState) => {
|
|
1225
|
-
oldState.storage.state = import_types.StorageState.Synced;
|
|
1226
|
-
return oldState;
|
|
1227
|
-
});
|
|
1228
|
-
this._stateNotifier.subjects["storage-loaded"].next(true);
|
|
1229
|
-
this._sendMessage({
|
|
1230
|
-
type: "$updateStorage",
|
|
1231
|
-
data: { origin, update: encodedState }
|
|
1232
|
-
});
|
|
1233
|
-
});
|
|
1234
|
-
}
|
|
1235
|
-
_handleStorageUpdatedMessage(message) {
|
|
1236
|
-
const { connectionId } = message;
|
|
1237
|
-
if (!connectionId) return;
|
|
1238
|
-
if (!this._state.webSocket) throw new Error("Could not find WebSocket");
|
|
1239
|
-
const data = message.data;
|
|
1240
|
-
this._crdtManager.doc.applyEncodedState({
|
|
1241
|
-
origin: ORIGIN_STORAGE_UPDATED,
|
|
1242
|
-
update: data.state
|
|
1243
|
-
});
|
|
1244
|
-
const sharedTypes = this._crdtManager.doc.get();
|
|
1245
|
-
const storageRoot = Object.keys(sharedTypes).reduce(
|
|
1246
|
-
(acc, prop) => {
|
|
1247
|
-
const serialized = this._crdtManager.doc.toJson(prop);
|
|
1248
|
-
this._crdtNotifier.subject(prop).next(serialized);
|
|
1249
|
-
return __spreadProps(__spreadValues({}, acc), { [prop]: serialized });
|
|
1250
|
-
},
|
|
1251
|
-
{}
|
|
1252
|
-
);
|
|
1253
|
-
this._crdtNotifier.rootSubject.next(storageRoot);
|
|
1254
|
-
}
|
|
1255
|
-
_handleSyncStateReceived(message) {
|
|
1256
|
-
var _a;
|
|
1257
|
-
const { connectionId } = message;
|
|
1258
|
-
if (!connectionId) return;
|
|
1259
|
-
if (!this._state.webSocket) throw new Error("Could not find WebSocket");
|
|
1260
|
-
if (!this._usersManager.myself) return;
|
|
1261
|
-
const data = message.data;
|
|
1262
|
-
const active = new Set((_a = data == null ? void 0 : data.connectionIds) != null ? _a : []);
|
|
1263
|
-
const quitters = this._usersManager.getOthers().filter((other) => !active.has(other.connectionId));
|
|
1264
|
-
quitters.forEach((quitter) => {
|
|
1265
|
-
this._usersManager.deleteConnection(quitter.connectionId);
|
|
1266
|
-
const remaining2 = this._usersManager.getOthers();
|
|
1267
|
-
this._usersNotifier.others.next({
|
|
1268
|
-
others: remaining2,
|
|
1269
|
-
event: { kind: "leave", user: quitter }
|
|
1270
|
-
});
|
|
1271
|
-
});
|
|
1272
|
-
const remaining = this._usersManager.getOthers();
|
|
1273
|
-
this._stateNotifier.subjects.others.next(remaining);
|
|
1274
|
-
}
|
|
1275
|
-
_handleUserJoinedMessage(message) {
|
|
1276
|
-
const { connectionId } = message;
|
|
1277
|
-
if (!connectionId) return;
|
|
1278
|
-
if (!this._state.webSocket) throw new Error("Could not find WebSocket");
|
|
1279
|
-
if (!this._usersManager.myself) return;
|
|
1280
|
-
const data = message.data;
|
|
1281
|
-
const myself = this._usersManager.myself;
|
|
1282
|
-
if (myself.connectionId === connectionId) {
|
|
1283
|
-
this._sendMessage({ type: "$getOthers", data: {} });
|
|
1284
|
-
return;
|
|
1285
|
-
}
|
|
1286
|
-
const added = this._usersManager.addConnection({
|
|
1287
|
-
connectionId,
|
|
1288
|
-
presence: data.presence,
|
|
1289
|
-
user: data.user
|
|
1290
|
-
});
|
|
1291
|
-
const other = this._usersManager.getOther(connectionId);
|
|
1292
|
-
const others = this._usersManager.getOthers();
|
|
1293
|
-
this._usersNotifier.other(added.clientId).next(other);
|
|
1294
|
-
this._stateNotifier.subjects.others.next(others);
|
|
1295
|
-
this._usersNotifier.others.next({
|
|
1296
|
-
others,
|
|
1297
|
-
event: { kind: "enter", user: added.data }
|
|
1298
|
-
});
|
|
1299
|
-
}
|
|
1300
|
-
_heartbeat() {
|
|
1301
|
-
this._clearTimeout(this._timeouts.pong);
|
|
1302
|
-
this._timeouts.pong = setTimeout(
|
|
1303
|
-
this._reconnect.bind(this),
|
|
1304
|
-
PONG_TIMEOUT_MS
|
|
1305
|
-
);
|
|
1306
|
-
this._sendMessage('{"type":"$ping","data":{}}');
|
|
1307
|
-
}
|
|
1308
|
-
_logDebug(...data) {
|
|
1309
|
-
var _a;
|
|
1310
|
-
if (typeof process === "undefined") return;
|
|
1311
|
-
if (((_a = process.env) == null ? void 0 : _a.NODE_ENV) === "production") return;
|
|
1312
|
-
if (this._debug) console.log(...data);
|
|
1313
|
-
}
|
|
1314
|
-
_observeCrdt() {
|
|
1315
|
-
var _a, _b;
|
|
1316
|
-
(_b = (_a = this._subscriptions).observeCrdt) == null ? void 0 : _b.call(_a);
|
|
1317
|
-
const unsubscribe = this._crdtManager.doc.subscribe((event) => {
|
|
1318
|
-
var _a2;
|
|
1319
|
-
const origin = (_a2 = event.origin) != null ? _a2 : null;
|
|
1320
|
-
this._emitSharedTypes();
|
|
1321
|
-
if (origin === ORIGIN_INITIALIZED || origin === ORIGIN_STORAGE_UPDATED) {
|
|
1322
|
-
return;
|
|
1323
|
-
}
|
|
1324
|
-
this._addToStorageStore(event.update);
|
|
1325
|
-
if (!this._state.webSocket) return;
|
|
1326
|
-
if (!this._state.connection.id) return;
|
|
1327
|
-
if (this._state.webSocket.readyState !== WebSocket.OPEN) return;
|
|
1328
|
-
this._sendMessage({
|
|
1329
|
-
type: "$updateStorage",
|
|
1330
|
-
data: { origin, update: event.update }
|
|
1331
|
-
});
|
|
1332
|
-
});
|
|
1333
|
-
this._subscriptions.observeCrdt = unsubscribe;
|
|
1334
|
-
}
|
|
1335
|
-
_onAuthorizationFail(error) {
|
|
1336
|
-
const authError = error instanceof Error ? error : new Error("Authorization failed");
|
|
1337
|
-
this._listeners.onAuthorizationFail(authError);
|
|
1338
|
-
this._updateState((oldState) => {
|
|
1339
|
-
oldState.connection.state = import_types.ConnectionState.Unavailable;
|
|
1340
|
-
oldState.storage.state = import_types.StorageState.Unavailable;
|
|
1341
|
-
return oldState;
|
|
1342
|
-
});
|
|
1343
|
-
}
|
|
1344
|
-
_onClose(event) {
|
|
1345
|
-
return __async(this, null, function* () {
|
|
1346
|
-
this._listenerManager.subjects.close.next(event);
|
|
1347
|
-
this._logDebug("WebSocket closed");
|
|
1348
|
-
if (!!event.reason) this._logDebug(event.reason);
|
|
1349
|
-
const shouldRetry = [
|
|
1350
|
-
// Going away: Client/server is shutting down or navigating
|
|
1351
|
-
1001,
|
|
1352
|
-
// Abnormal closure: Usually network error
|
|
1353
|
-
1006,
|
|
1354
|
-
// Internal error: may be a larger problem, but try reconnecting for now
|
|
1355
|
-
1011,
|
|
1356
|
-
// Service restart: Server restarted
|
|
1357
|
-
1012,
|
|
1358
|
-
// Try again later: server overloaded
|
|
1359
|
-
1013,
|
|
1360
|
-
// Bad gateway
|
|
1361
|
-
1014
|
|
1362
|
-
].some((okToRetryCode) => event.code === okToRetryCode);
|
|
1363
|
-
this._updateState((oldState) => {
|
|
1364
|
-
oldState.authorization.token = null;
|
|
1365
|
-
oldState.connection.state = shouldRetry ? import_types.ConnectionState.Unavailable : (
|
|
1366
|
-
/**
|
|
1367
|
-
* !HACK
|
|
1368
|
-
* @description Assume these are not recoverable. We're just going to mark this
|
|
1369
|
-
* as Closed to avoid reconnect attempts.
|
|
1370
|
-
* @date April 28, 2025
|
|
1371
|
-
*/
|
|
1372
|
-
import_types.ConnectionState.Closed
|
|
1373
|
-
);
|
|
1374
|
-
oldState.storage.state = oldState.storage.state === import_types.StorageState.Synced ? import_types.StorageState.Offline : import_types.StorageState.Unavailable;
|
|
1375
|
-
oldState.webSocket = null;
|
|
1376
|
-
return oldState;
|
|
1377
|
-
});
|
|
1378
|
-
if (shouldRetry) {
|
|
1379
|
-
yield this._reconnect();
|
|
1380
|
-
return;
|
|
1381
|
-
}
|
|
1382
|
-
this._closeWs();
|
|
1383
|
-
this._clearTimeout(this._timeouts.reconnect);
|
|
1384
|
-
});
|
|
1385
|
-
}
|
|
1386
|
-
/**
|
|
1387
|
-
* TODO
|
|
1388
|
-
* @description Handle websocket errors in a meaningful way, should they occur
|
|
1389
|
-
* @date August 19, 2022
|
|
1390
|
-
*/
|
|
1391
|
-
_onError(event) {
|
|
1392
|
-
this._listenerManager.subjects.error.next(event);
|
|
1393
|
-
}
|
|
1394
|
-
_onMessage(event) {
|
|
1395
|
-
this._listenerManager.subjects.message.next(event);
|
|
1396
|
-
const message = this._parseMessage(event);
|
|
1397
|
-
if (!message) return;
|
|
1398
|
-
const shouldLog = typeof this._debug === "boolean" ? this._debug : this._debug.output.find((value) => value === message.type);
|
|
1399
|
-
if (shouldLog) {
|
|
1400
|
-
this._logDebug("WebSocket event received: ", message.type, message);
|
|
1401
|
-
}
|
|
1402
|
-
this._eventNotifier.subject(message.type).next(message);
|
|
1403
|
-
switch (message.type) {
|
|
1404
|
-
case "$exit": {
|
|
1405
|
-
this._handleExit(message);
|
|
1406
|
-
return;
|
|
1407
|
-
}
|
|
1408
|
-
case "$othersReceived": {
|
|
1409
|
-
this._handleReceiveOthers(message);
|
|
1410
|
-
return;
|
|
1411
|
-
}
|
|
1412
|
-
case "$pong": {
|
|
1413
|
-
this._clearTimeout(this._timeouts.pong);
|
|
1414
|
-
return;
|
|
1415
|
-
}
|
|
1416
|
-
case "$presenceUpdated": {
|
|
1417
|
-
this._handlePresenceUpdatedMessage(message);
|
|
1418
|
-
return;
|
|
1419
|
-
}
|
|
1420
|
-
case "$registered": {
|
|
1421
|
-
this._handleRegisteredMessage(message);
|
|
1422
|
-
return;
|
|
1423
|
-
}
|
|
1424
|
-
case "$storageReceived": {
|
|
1425
|
-
this._handleStorageReceivedMessage(message);
|
|
1426
|
-
return;
|
|
1427
|
-
}
|
|
1428
|
-
case "$storageUpdated": {
|
|
1429
|
-
this._handleStorageUpdatedMessage(message);
|
|
1430
|
-
return;
|
|
1431
|
-
}
|
|
1432
|
-
case "$syncStateReceived": {
|
|
1433
|
-
this._handleSyncStateReceived(message);
|
|
1434
|
-
return;
|
|
1435
|
-
}
|
|
1436
|
-
case "$userJoined": {
|
|
1437
|
-
this._handleUserJoinedMessage(message);
|
|
1438
|
-
return;
|
|
1439
|
-
}
|
|
1440
|
-
default:
|
|
1441
|
-
}
|
|
1442
|
-
}
|
|
1443
|
-
_onOpen(event) {
|
|
1444
|
-
this._listenerManager.subjects.open.next(event);
|
|
1445
|
-
if (this._state.connection.state !== import_types.ConnectionState.Connecting) return;
|
|
1446
|
-
this._logDebug("WebSocket connected");
|
|
1447
|
-
this._updateState((oldState) => {
|
|
1448
|
-
oldState.connection.state = import_types.ConnectionState.Open;
|
|
1449
|
-
return oldState;
|
|
1450
|
-
});
|
|
1451
|
-
this._clearInterval(this._intervals.heartbeat);
|
|
1452
|
-
this._intervals.heartbeat = setInterval(
|
|
1453
|
-
this._heartbeat.bind(this),
|
|
1454
|
-
HEARTBEAT_INTERVAL_MS
|
|
1455
|
-
);
|
|
1456
|
-
}
|
|
1457
|
-
_onNavigatorOffline() {
|
|
1458
|
-
return __async(this, null, function* () {
|
|
1459
|
-
if (this._state.connection.state === import_types.ConnectionState.Closed) return;
|
|
1460
|
-
this._updateState((oldState) => {
|
|
1461
|
-
oldState.connection.state = import_types.ConnectionState.Unavailable;
|
|
1462
|
-
oldState.storage.state = oldState.storage.state === import_types.StorageState.Synced ? import_types.StorageState.Offline : import_types.StorageState.Unavailable;
|
|
1463
|
-
return oldState;
|
|
1464
|
-
});
|
|
1465
|
-
});
|
|
1466
|
-
}
|
|
1467
|
-
_onNavigatorOnline() {
|
|
1468
|
-
return __async(this, null, function* () {
|
|
1469
|
-
if (this._state.connection.state !== import_types.ConnectionState.Unavailable) return;
|
|
1470
|
-
yield this._reconnect();
|
|
1471
|
-
});
|
|
1472
|
-
}
|
|
1473
|
-
_onVisibilityChange() {
|
|
1474
|
-
return __async(this, null, function* () {
|
|
1475
|
-
if (typeof document === "undefined") return;
|
|
1476
|
-
if (document.visibilityState !== "visible") return;
|
|
1477
|
-
switch (this._state.connection.state) {
|
|
1478
|
-
case import_types.ConnectionState.Open: {
|
|
1479
|
-
this._clearInterval(this._intervals.heartbeat);
|
|
1480
|
-
setInterval(this._heartbeat.bind(this), HEARTBEAT_INTERVAL_MS);
|
|
1481
|
-
return;
|
|
1482
|
-
}
|
|
1483
|
-
case import_types.ConnectionState.Unavailable: {
|
|
1484
|
-
yield this._reconnect();
|
|
1485
|
-
return;
|
|
1486
|
-
}
|
|
1487
|
-
default:
|
|
1488
|
-
}
|
|
1489
|
-
});
|
|
1490
|
-
}
|
|
1491
|
-
_onWindowFocus() {
|
|
1492
|
-
return __async(this, null, function* () {
|
|
1493
|
-
return yield this._onVisibilityChange();
|
|
1494
|
-
});
|
|
1495
|
-
}
|
|
1496
|
-
_parseMessage(message) {
|
|
1497
|
-
if (typeof message.data !== "string") return null;
|
|
1498
|
-
try {
|
|
1499
|
-
const config = JSON.parse(message.data);
|
|
1500
|
-
if (typeof config !== "object") return null;
|
|
1501
|
-
if (typeof config.type !== "string") return null;
|
|
1502
|
-
if (typeof config.data !== "object") return null;
|
|
1503
|
-
return config;
|
|
1504
|
-
} catch (e) {
|
|
1505
|
-
return null;
|
|
1506
|
-
}
|
|
1507
|
-
}
|
|
1508
|
-
_pollReconnect() {
|
|
1509
|
-
this._clearTimeout(this._timeouts.reconnect);
|
|
1510
|
-
const timeoutMs = typeof this._reconnectTimeoutMs === "number" ? this._reconnectTimeoutMs : this._reconnectTimeoutMs({ attempts: this._state.connection.attempts });
|
|
1511
|
-
const clampedMs = Math.max(
|
|
1512
|
-
Math.min(MIN_RECONNECT_TIMEOUT_MS, timeoutMs),
|
|
1513
|
-
MAX_RECONNECT_TIMEOUT_MS
|
|
1514
|
-
);
|
|
1515
|
-
this._timeouts.reconnect = setTimeout(
|
|
1516
|
-
this._reconnect.bind(this),
|
|
1517
|
-
clampedMs
|
|
1518
|
-
);
|
|
1519
|
-
}
|
|
1520
|
-
_reconnect() {
|
|
1521
|
-
return __async(this, null, function* () {
|
|
1522
|
-
this._closeWs();
|
|
1523
|
-
if (this._state.connection.state === import_types.ConnectionState.Closed) return;
|
|
1524
|
-
const metadata = this._lastMetadata;
|
|
1525
|
-
const params = typeof metadata === "undefined" ? [] : [{ metadata }];
|
|
1526
|
-
yield this.connect(...params);
|
|
1527
|
-
});
|
|
1528
|
-
}
|
|
1529
|
-
_sendMessage(data) {
|
|
1530
|
-
const webSocket = this._state.webSocket;
|
|
1531
|
-
if ((webSocket == null ? void 0 : webSocket.readyState) !== WebSocket.OPEN) return;
|
|
1532
|
-
const dataType = (() => {
|
|
1533
|
-
var _a, _b, _c;
|
|
1534
|
-
if (typeof data === "object") return (_a = data.type) != null ? _a : null;
|
|
1535
|
-
if (typeof data !== "string") return null;
|
|
1536
|
-
try {
|
|
1537
|
-
return (_c = (_b = JSON.parse(data)) == null ? void 0 : _b.type) != null ? _c : null;
|
|
1538
|
-
} catch (e) {
|
|
1539
|
-
return null;
|
|
1540
|
-
}
|
|
1541
|
-
})();
|
|
1542
|
-
const shouldLog = typeof this._debug === "boolean" ? this._debug : !dataType ? false : this._debug.input.some((value) => value === data.type);
|
|
1543
|
-
if (shouldLog) this._logDebug("WebSocket event sent: ", data.type, data);
|
|
1544
|
-
const message = typeof data === "string" ? data : JSON.stringify(data);
|
|
1545
|
-
webSocket.send(message);
|
|
1546
|
-
}
|
|
1547
|
-
_setMetadata(metadata) {
|
|
1548
|
-
const parsed = this.metadata ? this.metadata.parse(metadata) : metadata;
|
|
1549
|
-
this._lastMetadata = parsed;
|
|
1550
|
-
return parsed;
|
|
1551
|
-
}
|
|
1552
|
-
_updateState(updater) {
|
|
1553
|
-
let authorization;
|
|
1554
|
-
try {
|
|
1555
|
-
authorization = JSON.parse(JSON.stringify(this._state.authorization));
|
|
1556
|
-
} catch (e) {
|
|
1557
|
-
throw new Error("User is not JSON serializable");
|
|
1558
|
-
}
|
|
1559
|
-
const oldState = __spreadProps(__spreadValues({}, this._state), {
|
|
1560
|
-
authorization,
|
|
1561
|
-
connection: JSON.parse(JSON.stringify(this._state.connection)),
|
|
1562
|
-
storage: JSON.parse(JSON.stringify(this._state.storage)),
|
|
1563
|
-
webSocket: this._state.webSocket
|
|
1564
|
-
});
|
|
1565
|
-
const newState = updater(oldState);
|
|
1566
|
-
this._state = newState;
|
|
1567
|
-
this._logDebug("WebSocket new state: ", newState);
|
|
1568
|
-
this._stateNotifier.subjects.connection.next(newState);
|
|
1569
|
-
return newState;
|
|
1570
|
-
}
|
|
1571
|
-
};
|
|
1572
|
-
__storage = new WeakMap();
|
|
1573
|
-
|
|
1574
|
-
// src/PluvClient.ts
|
|
1575
|
-
var PluvClient = class {
|
|
1576
|
-
constructor(options) {
|
|
1577
|
-
this._publicKey = null;
|
|
1578
|
-
this._rooms = /* @__PURE__ */ new Map();
|
|
1579
|
-
this.createRoom = (room, options = {}) => {
|
|
1580
|
-
var _a, _b;
|
|
1581
|
-
const oldRoom = this.getRoom(room);
|
|
1582
|
-
if (oldRoom) return oldRoom;
|
|
1583
|
-
const newRoom = new PluvRoom(room, {
|
|
1584
|
-
addons: options.addons,
|
|
1585
|
-
authEndpoint: this._authEndpoint,
|
|
1586
|
-
debug: options.debug,
|
|
1587
|
-
initialPresence: options.initialPresence,
|
|
1588
|
-
initialStorage: (_a = options.initialStorage) != null ? _a : this._initialStorage,
|
|
1589
|
-
limits: this._limits,
|
|
1590
|
-
metadata: this.metadata,
|
|
1591
|
-
onAuthorizationFail: options.onAuthorizationFail,
|
|
1592
|
-
presence: this._presence,
|
|
1593
|
-
publicKey: (_b = this._publicKey) != null ? _b : void 0,
|
|
1594
|
-
reconnectTimeoutMs: options.reconnectTimeoutMs,
|
|
1595
|
-
router: options.router,
|
|
1596
|
-
wsEndpoint: this._wsEndpoint
|
|
1597
|
-
});
|
|
1598
|
-
this._rooms.set(room, newRoom);
|
|
1599
|
-
this._logDebug(`New room was created: ${room}`);
|
|
1600
|
-
return newRoom;
|
|
1601
|
-
};
|
|
1602
|
-
this.enter = (room, ...args) => __async(this, null, function* () {
|
|
1603
|
-
const toEnter = typeof room === "string" ? this.getRoom(room) : room;
|
|
1604
|
-
if (!toEnter) throw new Error(`Could not find room: ${room}.`);
|
|
1605
|
-
this._rooms.set(toEnter.id, toEnter);
|
|
1606
|
-
yield toEnter.connect(...args);
|
|
1607
|
-
this._logDebug(`Entered room: ${room}`);
|
|
1608
|
-
return toEnter;
|
|
1609
|
-
});
|
|
1610
|
-
this.getRoom = (room) => {
|
|
1611
|
-
const found = this._rooms.get(room);
|
|
1612
|
-
return found != null ? found : null;
|
|
1613
|
-
};
|
|
1614
|
-
this.getRooms = () => {
|
|
1615
|
-
return Array.from(this._rooms.values());
|
|
1616
|
-
};
|
|
1617
|
-
this.leave = (room) => __async(this, null, function* () {
|
|
1618
|
-
const toLeave = typeof room === "string" ? this.getRoom(room) : room;
|
|
1619
|
-
if (!toLeave) return;
|
|
1620
|
-
yield toLeave.disconnect();
|
|
1621
|
-
this._rooms.delete(toLeave.id);
|
|
1622
|
-
this._logDebug(`Left and deleted room: ${room}`);
|
|
1623
|
-
});
|
|
1624
|
-
const {
|
|
1625
|
-
authEndpoint,
|
|
1626
|
-
debug = false,
|
|
1627
|
-
initialStorage,
|
|
1628
|
-
limits,
|
|
1629
|
-
metadata,
|
|
1630
|
-
presence,
|
|
1631
|
-
publicKey,
|
|
1632
|
-
wsEndpoint
|
|
1633
|
-
} = options;
|
|
1634
|
-
this.metadata = metadata;
|
|
1635
|
-
this._authEndpoint = authEndpoint;
|
|
1636
|
-
this._debug = debug;
|
|
1637
|
-
this._initialStorage = initialStorage;
|
|
1638
|
-
this._limits = __spreadValues({
|
|
1639
|
-
presenceMaxSize: MAX_PRESENCE_SIZE_BYTES
|
|
1640
|
-
}, limits);
|
|
1641
|
-
this._presence = presence;
|
|
1642
|
-
this._wsEndpoint = wsEndpoint;
|
|
1643
|
-
if (!!publicKey) this._publicKey = publicKey;
|
|
1644
|
-
}
|
|
1645
|
-
get _defs() {
|
|
1646
|
-
return {
|
|
1647
|
-
initialStorage: this._initialStorage
|
|
1648
|
-
};
|
|
1649
|
-
}
|
|
1650
|
-
get procedure() {
|
|
1651
|
-
return new PluvProcedure();
|
|
1652
|
-
}
|
|
1653
|
-
router(events) {
|
|
1654
|
-
const invalidName = Object.keys(events).find((name) => name.includes("$"));
|
|
1655
|
-
if (typeof invalidName === "string") {
|
|
1656
|
-
throw new Error(`Invalid event name. Event names must not contain $: "${invalidName}"`);
|
|
1657
|
-
}
|
|
1658
|
-
return new PluvRouter(events);
|
|
1659
|
-
}
|
|
1660
|
-
_logDebug(...data) {
|
|
1661
|
-
var _a;
|
|
1662
|
-
if (typeof process === "undefined") return;
|
|
1663
|
-
if (((_a = process.env) == null ? void 0 : _a.NODE_ENV) === "production") return;
|
|
1664
|
-
if (this._debug) console.log(...data);
|
|
1665
|
-
}
|
|
1666
|
-
};
|
|
1667
|
-
|
|
1668
|
-
// src/createClient.ts
|
|
1669
|
-
var createClient = (options) => {
|
|
1670
|
-
return new PluvClient(options);
|
|
1671
|
-
};
|
|
1672
|
-
|
|
1673
|
-
// src/infer.ts
|
|
1674
|
-
var infer = (callback) => callback;
|
|
1675
|
-
|
|
1676
|
-
// src/MockedRoom.ts
|
|
1677
|
-
var import_types2 = require("@pluv/types");
|
|
1678
|
-
var __storage2;
|
|
1679
|
-
var MockedRoom = class {
|
|
1680
|
-
constructor(room, options) {
|
|
1681
|
-
this._crdtNotifier = new CrdtNotifier();
|
|
1682
|
-
this._eventNotifier = new EventNotifier();
|
|
1683
|
-
this._usersNotifier = new UsersNotifier();
|
|
1684
|
-
this._state = {
|
|
1685
|
-
authorization: {
|
|
1686
|
-
token: null,
|
|
1687
|
-
user: null
|
|
1688
|
-
},
|
|
1689
|
-
connection: {
|
|
1690
|
-
attempts: 0,
|
|
1691
|
-
id: null,
|
|
1692
|
-
state: import_types2.ConnectionState.Untouched
|
|
1693
|
-
},
|
|
1694
|
-
storage: {
|
|
1695
|
-
state: import_types2.StorageState.Unavailable
|
|
1696
|
-
},
|
|
1697
|
-
webSocket: null
|
|
1698
|
-
};
|
|
1699
|
-
this._stateNotifier = new StateNotifier();
|
|
1700
|
-
this._subscriptions = {
|
|
1701
|
-
observeCrdt: null
|
|
1702
|
-
};
|
|
1703
|
-
this.broadcast = new Proxy(
|
|
1704
|
-
(event, data) => __async(this, null, function* () {
|
|
1705
|
-
if (!this._state.webSocket) return;
|
|
1706
|
-
if (this._state.connection.state !== import_types2.ConnectionState.Open) return;
|
|
1707
|
-
const type = event.toString();
|
|
1708
|
-
const procedure = this._router._defs.events[type];
|
|
1709
|
-
if (!(procedure == null ? void 0 : procedure.config.broadcast)) {
|
|
1710
|
-
this._simulateEvent(type, data);
|
|
1711
|
-
return;
|
|
1712
|
-
}
|
|
1713
|
-
const myself = this._usersManager.myself;
|
|
1714
|
-
if (!myself) return;
|
|
1715
|
-
const parsed = procedure.config.input ? procedure.config.input.parse(data) : data;
|
|
1716
|
-
const context = {
|
|
1717
|
-
doc: this._crdtManager.doc,
|
|
1718
|
-
others: this._usersManager.getOthers(),
|
|
1719
|
-
room: this.id,
|
|
1720
|
-
user: myself
|
|
1721
|
-
};
|
|
1722
|
-
const output = yield procedure.config.broadcast(parsed, context);
|
|
1723
|
-
Object.entries(output).forEach(([_type, _data]) => {
|
|
1724
|
-
this._simulateEvent(_type, _data);
|
|
1725
|
-
});
|
|
1726
|
-
}),
|
|
1727
|
-
{
|
|
1728
|
-
get(fn, prop) {
|
|
1729
|
-
return (data) => __async(null, null, function* () {
|
|
1730
|
-
return yield fn(prop, data);
|
|
1731
|
-
});
|
|
1732
|
-
}
|
|
1733
|
-
}
|
|
1734
|
-
);
|
|
1735
|
-
this.canRedo = () => {
|
|
1736
|
-
return !!this._crdtManager.doc.canRedo();
|
|
1737
|
-
};
|
|
1738
|
-
this.canUndo = () => {
|
|
1739
|
-
return !!this._crdtManager.doc.canUndo();
|
|
1740
|
-
};
|
|
1741
|
-
this.getConnection = () => {
|
|
1742
|
-
return Object.freeze(JSON.parse(JSON.stringify(this._state.connection)));
|
|
1743
|
-
};
|
|
1744
|
-
this.getMyPresence = () => {
|
|
1745
|
-
return this._usersManager.myPresence;
|
|
1746
|
-
};
|
|
1747
|
-
this.getMyself = () => {
|
|
1748
|
-
return this._usersManager.myself;
|
|
1749
|
-
};
|
|
1750
|
-
this.getOther = (connectionId) => {
|
|
1751
|
-
return this._usersManager.getOther(connectionId);
|
|
1752
|
-
};
|
|
1753
|
-
this.getOthers = () => {
|
|
1754
|
-
return this._usersManager.getOthers();
|
|
1755
|
-
};
|
|
1756
|
-
this.getStorage = (type) => {
|
|
1757
|
-
const sharedType = this._crdtManager.get(type);
|
|
1758
|
-
if (typeof sharedType === "undefined") return null;
|
|
1759
|
-
return sharedType;
|
|
1760
|
-
};
|
|
1761
|
-
this.redo = () => {
|
|
1762
|
-
this._crdtManager.doc.redo();
|
|
1763
|
-
};
|
|
1764
|
-
this.storageRoot = (fn) => {
|
|
1765
|
-
return this._crdtNotifier.subcribeRoot(fn);
|
|
1766
|
-
};
|
|
1767
|
-
this.subscribe = new Proxy(
|
|
1768
|
-
(name, callback) => {
|
|
1769
|
-
return this._stateNotifier.subscribe(name, callback);
|
|
1770
|
-
},
|
|
1771
|
-
{
|
|
1772
|
-
get: (fn, prop) => {
|
|
1773
|
-
if (prop === "connection") {
|
|
1774
|
-
return (callback) => {
|
|
1775
|
-
return fn("connection", callback);
|
|
1776
|
-
};
|
|
1777
|
-
}
|
|
1778
|
-
if (prop === "myPresence") {
|
|
1779
|
-
return (callback) => {
|
|
1780
|
-
return fn("my-presence", callback);
|
|
1781
|
-
};
|
|
1782
|
-
}
|
|
1783
|
-
if (prop === "myself") {
|
|
1784
|
-
return (callback) => {
|
|
1785
|
-
return fn("myself", callback);
|
|
1786
|
-
};
|
|
1787
|
-
}
|
|
1788
|
-
if (prop === "others") {
|
|
1789
|
-
return (callback) => {
|
|
1790
|
-
return this._usersNotifier.subscribeOthers(callback);
|
|
1791
|
-
};
|
|
1792
|
-
}
|
|
1793
|
-
if (prop === "storageLoaded") {
|
|
1794
|
-
return (callback) => {
|
|
1795
|
-
return fn("storage-loaded", callback);
|
|
1796
|
-
};
|
|
1797
|
-
}
|
|
1798
|
-
if (prop === "event") return this._event;
|
|
1799
|
-
if (prop === "other") return this._other;
|
|
1800
|
-
if (prop === "storage") return __privateGet(this, __storage2);
|
|
1801
|
-
}
|
|
1802
|
-
}
|
|
1803
|
-
);
|
|
1804
|
-
this.transact = (fn, origin) => {
|
|
1805
|
-
const _origin = origin != null ? origin : this._state.connection.id;
|
|
1806
|
-
const crdtManager = this._crdtManager;
|
|
1807
|
-
if (typeof _origin !== "string") return;
|
|
1808
|
-
if (!crdtManager) return;
|
|
1809
|
-
crdtManager.doc.transact(() => {
|
|
1810
|
-
const storage = crdtManager.doc.get();
|
|
1811
|
-
fn(storage);
|
|
1812
|
-
}, _origin);
|
|
1813
|
-
};
|
|
1814
|
-
this.undo = () => {
|
|
1815
|
-
this._crdtManager.doc.undo();
|
|
1816
|
-
};
|
|
1817
|
-
this.updateMyPresence = (presence) => {
|
|
1818
|
-
var _a;
|
|
1819
|
-
const newPresence = typeof presence === "function" ? presence(this.getMyPresence()) : presence;
|
|
1820
|
-
this._usersManager.updateMyPresence(newPresence);
|
|
1821
|
-
const myPresence = this._usersManager.myPresence;
|
|
1822
|
-
const myself = (_a = this._usersManager.myself) != null ? _a : null;
|
|
1823
|
-
this._stateNotifier.subjects["my-presence"].next(myPresence);
|
|
1824
|
-
if (!!myself) this._stateNotifier.subjects["myself"].next(myself);
|
|
1825
|
-
};
|
|
1826
|
-
this._event = new Proxy(
|
|
1827
|
-
(event, callback) => this._eventNotifier.subscribe(event, callback),
|
|
1828
|
-
{
|
|
1829
|
-
get(fn, prop) {
|
|
1830
|
-
return (callback) => fn(prop, callback);
|
|
1831
|
-
}
|
|
1832
|
-
}
|
|
1833
|
-
);
|
|
1834
|
-
this._other = (connectionId, callback) => {
|
|
1835
|
-
const clientId = this._usersManager.getClientId(connectionId);
|
|
1836
|
-
if (!clientId) return () => void 0;
|
|
1837
|
-
return this._usersNotifier.subscribeOther(clientId, callback);
|
|
1838
|
-
};
|
|
1839
|
-
__privateAdd(this, __storage2, new Proxy(
|
|
1840
|
-
(key, callback) => this._crdtNotifier.subscribe(key, callback),
|
|
1841
|
-
{
|
|
1842
|
-
get: (fn, prop) => {
|
|
1843
|
-
if (!!prop) {
|
|
1844
|
-
return (callback) => {
|
|
1845
|
-
return this._crdtNotifier.subcribeRoot(callback);
|
|
1846
|
-
};
|
|
1847
|
-
}
|
|
1848
|
-
return (callback) => {
|
|
1849
|
-
return fn(prop, callback);
|
|
1850
|
-
};
|
|
1851
|
-
}
|
|
1852
|
-
}
|
|
1853
|
-
));
|
|
1854
|
-
const { events, initialPresence, initialStorage, limits, presence, router } = options;
|
|
1855
|
-
this.id = room;
|
|
1856
|
-
this._events = events;
|
|
1857
|
-
this._limits = __spreadValues({
|
|
1858
|
-
presenceMaxSize: MAX_PRESENCE_SIZE_BYTES
|
|
1859
|
-
}, limits);
|
|
1860
|
-
this._router = router != null ? router : new PluvRouter({});
|
|
1861
|
-
this._usersManager = new UsersManager({
|
|
1862
|
-
initialPresence,
|
|
1863
|
-
limits: this._limits,
|
|
1864
|
-
presence
|
|
1865
|
-
});
|
|
1866
|
-
this._crdtManager = new CrdtManager({
|
|
1867
|
-
initialStorage
|
|
1868
|
-
});
|
|
1869
|
-
this._observeCrdt();
|
|
1870
|
-
}
|
|
1871
|
-
addEventListener(kind, handler) {
|
|
1872
|
-
return () => void 0;
|
|
1873
|
-
}
|
|
1874
|
-
getDoc() {
|
|
1875
|
-
return this._crdtManager.doc;
|
|
1876
|
-
}
|
|
1877
|
-
getStorageJson(type) {
|
|
1878
|
-
if (this._state.connection.id === null) return null;
|
|
1879
|
-
if (typeof type === "undefined") return this._crdtManager.doc.toJson();
|
|
1880
|
-
return this._crdtManager.doc.toJson(type);
|
|
1881
|
-
}
|
|
1882
|
-
getStorageLoaded() {
|
|
1883
|
-
return true;
|
|
1884
|
-
}
|
|
1885
|
-
_observeCrdt() {
|
|
1886
|
-
var _a, _b;
|
|
1887
|
-
(_b = (_a = this._subscriptions).observeCrdt) == null ? void 0 : _b.call(_a);
|
|
1888
|
-
if (!this._crdtManager) return;
|
|
1889
|
-
const unsubscribe = this._crdtManager.doc.subscribe((event) => {
|
|
1890
|
-
var _a2;
|
|
1891
|
-
const origin = (_a2 = event.origin) != null ? _a2 : null;
|
|
1892
|
-
if (!this._crdtManager) return;
|
|
1893
|
-
if (origin === "$storageUpdated") return;
|
|
1894
|
-
const sharedTypes = this._crdtManager.doc.get();
|
|
1895
|
-
const storageRoot = Object.keys(sharedTypes).reduce(
|
|
1896
|
-
(acc, prop) => {
|
|
1897
|
-
if (!this._crdtManager) return acc;
|
|
1898
|
-
const serialized = this._crdtManager.doc.toJson(prop);
|
|
1899
|
-
this._crdtNotifier.subject(prop).next(serialized);
|
|
1900
|
-
return __spreadProps(__spreadValues({}, acc), { [prop]: serialized });
|
|
1901
|
-
},
|
|
1902
|
-
{}
|
|
1903
|
-
);
|
|
1904
|
-
this._crdtNotifier.rootSubject.next(storageRoot);
|
|
1905
|
-
});
|
|
1906
|
-
this._subscriptions.observeCrdt = unsubscribe;
|
|
1907
|
-
}
|
|
1908
|
-
_simulateEvent(event, data) {
|
|
1909
|
-
if (!this._events) return;
|
|
1910
|
-
const type = event.toString();
|
|
1911
|
-
const resolver = this._events[type];
|
|
1912
|
-
if (!resolver) return;
|
|
1913
|
-
const result = resolver(data);
|
|
1914
|
-
Object.keys(result).forEach((type2) => {
|
|
1915
|
-
const _type = type2.toString();
|
|
1916
|
-
const data2 = result[_type];
|
|
1917
|
-
if (!data2) return;
|
|
1918
|
-
this._eventNotifier.subject(_type).next(data2);
|
|
1919
|
-
});
|
|
1920
|
-
}
|
|
1921
|
-
};
|
|
1922
|
-
__storage2 = new WeakMap();
|
|
1923
|
-
|
|
1924
|
-
// src/register.ts
|
|
1925
|
-
var register = (params) => {
|
|
1926
|
-
const { authEndpoint, publicKey } = params;
|
|
1927
|
-
const wsEndpoint = ({ room }) => `wss://connect.pluv.io/api/room?room=${room}`;
|
|
1928
|
-
return {
|
|
1929
|
-
authEndpoint,
|
|
1930
|
-
publicKey,
|
|
1931
|
-
wsEndpoint
|
|
1932
|
-
};
|
|
1933
|
-
};
|
|
1934
|
-
// Annotate the CommonJS export names for ESM import in node:
|
|
1935
|
-
0 && (module.exports = {
|
|
1936
|
-
AbstractStorageStore,
|
|
1937
|
-
ConnectionState,
|
|
1938
|
-
MockedRoom,
|
|
1939
|
-
PluvClient,
|
|
1940
|
-
PluvRoom,
|
|
1941
|
-
createClient,
|
|
1942
|
-
infer,
|
|
1943
|
-
register
|
|
1944
|
-
});
|