@hocuspocus/provider 1.0.0-alpha.14 → 1.0.0-alpha.18
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 +139 -80
- package/dist/hocuspocus-provider.cjs.map +1 -1
- package/dist/hocuspocus-provider.esm.js +137 -76
- package/dist/hocuspocus-provider.esm.js.map +1 -1
- package/dist/packages/provider/src/HocuspocusProvider.d.ts +80 -14
- package/dist/packages/provider/src/MessageSender.d.ts +2 -8
- package/dist/packages/provider/src/types.d.ts +7 -0
- package/dist/packages/server/src/Connection.d.ts +2 -1
- package/dist/packages/server/src/Document.d.ts +2 -2
- package/dist/packages/server/src/Hocuspocus.d.ts +8 -0
- package/dist/packages/server/src/types.d.ts +4 -0
- package/package.json +3 -2
- package/src/HocuspocusProvider.ts +215 -96
- package/src/MessageReceiver.ts +16 -15
- package/src/MessageSender.ts +2 -15
- package/src/types.ts +14 -0
- package/CHANGELOG.md +0 -110
|
@@ -1,13 +1,11 @@
|
|
|
1
|
-
// @ts-nocheck
|
|
2
1
|
import * as Y from 'yjs'
|
|
3
2
|
import * as bc from 'lib0/broadcastchannel'
|
|
4
3
|
import * as time from 'lib0/time'
|
|
5
4
|
import { Awareness, removeAwarenessStates } from 'y-protocols/awareness'
|
|
6
5
|
import * as mutex from 'lib0/mutex'
|
|
7
|
-
import * as math from 'lib0/math'
|
|
8
6
|
import * as url from 'lib0/url'
|
|
9
|
-
|
|
10
7
|
import { CloseEvent, MessageEvent, OpenEvent } from 'ws'
|
|
8
|
+
import { retry } from '@lifeomic/attempt'
|
|
11
9
|
import EventEmitter from './EventEmitter'
|
|
12
10
|
import { IncomingMessage } from './IncomingMessage'
|
|
13
11
|
import { MessageReceiver } from './MessageReceiver'
|
|
@@ -20,6 +18,7 @@ import { AwarenessMessage } from './OutgoingMessages/AwarenessMessage'
|
|
|
20
18
|
import { UpdateMessage } from './OutgoingMessages/UpdateMessage'
|
|
21
19
|
import { OutgoingMessage } from './OutgoingMessage'
|
|
22
20
|
import awarenessStatesToArray from './utils/awarenessStatesToArray'
|
|
21
|
+
import { ConstructableOutgoingMessage } from './types'
|
|
23
22
|
|
|
24
23
|
export enum WebSocketStatus {
|
|
25
24
|
Connecting = 'connecting',
|
|
@@ -28,21 +27,84 @@ export enum WebSocketStatus {
|
|
|
28
27
|
}
|
|
29
28
|
|
|
30
29
|
export interface HocuspocusProviderOptions {
|
|
30
|
+
/**
|
|
31
|
+
* URL of your @hocuspocus/server instance
|
|
32
|
+
*/
|
|
31
33
|
url: string,
|
|
34
|
+
/**
|
|
35
|
+
* The identifier/name of your document
|
|
36
|
+
*/
|
|
32
37
|
name: string,
|
|
38
|
+
/**
|
|
39
|
+
* The actual Y.js document
|
|
40
|
+
*/
|
|
33
41
|
document: Y.Doc,
|
|
42
|
+
/**
|
|
43
|
+
* Pass `false` to start the connection manually.
|
|
44
|
+
*/
|
|
34
45
|
connect: boolean,
|
|
46
|
+
/**
|
|
47
|
+
* Pass false to disable broadcasting between browser tabs.
|
|
48
|
+
*/
|
|
35
49
|
broadcast: boolean,
|
|
50
|
+
/**
|
|
51
|
+
* An Awareness instance to keep the presence state of all clients.
|
|
52
|
+
*/
|
|
36
53
|
awareness: Awareness,
|
|
37
|
-
|
|
54
|
+
/**
|
|
55
|
+
* A token that’s sent to the backend for authentication purposes.
|
|
56
|
+
*/
|
|
57
|
+
token: string | (() => string) | (() => Promise<string>) | null,
|
|
58
|
+
/**
|
|
59
|
+
* URL parameters that should be added.
|
|
60
|
+
*/
|
|
38
61
|
parameters: { [key: string]: any },
|
|
62
|
+
/**
|
|
63
|
+
* An optional WebSocket polyfill, for example for Node.js
|
|
64
|
+
*/
|
|
39
65
|
WebSocketPolyfill: any,
|
|
66
|
+
/**
|
|
67
|
+
* Force syncing the document in the defined interval.
|
|
68
|
+
*/
|
|
40
69
|
forceSyncInterval: false | number,
|
|
41
|
-
|
|
42
|
-
|
|
70
|
+
/**
|
|
71
|
+
* Disconnect when no message is received for the defined amount of milliseconds.
|
|
72
|
+
*/
|
|
43
73
|
messageReconnectTimeout: number,
|
|
74
|
+
/**
|
|
75
|
+
* The delay between each attempt in milliseconds. You can provide a factor to have the delay grow exponentially.
|
|
76
|
+
*/
|
|
77
|
+
delay: number,
|
|
78
|
+
/**
|
|
79
|
+
* The intialDelay is the amount of time to wait before making the first attempt. This option should typically be 0 since you typically want the first attempt to happen immediately.
|
|
80
|
+
*/
|
|
81
|
+
initialDelay: number,
|
|
82
|
+
/**
|
|
83
|
+
* The factor option is used to grow the delay exponentially.
|
|
84
|
+
*/
|
|
85
|
+
factor: number,
|
|
86
|
+
/**
|
|
87
|
+
* The maximum number of attempts or 0 if there is no limit on number of attempts.
|
|
88
|
+
*/
|
|
89
|
+
maxAttempts: number,
|
|
90
|
+
/**
|
|
91
|
+
* minDelay is used to set a lower bound of delay when jitter is enabled. This property has no effect if jitter is disabled.
|
|
92
|
+
*/
|
|
93
|
+
minDelay: number,
|
|
94
|
+
/**
|
|
95
|
+
* The maxDelay option is used to set an upper bound for the delay when factor is enabled. A value of 0 can be provided if there should be no upper bound when calculating delay.
|
|
96
|
+
*/
|
|
97
|
+
maxDelay: number,
|
|
98
|
+
/**
|
|
99
|
+
* If jitter is true then the calculated delay will be a random integer value between minDelay and the calculated delay for the current iteration.
|
|
100
|
+
*/
|
|
101
|
+
jitter: boolean,
|
|
102
|
+
/**
|
|
103
|
+
* A timeout in milliseconds. If timeout is non-zero then a timer is set using setTimeout. If the timeout is triggered then future attempts will be aborted.
|
|
104
|
+
*/
|
|
105
|
+
timeout: number,
|
|
44
106
|
onAuthenticated: () => void,
|
|
45
|
-
onAuthenticationFailed: ({ reason: string }) => void,
|
|
107
|
+
onAuthenticationFailed: ({ reason }: { reason: string }) => void,
|
|
46
108
|
onOpen: (event: OpenEvent) => void,
|
|
47
109
|
onConnect: () => void,
|
|
48
110
|
onMessage: (event: MessageEvent) => void,
|
|
@@ -54,23 +116,40 @@ export interface HocuspocusProviderOptions {
|
|
|
54
116
|
onDestroy: () => void,
|
|
55
117
|
onAwarenessUpdate: (states: any) => void,
|
|
56
118
|
onAwarenessChange: (states: any) => void,
|
|
57
|
-
debug: boolean,
|
|
58
119
|
}
|
|
59
120
|
|
|
60
121
|
export class HocuspocusProvider extends EventEmitter {
|
|
61
122
|
public options: HocuspocusProviderOptions = {
|
|
123
|
+
// @ts-ignore
|
|
124
|
+
document: undefined,
|
|
125
|
+
// @ts-ignore
|
|
126
|
+
awareness: undefined,
|
|
127
|
+
WebSocketPolyfill: undefined,
|
|
62
128
|
url: '',
|
|
63
129
|
name: '',
|
|
64
130
|
token: null,
|
|
65
131
|
parameters: {},
|
|
66
|
-
debug: false,
|
|
67
132
|
connect: true,
|
|
68
133
|
broadcast: true,
|
|
69
134
|
forceSyncInterval: false,
|
|
70
|
-
reconnectTimeoutBase: 1200,
|
|
71
|
-
maxReconnectTimeout: 2500,
|
|
72
135
|
// TODO: this should depend on awareness.outdatedTime
|
|
73
136
|
messageReconnectTimeout: 30000,
|
|
137
|
+
// 1 second
|
|
138
|
+
delay: 1000,
|
|
139
|
+
// instant
|
|
140
|
+
initialDelay: 0,
|
|
141
|
+
// double the delay each time
|
|
142
|
+
factor: 2,
|
|
143
|
+
// unlimited retries
|
|
144
|
+
maxAttempts: 0,
|
|
145
|
+
// wait at least 1 second
|
|
146
|
+
minDelay: 1000,
|
|
147
|
+
// at least every 30 seconds
|
|
148
|
+
maxDelay: 30000,
|
|
149
|
+
// randomize
|
|
150
|
+
jitter: true,
|
|
151
|
+
// retry forever
|
|
152
|
+
timeout: 0,
|
|
74
153
|
onAuthenticated: () => null,
|
|
75
154
|
onAuthenticationFailed: () => null,
|
|
76
155
|
onOpen: () => null,
|
|
@@ -86,17 +165,13 @@ export class HocuspocusProvider extends EventEmitter {
|
|
|
86
165
|
onAwarenessChange: () => null,
|
|
87
166
|
}
|
|
88
167
|
|
|
89
|
-
awareness: Awareness
|
|
90
|
-
|
|
91
168
|
subscribedToBroadcastChannel = false
|
|
92
169
|
|
|
93
170
|
webSocket: any = null
|
|
94
171
|
|
|
95
172
|
shouldConnect = true
|
|
96
173
|
|
|
97
|
-
status
|
|
98
|
-
|
|
99
|
-
failedConnectionAttempts = 0
|
|
174
|
+
status = WebSocketStatus.Disconnected
|
|
100
175
|
|
|
101
176
|
isSynced = false
|
|
102
177
|
|
|
@@ -111,15 +186,17 @@ export class HocuspocusProvider extends EventEmitter {
|
|
|
111
186
|
connectionChecker: null,
|
|
112
187
|
}
|
|
113
188
|
|
|
189
|
+
connectionAttempt: {
|
|
190
|
+
resolve: (value?: any) => void
|
|
191
|
+
reject: (reason?: any) => void
|
|
192
|
+
} | null = null
|
|
193
|
+
|
|
114
194
|
constructor(options: Partial<HocuspocusProviderOptions> = {}) {
|
|
115
195
|
super()
|
|
116
|
-
|
|
117
196
|
this.setOptions(options)
|
|
118
197
|
|
|
119
|
-
this.options.document = options.document ? options.document : new Y.Doc()
|
|
120
198
|
this.options.awareness = options.awareness ? options.awareness : new Awareness(this.document)
|
|
121
199
|
this.options.WebSocketPolyfill = options.WebSocketPolyfill ? options.WebSocketPolyfill : WebSocket
|
|
122
|
-
this.shouldConnect = options.connect !== undefined ? options.connect : this.shouldConnect
|
|
123
200
|
|
|
124
201
|
this.on('open', this.options.onOpen)
|
|
125
202
|
this.on('authenticated', this.options.onAuthenticated)
|
|
@@ -136,26 +213,22 @@ export class HocuspocusProvider extends EventEmitter {
|
|
|
136
213
|
this.on('awarenessChange', this.options.onAwarenessChange)
|
|
137
214
|
|
|
138
215
|
this.awareness.on('update', () => {
|
|
139
|
-
this.emit('awarenessUpdate', {
|
|
140
|
-
states: awarenessStatesToArray(this.awareness.getStates()),
|
|
141
|
-
})
|
|
216
|
+
this.emit('awarenessUpdate', { states: awarenessStatesToArray(this.awareness.getStates()) })
|
|
142
217
|
})
|
|
143
218
|
|
|
144
219
|
this.awareness.on('change', () => {
|
|
145
|
-
this.emit('awarenessChange', {
|
|
146
|
-
states: awarenessStatesToArray(this.awareness.getStates()),
|
|
147
|
-
})
|
|
220
|
+
this.emit('awarenessChange', { states: awarenessStatesToArray(this.awareness.getStates()) })
|
|
148
221
|
})
|
|
149
222
|
|
|
223
|
+
this.document.on('update', this.documentUpdateHandler.bind(this))
|
|
224
|
+
this.awareness.on('update', this.awarenessUpdateHandler.bind(this))
|
|
225
|
+
this.registerBeforeUnloadEventListener()
|
|
226
|
+
|
|
150
227
|
this.intervals.connectionChecker = setInterval(
|
|
151
228
|
this.checkConnection.bind(this),
|
|
152
229
|
this.options.messageReconnectTimeout / 10,
|
|
153
230
|
)
|
|
154
231
|
|
|
155
|
-
this.document.on('update', this.documentUpdateHandler.bind(this))
|
|
156
|
-
this.awareness.on('update', this.awarenessUpdateHandler.bind(this))
|
|
157
|
-
this.registerBeforeUnloadEventListener()
|
|
158
|
-
|
|
159
232
|
if (this.options.forceSyncInterval) {
|
|
160
233
|
this.intervals.forceSync = setInterval(
|
|
161
234
|
this.forceSync.bind(this),
|
|
@@ -163,15 +236,89 @@ export class HocuspocusProvider extends EventEmitter {
|
|
|
163
236
|
)
|
|
164
237
|
}
|
|
165
238
|
|
|
166
|
-
if (
|
|
167
|
-
this.connect
|
|
239
|
+
if (typeof options.connect !== 'undefined') {
|
|
240
|
+
this.shouldConnect = options.connect
|
|
168
241
|
}
|
|
242
|
+
|
|
243
|
+
if (!this.shouldConnect) {
|
|
244
|
+
return
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
this.connect()
|
|
169
248
|
}
|
|
170
249
|
|
|
171
250
|
public setOptions(options: Partial<HocuspocusProviderOptions> = {}): void {
|
|
172
251
|
this.options = { ...this.options, ...options }
|
|
173
252
|
}
|
|
174
253
|
|
|
254
|
+
async connect() {
|
|
255
|
+
if (this.status === WebSocketStatus.Connected) {
|
|
256
|
+
return
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
this.shouldConnect = true
|
|
260
|
+
this.subscribeToBroadcastChannel()
|
|
261
|
+
|
|
262
|
+
try {
|
|
263
|
+
await retry(this.createWebSocketConnection.bind(this), {
|
|
264
|
+
delay: this.options.delay,
|
|
265
|
+
initialDelay: this.options.initialDelay,
|
|
266
|
+
factor: this.options.factor,
|
|
267
|
+
maxAttempts: this.options.maxAttempts,
|
|
268
|
+
minDelay: this.options.minDelay,
|
|
269
|
+
maxDelay: this.options.maxDelay,
|
|
270
|
+
jitter: this.options.jitter,
|
|
271
|
+
timeout: this.options.timeout,
|
|
272
|
+
beforeAttempt: context => {
|
|
273
|
+
if (!this.shouldConnect) {
|
|
274
|
+
context.abort()
|
|
275
|
+
}
|
|
276
|
+
},
|
|
277
|
+
})
|
|
278
|
+
} catch (err: any) {
|
|
279
|
+
// If we aborted the connection attempt then don't throw an error
|
|
280
|
+
// ref: https://github.com/lifeomic/attempt/blob/master/src/index.ts#L136
|
|
281
|
+
if (err.code !== 'ATTEMPT_ABORTED') {
|
|
282
|
+
throw err
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
createWebSocketConnection() {
|
|
288
|
+
return new Promise((resolve, reject) => {
|
|
289
|
+
// Init the WebSocket connection
|
|
290
|
+
this.webSocket = new this.options.WebSocketPolyfill(this.url)
|
|
291
|
+
this.webSocket.binaryType = 'arraybuffer'
|
|
292
|
+
this.webSocket.onmessage = this.onMessage.bind(this)
|
|
293
|
+
this.webSocket.onclose = this.onClose.bind(this)
|
|
294
|
+
this.webSocket.onopen = this.onOpen.bind(this)
|
|
295
|
+
this.webSocket.onerror = () => {
|
|
296
|
+
reject()
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// Reset the status
|
|
300
|
+
this.synced = false
|
|
301
|
+
this.status = WebSocketStatus.Connecting
|
|
302
|
+
this.emit('status', { status: 'connecting' })
|
|
303
|
+
|
|
304
|
+
// Store resolve/reject for later use
|
|
305
|
+
this.connectionAttempt = {
|
|
306
|
+
resolve,
|
|
307
|
+
reject,
|
|
308
|
+
}
|
|
309
|
+
})
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
resolveConnectionAttempt() {
|
|
313
|
+
this.connectionAttempt?.resolve()
|
|
314
|
+
this.connectionAttempt = null
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
rejectConnectionAttempt() {
|
|
318
|
+
this.connectionAttempt?.reject()
|
|
319
|
+
this.connectionAttempt = null
|
|
320
|
+
}
|
|
321
|
+
|
|
175
322
|
get document() {
|
|
176
323
|
return this.options.document
|
|
177
324
|
}
|
|
@@ -181,12 +328,12 @@ export class HocuspocusProvider extends EventEmitter {
|
|
|
181
328
|
}
|
|
182
329
|
|
|
183
330
|
checkConnection() {
|
|
184
|
-
// Don’t
|
|
331
|
+
// Don’t check the connection when it’s not even established
|
|
185
332
|
if (this.status !== WebSocketStatus.Connected) {
|
|
186
333
|
return
|
|
187
334
|
}
|
|
188
335
|
|
|
189
|
-
// Don’t
|
|
336
|
+
// Don’t close then connection while waiting for the first message
|
|
190
337
|
if (!this.lastMessageReceived) {
|
|
191
338
|
return
|
|
192
339
|
}
|
|
@@ -238,8 +385,6 @@ export class HocuspocusProvider extends EventEmitter {
|
|
|
238
385
|
|
|
239
386
|
permissionDeniedHandler(reason: string) {
|
|
240
387
|
this.emit('authenticationFailed', { reason })
|
|
241
|
-
this.log('Permission denied', reason)
|
|
242
|
-
|
|
243
388
|
this.isAuthenticated = false
|
|
244
389
|
this.shouldConnect = false
|
|
245
390
|
}
|
|
@@ -284,15 +429,6 @@ export class HocuspocusProvider extends EventEmitter {
|
|
|
284
429
|
return !!this.options.token && !this.isAuthenticated
|
|
285
430
|
}
|
|
286
431
|
|
|
287
|
-
connect() {
|
|
288
|
-
this.shouldConnect = true
|
|
289
|
-
|
|
290
|
-
if (this.status !== WebSocketStatus.Connected) {
|
|
291
|
-
this.createWebSocketConnection()
|
|
292
|
-
this.subscribeToBroadcastChannel()
|
|
293
|
-
}
|
|
294
|
-
}
|
|
295
|
-
|
|
296
432
|
disconnect() {
|
|
297
433
|
this.shouldConnect = false
|
|
298
434
|
this.disconnectBroadcastChannel()
|
|
@@ -308,24 +444,6 @@ export class HocuspocusProvider extends EventEmitter {
|
|
|
308
444
|
}
|
|
309
445
|
}
|
|
310
446
|
|
|
311
|
-
createWebSocketConnection() {
|
|
312
|
-
if (this.webSocket !== null) {
|
|
313
|
-
return
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
this.webSocket = new this.options.WebSocketPolyfill(this.url)
|
|
317
|
-
this.webSocket.binaryType = 'arraybuffer'
|
|
318
|
-
|
|
319
|
-
this.status = WebSocketStatus.Connecting
|
|
320
|
-
this.synced = false
|
|
321
|
-
|
|
322
|
-
this.webSocket.onmessage = this.onMessage.bind(this)
|
|
323
|
-
this.webSocket.onclose = this.onClose.bind(this)
|
|
324
|
-
this.webSocket.onopen = this.onOpen.bind(this)
|
|
325
|
-
|
|
326
|
-
this.emit('status', { status: 'connecting' })
|
|
327
|
-
}
|
|
328
|
-
|
|
329
447
|
onOpen(event: OpenEvent) {
|
|
330
448
|
this.emit('open', { event })
|
|
331
449
|
|
|
@@ -344,18 +462,20 @@ export class HocuspocusProvider extends EventEmitter {
|
|
|
344
462
|
}
|
|
345
463
|
|
|
346
464
|
async webSocketConnectionEstablished() {
|
|
347
|
-
this.failedConnectionAttempts = 0
|
|
348
465
|
this.status = WebSocketStatus.Connected
|
|
349
466
|
this.emit('status', { status: 'connected' })
|
|
350
467
|
this.emit('connect')
|
|
351
468
|
|
|
352
469
|
if (this.isAuthenticationRequired) {
|
|
353
|
-
|
|
354
|
-
|
|
470
|
+
this.send(AuthenticationMessage, {
|
|
471
|
+
token: await this.getToken(),
|
|
472
|
+
})
|
|
355
473
|
return
|
|
356
474
|
}
|
|
357
475
|
|
|
358
476
|
this.startSync()
|
|
477
|
+
|
|
478
|
+
this.resolveConnectionAttempt()
|
|
359
479
|
}
|
|
360
480
|
|
|
361
481
|
startSync() {
|
|
@@ -369,11 +489,9 @@ export class HocuspocusProvider extends EventEmitter {
|
|
|
369
489
|
}
|
|
370
490
|
}
|
|
371
491
|
|
|
372
|
-
send(Message:
|
|
492
|
+
send(Message: ConstructableOutgoingMessage, args: any, broadcast = false) {
|
|
373
493
|
if (broadcast) {
|
|
374
|
-
this.mux(() => {
|
|
375
|
-
this.broadcast(Message, args)
|
|
376
|
-
})
|
|
494
|
+
this.mux(() => { this.broadcast(Message, args) })
|
|
377
495
|
}
|
|
378
496
|
|
|
379
497
|
if (this.status === WebSocketStatus.Connected) {
|
|
@@ -397,12 +515,11 @@ export class HocuspocusProvider extends EventEmitter {
|
|
|
397
515
|
onClose(event: CloseEvent) {
|
|
398
516
|
this.emit('close', { event })
|
|
399
517
|
|
|
400
|
-
this.isAuthenticated = false
|
|
401
518
|
this.webSocket = null
|
|
519
|
+
this.isAuthenticated = false
|
|
520
|
+
this.synced = false
|
|
402
521
|
|
|
403
522
|
if (this.status === WebSocketStatus.Connected) {
|
|
404
|
-
this.synced = false
|
|
405
|
-
|
|
406
523
|
// update awareness (all users except local left)
|
|
407
524
|
removeAwarenessStates(
|
|
408
525
|
this.awareness,
|
|
@@ -413,27 +530,30 @@ export class HocuspocusProvider extends EventEmitter {
|
|
|
413
530
|
this.status = WebSocketStatus.Disconnected
|
|
414
531
|
this.emit('status', { status: 'disconnected' })
|
|
415
532
|
this.emit('disconnect', { event })
|
|
416
|
-
} else {
|
|
417
|
-
this.failedConnectionAttempts += 1
|
|
418
533
|
}
|
|
419
534
|
|
|
420
|
-
if (this.
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
setTimeout(this.createWebSocketConnection.bind(this), wait)
|
|
535
|
+
if (this.connectionAttempt) {
|
|
536
|
+
// Okay, that connection attempt failed …
|
|
537
|
+
this.rejectConnectionAttempt()
|
|
538
|
+
} else if (this.shouldConnect) {
|
|
539
|
+
// The connection was closed by the server, so let’s just try again.
|
|
540
|
+
this.connect()
|
|
541
|
+
}
|
|
428
542
|
|
|
543
|
+
// If we’ll reconnect anyway, we’re done for now.
|
|
544
|
+
if (this.shouldConnect) {
|
|
429
545
|
return
|
|
430
546
|
}
|
|
431
547
|
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
this.emit('disconnect', { event })
|
|
548
|
+
// The status is set correctly already.
|
|
549
|
+
if (this.status === WebSocketStatus.Disconnected) {
|
|
550
|
+
return
|
|
436
551
|
}
|
|
552
|
+
|
|
553
|
+
// Let’s update the connection status.
|
|
554
|
+
this.status = WebSocketStatus.Disconnected
|
|
555
|
+
this.emit('status', { status: 'disconnected' })
|
|
556
|
+
this.emit('disconnect', { event })
|
|
437
557
|
}
|
|
438
558
|
|
|
439
559
|
destroy() {
|
|
@@ -445,6 +565,13 @@ export class HocuspocusProvider extends EventEmitter {
|
|
|
445
565
|
|
|
446
566
|
clearInterval(this.intervals.connectionChecker)
|
|
447
567
|
|
|
568
|
+
removeAwarenessStates(this.awareness, [this.document.clientID], 'provider destroy')
|
|
569
|
+
|
|
570
|
+
// If there is still a connection attempt outstanding then we should resolve
|
|
571
|
+
// it before calling disconnect, otherwise it will be rejected in the onClose
|
|
572
|
+
// handler and trigger a retry
|
|
573
|
+
this.resolveConnectionAttempt()
|
|
574
|
+
|
|
448
575
|
this.disconnect()
|
|
449
576
|
|
|
450
577
|
this.awareness.off('update', this.awarenessUpdateHandler)
|
|
@@ -460,7 +587,7 @@ export class HocuspocusProvider extends EventEmitter {
|
|
|
460
587
|
broadcastChannelSubscriber(data: ArrayBuffer) {
|
|
461
588
|
this.mux(() => {
|
|
462
589
|
const message = new IncomingMessage(data)
|
|
463
|
-
new MessageReceiver(message
|
|
590
|
+
new MessageReceiver(message)
|
|
464
591
|
.setBroadcasted(true)
|
|
465
592
|
.apply(this, false)
|
|
466
593
|
})
|
|
@@ -494,7 +621,7 @@ export class HocuspocusProvider extends EventEmitter {
|
|
|
494
621
|
}
|
|
495
622
|
}
|
|
496
623
|
|
|
497
|
-
broadcast(Message:
|
|
624
|
+
broadcast(Message: ConstructableOutgoingMessage, args?: any) {
|
|
498
625
|
if (!this.options.broadcast) {
|
|
499
626
|
return
|
|
500
627
|
}
|
|
@@ -506,14 +633,6 @@ export class HocuspocusProvider extends EventEmitter {
|
|
|
506
633
|
new MessageSender(Message, args).broadcast(this.broadcastChannel)
|
|
507
634
|
}
|
|
508
635
|
|
|
509
|
-
log(message: string): void {
|
|
510
|
-
if (!this.options.debug) {
|
|
511
|
-
return
|
|
512
|
-
}
|
|
513
|
-
|
|
514
|
-
console.log(message)
|
|
515
|
-
}
|
|
516
|
-
|
|
517
636
|
setAwarenessField(key: string, value: any) {
|
|
518
637
|
this.awareness.setLocalStateField(key, value)
|
|
519
638
|
}
|
package/src/MessageReceiver.ts
CHANGED
|
@@ -23,7 +23,8 @@ export class MessageReceiver {
|
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
public apply(provider: HocuspocusProvider, emitSynced = true) {
|
|
26
|
-
const
|
|
26
|
+
const { message } = this
|
|
27
|
+
const type = message.readVarUint()
|
|
27
28
|
|
|
28
29
|
switch (type) {
|
|
29
30
|
case MessageType.Sync:
|
|
@@ -45,6 +46,19 @@ export class MessageReceiver {
|
|
|
45
46
|
default:
|
|
46
47
|
throw new Error(`Can’t apply message of unknown type: ${type}`)
|
|
47
48
|
}
|
|
49
|
+
|
|
50
|
+
// Reply
|
|
51
|
+
if (message.length() > 1) {
|
|
52
|
+
if (this.broadcasted) {
|
|
53
|
+
// TODO: Some weird TypeScript error
|
|
54
|
+
// @ts-ignore
|
|
55
|
+
provider.broadcast(OutgoingMessage, { encoder: message.encoder })
|
|
56
|
+
} else {
|
|
57
|
+
// TODO: Some weird TypeScript error
|
|
58
|
+
// @ts-ignore
|
|
59
|
+
provider.send(OutgoingMessage, { encoder: message.encoder })
|
|
60
|
+
}
|
|
61
|
+
}
|
|
48
62
|
}
|
|
49
63
|
|
|
50
64
|
private applySyncMessage(provider: HocuspocusProvider, emitSynced: boolean) {
|
|
@@ -60,23 +74,10 @@ export class MessageReceiver {
|
|
|
60
74
|
provider,
|
|
61
75
|
)
|
|
62
76
|
|
|
63
|
-
// Synced
|
|
77
|
+
// Synced once we receive Step2
|
|
64
78
|
if (emitSynced && syncMessageType === messageYjsSyncStep2) {
|
|
65
79
|
provider.synced = true
|
|
66
80
|
}
|
|
67
|
-
|
|
68
|
-
// Reply
|
|
69
|
-
if (message.length() > 1) {
|
|
70
|
-
if (this.broadcasted) {
|
|
71
|
-
// TODO: Some weird TypeScript error
|
|
72
|
-
// @ts-ignore
|
|
73
|
-
provider.broadcast(OutgoingMessage, { encoder: message.encoder })
|
|
74
|
-
} else {
|
|
75
|
-
// TODO: Some weird TypeScript error
|
|
76
|
-
// @ts-ignore
|
|
77
|
-
provider.send(OutgoingMessage, { encoder: message.encoder })
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
81
|
}
|
|
81
82
|
|
|
82
83
|
private applyAwarenessMessage(provider: HocuspocusProvider) {
|
package/src/MessageSender.ts
CHANGED
|
@@ -1,12 +1,6 @@
|
|
|
1
1
|
import { Encoder, toUint8Array } from 'lib0/encoding'
|
|
2
2
|
import * as bc from 'lib0/broadcastchannel'
|
|
3
|
-
import {
|
|
4
|
-
import { AwarenessMessage } from './OutgoingMessages/AwarenessMessage'
|
|
5
|
-
import { QueryAwarenessMessage } from './OutgoingMessages/QueryAwarenessMessage'
|
|
6
|
-
import { SyncStepOneMessage } from './OutgoingMessages/SyncStepOneMessage'
|
|
7
|
-
import { SyncStepTwoMessage } from './OutgoingMessages/SyncStepTwoMessage'
|
|
8
|
-
import { UpdateMessage } from './OutgoingMessages/UpdateMessage'
|
|
9
|
-
import { Constructable } from './types'
|
|
3
|
+
import { ConstructableOutgoingMessage } from './types'
|
|
10
4
|
|
|
11
5
|
export class MessageSender {
|
|
12
6
|
|
|
@@ -14,14 +8,7 @@ export class MessageSender {
|
|
|
14
8
|
|
|
15
9
|
message: any
|
|
16
10
|
|
|
17
|
-
constructor(Message:
|
|
18
|
-
Constructable<AuthenticationMessage> |
|
|
19
|
-
Constructable<AwarenessMessage> |
|
|
20
|
-
Constructable<QueryAwarenessMessage> |
|
|
21
|
-
Constructable<SyncStepOneMessage> |
|
|
22
|
-
Constructable<SyncStepTwoMessage> |
|
|
23
|
-
Constructable<UpdateMessage>,
|
|
24
|
-
args: any = {}) {
|
|
11
|
+
constructor(Message: ConstructableOutgoingMessage, args: any = {}) {
|
|
25
12
|
this.message = new Message()
|
|
26
13
|
this.encoder = this.message.get(args)
|
|
27
14
|
}
|
package/src/types.ts
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import { Awareness } from 'y-protocols/awareness'
|
|
2
2
|
import * as Y from 'yjs'
|
|
3
3
|
import { Encoder } from 'lib0/encoding'
|
|
4
|
+
import { AuthenticationMessage } from './OutgoingMessages/AuthenticationMessage'
|
|
5
|
+
import { AwarenessMessage } from './OutgoingMessages/AwarenessMessage'
|
|
6
|
+
import { QueryAwarenessMessage } from './OutgoingMessages/QueryAwarenessMessage'
|
|
7
|
+
import { SyncStepOneMessage } from './OutgoingMessages/SyncStepOneMessage'
|
|
8
|
+
import { SyncStepTwoMessage } from './OutgoingMessages/SyncStepTwoMessage'
|
|
9
|
+
import { UpdateMessage } from './OutgoingMessages/UpdateMessage'
|
|
4
10
|
|
|
5
11
|
export enum MessageType {
|
|
6
12
|
Sync = 0,
|
|
@@ -27,3 +33,11 @@ export interface OutgoingMessageArguments {
|
|
|
27
33
|
export interface Constructable<T> {
|
|
28
34
|
new(...args: any) : T
|
|
29
35
|
}
|
|
36
|
+
|
|
37
|
+
export type ConstructableOutgoingMessage =
|
|
38
|
+
Constructable<AuthenticationMessage> |
|
|
39
|
+
Constructable<AwarenessMessage> |
|
|
40
|
+
Constructable<QueryAwarenessMessage> |
|
|
41
|
+
Constructable<SyncStepOneMessage> |
|
|
42
|
+
Constructable<SyncStepTwoMessage> |
|
|
43
|
+
Constructable<UpdateMessage>
|