@hocuspocus/provider 1.1.1 → 2.0.0-alpha.1
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 +370 -217
- package/dist/hocuspocus-provider.cjs.map +1 -1
- package/dist/hocuspocus-provider.esm.js +370 -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 +11 -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/OutgoingMessages/CloseMessage.d.ts +8 -0
- package/dist/packages/provider/src/index.d.ts +1 -0
- package/dist/packages/provider/src/types.d.ts +3 -1
- package/dist/packages/server/src/Connection.d.ts +6 -12
- 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/packages/server/src/types.d.ts +2 -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 +4 -3
- package/src/HocuspocusCloudProvider.ts +8 -1
- package/src/HocuspocusProvider.ts +108 -361
- 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/CloseMessage.ts +16 -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 +2 -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,27 @@ 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 { CloseMessage } from './OutgoingMessages/CloseMessage'
|
|
32
|
+
import { onAwarenessChangeParameters, onAwarenessUpdateParameters } from '.'
|
|
27
33
|
|
|
28
34
|
export type HocuspocusProviderConfiguration =
|
|
29
|
-
Required<Pick<CompleteHocuspocusProviderConfiguration, '
|
|
35
|
+
Required<Pick<CompleteHocuspocusProviderConfiguration, 'name' | 'websocketProvider'>>
|
|
30
36
|
& Partial<CompleteHocuspocusProviderConfiguration>
|
|
31
37
|
|
|
32
38
|
export interface CompleteHocuspocusProviderConfiguration {
|
|
33
|
-
/**
|
|
34
|
-
* URL of your @hocuspocus/server instance
|
|
35
|
-
*/
|
|
36
|
-
url: string,
|
|
37
39
|
/**
|
|
38
40
|
* The identifier/name of your document
|
|
39
41
|
*/
|
|
@@ -42,10 +44,7 @@ export interface CompleteHocuspocusProviderConfiguration {
|
|
|
42
44
|
* The actual Y.js document
|
|
43
45
|
*/
|
|
44
46
|
document: Y.Doc,
|
|
45
|
-
|
|
46
|
-
* Pass `false` to start the connection manually.
|
|
47
|
-
*/
|
|
48
|
-
connect: boolean,
|
|
47
|
+
|
|
49
48
|
/**
|
|
50
49
|
* Pass false to disable broadcasting between browser tabs.
|
|
51
50
|
*/
|
|
@@ -63,49 +62,14 @@ export interface CompleteHocuspocusProviderConfiguration {
|
|
|
63
62
|
*/
|
|
64
63
|
parameters: { [key: string]: any },
|
|
65
64
|
/**
|
|
66
|
-
*
|
|
65
|
+
* Hocuspocus websocket provider
|
|
67
66
|
*/
|
|
68
|
-
|
|
67
|
+
websocketProvider: HocuspocusProviderWebsocket,
|
|
69
68
|
/**
|
|
70
69
|
* Force syncing the document in the defined interval.
|
|
71
70
|
*/
|
|
72
71
|
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,
|
|
72
|
+
|
|
109
73
|
onAuthenticated: () => void,
|
|
110
74
|
onAuthenticationFailed: (data: onAuthenticationFailedParameters) => void,
|
|
111
75
|
onOpen: (data: onOpenParameters) => void,
|
|
@@ -130,35 +94,14 @@ export interface CompleteHocuspocusProviderConfiguration {
|
|
|
130
94
|
export class HocuspocusProvider extends EventEmitter {
|
|
131
95
|
public configuration: CompleteHocuspocusProviderConfiguration = {
|
|
132
96
|
name: '',
|
|
133
|
-
url: '',
|
|
134
97
|
// @ts-ignore
|
|
135
98
|
document: undefined,
|
|
136
99
|
// @ts-ignore
|
|
137
100
|
awareness: undefined,
|
|
138
|
-
WebSocketPolyfill: undefined,
|
|
139
101
|
token: null,
|
|
140
102
|
parameters: {},
|
|
141
|
-
connect: true,
|
|
142
103
|
broadcast: true,
|
|
143
104
|
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
105
|
onAuthenticated: () => null,
|
|
163
106
|
onAuthenticationFailed: () => null,
|
|
164
107
|
onOpen: () => null,
|
|
@@ -178,55 +121,59 @@ export class HocuspocusProvider extends EventEmitter {
|
|
|
178
121
|
|
|
179
122
|
subscribedToBroadcastChannel = false
|
|
180
123
|
|
|
181
|
-
webSocket: WebSocket | null = null
|
|
182
|
-
|
|
183
|
-
shouldConnect = true
|
|
184
|
-
|
|
185
|
-
status = WebSocketStatus.Disconnected
|
|
186
|
-
|
|
187
124
|
isSynced = false
|
|
188
125
|
|
|
189
126
|
unsyncedChanges = 0
|
|
190
127
|
|
|
191
|
-
|
|
128
|
+
status = WebSocketStatus.Disconnected
|
|
192
129
|
|
|
193
|
-
|
|
130
|
+
isAuthenticated = false
|
|
194
131
|
|
|
195
132
|
mux = mutex.createMutex()
|
|
196
133
|
|
|
197
134
|
intervals: any = {
|
|
198
135
|
forceSync: null,
|
|
199
|
-
connectionChecker: null,
|
|
200
136
|
}
|
|
201
137
|
|
|
202
|
-
connectionAttempt: {
|
|
203
|
-
resolve: (value?: any) => void
|
|
204
|
-
reject: (reason?: any) => void
|
|
205
|
-
} | null = null
|
|
206
|
-
|
|
207
138
|
constructor(configuration: HocuspocusProviderConfiguration) {
|
|
208
139
|
super()
|
|
209
140
|
this.setConfiguration(configuration)
|
|
210
141
|
|
|
211
142
|
this.configuration.document = configuration.document ? configuration.document : new Y.Doc()
|
|
212
143
|
this.configuration.awareness = configuration.awareness ? configuration.awareness : new Awareness(this.document)
|
|
213
|
-
this.configuration.WebSocketPolyfill = configuration.WebSocketPolyfill ? configuration.WebSocketPolyfill : WebSocket
|
|
214
144
|
|
|
215
145
|
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
146
|
this.on('message', this.configuration.onMessage)
|
|
220
147
|
this.on('outgoingMessage', this.configuration.onOutgoingMessage)
|
|
221
148
|
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
149
|
this.on('destroy', this.configuration.onDestroy)
|
|
226
150
|
this.on('awarenessUpdate', this.configuration.onAwarenessUpdate)
|
|
227
151
|
this.on('awarenessChange', this.configuration.onAwarenessChange)
|
|
228
152
|
this.on('stateless', this.configuration.onStateless)
|
|
229
153
|
|
|
154
|
+
this.on('authenticated', this.configuration.onAuthenticated)
|
|
155
|
+
this.on('authenticationFailed', this.configuration.onAuthenticationFailed)
|
|
156
|
+
|
|
157
|
+
this.configuration.websocketProvider.on('connect', this.configuration.onConnect)
|
|
158
|
+
this.configuration.websocketProvider.on('connect', (e: Event) => this.emit('connect', e))
|
|
159
|
+
|
|
160
|
+
this.configuration.websocketProvider.on('open', this.onOpen.bind(this))
|
|
161
|
+
this.configuration.websocketProvider.on('open', (e: Event) => this.emit('open', e))
|
|
162
|
+
|
|
163
|
+
this.configuration.websocketProvider.on('message', this.onMessage.bind(this))
|
|
164
|
+
|
|
165
|
+
this.configuration.websocketProvider.on('close', this.onClose.bind(this))
|
|
166
|
+
this.configuration.websocketProvider.on('close', this.configuration.onClose)
|
|
167
|
+
this.configuration.websocketProvider.on('close', (e: Event) => this.emit('close', e))
|
|
168
|
+
|
|
169
|
+
this.configuration.websocketProvider.on('status', this.onStatus.bind(this))
|
|
170
|
+
|
|
171
|
+
this.configuration.websocketProvider.on('disconnect', this.configuration.onDisconnect)
|
|
172
|
+
this.configuration.websocketProvider.on('disconnect', (e: Event) => this.emit('disconnect', e))
|
|
173
|
+
|
|
174
|
+
this.configuration.websocketProvider.on('destroy', this.configuration.onDestroy)
|
|
175
|
+
this.configuration.websocketProvider.on('destroy', (e: Event) => this.emit('destroy', e))
|
|
176
|
+
|
|
230
177
|
this.awareness.on('update', () => {
|
|
231
178
|
this.emit('awarenessUpdate', { states: awarenessStatesToArray(this.awareness.getStates()) })
|
|
232
179
|
})
|
|
@@ -239,11 +186,6 @@ export class HocuspocusProvider extends EventEmitter {
|
|
|
239
186
|
this.awareness.on('update', this.awarenessUpdateHandler.bind(this))
|
|
240
187
|
this.registerEventListeners()
|
|
241
188
|
|
|
242
|
-
this.intervals.connectionChecker = setInterval(
|
|
243
|
-
this.checkConnection.bind(this),
|
|
244
|
-
this.configuration.messageReconnectTimeout / 10,
|
|
245
|
-
)
|
|
246
|
-
|
|
247
189
|
if (this.configuration.forceSyncInterval) {
|
|
248
190
|
this.intervals.forceSync = setInterval(
|
|
249
191
|
this.forceSync.bind(this),
|
|
@@ -251,128 +193,20 @@ export class HocuspocusProvider extends EventEmitter {
|
|
|
251
193
|
)
|
|
252
194
|
}
|
|
253
195
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
}
|
|
196
|
+
this.configuration.websocketProvider.attach(this)
|
|
197
|
+
}
|
|
257
198
|
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
}
|
|
199
|
+
public onStatus({ status } : onStatusParameters) {
|
|
200
|
+
this.status = status
|
|
261
201
|
|
|
262
|
-
this.
|
|
202
|
+
this.configuration.onStatus({ status })
|
|
203
|
+
this.emit('status', { status })
|
|
263
204
|
}
|
|
264
205
|
|
|
265
206
|
public setConfiguration(configuration: Partial<HocuspocusProviderConfiguration> = {}): void {
|
|
266
207
|
this.configuration = { ...this.configuration, ...configuration }
|
|
267
208
|
}
|
|
268
209
|
|
|
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
210
|
get document() {
|
|
377
211
|
return this.configuration.document
|
|
378
212
|
}
|
|
@@ -385,33 +219,8 @@ export class HocuspocusProvider extends EventEmitter {
|
|
|
385
219
|
return this.unsyncedChanges > 0
|
|
386
220
|
}
|
|
387
221
|
|
|
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
222
|
forceSync() {
|
|
410
|
-
|
|
411
|
-
return
|
|
412
|
-
}
|
|
413
|
-
|
|
414
|
-
this.send(SyncStepOneMessage, { document: this.document })
|
|
223
|
+
this.send(SyncStepOneMessage, { document: this.document, documentName: this.configuration.name })
|
|
415
224
|
}
|
|
416
225
|
|
|
417
226
|
boundBeforeUnload = this.beforeUnload.bind(this)
|
|
@@ -425,12 +234,11 @@ export class HocuspocusProvider extends EventEmitter {
|
|
|
425
234
|
return
|
|
426
235
|
}
|
|
427
236
|
|
|
428
|
-
window.addEventListener('online', this.boundConnect)
|
|
429
237
|
window.addEventListener('beforeunload', this.boundBeforeUnload)
|
|
430
238
|
}
|
|
431
239
|
|
|
432
240
|
sendStateless(payload: string) {
|
|
433
|
-
this.send(StatelessMessage, { payload })
|
|
241
|
+
this.send(StatelessMessage, { documentName: this.configuration.name, payload })
|
|
434
242
|
}
|
|
435
243
|
|
|
436
244
|
documentUpdateHandler(update: Uint8Array, origin: any) {
|
|
@@ -439,7 +247,7 @@ export class HocuspocusProvider extends EventEmitter {
|
|
|
439
247
|
}
|
|
440
248
|
|
|
441
249
|
this.unsyncedChanges += 1
|
|
442
|
-
this.send(UpdateMessage, { update }, true)
|
|
250
|
+
this.send(UpdateMessage, { update, documentName: this.configuration.name }, true)
|
|
443
251
|
}
|
|
444
252
|
|
|
445
253
|
awarenessUpdateHandler({ added, updated, removed }: any, origin: any) {
|
|
@@ -448,37 +256,10 @@ export class HocuspocusProvider extends EventEmitter {
|
|
|
448
256
|
this.send(AwarenessMessage, {
|
|
449
257
|
awareness: this.awareness,
|
|
450
258
|
clients: changedClients,
|
|
259
|
+
documentName: this.configuration.name,
|
|
451
260
|
}, true)
|
|
452
261
|
}
|
|
453
262
|
|
|
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
263
|
get synced(): boolean {
|
|
483
264
|
return this.isSynced
|
|
484
265
|
}
|
|
@@ -501,19 +282,14 @@ export class HocuspocusProvider extends EventEmitter {
|
|
|
501
282
|
return !!this.configuration.token && !this.isAuthenticated
|
|
502
283
|
}
|
|
503
284
|
|
|
285
|
+
// not needed, but provides backward compatibility with e.g. lexicla/yjs
|
|
286
|
+
async connect() {
|
|
287
|
+
return this.configuration.websocketProvider.connect()
|
|
288
|
+
}
|
|
289
|
+
|
|
504
290
|
disconnect() {
|
|
505
|
-
this.shouldConnect = false
|
|
506
291
|
this.disconnectBroadcastChannel()
|
|
507
|
-
|
|
508
|
-
if (this.webSocket === null) {
|
|
509
|
-
return
|
|
510
|
-
}
|
|
511
|
-
|
|
512
|
-
try {
|
|
513
|
-
this.webSocket.close()
|
|
514
|
-
} catch {
|
|
515
|
-
//
|
|
516
|
-
}
|
|
292
|
+
this.configuration.websocketProvider.detach(this)
|
|
517
293
|
}
|
|
518
294
|
|
|
519
295
|
async onOpen(event: Event) {
|
|
@@ -522,8 +298,8 @@ export class HocuspocusProvider extends EventEmitter {
|
|
|
522
298
|
if (this.isAuthenticationRequired) {
|
|
523
299
|
this.send(AuthenticationMessage, {
|
|
524
300
|
token: await this.getToken(),
|
|
301
|
+
documentName: this.configuration.name,
|
|
525
302
|
})
|
|
526
|
-
return
|
|
527
303
|
}
|
|
528
304
|
|
|
529
305
|
this.startSync()
|
|
@@ -539,97 +315,54 @@ export class HocuspocusProvider extends EventEmitter {
|
|
|
539
315
|
}
|
|
540
316
|
|
|
541
317
|
startSync() {
|
|
542
|
-
this.send(SyncStepOneMessage, { document: this.document })
|
|
318
|
+
this.send(SyncStepOneMessage, { document: this.document, documentName: this.configuration.name })
|
|
543
319
|
|
|
544
320
|
if (this.awareness.getLocalState() !== null) {
|
|
545
321
|
this.send(AwarenessMessage, {
|
|
546
322
|
awareness: this.awareness,
|
|
547
323
|
clients: [this.document.clientID],
|
|
324
|
+
documentName: this.configuration.name,
|
|
548
325
|
})
|
|
549
326
|
}
|
|
550
327
|
}
|
|
551
328
|
|
|
552
|
-
send(
|
|
329
|
+
send(message: ConstructableOutgoingMessage, args: any, broadcast = false) {
|
|
553
330
|
if (broadcast) {
|
|
554
|
-
this.mux(() => { this.broadcast(
|
|
331
|
+
this.mux(() => { this.broadcast(message, args) })
|
|
555
332
|
}
|
|
556
333
|
|
|
557
|
-
|
|
558
|
-
const messageSender = new MessageSender(Message, args)
|
|
334
|
+
const messageSender = new MessageSender(message, args)
|
|
559
335
|
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
}
|
|
336
|
+
this.emit('outgoingMessage', { message: messageSender.message })
|
|
337
|
+
messageSender.send(this.configuration.websocketProvider)
|
|
563
338
|
}
|
|
564
339
|
|
|
565
340
|
onMessage(event: MessageEvent) {
|
|
566
|
-
|
|
341
|
+
const message = new IncomingMessage(event.data)
|
|
567
342
|
|
|
568
|
-
|
|
343
|
+
const documentName = message.readVarString()
|
|
569
344
|
|
|
570
|
-
|
|
345
|
+
if (documentName !== this.configuration.name) {
|
|
346
|
+
return // message is meant for another provider
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
message.writeVarString(documentName)
|
|
571
350
|
|
|
572
|
-
this.emit('message', { event, message })
|
|
351
|
+
this.emit('message', { event, message: new IncomingMessage(event.data) })
|
|
573
352
|
|
|
574
353
|
new MessageReceiver(message).apply(this)
|
|
575
354
|
}
|
|
576
355
|
|
|
577
356
|
onClose(event: CloseEvent) {
|
|
578
|
-
this.emit('close', { event })
|
|
579
|
-
|
|
580
|
-
this.webSocket = null
|
|
581
357
|
this.isAuthenticated = false
|
|
582
358
|
this.synced = false
|
|
583
359
|
|
|
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 })
|
|
360
|
+
// update awareness (all users except local left)
|
|
361
|
+
removeAwarenessStates(
|
|
362
|
+
this.awareness,
|
|
363
|
+
Array.from(this.awareness.getStates().keys()).filter(client => client !== this.document.clientID),
|
|
364
|
+
this,
|
|
365
|
+
)
|
|
633
366
|
}
|
|
634
367
|
|
|
635
368
|
destroy() {
|
|
@@ -639,15 +372,8 @@ export class HocuspocusProvider extends EventEmitter {
|
|
|
639
372
|
clearInterval(this.intervals.forceSync)
|
|
640
373
|
}
|
|
641
374
|
|
|
642
|
-
clearInterval(this.intervals.connectionChecker)
|
|
643
|
-
|
|
644
375
|
removeAwarenessStates(this.awareness, [this.document.clientID], 'provider destroy')
|
|
645
376
|
|
|
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
377
|
this.disconnect()
|
|
652
378
|
|
|
653
379
|
this.awareness.off('update', this.awarenessUpdateHandler)
|
|
@@ -655,16 +381,31 @@ export class HocuspocusProvider extends EventEmitter {
|
|
|
655
381
|
|
|
656
382
|
this.removeAllListeners()
|
|
657
383
|
|
|
384
|
+
this.send(CloseMessage, { documentName: this.configuration.name })
|
|
385
|
+
|
|
658
386
|
if (typeof window === 'undefined') {
|
|
659
387
|
return
|
|
660
388
|
}
|
|
661
389
|
|
|
662
|
-
window.removeEventListener('online', this.boundConnect)
|
|
663
390
|
window.removeEventListener('beforeunload', this.boundBeforeUnload)
|
|
664
391
|
}
|
|
665
392
|
|
|
393
|
+
permissionDeniedHandler(reason: string) {
|
|
394
|
+
this.emit('authenticationFailed', { reason })
|
|
395
|
+
this.isAuthenticated = false
|
|
396
|
+
this.disconnect()
|
|
397
|
+
this.status = WebSocketStatus.Disconnected
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
authenticatedHandler() {
|
|
401
|
+
this.isAuthenticated = true
|
|
402
|
+
|
|
403
|
+
this.emit('authenticated')
|
|
404
|
+
this.startSync()
|
|
405
|
+
}
|
|
406
|
+
|
|
666
407
|
get broadcastChannel() {
|
|
667
|
-
return `${this.
|
|
408
|
+
return `${this.configuration.name}`
|
|
668
409
|
}
|
|
669
410
|
|
|
670
411
|
boundBroadcastChannelSubscriber = this.broadcastChannelSubscriber.bind(this)
|
|
@@ -672,6 +413,11 @@ export class HocuspocusProvider extends EventEmitter {
|
|
|
672
413
|
broadcastChannelSubscriber(data: ArrayBuffer) {
|
|
673
414
|
this.mux(() => {
|
|
674
415
|
const message = new IncomingMessage(data)
|
|
416
|
+
|
|
417
|
+
const documentName = message.readVarString()
|
|
418
|
+
|
|
419
|
+
message.writeVarString(documentName)
|
|
420
|
+
|
|
675
421
|
new MessageReceiver(message)
|
|
676
422
|
.setBroadcasted(true)
|
|
677
423
|
.apply(this, false)
|
|
@@ -687,8 +433,8 @@ export class HocuspocusProvider extends EventEmitter {
|
|
|
687
433
|
this.mux(() => {
|
|
688
434
|
this.broadcast(SyncStepOneMessage, { document: this.document })
|
|
689
435
|
this.broadcast(SyncStepTwoMessage, { document: this.document })
|
|
690
|
-
this.broadcast(QueryAwarenessMessage)
|
|
691
|
-
this.broadcast(AwarenessMessage, { awareness: this.awareness, clients: [this.document.clientID] })
|
|
436
|
+
this.broadcast(QueryAwarenessMessage, { document: this.document })
|
|
437
|
+
this.broadcast(AwarenessMessage, { awareness: this.awareness, clients: [this.document.clientID], document: this.document })
|
|
692
438
|
})
|
|
693
439
|
}
|
|
694
440
|
|
|
@@ -698,6 +444,7 @@ export class HocuspocusProvider extends EventEmitter {
|
|
|
698
444
|
awareness: this.awareness,
|
|
699
445
|
clients: [this.document.clientID],
|
|
700
446
|
states: new Map(),
|
|
447
|
+
documentName: this.configuration.name,
|
|
701
448
|
}, true)
|
|
702
449
|
|
|
703
450
|
if (this.subscribedToBroadcastChannel) {
|