@hocuspocus/provider 1.0.0-alpha.12 → 1.0.0-alpha.16
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/CHANGELOG.md +35 -0
- package/dist/hocuspocus-provider.cjs +153 -84
- package/dist/hocuspocus-provider.cjs.map +1 -1
- package/dist/hocuspocus-provider.esm.js +153 -84
- package/dist/hocuspocus-provider.esm.js.map +1 -1
- package/dist/packages/provider/src/HocuspocusProvider.d.ts +81 -14
- package/dist/packages/provider/src/IncomingMessage.d.ts +1 -0
- package/dist/packages/provider/src/MessageReceiver.d.ts +2 -0
- package/dist/packages/provider/src/MessageSender.d.ts +2 -8
- package/dist/packages/provider/src/OutgoingMessage.d.ts +2 -1
- package/dist/packages/provider/src/types.d.ts +10 -2
- package/dist/packages/server/src/Connection.d.ts +2 -2
- package/dist/packages/server/src/Document.d.ts +2 -2
- package/dist/packages/server/src/Hocuspocus.d.ts +12 -4
- package/dist/packages/server/src/types.d.ts +4 -0
- package/package.json +3 -2
- package/src/HocuspocusProvider.ts +207 -107
- package/src/IncomingMessage.ts +5 -0
- package/src/MessageReceiver.ts +26 -1
- package/src/MessageSender.ts +2 -15
- package/src/OutgoingMessage.ts +5 -1
- package/src/types.ts +17 -2
|
@@ -2,7 +2,9 @@ import { HocuspocusProvider } from './HocuspocusProvider';
|
|
|
2
2
|
import { IncomingMessage } from './IncomingMessage';
|
|
3
3
|
export declare class MessageReceiver {
|
|
4
4
|
message: IncomingMessage;
|
|
5
|
+
broadcasted: boolean;
|
|
5
6
|
constructor(message: IncomingMessage);
|
|
7
|
+
setBroadcasted(value: boolean): this;
|
|
6
8
|
apply(provider: HocuspocusProvider, emitSynced?: boolean): void;
|
|
7
9
|
private applySyncMessage;
|
|
8
10
|
private applyAwarenessMessage;
|
|
@@ -1,15 +1,9 @@
|
|
|
1
1
|
import { Encoder } from 'lib0/encoding';
|
|
2
|
-
import {
|
|
3
|
-
import { AwarenessMessage } from './OutgoingMessages/AwarenessMessage';
|
|
4
|
-
import { QueryAwarenessMessage } from './OutgoingMessages/QueryAwarenessMessage';
|
|
5
|
-
import { SyncStepOneMessage } from './OutgoingMessages/SyncStepOneMessage';
|
|
6
|
-
import { SyncStepTwoMessage } from './OutgoingMessages/SyncStepTwoMessage';
|
|
7
|
-
import { UpdateMessage } from './OutgoingMessages/UpdateMessage';
|
|
8
|
-
import { Constructable } from './types';
|
|
2
|
+
import { ConstructableOutgoingMessage } from './types';
|
|
9
3
|
export declare class MessageSender {
|
|
10
4
|
encoder: Encoder;
|
|
11
5
|
message: any;
|
|
12
|
-
constructor(Message:
|
|
6
|
+
constructor(Message: ConstructableOutgoingMessage, args?: any);
|
|
13
7
|
create(): Uint8Array;
|
|
14
8
|
send(webSocket: any): void;
|
|
15
9
|
broadcast(channel: string): void;
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { Encoder } from 'lib0/encoding';
|
|
2
|
-
import { MessageType, OutgoingMessageInterface } from './types';
|
|
2
|
+
import { MessageType, OutgoingMessageArguments, OutgoingMessageInterface } from './types';
|
|
3
3
|
export declare class OutgoingMessage implements OutgoingMessageInterface {
|
|
4
4
|
encoder: Encoder;
|
|
5
5
|
type?: MessageType;
|
|
6
6
|
constructor();
|
|
7
|
+
get(args: Partial<OutgoingMessageArguments>): Encoder | undefined;
|
|
7
8
|
toUint8Array(): Uint8Array;
|
|
8
9
|
}
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import { Awareness } from 'y-protocols/awareness';
|
|
2
2
|
import * as Y from 'yjs';
|
|
3
|
-
import
|
|
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
|
export declare enum MessageType {
|
|
5
11
|
Sync = 0,
|
|
6
12
|
Awareness = 1,
|
|
@@ -8,7 +14,7 @@ export declare enum MessageType {
|
|
|
8
14
|
QueryAwareness = 3
|
|
9
15
|
}
|
|
10
16
|
export interface OutgoingMessageInterface {
|
|
11
|
-
encoder:
|
|
17
|
+
encoder: Encoder;
|
|
12
18
|
type?: MessageType;
|
|
13
19
|
}
|
|
14
20
|
export interface OutgoingMessageArguments {
|
|
@@ -20,7 +26,9 @@ export interface OutgoingMessageArguments {
|
|
|
20
26
|
[key: string]: any;
|
|
21
27
|
}>;
|
|
22
28
|
update: any;
|
|
29
|
+
encoder: Encoder;
|
|
23
30
|
}
|
|
24
31
|
export interface Constructable<T> {
|
|
25
32
|
new (...args: any): T;
|
|
26
33
|
}
|
|
34
|
+
export declare type ConstructableOutgoingMessage = Constructable<AuthenticationMessage> | Constructable<AwarenessMessage> | Constructable<QueryAwarenessMessage> | Constructable<SyncStepOneMessage> | Constructable<SyncStepTwoMessage> | Constructable<UpdateMessage>;
|
|
@@ -40,10 +40,10 @@ declare class Connection {
|
|
|
40
40
|
*/
|
|
41
41
|
private check;
|
|
42
42
|
/**
|
|
43
|
-
* Send
|
|
43
|
+
* Send the current document awareness to the client, if any
|
|
44
44
|
* @private
|
|
45
45
|
*/
|
|
46
|
-
private
|
|
46
|
+
private sendCurrentAwareness;
|
|
47
47
|
/**
|
|
48
48
|
* Handle an incoming message
|
|
49
49
|
* @private
|
|
@@ -43,9 +43,9 @@ declare class Document extends Doc {
|
|
|
43
43
|
*/
|
|
44
44
|
removeConnection(connection: Connection): Document;
|
|
45
45
|
/**
|
|
46
|
-
* Get the number of active connections
|
|
46
|
+
* Get the number of active connections for this document
|
|
47
47
|
*/
|
|
48
|
-
|
|
48
|
+
getConnectionsCount(): number;
|
|
49
49
|
/**
|
|
50
50
|
* Get an array of registered connections
|
|
51
51
|
*/
|
|
@@ -8,7 +8,7 @@ export declare const defaultConfiguration: {
|
|
|
8
8
|
timeout: number;
|
|
9
9
|
};
|
|
10
10
|
/**
|
|
11
|
-
* Hocuspocus
|
|
11
|
+
* Hocuspocus server
|
|
12
12
|
*/
|
|
13
13
|
export declare class Hocuspocus {
|
|
14
14
|
configuration: Configuration;
|
|
@@ -26,7 +26,15 @@ export declare class Hocuspocus {
|
|
|
26
26
|
*/
|
|
27
27
|
listen(): Promise<void>;
|
|
28
28
|
/**
|
|
29
|
-
*
|
|
29
|
+
* Get the total number of active documents
|
|
30
|
+
*/
|
|
31
|
+
getDocumentsCount(): number;
|
|
32
|
+
/**
|
|
33
|
+
* Get the total number of active connections
|
|
34
|
+
*/
|
|
35
|
+
getConnectionsCount(): number;
|
|
36
|
+
/**
|
|
37
|
+
* Force close one or more connections
|
|
30
38
|
*/
|
|
31
39
|
closeConnections(documentName?: string): void;
|
|
32
40
|
/**
|
|
@@ -34,7 +42,7 @@ export declare class Hocuspocus {
|
|
|
34
42
|
*/
|
|
35
43
|
destroy(): Promise<any>;
|
|
36
44
|
/**
|
|
37
|
-
* Handle the incoming
|
|
45
|
+
* Handle the incoming WebSocket connection
|
|
38
46
|
*/
|
|
39
47
|
handleConnection(incoming: WebSocket, request: IncomingMessage, documentName: string, context?: any): void;
|
|
40
48
|
/**
|
|
@@ -69,7 +77,7 @@ export declare class Hocuspocus {
|
|
|
69
77
|
*/
|
|
70
78
|
private static getDocumentName;
|
|
71
79
|
enableDebugging(): void;
|
|
72
|
-
|
|
80
|
+
enableMessageLogging(): void;
|
|
73
81
|
disableLogging(): void;
|
|
74
82
|
disableDebugging(): void;
|
|
75
83
|
flushMessageLogs(): this;
|
|
@@ -77,6 +77,7 @@ export interface onCreateDocumentPayload {
|
|
|
77
77
|
context: any;
|
|
78
78
|
document: Document;
|
|
79
79
|
documentName: string;
|
|
80
|
+
instance: Hocuspocus;
|
|
80
81
|
requestHeaders: IncomingHttpHeaders;
|
|
81
82
|
requestParameters: URLSearchParams;
|
|
82
83
|
socketId: string;
|
|
@@ -87,6 +88,7 @@ export interface onChangePayload {
|
|
|
87
88
|
context: any;
|
|
88
89
|
document: Document;
|
|
89
90
|
documentName: string;
|
|
91
|
+
instance: Hocuspocus;
|
|
90
92
|
requestHeaders: IncomingHttpHeaders;
|
|
91
93
|
requestParameters: URLSearchParams;
|
|
92
94
|
update: Uint8Array;
|
|
@@ -97,6 +99,7 @@ export interface onDisconnectPayload {
|
|
|
97
99
|
context: any;
|
|
98
100
|
document: Document;
|
|
99
101
|
documentName: string;
|
|
102
|
+
instance: Hocuspocus;
|
|
100
103
|
requestHeaders: IncomingHttpHeaders;
|
|
101
104
|
requestParameters: URLSearchParams;
|
|
102
105
|
socketId: string;
|
|
@@ -116,6 +119,7 @@ export interface onListenPayload {
|
|
|
116
119
|
port: number;
|
|
117
120
|
}
|
|
118
121
|
export interface onDestroyPayload {
|
|
122
|
+
instance: Hocuspocus;
|
|
119
123
|
}
|
|
120
124
|
export interface onConfigurePayload {
|
|
121
125
|
configuration: Configuration;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hocuspocus/provider",
|
|
3
|
-
"version": "1.0.0-alpha.
|
|
3
|
+
"version": "1.0.0-alpha.16",
|
|
4
4
|
"description": "hocuspocus provider",
|
|
5
5
|
"homepage": "https://hocuspocus.dev",
|
|
6
6
|
"keywords": [
|
|
@@ -23,11 +23,12 @@
|
|
|
23
23
|
"dist"
|
|
24
24
|
],
|
|
25
25
|
"dependencies": {
|
|
26
|
+
"@lifeomic/attempt": "^3.0.0",
|
|
26
27
|
"lib0": "^0.2.42",
|
|
27
28
|
"y-protocols": "^1.0.5",
|
|
28
29
|
"yjs": "^13.5.8"
|
|
29
30
|
},
|
|
30
|
-
"gitHead": "
|
|
31
|
+
"gitHead": "981e76320fd0bac82cbbdb027ec3c91a4c6cfce4",
|
|
31
32
|
"publishConfig": {
|
|
32
33
|
"access": "public"
|
|
33
34
|
}
|
|
@@ -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
|
-
|
|
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
|
-
|
|
42
|
-
|
|
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
|
|
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,76 @@ export class HocuspocusProvider extends EventEmitter {
|
|
|
163
236
|
)
|
|
164
237
|
}
|
|
165
238
|
|
|
166
|
-
if (
|
|
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
|
+
connect() {
|
|
255
|
+
if (this.status === WebSocketStatus.Connected) {
|
|
256
|
+
return
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
this.shouldConnect = true
|
|
260
|
+
this.subscribeToBroadcastChannel()
|
|
261
|
+
|
|
262
|
+
retry(this.createWebSocketConnection.bind(this), {
|
|
263
|
+
delay: this.options.delay,
|
|
264
|
+
initialDelay: this.options.initialDelay,
|
|
265
|
+
factor: this.options.factor,
|
|
266
|
+
maxAttempts: this.options.maxAttempts,
|
|
267
|
+
minDelay: this.options.minDelay,
|
|
268
|
+
maxDelay: this.options.maxDelay,
|
|
269
|
+
jitter: this.options.jitter,
|
|
270
|
+
timeout: this.options.timeout,
|
|
271
|
+
})
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
createWebSocketConnection() {
|
|
275
|
+
return new Promise((resolve, reject) => {
|
|
276
|
+
// Init the WebSocket connection
|
|
277
|
+
this.webSocket = new this.options.WebSocketPolyfill(this.url)
|
|
278
|
+
this.webSocket.binaryType = 'arraybuffer'
|
|
279
|
+
this.webSocket.onmessage = this.onMessage.bind(this)
|
|
280
|
+
this.webSocket.onclose = this.onClose.bind(this)
|
|
281
|
+
this.webSocket.onopen = this.onOpen.bind(this)
|
|
282
|
+
this.webSocket.onerror = () => {
|
|
283
|
+
reject()
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// Reset the status
|
|
287
|
+
this.synced = false
|
|
288
|
+
this.status = WebSocketStatus.Connecting
|
|
289
|
+
this.emit('status', { status: 'connecting' })
|
|
290
|
+
|
|
291
|
+
// Store resolve/reject for later use
|
|
292
|
+
this.connectionAttempt = {
|
|
293
|
+
resolve,
|
|
294
|
+
reject,
|
|
295
|
+
}
|
|
296
|
+
})
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
resolveConnectionAttempt() {
|
|
300
|
+
this.connectionAttempt?.resolve()
|
|
301
|
+
this.connectionAttempt = null
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
rejectConnectionAttempt() {
|
|
305
|
+
this.connectionAttempt?.reject()
|
|
306
|
+
this.connectionAttempt = null
|
|
307
|
+
}
|
|
308
|
+
|
|
175
309
|
get document() {
|
|
176
310
|
return this.options.document
|
|
177
311
|
}
|
|
@@ -181,12 +315,12 @@ export class HocuspocusProvider extends EventEmitter {
|
|
|
181
315
|
}
|
|
182
316
|
|
|
183
317
|
checkConnection() {
|
|
184
|
-
// Don’t
|
|
318
|
+
// Don’t check the connection when it’s not even established
|
|
185
319
|
if (this.status !== WebSocketStatus.Connected) {
|
|
186
320
|
return
|
|
187
321
|
}
|
|
188
322
|
|
|
189
|
-
// Don’t
|
|
323
|
+
// Don’t close then connection while waiting for the first message
|
|
190
324
|
if (!this.lastMessageReceived) {
|
|
191
325
|
return
|
|
192
326
|
}
|
|
@@ -238,8 +372,6 @@ export class HocuspocusProvider extends EventEmitter {
|
|
|
238
372
|
|
|
239
373
|
permissionDeniedHandler(reason: string) {
|
|
240
374
|
this.emit('authenticationFailed', { reason })
|
|
241
|
-
this.log('Permission denied', reason)
|
|
242
|
-
|
|
243
375
|
this.isAuthenticated = false
|
|
244
376
|
this.shouldConnect = false
|
|
245
377
|
}
|
|
@@ -284,15 +416,6 @@ export class HocuspocusProvider extends EventEmitter {
|
|
|
284
416
|
return !!this.options.token && !this.isAuthenticated
|
|
285
417
|
}
|
|
286
418
|
|
|
287
|
-
connect() {
|
|
288
|
-
this.shouldConnect = true
|
|
289
|
-
|
|
290
|
-
if (this.status !== WebSocketStatus.Connected) {
|
|
291
|
-
this.createWebSocketConnection()
|
|
292
|
-
this.subscribeToBroadcastChannel()
|
|
293
|
-
}
|
|
294
|
-
}
|
|
295
|
-
|
|
296
419
|
disconnect() {
|
|
297
420
|
this.shouldConnect = false
|
|
298
421
|
this.disconnectBroadcastChannel()
|
|
@@ -308,24 +431,6 @@ export class HocuspocusProvider extends EventEmitter {
|
|
|
308
431
|
}
|
|
309
432
|
}
|
|
310
433
|
|
|
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
434
|
onOpen(event: OpenEvent) {
|
|
330
435
|
this.emit('open', { event })
|
|
331
436
|
|
|
@@ -334,18 +439,30 @@ export class HocuspocusProvider extends EventEmitter {
|
|
|
334
439
|
}
|
|
335
440
|
}
|
|
336
441
|
|
|
337
|
-
|
|
338
|
-
this.
|
|
442
|
+
async getToken() {
|
|
443
|
+
if (typeof this.options.token === 'function') {
|
|
444
|
+
const token = await this.options.token()
|
|
445
|
+
return token
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
return this.options.token
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
async webSocketConnectionEstablished() {
|
|
339
452
|
this.status = WebSocketStatus.Connected
|
|
340
453
|
this.emit('status', { status: 'connected' })
|
|
341
454
|
this.emit('connect')
|
|
342
455
|
|
|
343
456
|
if (this.isAuthenticationRequired) {
|
|
344
|
-
this.send(AuthenticationMessage, {
|
|
457
|
+
this.send(AuthenticationMessage, {
|
|
458
|
+
token: await this.getToken(),
|
|
459
|
+
})
|
|
345
460
|
return
|
|
346
461
|
}
|
|
347
462
|
|
|
348
463
|
this.startSync()
|
|
464
|
+
|
|
465
|
+
this.resolveConnectionAttempt()
|
|
349
466
|
}
|
|
350
467
|
|
|
351
468
|
startSync() {
|
|
@@ -359,11 +476,9 @@ export class HocuspocusProvider extends EventEmitter {
|
|
|
359
476
|
}
|
|
360
477
|
}
|
|
361
478
|
|
|
362
|
-
send(Message:
|
|
479
|
+
send(Message: ConstructableOutgoingMessage, args: any, broadcast = false) {
|
|
363
480
|
if (broadcast) {
|
|
364
|
-
this.mux(() => {
|
|
365
|
-
this.broadcast(Message, args)
|
|
366
|
-
})
|
|
481
|
+
this.mux(() => { this.broadcast(Message, args) })
|
|
367
482
|
}
|
|
368
483
|
|
|
369
484
|
if (this.status === WebSocketStatus.Connected) {
|
|
@@ -382,23 +497,16 @@ export class HocuspocusProvider extends EventEmitter {
|
|
|
382
497
|
this.emit('message', { event, message })
|
|
383
498
|
|
|
384
499
|
new MessageReceiver(message).apply(this)
|
|
385
|
-
|
|
386
|
-
// TODO: What’s that doing?
|
|
387
|
-
// Move to the MessageReceiver
|
|
388
|
-
// if (encoding.length(encoder) > 1) {
|
|
389
|
-
// this.send(encoding.toUint8Array(encoder))
|
|
390
|
-
// }
|
|
391
500
|
}
|
|
392
501
|
|
|
393
502
|
onClose(event: CloseEvent) {
|
|
394
503
|
this.emit('close', { event })
|
|
395
504
|
|
|
396
|
-
this.isAuthenticated = false
|
|
397
505
|
this.webSocket = null
|
|
506
|
+
this.isAuthenticated = false
|
|
507
|
+
this.synced = false
|
|
398
508
|
|
|
399
509
|
if (this.status === WebSocketStatus.Connected) {
|
|
400
|
-
this.synced = false
|
|
401
|
-
|
|
402
510
|
// update awareness (all users except local left)
|
|
403
511
|
removeAwarenessStates(
|
|
404
512
|
this.awareness,
|
|
@@ -409,27 +517,30 @@ export class HocuspocusProvider extends EventEmitter {
|
|
|
409
517
|
this.status = WebSocketStatus.Disconnected
|
|
410
518
|
this.emit('status', { status: 'disconnected' })
|
|
411
519
|
this.emit('disconnect', { event })
|
|
412
|
-
} else {
|
|
413
|
-
this.failedConnectionAttempts += 1
|
|
414
520
|
}
|
|
415
521
|
|
|
416
|
-
if (this.
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
setTimeout(this.createWebSocketConnection.bind(this), wait)
|
|
522
|
+
if (this.connectionAttempt) {
|
|
523
|
+
// Okay, that connection attempt failed …
|
|
524
|
+
this.rejectConnectionAttempt()
|
|
525
|
+
} else if (this.shouldConnect) {
|
|
526
|
+
// The connection was closed by the server, so let’s just try again.
|
|
527
|
+
this.connect()
|
|
528
|
+
}
|
|
424
529
|
|
|
530
|
+
// If we’ll reconnect anyway, we’re done for now.
|
|
531
|
+
if (this.shouldConnect) {
|
|
425
532
|
return
|
|
426
533
|
}
|
|
427
534
|
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
this.emit('disconnect', { event })
|
|
535
|
+
// The status is set correctly already.
|
|
536
|
+
if (this.status === WebSocketStatus.Disconnected) {
|
|
537
|
+
return
|
|
432
538
|
}
|
|
539
|
+
|
|
540
|
+
// Let’s update the connection status.
|
|
541
|
+
this.status = WebSocketStatus.Disconnected
|
|
542
|
+
this.emit('status', { status: 'disconnected' })
|
|
543
|
+
this.emit('disconnect', { event })
|
|
433
544
|
}
|
|
434
545
|
|
|
435
546
|
destroy() {
|
|
@@ -456,12 +567,9 @@ export class HocuspocusProvider extends EventEmitter {
|
|
|
456
567
|
broadcastChannelSubscriber(data: ArrayBuffer) {
|
|
457
568
|
this.mux(() => {
|
|
458
569
|
const message = new IncomingMessage(data)
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
// if (encoding.length(encoder) > 1) {
|
|
463
|
-
// this.broadcast(encoding.toUint8Array(encoder))
|
|
464
|
-
// }
|
|
570
|
+
new MessageReceiver(message)
|
|
571
|
+
.setBroadcasted(true)
|
|
572
|
+
.apply(this, false)
|
|
465
573
|
})
|
|
466
574
|
}
|
|
467
575
|
|
|
@@ -493,7 +601,7 @@ export class HocuspocusProvider extends EventEmitter {
|
|
|
493
601
|
}
|
|
494
602
|
}
|
|
495
603
|
|
|
496
|
-
broadcast(Message:
|
|
604
|
+
broadcast(Message: ConstructableOutgoingMessage, args?: any) {
|
|
497
605
|
if (!this.options.broadcast) {
|
|
498
606
|
return
|
|
499
607
|
}
|
|
@@ -505,14 +613,6 @@ export class HocuspocusProvider extends EventEmitter {
|
|
|
505
613
|
new MessageSender(Message, args).broadcast(this.broadcastChannel)
|
|
506
614
|
}
|
|
507
615
|
|
|
508
|
-
log(message: string): void {
|
|
509
|
-
if (!this.options.debug) {
|
|
510
|
-
return
|
|
511
|
-
}
|
|
512
|
-
|
|
513
|
-
console.log(message)
|
|
514
|
-
}
|
|
515
|
-
|
|
516
616
|
setAwarenessField(key: string, value: any) {
|
|
517
617
|
this.awareness.setLocalStateField(key, value)
|
|
518
618
|
}
|