@hocuspocus/provider 1.0.0-alpha.25 → 1.0.0-alpha.29

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.
Files changed (31) hide show
  1. package/dist/hocuspocus-provider.cjs +132 -330
  2. package/dist/hocuspocus-provider.cjs.map +1 -1
  3. package/dist/hocuspocus-provider.esm.js +130 -329
  4. package/dist/hocuspocus-provider.esm.js.map +1 -1
  5. package/dist/packages/common/src/CloseEvents.d.ts +23 -0
  6. package/dist/packages/common/src/index.d.ts +1 -0
  7. package/dist/packages/extension-database/src/Database.d.ts +36 -0
  8. package/dist/packages/extension-database/src/index.d.ts +1 -0
  9. package/dist/packages/extension-logger/src/Logger.d.ts +2 -8
  10. package/dist/packages/extension-sqlite/src/SQLite.d.ts +26 -0
  11. package/dist/packages/extension-sqlite/src/index.d.ts +1 -0
  12. package/dist/packages/extension-webhook/src/index.d.ts +1 -1
  13. package/dist/packages/provider/src/HocuspocusCloudProvider.d.ts +11 -0
  14. package/dist/packages/provider/src/HocuspocusProvider.d.ts +10 -5
  15. package/dist/packages/provider/src/index.d.ts +1 -0
  16. package/dist/packages/server/src/Connection.d.ts +1 -1
  17. package/dist/packages/server/src/Hocuspocus.d.ts +15 -5
  18. package/dist/packages/server/src/types.d.ts +17 -8
  19. package/dist/{demos/backend/src/express.d.ts → playground/backend/src/default.d.ts} +0 -0
  20. package/dist/{demos/backend/src/koa.d.ts → playground/backend/src/express.d.ts} +0 -0
  21. package/dist/{demos/backend/src/load-document.d.ts → playground/backend/src/koa.d.ts} +0 -0
  22. package/dist/{demos/backend/src/minimal.d.ts → playground/backend/src/load-document.d.ts} +0 -0
  23. package/dist/{demos → playground}/backend/src/monitor.d.ts +0 -0
  24. package/dist/{demos → playground}/backend/src/redis.d.ts +0 -0
  25. package/dist/{demos → playground}/backend/src/slow.d.ts +0 -0
  26. package/dist/{demos → playground}/backend/src/webhook.d.ts +0 -0
  27. package/package.json +6 -8
  28. package/src/HocuspocusCloudProvider.ts +34 -0
  29. package/src/HocuspocusProvider.ts +95 -63
  30. package/src/index.ts +1 -0
  31. package/dist/packages/server/src/CloseEvents.d.ts +0 -4
@@ -6,6 +6,7 @@ import * as mutex from 'lib0/mutex'
6
6
  import * as url from 'lib0/url'
7
7
  import { Event, CloseEvent, MessageEvent } from 'ws'
8
8
  import { retry } from '@lifeomic/attempt'
9
+ import { Forbidden, Unauthorized } from '@hocuspocus/common'
9
10
  import EventEmitter from './EventEmitter'
10
11
  import { IncomingMessage } from './IncomingMessage'
11
12
  import { MessageReceiver } from './MessageReceiver'
@@ -26,15 +27,19 @@ export enum WebSocketStatus {
26
27
  Disconnected = 'disconnected',
27
28
  }
28
29
 
29
- export interface HocuspocusProviderOptions {
30
+ export type HocuspocusProviderConfiguration =
31
+ Required<Pick<CompleteHocuspocusProviderConfiguration, 'url' | 'name'>>
32
+ & Partial<CompleteHocuspocusProviderConfiguration>
33
+
34
+ export interface CompleteHocuspocusProviderConfiguration {
30
35
  /**
31
36
  * URL of your @hocuspocus/server instance
32
37
  */
33
- url: string,
34
- /**
35
- * The identifier/name of your document
36
- */
37
- name: string,
38
+ url: string,
39
+ /**
40
+ * The identifier/name of your document
41
+ */
42
+ name: string,
38
43
  /**
39
44
  * The actual Y.js document
40
45
  */
@@ -116,17 +121,21 @@ export interface HocuspocusProviderOptions {
116
121
  onDestroy: () => void,
117
122
  onAwarenessUpdate: (states: any) => void,
118
123
  onAwarenessChange: (states: any) => void,
124
+ /**
125
+ * Don’t output any warnings.
126
+ */
127
+ quiet: boolean,
119
128
  }
120
129
 
121
130
  export class HocuspocusProvider extends EventEmitter {
122
- public options: HocuspocusProviderOptions = {
131
+ public configuration: CompleteHocuspocusProviderConfiguration = {
132
+ name: '',
133
+ url: '',
123
134
  // @ts-ignore
124
135
  document: undefined,
125
136
  // @ts-ignore
126
137
  awareness: undefined,
127
138
  WebSocketPolyfill: undefined,
128
- url: '',
129
- name: '',
130
139
  token: null,
131
140
  parameters: {},
132
141
  connect: true,
@@ -163,6 +172,7 @@ export class HocuspocusProvider extends EventEmitter {
163
172
  onDestroy: () => null,
164
173
  onAwarenessUpdate: () => null,
165
174
  onAwarenessChange: () => null,
175
+ quiet: false,
166
176
  }
167
177
 
168
178
  subscribedToBroadcastChannel = false
@@ -191,26 +201,27 @@ export class HocuspocusProvider extends EventEmitter {
191
201
  reject: (reason?: any) => void
192
202
  } | null = null
193
203
 
194
- constructor(options: Partial<HocuspocusProviderOptions> = {}) {
204
+ constructor(configuration: HocuspocusProviderConfiguration) {
195
205
  super()
196
- this.setOptions(options)
197
-
198
- this.options.awareness = options.awareness ? options.awareness : new Awareness(this.document)
199
- this.options.WebSocketPolyfill = options.WebSocketPolyfill ? options.WebSocketPolyfill : WebSocket
200
-
201
- this.on('open', this.options.onOpen)
202
- this.on('authenticated', this.options.onAuthenticated)
203
- this.on('authenticationFailed', this.options.onAuthenticationFailed)
204
- this.on('connect', this.options.onConnect)
205
- this.on('message', this.options.onMessage)
206
- this.on('outgoingMessage', this.options.onOutgoingMessage)
207
- this.on('synced', this.options.onSynced)
208
- this.on('status', this.options.onStatus)
209
- this.on('disconnect', this.options.onDisconnect)
210
- this.on('close', this.options.onClose)
211
- this.on('destroy', this.options.onDestroy)
212
- this.on('awarenessUpdate', this.options.onAwarenessUpdate)
213
- this.on('awarenessChange', this.options.onAwarenessChange)
206
+ this.setConfiguration(configuration)
207
+
208
+ this.configuration.document = configuration.document ? configuration.document : new Y.Doc()
209
+ this.configuration.awareness = configuration.awareness ? configuration.awareness : new Awareness(this.document)
210
+ this.configuration.WebSocketPolyfill = configuration.WebSocketPolyfill ? configuration.WebSocketPolyfill : WebSocket
211
+
212
+ this.on('open', this.configuration.onOpen)
213
+ this.on('authenticated', this.configuration.onAuthenticated)
214
+ this.on('authenticationFailed', this.configuration.onAuthenticationFailed)
215
+ this.on('connect', this.configuration.onConnect)
216
+ this.on('message', this.configuration.onMessage)
217
+ this.on('outgoingMessage', this.configuration.onOutgoingMessage)
218
+ this.on('synced', this.configuration.onSynced)
219
+ this.on('status', this.configuration.onStatus)
220
+ this.on('disconnect', this.configuration.onDisconnect)
221
+ this.on('close', this.configuration.onClose)
222
+ this.on('destroy', this.configuration.onDestroy)
223
+ this.on('awarenessUpdate', this.configuration.onAwarenessUpdate)
224
+ this.on('awarenessChange', this.configuration.onAwarenessChange)
214
225
 
215
226
  this.awareness.on('update', () => {
216
227
  this.emit('awarenessUpdate', { states: awarenessStatesToArray(this.awareness.getStates()) })
@@ -222,22 +233,22 @@ export class HocuspocusProvider extends EventEmitter {
222
233
 
223
234
  this.document.on('update', this.documentUpdateHandler.bind(this))
224
235
  this.awareness.on('update', this.awarenessUpdateHandler.bind(this))
225
- this.registerBeforeUnloadEventListener()
236
+ this.registerEventListeners()
226
237
 
227
238
  this.intervals.connectionChecker = setInterval(
228
239
  this.checkConnection.bind(this),
229
- this.options.messageReconnectTimeout / 10,
240
+ this.configuration.messageReconnectTimeout / 10,
230
241
  )
231
242
 
232
- if (this.options.forceSyncInterval) {
243
+ if (this.configuration.forceSyncInterval) {
233
244
  this.intervals.forceSync = setInterval(
234
245
  this.forceSync.bind(this),
235
- this.options.forceSyncInterval,
246
+ this.configuration.forceSyncInterval,
236
247
  )
237
248
  }
238
249
 
239
- if (typeof options.connect !== 'undefined') {
240
- this.shouldConnect = options.connect
250
+ if (typeof configuration.connect !== 'undefined') {
251
+ this.shouldConnect = configuration.connect
241
252
  }
242
253
 
243
254
  if (!this.shouldConnect) {
@@ -247,8 +258,8 @@ export class HocuspocusProvider extends EventEmitter {
247
258
  this.connect()
248
259
  }
249
260
 
250
- public setOptions(options: Partial<HocuspocusProviderOptions> = {}): void {
251
- this.options = { ...this.options, ...options }
261
+ public setConfiguration(configuration: Partial<HocuspocusProviderConfiguration> = {}): void {
262
+ this.configuration = { ...this.configuration, ...configuration }
252
263
  }
253
264
 
254
265
  async connect() {
@@ -261,14 +272,14 @@ export class HocuspocusProvider extends EventEmitter {
261
272
 
262
273
  try {
263
274
  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,
275
+ delay: this.configuration.delay,
276
+ initialDelay: this.configuration.initialDelay,
277
+ factor: this.configuration.factor,
278
+ maxAttempts: this.configuration.maxAttempts,
279
+ minDelay: this.configuration.minDelay,
280
+ maxDelay: this.configuration.maxDelay,
281
+ jitter: this.configuration.jitter,
282
+ timeout: this.configuration.timeout,
272
283
  beforeAttempt: context => {
273
284
  if (!this.shouldConnect) {
274
285
  context.abort()
@@ -287,7 +298,7 @@ export class HocuspocusProvider extends EventEmitter {
287
298
  createWebSocketConnection() {
288
299
  return new Promise((resolve, reject) => {
289
300
  // Init the WebSocket connection
290
- const ws = new this.options.WebSocketPolyfill(this.url)
301
+ const ws = new this.configuration.WebSocketPolyfill(this.url)
291
302
  ws.binaryType = 'arraybuffer'
292
303
  ws.onmessage = this.onMessage.bind(this)
293
304
  ws.onclose = this.onClose.bind(this)
@@ -321,11 +332,11 @@ export class HocuspocusProvider extends EventEmitter {
321
332
  }
322
333
 
323
334
  get document() {
324
- return this.options.document
335
+ return this.configuration.document
325
336
  }
326
337
 
327
338
  get awareness() {
328
- return this.options.awareness
339
+ return this.configuration.awareness
329
340
  }
330
341
 
331
342
  checkConnection() {
@@ -340,7 +351,7 @@ export class HocuspocusProvider extends EventEmitter {
340
351
  }
341
352
 
342
353
  // Don’t close the connection when a message was received recently
343
- if (this.options.messageReconnectTimeout >= time.getUnixTime() - this.lastMessageReceived) {
354
+ if (this.configuration.messageReconnectTimeout >= time.getUnixTime() - this.lastMessageReceived) {
344
355
  return
345
356
  }
346
357
 
@@ -357,11 +368,12 @@ export class HocuspocusProvider extends EventEmitter {
357
368
  this.send(SyncStepOneMessage, { document: this.document })
358
369
  }
359
370
 
360
- registerBeforeUnloadEventListener() {
371
+ registerEventListeners() {
361
372
  if (typeof window === 'undefined') {
362
373
  return
363
374
  }
364
375
 
376
+ window.addEventListener('online', this.connect.bind(this))
365
377
  window.addEventListener('beforeunload', () => {
366
378
  removeAwarenessStates(this.awareness, [this.document.clientID], 'window unload')
367
379
  })
@@ -399,17 +411,17 @@ export class HocuspocusProvider extends EventEmitter {
399
411
 
400
412
  // Ensure that the URL always ends with /
401
413
  get serverUrl() {
402
- while (this.options.url[this.options.url.length - 1] === '/') {
403
- return this.options.url.slice(0, this.options.url.length - 1)
414
+ while (this.configuration.url[this.configuration.url.length - 1] === '/') {
415
+ return this.configuration.url.slice(0, this.configuration.url.length - 1)
404
416
  }
405
417
 
406
- return this.options.url
418
+ return this.configuration.url
407
419
  }
408
420
 
409
421
  get url() {
410
- const encodedParams = url.encodeQueryParams(this.options.parameters)
422
+ const encodedParams = url.encodeQueryParams(this.configuration.parameters)
411
423
 
412
- return `${this.serverUrl}/${this.options.name}${encodedParams.length === 0 ? '' : `?${encodedParams}`}`
424
+ return `${this.serverUrl}/${this.configuration.name}${encodedParams.length === 0 ? '' : `?${encodedParams}`}`
413
425
  }
414
426
 
415
427
  get synced(): boolean {
@@ -427,7 +439,7 @@ export class HocuspocusProvider extends EventEmitter {
427
439
  }
428
440
 
429
441
  get isAuthenticationRequired(): boolean {
430
- return !!this.options.token && !this.isAuthenticated
442
+ return !!this.configuration.token && !this.isAuthenticated
431
443
  }
432
444
 
433
445
  disconnect() {
@@ -454,12 +466,12 @@ export class HocuspocusProvider extends EventEmitter {
454
466
  }
455
467
 
456
468
  async getToken() {
457
- if (typeof this.options.token === 'function') {
458
- const token = await this.options.token()
469
+ if (typeof this.configuration.token === 'function') {
470
+ const token = await this.configuration.token()
459
471
  return token
460
472
  }
461
473
 
462
- return this.options.token
474
+ return this.configuration.token
463
475
  }
464
476
 
465
477
  async webSocketConnectionEstablished() {
@@ -533,15 +545,29 @@ export class HocuspocusProvider extends EventEmitter {
533
545
  this.emit('disconnect', { event })
534
546
  }
535
547
 
548
+ if (event.code === Unauthorized.code) {
549
+ if (!this.configuration.quiet) {
550
+ 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.')
551
+ }
552
+
553
+ this.shouldConnect = false
554
+ }
555
+
556
+ if (event.code === Forbidden.code) {
557
+ if (!this.configuration.quiet) {
558
+ console.warn('[HocuspocusProvider] The provided authentication token isn’t allowed to connect to this server. Will try again.')
559
+ }
560
+ }
561
+
536
562
  if (this.connectionAttempt) {
537
- // Okay, that connection attempt failed
563
+ // That connection attempt failed.
538
564
  this.rejectConnectionAttempt()
539
565
  } else if (this.shouldConnect) {
540
- // The connection was closed by the server, so let’s just try again.
566
+ // The connection was closed by the server. Let’s just try again.
541
567
  this.connect()
542
568
  }
543
569
 
544
- // If we’ll reconnect anyway, we’re done for now.
570
+ // If we’ll reconnect, we’re done for now.
545
571
  if (this.shouldConnect) {
546
572
  return
547
573
  }
@@ -579,10 +605,16 @@ export class HocuspocusProvider extends EventEmitter {
579
605
  this.document.off('update', this.documentUpdateHandler)
580
606
 
581
607
  this.removeAllListeners()
608
+
609
+ if (typeof window === 'undefined') {
610
+ return
611
+ }
612
+
613
+ window.removeEventListener('online', this.connect.bind(this))
582
614
  }
583
615
 
584
616
  get broadcastChannel() {
585
- return `${this.serverUrl}/${this.options.name}`
617
+ return `${this.serverUrl}/${this.configuration.name}`
586
618
  }
587
619
 
588
620
  broadcastChannelSubscriber(data: ArrayBuffer) {
@@ -623,7 +655,7 @@ export class HocuspocusProvider extends EventEmitter {
623
655
  }
624
656
 
625
657
  broadcast(Message: ConstructableOutgoingMessage, args?: any) {
626
- if (!this.options.broadcast) {
658
+ if (!this.configuration.broadcast) {
627
659
  return
628
660
  }
629
661
 
package/src/index.ts CHANGED
@@ -1,3 +1,4 @@
1
1
  export * from './HocuspocusProvider'
2
+ export * from './HocuspocusCloudProvider'
2
3
  export * from './types'
3
4
  export * from './utils'
@@ -1,4 +0,0 @@
1
- import { CloseEvent } from './types';
2
- export declare const Forbidden: CloseEvent;
3
- export declare const ResetConnection: CloseEvent;
4
- export declare const CloseEvents: CloseEvent[];