@abraca/dabra 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/hocuspocus-provider.cjs +3237 -0
- package/dist/hocuspocus-provider.cjs.map +1 -0
- package/dist/hocuspocus-provider.esm.js +3199 -0
- package/dist/hocuspocus-provider.esm.js.map +1 -0
- package/dist/index.d.ts +784 -0
- package/package.json +42 -0
- package/src/AbracadabraProvider.ts +381 -0
- package/src/CryptoIdentityKeystore.ts +294 -0
- package/src/EventEmitter.ts +44 -0
- package/src/HocuspocusProvider.ts +603 -0
- package/src/HocuspocusProviderWebsocket.ts +533 -0
- package/src/IncomingMessage.ts +63 -0
- package/src/MessageReceiver.ts +139 -0
- package/src/MessageSender.ts +22 -0
- package/src/OfflineStore.ts +185 -0
- package/src/OutgoingMessage.ts +25 -0
- package/src/OutgoingMessages/AuthenticationMessage.ts +25 -0
- package/src/OutgoingMessages/AwarenessMessage.ts +41 -0
- package/src/OutgoingMessages/CloseMessage.ts +17 -0
- package/src/OutgoingMessages/QueryAwarenessMessage.ts +17 -0
- package/src/OutgoingMessages/StatelessMessage.ts +18 -0
- package/src/OutgoingMessages/SubdocMessage.ts +35 -0
- package/src/OutgoingMessages/SyncStepOneMessage.ts +25 -0
- package/src/OutgoingMessages/SyncStepTwoMessage.ts +25 -0
- package/src/OutgoingMessages/UpdateMessage.ts +20 -0
- package/src/index.ts +7 -0
- package/src/types.ts +144 -0
|
@@ -0,0 +1,603 @@
|
|
|
1
|
+
import { awarenessStatesToArray } from "@hocuspocus/common";
|
|
2
|
+
import type { Event, MessageEvent } from "ws";
|
|
3
|
+
import { Awareness, removeAwarenessStates } from "y-protocols/awareness";
|
|
4
|
+
import * as Y from "yjs";
|
|
5
|
+
import EventEmitter from "./EventEmitter.ts";
|
|
6
|
+
import type { CompleteHocuspocusProviderWebsocketConfiguration } from "./HocuspocusProviderWebsocket.ts";
|
|
7
|
+
import { HocuspocusProviderWebsocket } from "./HocuspocusProviderWebsocket.ts";
|
|
8
|
+
import { IncomingMessage } from "./IncomingMessage.ts";
|
|
9
|
+
import { MessageReceiver } from "./MessageReceiver.ts";
|
|
10
|
+
import { MessageSender } from "./MessageSender.ts";
|
|
11
|
+
import { AuthenticationMessage } from "./OutgoingMessages/AuthenticationMessage.ts";
|
|
12
|
+
import { AwarenessMessage } from "./OutgoingMessages/AwarenessMessage.ts";
|
|
13
|
+
import { StatelessMessage } from "./OutgoingMessages/StatelessMessage.ts";
|
|
14
|
+
import { SyncStepOneMessage } from "./OutgoingMessages/SyncStepOneMessage.ts";
|
|
15
|
+
import { UpdateMessage } from "./OutgoingMessages/UpdateMessage.ts";
|
|
16
|
+
import type {
|
|
17
|
+
AuthorizedScope,
|
|
18
|
+
ConstructableOutgoingMessage,
|
|
19
|
+
onAuthenticatedParameters,
|
|
20
|
+
onAuthenticationFailedParameters,
|
|
21
|
+
onAwarenessChangeParameters,
|
|
22
|
+
onAwarenessUpdateParameters,
|
|
23
|
+
onCloseParameters,
|
|
24
|
+
onDisconnectParameters,
|
|
25
|
+
onMessageParameters,
|
|
26
|
+
onOpenParameters,
|
|
27
|
+
onOutgoingMessageParameters,
|
|
28
|
+
onStatelessParameters,
|
|
29
|
+
onStatusParameters,
|
|
30
|
+
onSyncedParameters,
|
|
31
|
+
onUnsyncedChangesParameters,
|
|
32
|
+
} from "./types.ts";
|
|
33
|
+
|
|
34
|
+
export type HocuspocusProviderConfiguration = Required<
|
|
35
|
+
Pick<CompleteHocuspocusProviderConfiguration, "name">
|
|
36
|
+
> &
|
|
37
|
+
Partial<CompleteHocuspocusProviderConfiguration> &
|
|
38
|
+
(
|
|
39
|
+
| (Required<Pick<CompleteHocuspocusProviderWebsocketConfiguration, "url">> &
|
|
40
|
+
Partial<
|
|
41
|
+
Pick<
|
|
42
|
+
CompleteHocuspocusProviderWebsocketConfiguration,
|
|
43
|
+
"preserveTrailingSlash"
|
|
44
|
+
>
|
|
45
|
+
>)
|
|
46
|
+
| Required<
|
|
47
|
+
Pick<CompleteHocuspocusProviderConfiguration, "websocketProvider">
|
|
48
|
+
>
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
export interface CompleteHocuspocusProviderConfiguration {
|
|
52
|
+
/**
|
|
53
|
+
* The identifier/name of your document
|
|
54
|
+
*/
|
|
55
|
+
name: string;
|
|
56
|
+
/**
|
|
57
|
+
* The actual Y.js document
|
|
58
|
+
*/
|
|
59
|
+
document: Y.Doc;
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* An Awareness instance to keep the presence state of all clients.
|
|
63
|
+
*
|
|
64
|
+
* You can disable sharing awareness information by passing `null`.
|
|
65
|
+
* Note that having no awareness information shared across all connections will break our ping checks
|
|
66
|
+
* and thus trigger reconnects. You should always have at least one Provider with enabled awareness per
|
|
67
|
+
* socket connection, or ensure that the Provider receives messages before running into `HocuspocusProviderWebsocket.messageReconnectTimeout`.
|
|
68
|
+
*/
|
|
69
|
+
awareness: Awareness | null;
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* A token that’s sent to the backend for authentication purposes.
|
|
73
|
+
*/
|
|
74
|
+
token: string | (() => string) | (() => Promise<string>) | null;
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Hocuspocus websocket provider
|
|
78
|
+
*/
|
|
79
|
+
websocketProvider: HocuspocusProviderWebsocket;
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Force syncing the document in the defined interval.
|
|
83
|
+
*/
|
|
84
|
+
forceSyncInterval: false | number;
|
|
85
|
+
|
|
86
|
+
onAuthenticated: (data: onAuthenticatedParameters) => void;
|
|
87
|
+
onAuthenticationFailed: (data: onAuthenticationFailedParameters) => void;
|
|
88
|
+
onOpen: (data: onOpenParameters) => void;
|
|
89
|
+
onConnect: () => void;
|
|
90
|
+
onStatus: (data: onStatusParameters) => void;
|
|
91
|
+
onMessage: (data: onMessageParameters) => void;
|
|
92
|
+
onOutgoingMessage: (data: onOutgoingMessageParameters) => void;
|
|
93
|
+
onSynced: (data: onSyncedParameters) => void;
|
|
94
|
+
onDisconnect: (data: onDisconnectParameters) => void;
|
|
95
|
+
onClose: (data: onCloseParameters) => void;
|
|
96
|
+
onDestroy: () => void;
|
|
97
|
+
onAwarenessUpdate: (data: onAwarenessUpdateParameters) => void;
|
|
98
|
+
onAwarenessChange: (data: onAwarenessChangeParameters) => void;
|
|
99
|
+
onStateless: (data: onStatelessParameters) => void;
|
|
100
|
+
onUnsyncedChanges: (data: onUnsyncedChangesParameters) => void;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export class AwarenessError extends Error {
|
|
104
|
+
code = 1001;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export class HocuspocusProvider extends EventEmitter {
|
|
108
|
+
public configuration: CompleteHocuspocusProviderConfiguration = {
|
|
109
|
+
name: "",
|
|
110
|
+
// @ts-ignore
|
|
111
|
+
document: undefined,
|
|
112
|
+
// @ts-ignore
|
|
113
|
+
awareness: undefined,
|
|
114
|
+
token: null,
|
|
115
|
+
forceSyncInterval: false,
|
|
116
|
+
onAuthenticated: () => null,
|
|
117
|
+
onAuthenticationFailed: () => null,
|
|
118
|
+
onOpen: () => null,
|
|
119
|
+
onConnect: () => null,
|
|
120
|
+
onMessage: () => null,
|
|
121
|
+
onOutgoingMessage: () => null,
|
|
122
|
+
onSynced: () => null,
|
|
123
|
+
onStatus: () => null,
|
|
124
|
+
onDisconnect: () => null,
|
|
125
|
+
onClose: () => null,
|
|
126
|
+
onDestroy: () => null,
|
|
127
|
+
onAwarenessUpdate: () => null,
|
|
128
|
+
onAwarenessChange: () => null,
|
|
129
|
+
onStateless: () => null,
|
|
130
|
+
onUnsyncedChanges: () => null,
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
isSynced = false;
|
|
134
|
+
|
|
135
|
+
unsyncedChanges = 0;
|
|
136
|
+
|
|
137
|
+
isAuthenticated = false;
|
|
138
|
+
|
|
139
|
+
authorizedScope: AuthorizedScope | undefined = undefined;
|
|
140
|
+
|
|
141
|
+
// @internal
|
|
142
|
+
manageSocket = false;
|
|
143
|
+
|
|
144
|
+
private _isAttached = false;
|
|
145
|
+
|
|
146
|
+
intervals: any = {
|
|
147
|
+
forceSync: null,
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
constructor(configuration: HocuspocusProviderConfiguration) {
|
|
151
|
+
super();
|
|
152
|
+
this.setConfiguration(configuration);
|
|
153
|
+
|
|
154
|
+
this.configuration.document = configuration.document
|
|
155
|
+
? configuration.document
|
|
156
|
+
: new Y.Doc();
|
|
157
|
+
this.configuration.awareness =
|
|
158
|
+
configuration.awareness !== undefined
|
|
159
|
+
? configuration.awareness
|
|
160
|
+
: new Awareness(this.document);
|
|
161
|
+
|
|
162
|
+
this.on("open", this.configuration.onOpen);
|
|
163
|
+
this.on("message", this.configuration.onMessage);
|
|
164
|
+
this.on("outgoingMessage", this.configuration.onOutgoingMessage);
|
|
165
|
+
this.on("synced", this.configuration.onSynced);
|
|
166
|
+
this.on("destroy", this.configuration.onDestroy);
|
|
167
|
+
this.on("awarenessUpdate", this.configuration.onAwarenessUpdate);
|
|
168
|
+
this.on("awarenessChange", this.configuration.onAwarenessChange);
|
|
169
|
+
this.on("stateless", this.configuration.onStateless);
|
|
170
|
+
this.on("unsyncedChanges", this.configuration.onUnsyncedChanges);
|
|
171
|
+
|
|
172
|
+
this.on("authenticated", this.configuration.onAuthenticated);
|
|
173
|
+
this.on("authenticationFailed", this.configuration.onAuthenticationFailed);
|
|
174
|
+
|
|
175
|
+
this.awareness?.on("update", () => {
|
|
176
|
+
this.emit("awarenessUpdate", {
|
|
177
|
+
states: awarenessStatesToArray(this.awareness!.getStates()),
|
|
178
|
+
});
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
this.awareness?.on("change", () => {
|
|
182
|
+
this.emit("awarenessChange", {
|
|
183
|
+
states: awarenessStatesToArray(this.awareness!.getStates()),
|
|
184
|
+
});
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
this.document.on("update", this.boundDocumentUpdateHandler);
|
|
188
|
+
this.awareness?.on("update", this.boundAwarenessUpdateHandler);
|
|
189
|
+
|
|
190
|
+
this.registerEventListeners();
|
|
191
|
+
|
|
192
|
+
if (
|
|
193
|
+
this.configuration.forceSyncInterval &&
|
|
194
|
+
typeof this.configuration.forceSyncInterval === "number"
|
|
195
|
+
) {
|
|
196
|
+
this.intervals.forceSync = setInterval(
|
|
197
|
+
this.forceSync.bind(this),
|
|
198
|
+
this.configuration.forceSyncInterval,
|
|
199
|
+
);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
if (this.manageSocket) {
|
|
203
|
+
this.attach();
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
boundDocumentUpdateHandler = this.documentUpdateHandler.bind(this);
|
|
208
|
+
|
|
209
|
+
boundAwarenessUpdateHandler = this.awarenessUpdateHandler.bind(this);
|
|
210
|
+
|
|
211
|
+
boundPageHide = this.pageHide.bind(this);
|
|
212
|
+
|
|
213
|
+
boundOnOpen = this.onOpen.bind(this);
|
|
214
|
+
|
|
215
|
+
boundOnClose = this.onClose.bind(this);
|
|
216
|
+
|
|
217
|
+
forwardConnect = () => this.emit("connect");
|
|
218
|
+
|
|
219
|
+
forwardStatus = (e: onStatusParameters) => this.emit("status", e);
|
|
220
|
+
|
|
221
|
+
forwardClose = (e: onCloseParameters) => this.emit("close", e);
|
|
222
|
+
|
|
223
|
+
forwardDisconnect = (e: onDisconnectParameters) => this.emit("disconnect", e);
|
|
224
|
+
|
|
225
|
+
forwardDestroy = () => this.emit("destroy");
|
|
226
|
+
|
|
227
|
+
public setConfiguration(
|
|
228
|
+
configuration: Partial<HocuspocusProviderConfiguration> = {},
|
|
229
|
+
): void {
|
|
230
|
+
if (!configuration.websocketProvider) {
|
|
231
|
+
this.manageSocket = true;
|
|
232
|
+
this.configuration.websocketProvider = new HocuspocusProviderWebsocket(
|
|
233
|
+
configuration as CompleteHocuspocusProviderWebsocketConfiguration,
|
|
234
|
+
);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
this.configuration = { ...this.configuration, ...configuration };
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
get document() {
|
|
241
|
+
return this.configuration.document;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
public get isAttached() {
|
|
245
|
+
return this._isAttached;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
get awareness() {
|
|
249
|
+
return this.configuration.awareness;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
get hasUnsyncedChanges(): boolean {
|
|
253
|
+
return this.unsyncedChanges > 0;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
private resetUnsyncedChanges() {
|
|
257
|
+
this.unsyncedChanges = 1;
|
|
258
|
+
this.emit("unsyncedChanges", { number: this.unsyncedChanges });
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
incrementUnsyncedChanges() {
|
|
262
|
+
this.unsyncedChanges += 1;
|
|
263
|
+
this.emit("unsyncedChanges", { number: this.unsyncedChanges });
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
decrementUnsyncedChanges() {
|
|
267
|
+
if (this.unsyncedChanges > 0) {
|
|
268
|
+
this.unsyncedChanges -= 1;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
if (this.unsyncedChanges === 0) {
|
|
272
|
+
this.synced = true;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
this.emit("unsyncedChanges", { number: this.unsyncedChanges });
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
forceSync() {
|
|
279
|
+
this.resetUnsyncedChanges();
|
|
280
|
+
|
|
281
|
+
this.send(SyncStepOneMessage, {
|
|
282
|
+
document: this.document,
|
|
283
|
+
documentName: this.configuration.name,
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
pageHide() {
|
|
288
|
+
if (this.awareness) {
|
|
289
|
+
removeAwarenessStates(
|
|
290
|
+
this.awareness,
|
|
291
|
+
[this.document.clientID],
|
|
292
|
+
"page hide",
|
|
293
|
+
);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
registerEventListeners() {
|
|
298
|
+
if (typeof window === "undefined" || !("addEventListener" in window)) {
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
window.addEventListener("pagehide", this.boundPageHide);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
sendStateless(payload: string) {
|
|
306
|
+
this.send(StatelessMessage, {
|
|
307
|
+
documentName: this.configuration.name,
|
|
308
|
+
payload,
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
async sendToken() {
|
|
313
|
+
let token: string | null;
|
|
314
|
+
try {
|
|
315
|
+
token = await this.getToken();
|
|
316
|
+
} catch (error) {
|
|
317
|
+
this.permissionDeniedHandler(
|
|
318
|
+
`Failed to get token during sendToken(): ${error}`,
|
|
319
|
+
);
|
|
320
|
+
return;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
this.send(AuthenticationMessage, {
|
|
324
|
+
token: token ?? "",
|
|
325
|
+
documentName: this.configuration.name,
|
|
326
|
+
});
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
documentUpdateHandler(update: Uint8Array, origin: any) {
|
|
330
|
+
if (origin === this) {
|
|
331
|
+
return;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
this.incrementUnsyncedChanges();
|
|
335
|
+
this.send(UpdateMessage, { update, documentName: this.configuration.name });
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
awarenessUpdateHandler({ added, updated, removed }: any, origin: any) {
|
|
339
|
+
const changedClients = added.concat(updated).concat(removed);
|
|
340
|
+
|
|
341
|
+
this.send(AwarenessMessage, {
|
|
342
|
+
awareness: this.awareness,
|
|
343
|
+
clients: changedClients,
|
|
344
|
+
documentName: this.configuration.name,
|
|
345
|
+
});
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
/**
|
|
349
|
+
* Indicates whether a first handshake with the server has been established
|
|
350
|
+
*
|
|
351
|
+
* Note: this does not mean all updates from the client have been persisted to the backend. For this,
|
|
352
|
+
* use `hasUnsyncedChanges`.
|
|
353
|
+
*/
|
|
354
|
+
get synced(): boolean {
|
|
355
|
+
return this.isSynced;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
set synced(state) {
|
|
359
|
+
if (this.isSynced === state) {
|
|
360
|
+
return;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
this.isSynced = state;
|
|
364
|
+
|
|
365
|
+
if (state) {
|
|
366
|
+
this.emit("synced", { state });
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
receiveStateless(payload: string) {
|
|
371
|
+
this.emit("stateless", { payload });
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
// not needed, but provides backward compatibility with e.g. lexical/yjs
|
|
375
|
+
async connect() {
|
|
376
|
+
if (this.manageSocket) {
|
|
377
|
+
return this.configuration.websocketProvider.connect();
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
console.warn(
|
|
381
|
+
"HocuspocusProvider::connect() is deprecated and does not do anything. Please connect/disconnect on the websocketProvider, or attach/deattach providers.",
|
|
382
|
+
);
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
disconnect() {
|
|
386
|
+
if (this.manageSocket) {
|
|
387
|
+
return this.configuration.websocketProvider.disconnect();
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
console.warn(
|
|
391
|
+
"HocuspocusProvider::disconnect() is deprecated and does not do anything. Please connect/disconnect on the websocketProvider, or attach/deattach providers.",
|
|
392
|
+
);
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
async onOpen(event: Event) {
|
|
396
|
+
this.isAuthenticated = false;
|
|
397
|
+
|
|
398
|
+
this.emit("open", { event });
|
|
399
|
+
await this.sendToken();
|
|
400
|
+
this.startSync();
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
async getToken() {
|
|
404
|
+
if (typeof this.configuration.token === "function") {
|
|
405
|
+
const token = await this.configuration.token();
|
|
406
|
+
return token;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
return this.configuration.token;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
startSync() {
|
|
413
|
+
this.resetUnsyncedChanges();
|
|
414
|
+
|
|
415
|
+
this.send(SyncStepOneMessage, {
|
|
416
|
+
document: this.document,
|
|
417
|
+
documentName: this.configuration.name,
|
|
418
|
+
});
|
|
419
|
+
|
|
420
|
+
if (this.awareness && this.awareness.getLocalState() !== null) {
|
|
421
|
+
this.send(AwarenessMessage, {
|
|
422
|
+
awareness: this.awareness,
|
|
423
|
+
clients: [this.document.clientID],
|
|
424
|
+
documentName: this.configuration.name,
|
|
425
|
+
});
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
send(message: ConstructableOutgoingMessage, args: any) {
|
|
430
|
+
if (!this._isAttached) return;
|
|
431
|
+
|
|
432
|
+
const messageSender = new MessageSender(message, args);
|
|
433
|
+
|
|
434
|
+
this.emit("outgoingMessage", { message: messageSender.message });
|
|
435
|
+
messageSender.send(this.configuration.websocketProvider);
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
onMessage(event: MessageEvent) {
|
|
439
|
+
const message = new IncomingMessage(event.data);
|
|
440
|
+
|
|
441
|
+
const documentName = message.readVarString();
|
|
442
|
+
|
|
443
|
+
message.writeVarString(documentName);
|
|
444
|
+
|
|
445
|
+
this.emit("message", { event, message: new IncomingMessage(event.data) });
|
|
446
|
+
|
|
447
|
+
new MessageReceiver(message).apply(this, true);
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
onClose() {
|
|
451
|
+
this.isAuthenticated = false;
|
|
452
|
+
this.synced = false;
|
|
453
|
+
|
|
454
|
+
// update awareness (all users except local left)
|
|
455
|
+
if (this.awareness) {
|
|
456
|
+
removeAwarenessStates(
|
|
457
|
+
this.awareness,
|
|
458
|
+
Array.from(this.awareness.getStates().keys()).filter(
|
|
459
|
+
(client) => client !== this.document.clientID,
|
|
460
|
+
),
|
|
461
|
+
this,
|
|
462
|
+
);
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
destroy() {
|
|
467
|
+
this.emit("destroy");
|
|
468
|
+
|
|
469
|
+
if (this.intervals.forceSync) {
|
|
470
|
+
clearInterval(this.intervals.forceSync);
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
if (this.awareness) {
|
|
474
|
+
removeAwarenessStates(
|
|
475
|
+
this.awareness,
|
|
476
|
+
[this.document.clientID],
|
|
477
|
+
"provider destroy",
|
|
478
|
+
);
|
|
479
|
+
this.awareness.off("update", this.boundAwarenessUpdateHandler);
|
|
480
|
+
this.awareness.destroy();
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
this.document.off("update", this.boundDocumentUpdateHandler);
|
|
484
|
+
|
|
485
|
+
this.removeAllListeners();
|
|
486
|
+
|
|
487
|
+
this.detach();
|
|
488
|
+
|
|
489
|
+
if (this.manageSocket) {
|
|
490
|
+
this.configuration.websocketProvider.destroy();
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
if (typeof window === "undefined" || !("removeEventListener" in window)) {
|
|
494
|
+
return;
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
window.removeEventListener("pagehide", this.boundPageHide);
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
detach() {
|
|
501
|
+
this.configuration.websocketProvider.off(
|
|
502
|
+
"connect",
|
|
503
|
+
this.configuration.onConnect,
|
|
504
|
+
);
|
|
505
|
+
this.configuration.websocketProvider.off("connect", this.forwardConnect);
|
|
506
|
+
|
|
507
|
+
this.configuration.websocketProvider.off("status", this.forwardStatus);
|
|
508
|
+
this.configuration.websocketProvider.off(
|
|
509
|
+
"status",
|
|
510
|
+
this.configuration.onStatus,
|
|
511
|
+
);
|
|
512
|
+
|
|
513
|
+
this.configuration.websocketProvider.off("open", this.boundOnOpen);
|
|
514
|
+
this.configuration.websocketProvider.off("close", this.boundOnClose);
|
|
515
|
+
this.configuration.websocketProvider.off(
|
|
516
|
+
"close",
|
|
517
|
+
this.configuration.onClose,
|
|
518
|
+
);
|
|
519
|
+
this.configuration.websocketProvider.off("close", this.forwardClose);
|
|
520
|
+
this.configuration.websocketProvider.off(
|
|
521
|
+
"disconnect",
|
|
522
|
+
this.configuration.onDisconnect,
|
|
523
|
+
);
|
|
524
|
+
this.configuration.websocketProvider.off(
|
|
525
|
+
"disconnect",
|
|
526
|
+
this.forwardDisconnect,
|
|
527
|
+
);
|
|
528
|
+
this.configuration.websocketProvider.off(
|
|
529
|
+
"destroy",
|
|
530
|
+
this.configuration.onDestroy,
|
|
531
|
+
);
|
|
532
|
+
this.configuration.websocketProvider.off("destroy", this.forwardDestroy);
|
|
533
|
+
|
|
534
|
+
this.configuration.websocketProvider.detach(this);
|
|
535
|
+
|
|
536
|
+
this._isAttached = false;
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
attach() {
|
|
540
|
+
if (this._isAttached) return;
|
|
541
|
+
|
|
542
|
+
this.configuration.websocketProvider.on(
|
|
543
|
+
"connect",
|
|
544
|
+
this.configuration.onConnect,
|
|
545
|
+
);
|
|
546
|
+
this.configuration.websocketProvider.on("connect", this.forwardConnect);
|
|
547
|
+
|
|
548
|
+
this.configuration.websocketProvider.on(
|
|
549
|
+
"status",
|
|
550
|
+
this.configuration.onStatus,
|
|
551
|
+
);
|
|
552
|
+
this.configuration.websocketProvider.on("status", this.forwardStatus);
|
|
553
|
+
|
|
554
|
+
this.configuration.websocketProvider.on("open", this.boundOnOpen);
|
|
555
|
+
|
|
556
|
+
this.configuration.websocketProvider.on("close", this.boundOnClose);
|
|
557
|
+
this.configuration.websocketProvider.on(
|
|
558
|
+
"close",
|
|
559
|
+
this.configuration.onClose,
|
|
560
|
+
);
|
|
561
|
+
this.configuration.websocketProvider.on("close", this.forwardClose);
|
|
562
|
+
|
|
563
|
+
this.configuration.websocketProvider.on(
|
|
564
|
+
"disconnect",
|
|
565
|
+
this.configuration.onDisconnect,
|
|
566
|
+
);
|
|
567
|
+
this.configuration.websocketProvider.on(
|
|
568
|
+
"disconnect",
|
|
569
|
+
this.forwardDisconnect,
|
|
570
|
+
);
|
|
571
|
+
|
|
572
|
+
this.configuration.websocketProvider.on(
|
|
573
|
+
"destroy",
|
|
574
|
+
this.configuration.onDestroy,
|
|
575
|
+
);
|
|
576
|
+
this.configuration.websocketProvider.on("destroy", this.forwardDestroy);
|
|
577
|
+
|
|
578
|
+
this.configuration.websocketProvider.attach(this);
|
|
579
|
+
|
|
580
|
+
this._isAttached = true;
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
permissionDeniedHandler(reason: string) {
|
|
584
|
+
this.emit("authenticationFailed", { reason });
|
|
585
|
+
this.isAuthenticated = false;
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
authenticatedHandler(scope: string) {
|
|
589
|
+
this.isAuthenticated = true;
|
|
590
|
+
this.authorizedScope = scope as AuthorizedScope;
|
|
591
|
+
|
|
592
|
+
this.emit("authenticated", { scope });
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
setAwarenessField(key: string, value: any) {
|
|
596
|
+
if (!this.awareness) {
|
|
597
|
+
throw new AwarenessError(
|
|
598
|
+
`Cannot set awareness field "${key}" to ${JSON.stringify(value)}. You have disabled Awareness for this provider by explicitly passing awareness: null in the provider configuration.`,
|
|
599
|
+
);
|
|
600
|
+
}
|
|
601
|
+
this.awareness.setLocalStateField(key, value);
|
|
602
|
+
}
|
|
603
|
+
}
|