@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.
@@ -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
- token: string | (() => string) | (() => Promise<string>),
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
- reconnectTimeoutBase: number,
42
- maxReconnectTimeout: number,
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: WebSocketStatus = WebSocketStatus.Disconnected
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 (this.options.connect) {
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 close the connection when it’s not established anyway
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 just close then connection while waiting for the first message
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
- const token = await this.getToken()
354
- this.send(AuthenticationMessage, { token })
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: OutgoingMessage, args: any, broadcast = false) {
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.shouldConnect) {
421
- const wait = math.round(math.min(
422
- math.log10(this.failedConnectionAttempts + 1) * this.options.reconnectTimeoutBase,
423
- this.options.maxReconnectTimeout,
424
- ))
425
-
426
- this.log(`[close] Reconnecting in ${wait}ms …`)
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
- if (this.status !== WebSocketStatus.Disconnected) {
433
- this.status = WebSocketStatus.Disconnected
434
- this.emit('status', { status: 'disconnected' })
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, this)
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: OutgoingMessage, args: any) {
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
  }
@@ -23,7 +23,8 @@ export class MessageReceiver {
23
23
  }
24
24
 
25
25
  public apply(provider: HocuspocusProvider, emitSynced = true) {
26
- const type = this.message.readVarUint()
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) {
@@ -1,12 +1,6 @@
1
1
  import { Encoder, toUint8Array } from 'lib0/encoding'
2
2
  import * as bc from 'lib0/broadcastchannel'
3
- import { AuthenticationMessage } from './OutgoingMessages/AuthenticationMessage'
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>