@hocuspocus/provider 1.1.0 → 2.0.0-alpha.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 +352 -217
- package/dist/hocuspocus-provider.cjs.map +1 -1
- package/dist/hocuspocus-provider.esm.js +352 -218
- package/dist/hocuspocus-provider.esm.js.map +1 -1
- package/dist/packages/extension-monitor/src/Dashboard.d.ts +1 -1
- package/dist/packages/extension-monitor/src/index.d.ts +1 -1
- package/dist/packages/provider/src/HocuspocusCloudProvider.d.ts +2 -1
- package/dist/packages/provider/src/HocuspocusProvider.d.ts +10 -69
- package/dist/packages/provider/src/HocuspocusProviderWebsocket.d.ts +115 -0
- package/dist/packages/provider/src/IncomingMessage.d.ts +2 -0
- package/dist/packages/provider/src/index.d.ts +1 -0
- package/dist/packages/provider/src/types.d.ts +1 -0
- package/dist/packages/server/src/Connection.d.ts +1 -11
- package/dist/packages/server/src/Hocuspocus.d.ts +3 -8
- package/dist/packages/server/src/IncomingMessage.d.ts +1 -0
- package/dist/packages/server/src/MessageReceiver.d.ts +1 -2
- package/dist/packages/server/src/OutgoingMessage.d.ts +1 -1
- package/dist/tests/utils/index.d.ts +1 -0
- package/dist/tests/utils/newHocuspocusProvider.d.ts +2 -2
- package/dist/tests/utils/newHocuspocusProviderWebsocket.d.ts +3 -0
- package/package.json +3 -3
- package/src/HocuspocusCloudProvider.ts +8 -1
- package/src/HocuspocusProvider.ts +100 -360
- package/src/HocuspocusProviderWebsocket.ts +475 -0
- package/src/IncomingMessage.ts +10 -0
- package/src/MessageReceiver.ts +4 -2
- package/src/OutgoingMessages/AuthenticationMessage.ts +2 -1
- package/src/OutgoingMessages/AwarenessMessage.ts +1 -0
- package/src/OutgoingMessages/QueryAwarenessMessage.ts +5 -0
- package/src/OutgoingMessages/StatelessMessage.ts +1 -0
- package/src/OutgoingMessages/SyncStepOneMessage.ts +1 -0
- package/src/OutgoingMessages/SyncStepTwoMessage.ts +1 -1
- package/src/OutgoingMessages/UpdateMessage.ts +3 -1
- package/src/index.ts +1 -0
- package/src/types.ts +1 -0
- package/dist/tests/server/getDocumentName.d.ts +0 -1
- /package/dist/tests/{provider → providerwebsocket}/configuration.d.ts +0 -0
|
@@ -1,14 +1,9 @@
|
|
|
1
1
|
import * as Y from 'yjs'
|
|
2
2
|
import * as bc from 'lib0/broadcastchannel'
|
|
3
|
-
import * as time from 'lib0/time'
|
|
4
3
|
import { Awareness, removeAwarenessStates } from 'y-protocols/awareness'
|
|
5
4
|
import * as mutex from 'lib0/mutex'
|
|
6
|
-
import
|
|
7
|
-
import
|
|
8
|
-
import { retry } from '@lifeomic/attempt'
|
|
9
|
-
import {
|
|
10
|
-
awarenessStatesToArray, Forbidden, Unauthorized, WsReadyStates,
|
|
11
|
-
} from '@hocuspocus/common'
|
|
5
|
+
import type { CloseEvent, Event, MessageEvent } from 'ws'
|
|
6
|
+
import { awarenessStatesToArray } from '@hocuspocus/common'
|
|
12
7
|
import EventEmitter from './EventEmitter'
|
|
13
8
|
import { IncomingMessage } from './IncomingMessage'
|
|
14
9
|
import { MessageReceiver } from './MessageReceiver'
|
|
@@ -20,20 +15,26 @@ import { AuthenticationMessage } from './OutgoingMessages/AuthenticationMessage'
|
|
|
20
15
|
import { AwarenessMessage } from './OutgoingMessages/AwarenessMessage'
|
|
21
16
|
import { UpdateMessage } from './OutgoingMessages/UpdateMessage'
|
|
22
17
|
import {
|
|
23
|
-
ConstructableOutgoingMessage,
|
|
18
|
+
ConstructableOutgoingMessage,
|
|
19
|
+
onAuthenticationFailedParameters,
|
|
20
|
+
onCloseParameters,
|
|
21
|
+
onDisconnectParameters,
|
|
22
|
+
onMessageParameters,
|
|
23
|
+
onOpenParameters,
|
|
24
|
+
onOutgoingMessageParameters, onStatelessParameters,
|
|
25
|
+
onStatusParameters,
|
|
26
|
+
onSyncedParameters,
|
|
27
|
+
WebSocketStatus,
|
|
24
28
|
} from './types'
|
|
25
|
-
import {
|
|
29
|
+
import { HocuspocusProviderWebsocket } from './HocuspocusProviderWebsocket'
|
|
26
30
|
import { StatelessMessage } from './OutgoingMessages/StatelessMessage'
|
|
31
|
+
import { onAwarenessChangeParameters, onAwarenessUpdateParameters } from '.'
|
|
27
32
|
|
|
28
33
|
export type HocuspocusProviderConfiguration =
|
|
29
|
-
Required<Pick<CompleteHocuspocusProviderConfiguration, '
|
|
34
|
+
Required<Pick<CompleteHocuspocusProviderConfiguration, 'name' | 'websocketProvider'>>
|
|
30
35
|
& Partial<CompleteHocuspocusProviderConfiguration>
|
|
31
36
|
|
|
32
37
|
export interface CompleteHocuspocusProviderConfiguration {
|
|
33
|
-
/**
|
|
34
|
-
* URL of your @hocuspocus/server instance
|
|
35
|
-
*/
|
|
36
|
-
url: string,
|
|
37
38
|
/**
|
|
38
39
|
* The identifier/name of your document
|
|
39
40
|
*/
|
|
@@ -42,10 +43,7 @@ export interface CompleteHocuspocusProviderConfiguration {
|
|
|
42
43
|
* The actual Y.js document
|
|
43
44
|
*/
|
|
44
45
|
document: Y.Doc,
|
|
45
|
-
|
|
46
|
-
* Pass `false` to start the connection manually.
|
|
47
|
-
*/
|
|
48
|
-
connect: boolean,
|
|
46
|
+
|
|
49
47
|
/**
|
|
50
48
|
* Pass false to disable broadcasting between browser tabs.
|
|
51
49
|
*/
|
|
@@ -63,49 +61,14 @@ export interface CompleteHocuspocusProviderConfiguration {
|
|
|
63
61
|
*/
|
|
64
62
|
parameters: { [key: string]: any },
|
|
65
63
|
/**
|
|
66
|
-
*
|
|
64
|
+
* Hocuspocus websocket provider
|
|
67
65
|
*/
|
|
68
|
-
|
|
66
|
+
websocketProvider: HocuspocusProviderWebsocket,
|
|
69
67
|
/**
|
|
70
68
|
* Force syncing the document in the defined interval.
|
|
71
69
|
*/
|
|
72
70
|
forceSyncInterval: false | number,
|
|
73
|
-
|
|
74
|
-
* Disconnect when no message is received for the defined amount of milliseconds.
|
|
75
|
-
*/
|
|
76
|
-
messageReconnectTimeout: number,
|
|
77
|
-
/**
|
|
78
|
-
* The delay between each attempt in milliseconds. You can provide a factor to have the delay grow exponentially.
|
|
79
|
-
*/
|
|
80
|
-
delay: number,
|
|
81
|
-
/**
|
|
82
|
-
* 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.
|
|
83
|
-
*/
|
|
84
|
-
initialDelay: number,
|
|
85
|
-
/**
|
|
86
|
-
* The factor option is used to grow the delay exponentially.
|
|
87
|
-
*/
|
|
88
|
-
factor: number,
|
|
89
|
-
/**
|
|
90
|
-
* The maximum number of attempts or 0 if there is no limit on number of attempts.
|
|
91
|
-
*/
|
|
92
|
-
maxAttempts: number,
|
|
93
|
-
/**
|
|
94
|
-
* minDelay is used to set a lower bound of delay when jitter is enabled. This property has no effect if jitter is disabled.
|
|
95
|
-
*/
|
|
96
|
-
minDelay: number,
|
|
97
|
-
/**
|
|
98
|
-
* 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.
|
|
99
|
-
*/
|
|
100
|
-
maxDelay: number,
|
|
101
|
-
/**
|
|
102
|
-
* If jitter is true then the calculated delay will be a random integer value between minDelay and the calculated delay for the current iteration.
|
|
103
|
-
*/
|
|
104
|
-
jitter: boolean,
|
|
105
|
-
/**
|
|
106
|
-
* 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.
|
|
107
|
-
*/
|
|
108
|
-
timeout: number,
|
|
71
|
+
|
|
109
72
|
onAuthenticated: () => void,
|
|
110
73
|
onAuthenticationFailed: (data: onAuthenticationFailedParameters) => void,
|
|
111
74
|
onOpen: (data: onOpenParameters) => void,
|
|
@@ -130,35 +93,14 @@ export interface CompleteHocuspocusProviderConfiguration {
|
|
|
130
93
|
export class HocuspocusProvider extends EventEmitter {
|
|
131
94
|
public configuration: CompleteHocuspocusProviderConfiguration = {
|
|
132
95
|
name: '',
|
|
133
|
-
url: '',
|
|
134
96
|
// @ts-ignore
|
|
135
97
|
document: undefined,
|
|
136
98
|
// @ts-ignore
|
|
137
99
|
awareness: undefined,
|
|
138
|
-
WebSocketPolyfill: undefined,
|
|
139
100
|
token: null,
|
|
140
101
|
parameters: {},
|
|
141
|
-
connect: true,
|
|
142
102
|
broadcast: true,
|
|
143
103
|
forceSyncInterval: false,
|
|
144
|
-
// TODO: this should depend on awareness.outdatedTime
|
|
145
|
-
messageReconnectTimeout: 30000,
|
|
146
|
-
// 1 second
|
|
147
|
-
delay: 1000,
|
|
148
|
-
// instant
|
|
149
|
-
initialDelay: 0,
|
|
150
|
-
// double the delay each time
|
|
151
|
-
factor: 2,
|
|
152
|
-
// unlimited retries
|
|
153
|
-
maxAttempts: 0,
|
|
154
|
-
// wait at least 1 second
|
|
155
|
-
minDelay: 1000,
|
|
156
|
-
// at least every 30 seconds
|
|
157
|
-
maxDelay: 30000,
|
|
158
|
-
// randomize
|
|
159
|
-
jitter: true,
|
|
160
|
-
// retry forever
|
|
161
|
-
timeout: 0,
|
|
162
104
|
onAuthenticated: () => null,
|
|
163
105
|
onAuthenticationFailed: () => null,
|
|
164
106
|
onOpen: () => null,
|
|
@@ -178,55 +120,59 @@ export class HocuspocusProvider extends EventEmitter {
|
|
|
178
120
|
|
|
179
121
|
subscribedToBroadcastChannel = false
|
|
180
122
|
|
|
181
|
-
webSocket: WebSocket | null = null
|
|
182
|
-
|
|
183
|
-
shouldConnect = true
|
|
184
|
-
|
|
185
|
-
status = WebSocketStatus.Disconnected
|
|
186
|
-
|
|
187
123
|
isSynced = false
|
|
188
124
|
|
|
189
125
|
unsyncedChanges = 0
|
|
190
126
|
|
|
191
|
-
|
|
127
|
+
status = WebSocketStatus.Disconnected
|
|
192
128
|
|
|
193
|
-
|
|
129
|
+
isAuthenticated = false
|
|
194
130
|
|
|
195
131
|
mux = mutex.createMutex()
|
|
196
132
|
|
|
197
133
|
intervals: any = {
|
|
198
134
|
forceSync: null,
|
|
199
|
-
connectionChecker: null,
|
|
200
135
|
}
|
|
201
136
|
|
|
202
|
-
connectionAttempt: {
|
|
203
|
-
resolve: (value?: any) => void
|
|
204
|
-
reject: (reason?: any) => void
|
|
205
|
-
} | null = null
|
|
206
|
-
|
|
207
137
|
constructor(configuration: HocuspocusProviderConfiguration) {
|
|
208
138
|
super()
|
|
209
139
|
this.setConfiguration(configuration)
|
|
210
140
|
|
|
211
141
|
this.configuration.document = configuration.document ? configuration.document : new Y.Doc()
|
|
212
142
|
this.configuration.awareness = configuration.awareness ? configuration.awareness : new Awareness(this.document)
|
|
213
|
-
this.configuration.WebSocketPolyfill = configuration.WebSocketPolyfill ? configuration.WebSocketPolyfill : WebSocket
|
|
214
143
|
|
|
215
144
|
this.on('open', this.configuration.onOpen)
|
|
216
|
-
this.on('authenticated', this.configuration.onAuthenticated)
|
|
217
|
-
this.on('authenticationFailed', this.configuration.onAuthenticationFailed)
|
|
218
|
-
this.on('connect', this.configuration.onConnect)
|
|
219
145
|
this.on('message', this.configuration.onMessage)
|
|
220
146
|
this.on('outgoingMessage', this.configuration.onOutgoingMessage)
|
|
221
147
|
this.on('synced', this.configuration.onSynced)
|
|
222
|
-
this.on('status', this.configuration.onStatus)
|
|
223
|
-
this.on('disconnect', this.configuration.onDisconnect)
|
|
224
|
-
this.on('close', this.configuration.onClose)
|
|
225
148
|
this.on('destroy', this.configuration.onDestroy)
|
|
226
149
|
this.on('awarenessUpdate', this.configuration.onAwarenessUpdate)
|
|
227
150
|
this.on('awarenessChange', this.configuration.onAwarenessChange)
|
|
228
151
|
this.on('stateless', this.configuration.onStateless)
|
|
229
152
|
|
|
153
|
+
this.on('authenticated', this.configuration.onAuthenticated)
|
|
154
|
+
this.on('authenticationFailed', this.configuration.onAuthenticationFailed)
|
|
155
|
+
|
|
156
|
+
this.configuration.websocketProvider.on('connect', this.configuration.onConnect)
|
|
157
|
+
this.configuration.websocketProvider.on('connect', (e: Event) => this.emit('connect', e))
|
|
158
|
+
|
|
159
|
+
this.configuration.websocketProvider.on('open', this.onOpen.bind(this))
|
|
160
|
+
this.configuration.websocketProvider.on('open', (e: Event) => this.emit('open', e))
|
|
161
|
+
|
|
162
|
+
this.configuration.websocketProvider.on('message', this.onMessage.bind(this))
|
|
163
|
+
|
|
164
|
+
this.configuration.websocketProvider.on('close', this.onClose.bind(this))
|
|
165
|
+
this.configuration.websocketProvider.on('close', this.configuration.onClose)
|
|
166
|
+
this.configuration.websocketProvider.on('close', (e: Event) => this.emit('close', e))
|
|
167
|
+
|
|
168
|
+
this.configuration.websocketProvider.on('status', this.onStatus.bind(this))
|
|
169
|
+
|
|
170
|
+
this.configuration.websocketProvider.on('disconnect', this.configuration.onDisconnect)
|
|
171
|
+
this.configuration.websocketProvider.on('disconnect', (e: Event) => this.emit('disconnect', e))
|
|
172
|
+
|
|
173
|
+
this.configuration.websocketProvider.on('destroy', this.configuration.onDestroy)
|
|
174
|
+
this.configuration.websocketProvider.on('destroy', (e: Event) => this.emit('destroy', e))
|
|
175
|
+
|
|
230
176
|
this.awareness.on('update', () => {
|
|
231
177
|
this.emit('awarenessUpdate', { states: awarenessStatesToArray(this.awareness.getStates()) })
|
|
232
178
|
})
|
|
@@ -239,11 +185,6 @@ export class HocuspocusProvider extends EventEmitter {
|
|
|
239
185
|
this.awareness.on('update', this.awarenessUpdateHandler.bind(this))
|
|
240
186
|
this.registerEventListeners()
|
|
241
187
|
|
|
242
|
-
this.intervals.connectionChecker = setInterval(
|
|
243
|
-
this.checkConnection.bind(this),
|
|
244
|
-
this.configuration.messageReconnectTimeout / 10,
|
|
245
|
-
)
|
|
246
|
-
|
|
247
188
|
if (this.configuration.forceSyncInterval) {
|
|
248
189
|
this.intervals.forceSync = setInterval(
|
|
249
190
|
this.forceSync.bind(this),
|
|
@@ -251,128 +192,20 @@ export class HocuspocusProvider extends EventEmitter {
|
|
|
251
192
|
)
|
|
252
193
|
}
|
|
253
194
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
}
|
|
195
|
+
this.configuration.websocketProvider.attach(this)
|
|
196
|
+
}
|
|
257
197
|
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
}
|
|
198
|
+
public onStatus({ status } : onStatusParameters) {
|
|
199
|
+
this.status = status
|
|
261
200
|
|
|
262
|
-
this.
|
|
201
|
+
this.configuration.onStatus({ status })
|
|
202
|
+
this.emit('status', { status })
|
|
263
203
|
}
|
|
264
204
|
|
|
265
205
|
public setConfiguration(configuration: Partial<HocuspocusProviderConfiguration> = {}): void {
|
|
266
206
|
this.configuration = { ...this.configuration, ...configuration }
|
|
267
207
|
}
|
|
268
208
|
|
|
269
|
-
boundConnect = this.connect.bind(this)
|
|
270
|
-
|
|
271
|
-
cancelWebsocketRetry?: () => void
|
|
272
|
-
|
|
273
|
-
async connect() {
|
|
274
|
-
if (this.status === WebSocketStatus.Connected) {
|
|
275
|
-
return
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
// Always cancel any previously initiated connection retryer instances
|
|
279
|
-
if (this.cancelWebsocketRetry) {
|
|
280
|
-
this.cancelWebsocketRetry()
|
|
281
|
-
this.cancelWebsocketRetry = undefined
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
this.unsyncedChanges = 0 // set to 0 in case we got reconnected
|
|
285
|
-
this.shouldConnect = true
|
|
286
|
-
this.subscribeToBroadcastChannel()
|
|
287
|
-
|
|
288
|
-
const abortableRetry = () => {
|
|
289
|
-
let cancelAttempt = false
|
|
290
|
-
|
|
291
|
-
const retryPromise = retry(this.createWebSocketConnection.bind(this), {
|
|
292
|
-
delay: this.configuration.delay,
|
|
293
|
-
initialDelay: this.configuration.initialDelay,
|
|
294
|
-
factor: this.configuration.factor,
|
|
295
|
-
maxAttempts: this.configuration.maxAttempts,
|
|
296
|
-
minDelay: this.configuration.minDelay,
|
|
297
|
-
maxDelay: this.configuration.maxDelay,
|
|
298
|
-
jitter: this.configuration.jitter,
|
|
299
|
-
timeout: this.configuration.timeout,
|
|
300
|
-
beforeAttempt: context => {
|
|
301
|
-
if (!this.shouldConnect || cancelAttempt) {
|
|
302
|
-
context.abort()
|
|
303
|
-
}
|
|
304
|
-
},
|
|
305
|
-
}).catch((error: any) => {
|
|
306
|
-
// If we aborted the connection attempt then don’t throw an error
|
|
307
|
-
// ref: https://github.com/lifeomic/attempt/blob/master/src/index.ts#L136
|
|
308
|
-
if (error && error.code !== 'ATTEMPT_ABORTED') {
|
|
309
|
-
throw error
|
|
310
|
-
}
|
|
311
|
-
})
|
|
312
|
-
|
|
313
|
-
return {
|
|
314
|
-
retryPromise,
|
|
315
|
-
cancelFunc: () => {
|
|
316
|
-
cancelAttempt = true
|
|
317
|
-
},
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
const { retryPromise, cancelFunc } = abortableRetry()
|
|
322
|
-
this.cancelWebsocketRetry = cancelFunc
|
|
323
|
-
|
|
324
|
-
return retryPromise
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
createWebSocketConnection() {
|
|
328
|
-
return new Promise((resolve, reject) => {
|
|
329
|
-
if (this.webSocket) {
|
|
330
|
-
this.webSocket.close()
|
|
331
|
-
this.webSocket = null
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
// Init the WebSocket connection
|
|
335
|
-
const ws = new this.configuration.WebSocketPolyfill(this.url)
|
|
336
|
-
ws.binaryType = 'arraybuffer'
|
|
337
|
-
ws.onmessage = this.onMessage.bind(this)
|
|
338
|
-
ws.onclose = this.onClose.bind(this)
|
|
339
|
-
ws.onopen = this.onOpen.bind(this)
|
|
340
|
-
ws.onerror = (err: any) => {
|
|
341
|
-
reject(err)
|
|
342
|
-
}
|
|
343
|
-
this.webSocket = ws
|
|
344
|
-
|
|
345
|
-
// Reset the status
|
|
346
|
-
this.synced = false
|
|
347
|
-
this.status = WebSocketStatus.Connecting
|
|
348
|
-
this.emit('status', { status: WebSocketStatus.Connecting })
|
|
349
|
-
|
|
350
|
-
// Store resolve/reject for later use
|
|
351
|
-
this.connectionAttempt = {
|
|
352
|
-
resolve,
|
|
353
|
-
reject,
|
|
354
|
-
}
|
|
355
|
-
})
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
resolveConnectionAttempt() {
|
|
359
|
-
this.connectionAttempt?.resolve()
|
|
360
|
-
this.connectionAttempt = null
|
|
361
|
-
|
|
362
|
-
this.status = WebSocketStatus.Connected
|
|
363
|
-
this.emit('status', { status: WebSocketStatus.Connected })
|
|
364
|
-
this.emit('connect')
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
stopConnectionAttempt() {
|
|
368
|
-
this.connectionAttempt = null
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
rejectConnectionAttempt() {
|
|
372
|
-
this.connectionAttempt?.reject()
|
|
373
|
-
this.connectionAttempt = null
|
|
374
|
-
}
|
|
375
|
-
|
|
376
209
|
get document() {
|
|
377
210
|
return this.configuration.document
|
|
378
211
|
}
|
|
@@ -385,33 +218,8 @@ export class HocuspocusProvider extends EventEmitter {
|
|
|
385
218
|
return this.unsyncedChanges > 0
|
|
386
219
|
}
|
|
387
220
|
|
|
388
|
-
checkConnection() {
|
|
389
|
-
// Don’t check the connection when it’s not even established
|
|
390
|
-
if (this.status !== WebSocketStatus.Connected) {
|
|
391
|
-
return
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
// Don’t close then connection while waiting for the first message
|
|
395
|
-
if (!this.lastMessageReceived) {
|
|
396
|
-
return
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
// Don’t close the connection when a message was received recently
|
|
400
|
-
if (this.configuration.messageReconnectTimeout >= time.getUnixTime() - this.lastMessageReceived) {
|
|
401
|
-
return
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
// No message received in a long time, not even your own
|
|
405
|
-
// Awareness updates, which are updated every 15 seconds.
|
|
406
|
-
this.webSocket?.close()
|
|
407
|
-
}
|
|
408
|
-
|
|
409
221
|
forceSync() {
|
|
410
|
-
|
|
411
|
-
return
|
|
412
|
-
}
|
|
413
|
-
|
|
414
|
-
this.send(SyncStepOneMessage, { document: this.document })
|
|
222
|
+
this.send(SyncStepOneMessage, { document: this.document, documentName: this.configuration.name })
|
|
415
223
|
}
|
|
416
224
|
|
|
417
225
|
boundBeforeUnload = this.beforeUnload.bind(this)
|
|
@@ -425,12 +233,11 @@ export class HocuspocusProvider extends EventEmitter {
|
|
|
425
233
|
return
|
|
426
234
|
}
|
|
427
235
|
|
|
428
|
-
window.addEventListener('online', this.boundConnect)
|
|
429
236
|
window.addEventListener('beforeunload', this.boundBeforeUnload)
|
|
430
237
|
}
|
|
431
238
|
|
|
432
239
|
sendStateless(payload: string) {
|
|
433
|
-
this.send(StatelessMessage, { payload })
|
|
240
|
+
this.send(StatelessMessage, { documentName: this.configuration.name, payload })
|
|
434
241
|
}
|
|
435
242
|
|
|
436
243
|
documentUpdateHandler(update: Uint8Array, origin: any) {
|
|
@@ -439,7 +246,7 @@ export class HocuspocusProvider extends EventEmitter {
|
|
|
439
246
|
}
|
|
440
247
|
|
|
441
248
|
this.unsyncedChanges += 1
|
|
442
|
-
this.send(UpdateMessage, { update }, true)
|
|
249
|
+
this.send(UpdateMessage, { update, documentName: this.configuration.name }, true)
|
|
443
250
|
}
|
|
444
251
|
|
|
445
252
|
awarenessUpdateHandler({ added, updated, removed }: any, origin: any) {
|
|
@@ -448,37 +255,10 @@ export class HocuspocusProvider extends EventEmitter {
|
|
|
448
255
|
this.send(AwarenessMessage, {
|
|
449
256
|
awareness: this.awareness,
|
|
450
257
|
clients: changedClients,
|
|
258
|
+
documentName: this.configuration.name,
|
|
451
259
|
}, true)
|
|
452
260
|
}
|
|
453
261
|
|
|
454
|
-
permissionDeniedHandler(reason: string) {
|
|
455
|
-
this.emit('authenticationFailed', { reason })
|
|
456
|
-
this.isAuthenticated = false
|
|
457
|
-
this.shouldConnect = false
|
|
458
|
-
}
|
|
459
|
-
|
|
460
|
-
authenticatedHandler() {
|
|
461
|
-
this.isAuthenticated = true
|
|
462
|
-
|
|
463
|
-
this.emit('authenticated')
|
|
464
|
-
this.startSync()
|
|
465
|
-
}
|
|
466
|
-
|
|
467
|
-
// Ensure that the URL always ends with /
|
|
468
|
-
get serverUrl() {
|
|
469
|
-
while (this.configuration.url[this.configuration.url.length - 1] === '/') {
|
|
470
|
-
return this.configuration.url.slice(0, this.configuration.url.length - 1)
|
|
471
|
-
}
|
|
472
|
-
|
|
473
|
-
return this.configuration.url
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
get url() {
|
|
477
|
-
const encodedParams = url.encodeQueryParams(this.configuration.parameters)
|
|
478
|
-
|
|
479
|
-
return `${this.serverUrl}/${this.configuration.name}${encodedParams.length === 0 ? '' : `?${encodedParams}`}`
|
|
480
|
-
}
|
|
481
|
-
|
|
482
262
|
get synced(): boolean {
|
|
483
263
|
return this.isSynced
|
|
484
264
|
}
|
|
@@ -502,18 +282,8 @@ export class HocuspocusProvider extends EventEmitter {
|
|
|
502
282
|
}
|
|
503
283
|
|
|
504
284
|
disconnect() {
|
|
505
|
-
this.shouldConnect = false
|
|
506
285
|
this.disconnectBroadcastChannel()
|
|
507
|
-
|
|
508
|
-
if (this.webSocket === null) {
|
|
509
|
-
return
|
|
510
|
-
}
|
|
511
|
-
|
|
512
|
-
try {
|
|
513
|
-
this.webSocket.close()
|
|
514
|
-
} catch {
|
|
515
|
-
//
|
|
516
|
-
}
|
|
286
|
+
this.configuration.websocketProvider.detach(this)
|
|
517
287
|
}
|
|
518
288
|
|
|
519
289
|
async onOpen(event: Event) {
|
|
@@ -522,6 +292,7 @@ export class HocuspocusProvider extends EventEmitter {
|
|
|
522
292
|
if (this.isAuthenticationRequired) {
|
|
523
293
|
this.send(AuthenticationMessage, {
|
|
524
294
|
token: await this.getToken(),
|
|
295
|
+
documentName: this.configuration.name,
|
|
525
296
|
})
|
|
526
297
|
return
|
|
527
298
|
}
|
|
@@ -539,97 +310,54 @@ export class HocuspocusProvider extends EventEmitter {
|
|
|
539
310
|
}
|
|
540
311
|
|
|
541
312
|
startSync() {
|
|
542
|
-
this.send(SyncStepOneMessage, { document: this.document })
|
|
313
|
+
this.send(SyncStepOneMessage, { document: this.document, documentName: this.configuration.name })
|
|
543
314
|
|
|
544
315
|
if (this.awareness.getLocalState() !== null) {
|
|
545
316
|
this.send(AwarenessMessage, {
|
|
546
317
|
awareness: this.awareness,
|
|
547
318
|
clients: [this.document.clientID],
|
|
319
|
+
documentName: this.configuration.name,
|
|
548
320
|
})
|
|
549
321
|
}
|
|
550
322
|
}
|
|
551
323
|
|
|
552
|
-
send(
|
|
324
|
+
send(message: ConstructableOutgoingMessage, args: any, broadcast = false) {
|
|
553
325
|
if (broadcast) {
|
|
554
|
-
this.mux(() => { this.broadcast(
|
|
326
|
+
this.mux(() => { this.broadcast(message, args) })
|
|
555
327
|
}
|
|
556
328
|
|
|
557
|
-
|
|
558
|
-
const messageSender = new MessageSender(Message, args)
|
|
329
|
+
const messageSender = new MessageSender(message, args)
|
|
559
330
|
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
}
|
|
331
|
+
this.emit('outgoingMessage', { message: messageSender.message })
|
|
332
|
+
messageSender.send(this.configuration.websocketProvider)
|
|
563
333
|
}
|
|
564
334
|
|
|
565
335
|
onMessage(event: MessageEvent) {
|
|
566
|
-
|
|
336
|
+
const message = new IncomingMessage(event.data)
|
|
567
337
|
|
|
568
|
-
|
|
338
|
+
const documentName = message.readVarString()
|
|
569
339
|
|
|
570
|
-
|
|
340
|
+
if (documentName !== this.configuration.name) {
|
|
341
|
+
return // message is meant for another provider
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
message.writeVarString(documentName)
|
|
571
345
|
|
|
572
|
-
this.emit('message', { event, message })
|
|
346
|
+
this.emit('message', { event, message: new IncomingMessage(event.data) })
|
|
573
347
|
|
|
574
348
|
new MessageReceiver(message).apply(this)
|
|
575
349
|
}
|
|
576
350
|
|
|
577
351
|
onClose(event: CloseEvent) {
|
|
578
|
-
this.emit('close', { event })
|
|
579
|
-
|
|
580
|
-
this.webSocket = null
|
|
581
352
|
this.isAuthenticated = false
|
|
582
353
|
this.synced = false
|
|
583
354
|
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
)
|
|
591
|
-
|
|
592
|
-
this.status = WebSocketStatus.Disconnected
|
|
593
|
-
this.emit('status', { status: WebSocketStatus.Disconnected })
|
|
594
|
-
this.emit('disconnect', { event })
|
|
595
|
-
}
|
|
596
|
-
|
|
597
|
-
if (event.code === Unauthorized.code) {
|
|
598
|
-
if (!this.configuration.quiet) {
|
|
599
|
-
console.warn('[HocuspocusProvider] An authentication token is required, but you didn’t send one. Try adding a `token` to your HocuspocusProvider configuration. Won’t try again.')
|
|
600
|
-
}
|
|
601
|
-
|
|
602
|
-
this.shouldConnect = false
|
|
603
|
-
}
|
|
604
|
-
|
|
605
|
-
if (event.code === Forbidden.code) {
|
|
606
|
-
if (!this.configuration.quiet) {
|
|
607
|
-
console.warn('[HocuspocusProvider] The provided authentication token isn’t allowed to connect to this server. Will try again.')
|
|
608
|
-
}
|
|
609
|
-
}
|
|
610
|
-
|
|
611
|
-
if (this.connectionAttempt) {
|
|
612
|
-
// That connection attempt failed.
|
|
613
|
-
this.rejectConnectionAttempt()
|
|
614
|
-
} else if (this.shouldConnect) {
|
|
615
|
-
// The connection was closed by the server. Let’s just try again.
|
|
616
|
-
this.connect()
|
|
617
|
-
}
|
|
618
|
-
|
|
619
|
-
// If we’ll reconnect, we’re done for now.
|
|
620
|
-
if (this.shouldConnect) {
|
|
621
|
-
return
|
|
622
|
-
}
|
|
623
|
-
|
|
624
|
-
// The status is set correctly already.
|
|
625
|
-
if (this.status === WebSocketStatus.Disconnected) {
|
|
626
|
-
return
|
|
627
|
-
}
|
|
628
|
-
|
|
629
|
-
// Let’s update the connection status.
|
|
630
|
-
this.status = WebSocketStatus.Disconnected
|
|
631
|
-
this.emit('status', { status: WebSocketStatus.Disconnected })
|
|
632
|
-
this.emit('disconnect', { event })
|
|
355
|
+
// update awareness (all users except local left)
|
|
356
|
+
removeAwarenessStates(
|
|
357
|
+
this.awareness,
|
|
358
|
+
Array.from(this.awareness.getStates().keys()).filter(client => client !== this.document.clientID),
|
|
359
|
+
this,
|
|
360
|
+
)
|
|
633
361
|
}
|
|
634
362
|
|
|
635
363
|
destroy() {
|
|
@@ -639,15 +367,8 @@ export class HocuspocusProvider extends EventEmitter {
|
|
|
639
367
|
clearInterval(this.intervals.forceSync)
|
|
640
368
|
}
|
|
641
369
|
|
|
642
|
-
clearInterval(this.intervals.connectionChecker)
|
|
643
|
-
|
|
644
370
|
removeAwarenessStates(this.awareness, [this.document.clientID], 'provider destroy')
|
|
645
371
|
|
|
646
|
-
// If there is still a connection attempt outstanding then we should stop
|
|
647
|
-
// it before calling disconnect, otherwise it will be rejected in the onClose
|
|
648
|
-
// handler and trigger a retry
|
|
649
|
-
this.stopConnectionAttempt()
|
|
650
|
-
|
|
651
372
|
this.disconnect()
|
|
652
373
|
|
|
653
374
|
this.awareness.off('update', this.awarenessUpdateHandler)
|
|
@@ -659,12 +380,25 @@ export class HocuspocusProvider extends EventEmitter {
|
|
|
659
380
|
return
|
|
660
381
|
}
|
|
661
382
|
|
|
662
|
-
window.removeEventListener('online', this.boundConnect)
|
|
663
383
|
window.removeEventListener('beforeunload', this.boundBeforeUnload)
|
|
664
384
|
}
|
|
665
385
|
|
|
386
|
+
permissionDeniedHandler(reason: string) {
|
|
387
|
+
this.emit('authenticationFailed', { reason })
|
|
388
|
+
this.isAuthenticated = false
|
|
389
|
+
this.disconnect()
|
|
390
|
+
this.status = WebSocketStatus.Disconnected
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
authenticatedHandler() {
|
|
394
|
+
this.isAuthenticated = true
|
|
395
|
+
|
|
396
|
+
this.emit('authenticated')
|
|
397
|
+
this.startSync()
|
|
398
|
+
}
|
|
399
|
+
|
|
666
400
|
get broadcastChannel() {
|
|
667
|
-
return `${this.
|
|
401
|
+
return `${this.configuration.name}`
|
|
668
402
|
}
|
|
669
403
|
|
|
670
404
|
boundBroadcastChannelSubscriber = this.broadcastChannelSubscriber.bind(this)
|
|
@@ -672,6 +406,11 @@ export class HocuspocusProvider extends EventEmitter {
|
|
|
672
406
|
broadcastChannelSubscriber(data: ArrayBuffer) {
|
|
673
407
|
this.mux(() => {
|
|
674
408
|
const message = new IncomingMessage(data)
|
|
409
|
+
|
|
410
|
+
const documentName = message.readVarString()
|
|
411
|
+
|
|
412
|
+
message.writeVarString(documentName)
|
|
413
|
+
|
|
675
414
|
new MessageReceiver(message)
|
|
676
415
|
.setBroadcasted(true)
|
|
677
416
|
.apply(this, false)
|
|
@@ -687,8 +426,8 @@ export class HocuspocusProvider extends EventEmitter {
|
|
|
687
426
|
this.mux(() => {
|
|
688
427
|
this.broadcast(SyncStepOneMessage, { document: this.document })
|
|
689
428
|
this.broadcast(SyncStepTwoMessage, { document: this.document })
|
|
690
|
-
this.broadcast(QueryAwarenessMessage)
|
|
691
|
-
this.broadcast(AwarenessMessage, { awareness: this.awareness, clients: [this.document.clientID] })
|
|
429
|
+
this.broadcast(QueryAwarenessMessage, { document: this.document })
|
|
430
|
+
this.broadcast(AwarenessMessage, { awareness: this.awareness, clients: [this.document.clientID], document: this.document })
|
|
692
431
|
})
|
|
693
432
|
}
|
|
694
433
|
|
|
@@ -698,6 +437,7 @@ export class HocuspocusProvider extends EventEmitter {
|
|
|
698
437
|
awareness: this.awareness,
|
|
699
438
|
clients: [this.document.clientID],
|
|
700
439
|
states: new Map(),
|
|
440
|
+
documentName: this.configuration.name,
|
|
701
441
|
}, true)
|
|
702
442
|
|
|
703
443
|
if (this.subscribedToBroadcastChannel) {
|