@pluv/client 0.8.1 → 0.9.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 +8 -8
- package/CHANGELOG.md +50 -0
- package/dist/index.d.ts +29 -5
- package/dist/index.js +231 -96
- package/dist/index.mjs +230 -96
- package/package.json +10 -8
- package/src/AbstractStorageStore.ts +21 -0
- package/src/CrdtManager.ts +64 -0
- package/src/PluvClient.ts +9 -6
- package/src/PluvRoom.ts +160 -71
- package/src/StorageStore.ts +31 -0
- package/src/index.ts +7 -1
- package/src/utils/debounce.ts +27 -0
- package/src/utils/index.ts +1 -0
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
|
|
2
|
-
> @pluv/client@0.
|
|
2
|
+
> @pluv/client@0.9.0 build /home/runner/work/pluv/pluv/packages/client
|
|
3
3
|
> tsup src/index.ts --format esm,cjs --dts
|
|
4
4
|
|
|
5
5
|
[34mCLI[39m Building entry: src/index.ts
|
|
6
6
|
[34mCLI[39m Using tsconfig: tsconfig.json
|
|
7
|
-
[34mCLI[39m tsup
|
|
7
|
+
[34mCLI[39m tsup v7.0.0
|
|
8
8
|
[34mCLI[39m Target: es6
|
|
9
9
|
[34mESM[39m Build start
|
|
10
10
|
[34mCJS[39m Build start
|
|
11
|
-
[
|
|
12
|
-
[
|
|
13
|
-
[
|
|
14
|
-
[
|
|
11
|
+
[32mCJS[39m [1mdist/index.js [22m[32m40.23 KB[39m
|
|
12
|
+
[32mCJS[39m ⚡️ Build success in 98ms
|
|
13
|
+
[32mESM[39m [1mdist/index.mjs [22m[32m39.02 KB[39m
|
|
14
|
+
[32mESM[39m ⚡️ Build success in 100ms
|
|
15
15
|
[34mDTS[39m Build start
|
|
16
|
-
[32mDTS[39m ⚡️ Build success in
|
|
17
|
-
[32mDTS[39m [1mdist/index.d.ts [22m[
|
|
16
|
+
[32mDTS[39m ⚡️ Build success in 4765ms
|
|
17
|
+
[32mDTS[39m [1mdist/index.d.ts [22m[32m12.92 KB[39m
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,55 @@
|
|
|
1
1
|
# @pluv/client
|
|
2
2
|
|
|
3
|
+
## 0.9.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 829d31b: Added support for defining persistant frontend storage for rooms via a new `addons` option on rooms.
|
|
8
|
+
|
|
9
|
+
This also introduces the first new addon `@pluv/addon-indexeddb`, which is more-or-less the equivalent to `y-indexeddb` which you can install like so:
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
npm install @pluv/addon-indexeddb
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
To use this new addon, simply pass it to options when creating a room:
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
import { addonIndexedDB } from "@pluv/addon-indexeddb";
|
|
19
|
+
import { createClient } from "@pluv/client";
|
|
20
|
+
|
|
21
|
+
const client = createClient({
|
|
22
|
+
// ...
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
const room = client.createRoom("my-new-room", {
|
|
26
|
+
addons: [
|
|
27
|
+
// Define your addons in an array like so
|
|
28
|
+
addonIndexedDB(),
|
|
29
|
+
],
|
|
30
|
+
});
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Or when using `@pluv/react`:
|
|
34
|
+
|
|
35
|
+
```ts
|
|
36
|
+
const PluvRoom = createRoomBundle({
|
|
37
|
+
// ...
|
|
38
|
+
addons: [
|
|
39
|
+
// Define your addons in an array like so
|
|
40
|
+
addonIndexedDB(),
|
|
41
|
+
],
|
|
42
|
+
});
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### Patch Changes
|
|
46
|
+
|
|
47
|
+
- 8d11672: bumped dependencies to latest
|
|
48
|
+
- Updated dependencies [8d11672]
|
|
49
|
+
- Updated dependencies [829d31b]
|
|
50
|
+
- @pluv/crdt-yjs@0.4.0
|
|
51
|
+
- @pluv/types@0.2.1
|
|
52
|
+
|
|
3
53
|
## 0.8.1
|
|
4
54
|
|
|
5
55
|
### Patch Changes
|
package/dist/index.d.ts
CHANGED
|
@@ -64,6 +64,17 @@ declare abstract class AbstractRoom<TIO extends IOLike, TPresence extends JsonOb
|
|
|
64
64
|
abstract updateMyPresence(presence: Partial<TPresence>): void;
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
+
declare abstract class AbstractStorageStore {
|
|
68
|
+
room: string;
|
|
69
|
+
constructor(room: string);
|
|
70
|
+
abstract addUpdate(update: string): Promise<void>;
|
|
71
|
+
abstract destroy(): Promise<void>;
|
|
72
|
+
abstract flatten(encodedState: string): Promise<void>;
|
|
73
|
+
abstract getShouldFlatten(): Promise<boolean>;
|
|
74
|
+
abstract getUpdates(start?: number): Promise<readonly string[]>;
|
|
75
|
+
abstract initialize(): Promise<void>;
|
|
76
|
+
}
|
|
77
|
+
|
|
67
78
|
interface CrdtManagerOptions<TStorage extends Record<string, AbstractType<any>> = {}> {
|
|
68
79
|
encodedState?: string | Uint8Array | null;
|
|
69
80
|
initialStorage?: () => TStorage;
|
|
@@ -94,11 +105,19 @@ type RoomEndpoints<TIO extends IOLike> = {
|
|
|
94
105
|
} : {
|
|
95
106
|
authEndpoint?: undefined;
|
|
96
107
|
});
|
|
108
|
+
type PluvRoomAddon<TIO extends IOLike = IOLike, TPresence extends JsonObject = {}, TStorage extends Record<string, AbstractType<any>> = {}> = (input: PluvRoomAddonInput<TIO, TPresence, TStorage>) => Partial<PluvRoomAddonResult>;
|
|
109
|
+
interface PluvRoomAddonInput<TIO extends IOLike = IOLike, TPresence extends JsonObject = {}, TStorage extends Record<string, AbstractType<any>> = {}> {
|
|
110
|
+
room: PluvRoom<TIO, TPresence, TStorage>;
|
|
111
|
+
}
|
|
112
|
+
interface PluvRoomAddonResult {
|
|
113
|
+
storage: AbstractStorageStore;
|
|
114
|
+
}
|
|
97
115
|
type PluvRoomDebug<TIO extends IOLike> = Id<{
|
|
98
116
|
output: readonly (keyof InferIOOutput<TIO>)[];
|
|
99
117
|
input: readonly (keyof InferIOInput<TIO>)[];
|
|
100
118
|
}>;
|
|
101
119
|
type PluvRoomOptions<TIO extends IOLike, TPresence extends JsonObject = {}, TStorage extends Record<string, AbstractType<any>> = {}> = {
|
|
120
|
+
addons?: readonly PluvRoomAddon<TIO, TPresence, TStorage>[];
|
|
102
121
|
debug?: boolean | PluvRoomDebug<TIO>;
|
|
103
122
|
onAuthorizationFail?: (error: Error) => void;
|
|
104
123
|
} & Omit<CrdtManagerOptions<TStorage>, "encodedState"> & UsersManagerConfig<TPresence>;
|
|
@@ -109,12 +128,12 @@ declare class PluvRoom<TIO extends IOLike, TPresence extends JsonObject = {}, TS
|
|
|
109
128
|
private _crdtNotifier;
|
|
110
129
|
private _debug;
|
|
111
130
|
private _eventNotifier;
|
|
112
|
-
private _initialStorage;
|
|
113
131
|
private _intervals;
|
|
114
132
|
private _listeners;
|
|
115
133
|
private _otherNotifier;
|
|
116
134
|
private _state;
|
|
117
135
|
private _stateNotifier;
|
|
136
|
+
private _storageStore;
|
|
118
137
|
private _subscriptions;
|
|
119
138
|
private _timeouts;
|
|
120
139
|
private _windowListeners;
|
|
@@ -124,7 +143,7 @@ declare class PluvRoom<TIO extends IOLike, TPresence extends JsonObject = {}, TS
|
|
|
124
143
|
get webSocket(): WebSocket | null;
|
|
125
144
|
broadcast<TEvent extends keyof InferIOInput<TIO>>(event: TEvent, data: Id<InferIOInput<TIO>[TEvent]>): void;
|
|
126
145
|
connect(): Promise<void>;
|
|
127
|
-
disconnect(): void
|
|
146
|
+
disconnect(): Promise<void>;
|
|
128
147
|
event: <TEvent extends keyof InferIOOutput<TIO>>(event: TEvent, callback: EventNotifierSubscriptionCallback<TIO, TEvent>) => (() => void);
|
|
129
148
|
getConnection(): WebSocketConnection;
|
|
130
149
|
getMyPresence: () => TPresence;
|
|
@@ -136,6 +155,8 @@ declare class PluvRoom<TIO extends IOLike, TPresence extends JsonObject = {}, TS
|
|
|
136
155
|
storage: <TKey extends keyof TStorage>(key: TKey, fn: (value: InferYjsSharedTypeJson<TStorage[TKey]>) => void) => (() => void);
|
|
137
156
|
subscribe: <TSubject extends keyof StateNotifierSubjects<TIO, TPresence>>(name: TSubject, callback: SubscriptionCallback<TIO, TPresence, TSubject>) => (() => void);
|
|
138
157
|
updateMyPresence: (presence: Partial<TPresence>) => void;
|
|
158
|
+
private _addToStorageStore;
|
|
159
|
+
private _applyStorageStore;
|
|
139
160
|
private _attachWindowListeners;
|
|
140
161
|
private _attachWsListeners;
|
|
141
162
|
private _clearInterval;
|
|
@@ -143,8 +164,11 @@ declare class PluvRoom<TIO extends IOLike, TPresence extends JsonObject = {}, TS
|
|
|
143
164
|
private _closeWs;
|
|
144
165
|
private _detachWindowListeners;
|
|
145
166
|
private _detachWsListeners;
|
|
146
|
-
private
|
|
167
|
+
private _emitSharedTypes;
|
|
168
|
+
private _flattenStorageStore;
|
|
169
|
+
private _getAddon;
|
|
147
170
|
private _getAuthFetchOptions;
|
|
171
|
+
private _getAuthorization;
|
|
148
172
|
private _getWsEndpoint;
|
|
149
173
|
private _handleExit;
|
|
150
174
|
private _handlePresenceUpdatedMessage;
|
|
@@ -195,7 +219,7 @@ declare class PluvClient<TIO extends IOLike = IOLike> {
|
|
|
195
219
|
enter: (room: string | PluvRoom<TIO, any, any>) => Promise<PluvRoom<TIO, JsonObject, any>>;
|
|
196
220
|
getRoom: <TPresence extends JsonObject = {}, TStorage extends Record<string, AbstractType<any>> = {}>(room: string) => PluvRoom<TIO, TPresence, TStorage> | null;
|
|
197
221
|
getRooms: () => readonly PluvRoom<TIO, JsonObject, any>[];
|
|
198
|
-
leave: (room: string | PluvRoom<TIO, any, any>) => void
|
|
222
|
+
leave: (room: string | PluvRoom<TIO, any, any>) => Promise<void>;
|
|
199
223
|
private _logDebug;
|
|
200
224
|
}
|
|
201
225
|
|
|
@@ -246,4 +270,4 @@ declare namespace y {
|
|
|
246
270
|
};
|
|
247
271
|
}
|
|
248
272
|
|
|
249
|
-
export { AbstractRoom, ConnectionState, MockedRoom, MockedRoomConfig, MockedRoomEvents, PluvClient, PluvClientOptions, PluvRoom, PluvRoomDebug, UserInfo, WebSocketConnection, WebSocketState, createClient, y };
|
|
273
|
+
export { AbstractRoom, AbstractStorageStore, ConnectionState, MockedRoom, MockedRoomConfig, MockedRoomEvents, PluvClient, PluvClientOptions, PluvRoom, PluvRoomAddon, PluvRoomAddonInput, PluvRoomAddonResult, PluvRoomDebug, UserInfo, WebSocketConnection, WebSocketState, createClient, y };
|
package/dist/index.js
CHANGED
|
@@ -58,6 +58,7 @@ var __async = (__this, __arguments, generator) => {
|
|
|
58
58
|
var src_exports = {};
|
|
59
59
|
__export(src_exports, {
|
|
60
60
|
AbstractRoom: () => AbstractRoom,
|
|
61
|
+
AbstractStorageStore: () => AbstractStorageStore,
|
|
61
62
|
ConnectionState: () => ConnectionState,
|
|
62
63
|
MockedRoom: () => MockedRoom,
|
|
63
64
|
PluvClient: () => PluvClient,
|
|
@@ -74,10 +75,18 @@ var AbstractRoom = class {
|
|
|
74
75
|
}
|
|
75
76
|
};
|
|
76
77
|
|
|
78
|
+
// src/AbstractStorageStore.ts
|
|
79
|
+
var AbstractStorageStore = class {
|
|
80
|
+
constructor(room) {
|
|
81
|
+
this.room = room;
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
|
|
77
85
|
// src/CrdtManager.ts
|
|
78
86
|
var import_crdt_yjs = require("@pluv/crdt-yjs");
|
|
79
87
|
var CrdtManager = class {
|
|
80
88
|
constructor(options = {}) {
|
|
89
|
+
this.initialized = false;
|
|
81
90
|
const { encodedState, initialStorage } = options;
|
|
82
91
|
const _doc = encodedState ? (0, import_crdt_yjs.doc)().applyUpdate(encodedState) : null;
|
|
83
92
|
if (_doc && !!Object.keys(_doc.toJSON()).length) {
|
|
@@ -87,12 +96,50 @@ var CrdtManager = class {
|
|
|
87
96
|
_doc == null ? void 0 : _doc.destroy();
|
|
88
97
|
this.doc = (0, import_crdt_yjs.doc)(initialStorage == null ? void 0 : initialStorage());
|
|
89
98
|
}
|
|
99
|
+
/**
|
|
100
|
+
* !HACK
|
|
101
|
+
* @description If this is called before the doc is initialized. Omit the
|
|
102
|
+
* update. This unfortunately means updates can be dropped entirely
|
|
103
|
+
* @date June 13, 2023
|
|
104
|
+
*/
|
|
105
|
+
applyUpdate(update, origin) {
|
|
106
|
+
if (!this.initialized)
|
|
107
|
+
return this;
|
|
108
|
+
const updates = typeof update === "string" ? [update] : update;
|
|
109
|
+
this._applyDocUpdates(this.doc, updates, origin);
|
|
110
|
+
return this;
|
|
111
|
+
}
|
|
90
112
|
destroy() {
|
|
91
113
|
this.doc.destroy();
|
|
92
114
|
}
|
|
93
115
|
get(key) {
|
|
94
116
|
return this.doc.get(key);
|
|
95
117
|
}
|
|
118
|
+
initialize(params) {
|
|
119
|
+
const { onInitialized, origin, update } = params;
|
|
120
|
+
const updates = typeof update === "string" ? [update] : update;
|
|
121
|
+
if (!updates.length)
|
|
122
|
+
return this;
|
|
123
|
+
if (this.initialized) {
|
|
124
|
+
this._applyDocUpdates(this.doc, updates, origin);
|
|
125
|
+
return this;
|
|
126
|
+
}
|
|
127
|
+
const _doc = this._applyDocUpdates((0, import_crdt_yjs.doc)(), updates, origin);
|
|
128
|
+
this.initialized = true;
|
|
129
|
+
if (!!Object.keys(_doc.toJSON()).length) {
|
|
130
|
+
this.doc = _doc;
|
|
131
|
+
} else {
|
|
132
|
+
_doc.destroy();
|
|
133
|
+
}
|
|
134
|
+
onInitialized == null ? void 0 : onInitialized();
|
|
135
|
+
return this;
|
|
136
|
+
}
|
|
137
|
+
_applyDocUpdates(yjsDoc, updates, origin) {
|
|
138
|
+
yjsDoc.transact(() => {
|
|
139
|
+
updates.reduce((acc, _update) => acc.applyUpdate(_update), yjsDoc);
|
|
140
|
+
}, origin);
|
|
141
|
+
return yjsDoc;
|
|
142
|
+
}
|
|
96
143
|
};
|
|
97
144
|
|
|
98
145
|
// src/CrdtNotifier.ts
|
|
@@ -176,6 +223,31 @@ var StateNotifier = class {
|
|
|
176
223
|
}
|
|
177
224
|
};
|
|
178
225
|
|
|
226
|
+
// src/StorageStore.ts
|
|
227
|
+
var StorageStore = class extends AbstractStorageStore {
|
|
228
|
+
addUpdate() {
|
|
229
|
+
return Promise.resolve();
|
|
230
|
+
}
|
|
231
|
+
applyUpdates() {
|
|
232
|
+
return Promise.resolve();
|
|
233
|
+
}
|
|
234
|
+
destroy() {
|
|
235
|
+
return Promise.resolve();
|
|
236
|
+
}
|
|
237
|
+
flatten() {
|
|
238
|
+
return Promise.resolve();
|
|
239
|
+
}
|
|
240
|
+
getShouldFlatten() {
|
|
241
|
+
return Promise.resolve(false);
|
|
242
|
+
}
|
|
243
|
+
getUpdates() {
|
|
244
|
+
return Promise.resolve([]);
|
|
245
|
+
}
|
|
246
|
+
initialize() {
|
|
247
|
+
return Promise.resolve();
|
|
248
|
+
}
|
|
249
|
+
};
|
|
250
|
+
|
|
179
251
|
// src/types.ts
|
|
180
252
|
var ConnectionState = /* @__PURE__ */ ((ConnectionState2) => {
|
|
181
253
|
ConnectionState2["Closed"] = "Closed";
|
|
@@ -281,13 +353,40 @@ var UsersManager = class {
|
|
|
281
353
|
}
|
|
282
354
|
};
|
|
283
355
|
|
|
356
|
+
// src/utils/debounce.ts
|
|
357
|
+
var debounce = (fn, options) => {
|
|
358
|
+
const { wait, immediate = false } = options != null ? options : {};
|
|
359
|
+
let prevResult;
|
|
360
|
+
let timeout = null;
|
|
361
|
+
return (...args) => {
|
|
362
|
+
if (typeof timeout === "number")
|
|
363
|
+
clearTimeout(timeout);
|
|
364
|
+
timeout = setTimeout(() => {
|
|
365
|
+
timeout = null;
|
|
366
|
+
if (!immediate)
|
|
367
|
+
prevResult = fn(args);
|
|
368
|
+
}, wait);
|
|
369
|
+
if (immediate && !timeout) {
|
|
370
|
+
fn(args);
|
|
371
|
+
return;
|
|
372
|
+
}
|
|
373
|
+
return prevResult;
|
|
374
|
+
};
|
|
375
|
+
};
|
|
376
|
+
|
|
284
377
|
// src/PluvRoom.ts
|
|
378
|
+
var ADD_TO_STORAGE_STATE_DEBOUNCE_MS = 1e3;
|
|
285
379
|
var HEARTBEAT_INTERVAL_MS = 1e4;
|
|
286
380
|
var PONG_TIMEOUT_MS = 2e3;
|
|
287
381
|
var RECONNECT_TIMEOUT_MS = 3e4;
|
|
382
|
+
var Y_ORIGIN_INITIALIZED = "$INITIALIZED";
|
|
383
|
+
var DEFAULT_PLUV_CLIENT_ADDON = (input) => ({
|
|
384
|
+
storage: new StorageStore(input.room.id)
|
|
385
|
+
});
|
|
288
386
|
var PluvRoom = class extends AbstractRoom {
|
|
289
387
|
constructor(room, options) {
|
|
290
388
|
const {
|
|
389
|
+
addons = [],
|
|
291
390
|
authEndpoint,
|
|
292
391
|
debug = false,
|
|
293
392
|
initialPresence,
|
|
@@ -297,10 +396,8 @@ var PluvRoom = class extends AbstractRoom {
|
|
|
297
396
|
wsEndpoint
|
|
298
397
|
} = options;
|
|
299
398
|
super(room);
|
|
300
|
-
this._crdtManager = null;
|
|
301
399
|
this._crdtNotifier = new CrdtNotifier();
|
|
302
400
|
this._eventNotifier = new EventNotifier();
|
|
303
|
-
this._initialStorage = null;
|
|
304
401
|
this._intervals = {
|
|
305
402
|
heartbeat: null
|
|
306
403
|
};
|
|
@@ -367,6 +464,27 @@ var PluvRoom = class extends AbstractRoom {
|
|
|
367
464
|
{ presence }
|
|
368
465
|
);
|
|
369
466
|
};
|
|
467
|
+
this._flattenStorageStore = debounce(
|
|
468
|
+
() => __async(this, null, function* () {
|
|
469
|
+
const shouldFlatten = yield this._storageStore.getShouldFlatten();
|
|
470
|
+
if (!shouldFlatten)
|
|
471
|
+
return;
|
|
472
|
+
const updates = yield this._storageStore.getUpdates();
|
|
473
|
+
this._crdtManager.applyUpdate(updates, this);
|
|
474
|
+
const encodedState = this._crdtManager.doc.encodeStateAsUpdate();
|
|
475
|
+
yield this._storageStore.flatten(encodedState);
|
|
476
|
+
}),
|
|
477
|
+
{ wait: ADD_TO_STORAGE_STATE_DEBOUNCE_MS }
|
|
478
|
+
);
|
|
479
|
+
this._getAddon = (addons) => {
|
|
480
|
+
return addons.reduce(
|
|
481
|
+
(acc, addon) => () => __spreadValues(__spreadValues({}, acc({ room: this })), addon({ room: this })),
|
|
482
|
+
DEFAULT_PLUV_CLIENT_ADDON
|
|
483
|
+
);
|
|
484
|
+
};
|
|
485
|
+
const addon = this._getAddon(addons);
|
|
486
|
+
const { storage } = __spreadValues(__spreadValues({}, DEFAULT_PLUV_CLIENT_ADDON({ room: this })), addon({ room: this }));
|
|
487
|
+
this._storageStore = storage;
|
|
370
488
|
this._debug = debug;
|
|
371
489
|
this._endpoints = { authEndpoint, wsEndpoint };
|
|
372
490
|
this._listeners = {
|
|
@@ -383,8 +501,7 @@ var PluvRoom = class extends AbstractRoom {
|
|
|
383
501
|
initialPresence,
|
|
384
502
|
presence
|
|
385
503
|
});
|
|
386
|
-
|
|
387
|
-
this._initialStorage = initialStorage;
|
|
504
|
+
this._crdtManager = new CrdtManager({ initialStorage });
|
|
388
505
|
}
|
|
389
506
|
get webSocket() {
|
|
390
507
|
return this._state.webSocket;
|
|
@@ -413,19 +530,17 @@ var PluvRoom = class extends AbstractRoom {
|
|
|
413
530
|
oldState.connection.state = "Connecting" /* Connecting */;
|
|
414
531
|
return oldState;
|
|
415
532
|
});
|
|
533
|
+
yield this._storageStore.initialize();
|
|
534
|
+
yield this._applyStorageStore();
|
|
416
535
|
const url = new URL(this._getWsEndpoint(this.id));
|
|
417
536
|
let authToken = null;
|
|
418
|
-
try {
|
|
419
|
-
authToken = yield this._getAuthorization(this.id);
|
|
420
|
-
} catch (err) {
|
|
421
|
-
this._onAuthorizationFail(err);
|
|
422
|
-
return;
|
|
423
|
-
}
|
|
424
|
-
authToken && url.searchParams.set("token", encodeURIComponent(authToken));
|
|
425
537
|
let webSocket;
|
|
426
538
|
try {
|
|
539
|
+
authToken = yield this._getAuthorization(this.id);
|
|
540
|
+
authToken && url.searchParams.set("token", encodeURIComponent(authToken));
|
|
427
541
|
webSocket = new WebSocket(url.toString());
|
|
428
542
|
} catch (err) {
|
|
543
|
+
yield this._storageStore.destroy();
|
|
429
544
|
this._onAuthorizationFail(err);
|
|
430
545
|
return;
|
|
431
546
|
}
|
|
@@ -439,27 +554,24 @@ var PluvRoom = class extends AbstractRoom {
|
|
|
439
554
|
});
|
|
440
555
|
}
|
|
441
556
|
disconnect() {
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
oldState.connection.state = "Closed" /* Closed */;
|
|
461
|
-
oldState.webSocket = null;
|
|
462
|
-
return oldState;
|
|
557
|
+
return __async(this, null, function* () {
|
|
558
|
+
var _a;
|
|
559
|
+
if (!this._endpoints.wsEndpoint) {
|
|
560
|
+
throw new Error("Must provide an wsEndpoint.");
|
|
561
|
+
}
|
|
562
|
+
if (!this._state.webSocket)
|
|
563
|
+
return;
|
|
564
|
+
const canDisconnect = [
|
|
565
|
+
"Connecting" /* Connecting */,
|
|
566
|
+
"Open" /* Open */
|
|
567
|
+
].some((state) => this._state.connection.state === state);
|
|
568
|
+
if (!canDisconnect)
|
|
569
|
+
return;
|
|
570
|
+
this._clearInterval(this._intervals.heartbeat);
|
|
571
|
+
this._closeWs();
|
|
572
|
+
yield this._storageStore.destroy();
|
|
573
|
+
(_a = this._crdtManager) == null ? void 0 : _a.destroy();
|
|
574
|
+
this._stateNotifier.subjects["my-presence"].next(null);
|
|
463
575
|
});
|
|
464
576
|
}
|
|
465
577
|
getConnection() {
|
|
@@ -467,6 +579,25 @@ var PluvRoom = class extends AbstractRoom {
|
|
|
467
579
|
JSON.parse(JSON.stringify(this._state.connection))
|
|
468
580
|
);
|
|
469
581
|
}
|
|
582
|
+
_addToStorageStore(update) {
|
|
583
|
+
return __async(this, null, function* () {
|
|
584
|
+
yield this._storageStore.addUpdate(update);
|
|
585
|
+
this._flattenStorageStore();
|
|
586
|
+
});
|
|
587
|
+
}
|
|
588
|
+
_applyStorageStore() {
|
|
589
|
+
return __async(this, null, function* () {
|
|
590
|
+
const updates = yield this._storageStore.getUpdates();
|
|
591
|
+
this._crdtManager.initialize({
|
|
592
|
+
onInitialized: () => {
|
|
593
|
+
this._observeCrdt();
|
|
594
|
+
},
|
|
595
|
+
origin: Y_ORIGIN_INITIALIZED,
|
|
596
|
+
update: updates
|
|
597
|
+
});
|
|
598
|
+
this._emitSharedTypes();
|
|
599
|
+
});
|
|
600
|
+
}
|
|
470
601
|
_attachWindowListeners() {
|
|
471
602
|
if (typeof window === "undefined")
|
|
472
603
|
return;
|
|
@@ -539,7 +670,6 @@ var PluvRoom = class extends AbstractRoom {
|
|
|
539
670
|
if (canClose)
|
|
540
671
|
this._state.webSocket.close();
|
|
541
672
|
this._detachWsListeners();
|
|
542
|
-
this._stateNotifier.subjects["myself"].next(null);
|
|
543
673
|
}
|
|
544
674
|
_detachWindowListeners() {
|
|
545
675
|
if (!this._windowListeners)
|
|
@@ -579,23 +709,11 @@ var PluvRoom = class extends AbstractRoom {
|
|
|
579
709
|
);
|
|
580
710
|
this._wsListeners = null;
|
|
581
711
|
}
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
const { url, options } = fetchOptions;
|
|
588
|
-
const res = yield fetch(url, options);
|
|
589
|
-
if (!res.ok || res.status !== 200) {
|
|
590
|
-
throw new Error("Room is unauthorized");
|
|
591
|
-
}
|
|
592
|
-
try {
|
|
593
|
-
return yield res.text().then((text2) => text2.trim());
|
|
594
|
-
} catch (err) {
|
|
595
|
-
throw new Error(
|
|
596
|
-
err instanceof Error ? err.message : "Room is unauthorized"
|
|
597
|
-
);
|
|
598
|
-
}
|
|
712
|
+
_emitSharedTypes() {
|
|
713
|
+
const sharedTypes = this._crdtManager.doc.getSharedTypes();
|
|
714
|
+
Object.entries(sharedTypes).forEach(([prop, sharedType]) => {
|
|
715
|
+
const serialized = sharedType.toJSON();
|
|
716
|
+
this._crdtNotifier.subject(prop).next(serialized);
|
|
599
717
|
});
|
|
600
718
|
}
|
|
601
719
|
_getAuthFetchOptions(room) {
|
|
@@ -616,6 +734,25 @@ var PluvRoom = class extends AbstractRoom {
|
|
|
616
734
|
const result = this._endpoints.authEndpoint(room);
|
|
617
735
|
return typeof result === "string" ? { url: result, options: {} } : result;
|
|
618
736
|
}
|
|
737
|
+
_getAuthorization(room) {
|
|
738
|
+
return __async(this, null, function* () {
|
|
739
|
+
const fetchOptions = this._getAuthFetchOptions(room);
|
|
740
|
+
if (!fetchOptions)
|
|
741
|
+
return null;
|
|
742
|
+
const { url, options } = fetchOptions;
|
|
743
|
+
const res = yield fetch(url, options);
|
|
744
|
+
if (!res.ok || res.status !== 200) {
|
|
745
|
+
throw new Error("Room is unauthorized");
|
|
746
|
+
}
|
|
747
|
+
try {
|
|
748
|
+
return yield res.text().then((text2) => text2.trim());
|
|
749
|
+
} catch (err) {
|
|
750
|
+
throw new Error(
|
|
751
|
+
err instanceof Error ? err.message : "Room is unauthorized"
|
|
752
|
+
);
|
|
753
|
+
}
|
|
754
|
+
});
|
|
755
|
+
}
|
|
619
756
|
_getWsEndpoint(room) {
|
|
620
757
|
switch (typeof this._endpoints.wsEndpoint) {
|
|
621
758
|
case "undefined":
|
|
@@ -711,28 +848,28 @@ var PluvRoom = class extends AbstractRoom {
|
|
|
711
848
|
});
|
|
712
849
|
}
|
|
713
850
|
_handleStorageReceivedMessage(message) {
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
const
|
|
729
|
-
|
|
730
|
-
this.
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
851
|
+
return __async(this, null, function* () {
|
|
852
|
+
const { connectionId } = message;
|
|
853
|
+
if (!connectionId)
|
|
854
|
+
return;
|
|
855
|
+
if (!this._state.webSocket)
|
|
856
|
+
throw new Error("Could not find WebSocket");
|
|
857
|
+
const data = message.data;
|
|
858
|
+
this._crdtManager.initialize({
|
|
859
|
+
onInitialized: () => __async(this, null, function* () {
|
|
860
|
+
this._observeCrdt();
|
|
861
|
+
}),
|
|
862
|
+
origin: Y_ORIGIN_INITIALIZED,
|
|
863
|
+
update: data.state
|
|
864
|
+
});
|
|
865
|
+
const encodedState = this._crdtManager.doc.encodeStateAsUpdate();
|
|
866
|
+
yield this._storageStore.addUpdate(encodedState);
|
|
867
|
+
this._emitSharedTypes();
|
|
868
|
+
const update = this._crdtManager.doc.encodeStateAsUpdate();
|
|
869
|
+
this._sendMessage({
|
|
870
|
+
type: "$UPDATE_STORAGE",
|
|
871
|
+
data: { origin: Y_ORIGIN_INITIALIZED, update }
|
|
872
|
+
});
|
|
736
873
|
});
|
|
737
874
|
}
|
|
738
875
|
_handleStorageUpdatedMessage(message) {
|
|
@@ -746,14 +883,11 @@ var PluvRoom = class extends AbstractRoom {
|
|
|
746
883
|
return;
|
|
747
884
|
this._crdtManager.doc.applyUpdate(data.state, "$STORAGE_UPDATED");
|
|
748
885
|
const sharedTypes = this._crdtManager.doc.getSharedTypes();
|
|
749
|
-
Object.
|
|
886
|
+
Object.entries(sharedTypes).forEach(([prop, sharedType]) => {
|
|
750
887
|
if (!this._crdtManager)
|
|
751
888
|
return;
|
|
752
|
-
const sharedType = sharedTypes[key];
|
|
753
|
-
if (!sharedType)
|
|
754
|
-
return;
|
|
755
889
|
const serialized = sharedType.toJSON();
|
|
756
|
-
this._crdtNotifier.subject(
|
|
890
|
+
this._crdtNotifier.subject(prop).next(serialized);
|
|
757
891
|
});
|
|
758
892
|
}
|
|
759
893
|
_handleUserJoinedMessage(message) {
|
|
@@ -801,7 +935,9 @@ var PluvRoom = class extends AbstractRoom {
|
|
|
801
935
|
const unsubscribe = this._crdtManager.doc.subscribe(
|
|
802
936
|
(update, _origin) => {
|
|
803
937
|
const origin = _origin != null ? _origin : null;
|
|
804
|
-
|
|
938
|
+
this._addToStorageStore(update);
|
|
939
|
+
this._emitSharedTypes();
|
|
940
|
+
if (origin === Y_ORIGIN_INITIALIZED || origin === this)
|
|
805
941
|
return;
|
|
806
942
|
if (!this._state.webSocket)
|
|
807
943
|
return;
|
|
@@ -811,13 +947,6 @@ var PluvRoom = class extends AbstractRoom {
|
|
|
811
947
|
return;
|
|
812
948
|
if (origin === "$STORAGE_UPDATED")
|
|
813
949
|
return;
|
|
814
|
-
const sharedTypes = this._crdtManager.doc.getSharedTypes();
|
|
815
|
-
Object.entries(sharedTypes).forEach(([prop, sharedType]) => {
|
|
816
|
-
if (!this._crdtManager)
|
|
817
|
-
return;
|
|
818
|
-
const serialized = sharedType.toJSON();
|
|
819
|
-
this._crdtNotifier.subject(prop).next(serialized);
|
|
820
|
-
});
|
|
821
950
|
this._sendMessage({
|
|
822
951
|
type: "$UPDATE_STORAGE",
|
|
823
952
|
data: { origin, update }
|
|
@@ -842,14 +971,18 @@ var PluvRoom = class extends AbstractRoom {
|
|
|
842
971
|
* @date August 19, 2022
|
|
843
972
|
*/
|
|
844
973
|
_onClose(event) {
|
|
845
|
-
this
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
oldState
|
|
852
|
-
|
|
974
|
+
return __async(this, null, function* () {
|
|
975
|
+
this._logDebug("WebSocket closed");
|
|
976
|
+
!!event.reason && this._logDebug(event.reason);
|
|
977
|
+
this._clearInterval(this._intervals.heartbeat);
|
|
978
|
+
this._clearTimeout(this._timeouts.pong);
|
|
979
|
+
this._stateNotifier.subjects["myself"].next(null);
|
|
980
|
+
this._updateState((oldState) => {
|
|
981
|
+
oldState.authorization.token = null;
|
|
982
|
+
oldState.connection.state = "Closed" /* Closed */;
|
|
983
|
+
oldState.webSocket = null;
|
|
984
|
+
return oldState;
|
|
985
|
+
});
|
|
853
986
|
});
|
|
854
987
|
}
|
|
855
988
|
/**
|
|
@@ -1022,6 +1155,7 @@ var PluvClient = class {
|
|
|
1022
1155
|
if (oldRoom)
|
|
1023
1156
|
return oldRoom;
|
|
1024
1157
|
const newRoom = new PluvRoom(room, {
|
|
1158
|
+
addons: options.addons,
|
|
1025
1159
|
authEndpoint: this._authEndpoint,
|
|
1026
1160
|
debug: options.debug,
|
|
1027
1161
|
initialPresence: options.initialPresence,
|
|
@@ -1050,14 +1184,14 @@ var PluvClient = class {
|
|
|
1050
1184
|
this.getRooms = () => {
|
|
1051
1185
|
return Array.from(this._rooms.values());
|
|
1052
1186
|
};
|
|
1053
|
-
this.leave = (room) => {
|
|
1187
|
+
this.leave = (room) => __async(this, null, function* () {
|
|
1054
1188
|
const toLeave = typeof room === "string" ? this.getRoom(room) : room;
|
|
1055
1189
|
if (!toLeave)
|
|
1056
1190
|
return;
|
|
1057
|
-
toLeave.disconnect();
|
|
1191
|
+
yield toLeave.disconnect();
|
|
1058
1192
|
this._rooms.delete(toLeave.id);
|
|
1059
1193
|
this._logDebug(`Left and deleted room: ${room}`);
|
|
1060
|
-
};
|
|
1194
|
+
});
|
|
1061
1195
|
const { authEndpoint, debug = false, wsEndpoint } = options;
|
|
1062
1196
|
this._authEndpoint = authEndpoint;
|
|
1063
1197
|
this._debug = debug;
|
|
@@ -1204,6 +1338,7 @@ var import_crdt_yjs2 = require("@pluv/crdt-yjs");
|
|
|
1204
1338
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1205
1339
|
0 && (module.exports = {
|
|
1206
1340
|
AbstractRoom,
|
|
1341
|
+
AbstractStorageStore,
|
|
1207
1342
|
ConnectionState,
|
|
1208
1343
|
MockedRoom,
|
|
1209
1344
|
PluvClient,
|