@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.
- package/dist/hocuspocus-provider.cjs +132 -330
- package/dist/hocuspocus-provider.cjs.map +1 -1
- package/dist/hocuspocus-provider.esm.js +130 -329
- package/dist/hocuspocus-provider.esm.js.map +1 -1
- package/dist/packages/common/src/CloseEvents.d.ts +23 -0
- package/dist/packages/common/src/index.d.ts +1 -0
- package/dist/packages/extension-database/src/Database.d.ts +36 -0
- package/dist/packages/extension-database/src/index.d.ts +1 -0
- package/dist/packages/extension-logger/src/Logger.d.ts +2 -8
- package/dist/packages/extension-sqlite/src/SQLite.d.ts +26 -0
- package/dist/packages/extension-sqlite/src/index.d.ts +1 -0
- package/dist/packages/extension-webhook/src/index.d.ts +1 -1
- package/dist/packages/provider/src/HocuspocusCloudProvider.d.ts +11 -0
- package/dist/packages/provider/src/HocuspocusProvider.d.ts +10 -5
- package/dist/packages/provider/src/index.d.ts +1 -0
- package/dist/packages/server/src/Connection.d.ts +1 -1
- package/dist/packages/server/src/Hocuspocus.d.ts +15 -5
- package/dist/packages/server/src/types.d.ts +17 -8
- package/dist/{demos/backend/src/express.d.ts → playground/backend/src/default.d.ts} +0 -0
- package/dist/{demos/backend/src/koa.d.ts → playground/backend/src/express.d.ts} +0 -0
- package/dist/{demos/backend/src/load-document.d.ts → playground/backend/src/koa.d.ts} +0 -0
- package/dist/{demos/backend/src/minimal.d.ts → playground/backend/src/load-document.d.ts} +0 -0
- package/dist/{demos → playground}/backend/src/monitor.d.ts +0 -0
- package/dist/{demos → playground}/backend/src/redis.d.ts +0 -0
- package/dist/{demos → playground}/backend/src/slow.d.ts +0 -0
- package/dist/{demos → playground}/backend/src/webhook.d.ts +0 -0
- package/package.json +6 -8
- package/src/HocuspocusCloudProvider.ts +34 -0
- package/src/HocuspocusProvider.ts +95 -63
- package/src/index.ts +1 -0
- 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
|
|
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
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
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
|
|
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(
|
|
204
|
+
constructor(configuration: HocuspocusProviderConfiguration) {
|
|
195
205
|
super()
|
|
196
|
-
this.
|
|
197
|
-
|
|
198
|
-
this.
|
|
199
|
-
this.
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
this.on('
|
|
203
|
-
this.on('
|
|
204
|
-
this.on('
|
|
205
|
-
this.on('
|
|
206
|
-
this.on('
|
|
207
|
-
this.on('
|
|
208
|
-
this.on('
|
|
209
|
-
this.on('
|
|
210
|
-
this.on('
|
|
211
|
-
this.on('
|
|
212
|
-
this.on('
|
|
213
|
-
this.on('
|
|
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.
|
|
236
|
+
this.registerEventListeners()
|
|
226
237
|
|
|
227
238
|
this.intervals.connectionChecker = setInterval(
|
|
228
239
|
this.checkConnection.bind(this),
|
|
229
|
-
this.
|
|
240
|
+
this.configuration.messageReconnectTimeout / 10,
|
|
230
241
|
)
|
|
231
242
|
|
|
232
|
-
if (this.
|
|
243
|
+
if (this.configuration.forceSyncInterval) {
|
|
233
244
|
this.intervals.forceSync = setInterval(
|
|
234
245
|
this.forceSync.bind(this),
|
|
235
|
-
this.
|
|
246
|
+
this.configuration.forceSyncInterval,
|
|
236
247
|
)
|
|
237
248
|
}
|
|
238
249
|
|
|
239
|
-
if (typeof
|
|
240
|
-
this.shouldConnect =
|
|
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
|
|
251
|
-
this.
|
|
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.
|
|
265
|
-
initialDelay: this.
|
|
266
|
-
factor: this.
|
|
267
|
-
maxAttempts: this.
|
|
268
|
-
minDelay: this.
|
|
269
|
-
maxDelay: this.
|
|
270
|
-
jitter: this.
|
|
271
|
-
timeout: this.
|
|
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.
|
|
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.
|
|
335
|
+
return this.configuration.document
|
|
325
336
|
}
|
|
326
337
|
|
|
327
338
|
get awareness() {
|
|
328
|
-
return this.
|
|
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.
|
|
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
|
-
|
|
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.
|
|
403
|
-
return this.
|
|
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.
|
|
418
|
+
return this.configuration.url
|
|
407
419
|
}
|
|
408
420
|
|
|
409
421
|
get url() {
|
|
410
|
-
const encodedParams = url.encodeQueryParams(this.
|
|
422
|
+
const encodedParams = url.encodeQueryParams(this.configuration.parameters)
|
|
411
423
|
|
|
412
|
-
return `${this.serverUrl}/${this.
|
|
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.
|
|
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.
|
|
458
|
-
const token = await this.
|
|
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.
|
|
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
|
-
//
|
|
563
|
+
// That connection attempt failed.
|
|
538
564
|
this.rejectConnectionAttempt()
|
|
539
565
|
} else if (this.shouldConnect) {
|
|
540
|
-
// The connection was closed by the server
|
|
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
|
|
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.
|
|
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.
|
|
658
|
+
if (!this.configuration.broadcast) {
|
|
627
659
|
return
|
|
628
660
|
}
|
|
629
661
|
|
package/src/index.ts
CHANGED