@hocuspocus/provider 1.0.0-alpha.9 → 1.0.0-beta.2
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 +374 -186
- package/dist/hocuspocus-provider.cjs.map +1 -1
- package/dist/hocuspocus-provider.esm.js +372 -182
- package/dist/hocuspocus-provider.esm.js.map +1 -1
- package/dist/packages/common/src/CloseEvents.d.ts +23 -0
- package/dist/packages/common/src/auth.d.ts +6 -0
- package/dist/packages/common/src/awarenessStatesToArray.d.ts +3 -0
- package/dist/packages/common/src/index.d.ts +4 -0
- package/dist/packages/common/src/types.d.ts +10 -0
- package/dist/packages/extension-database/src/Database.d.ts +30 -0
- package/dist/packages/extension-database/src/index.d.ts +1 -0
- package/dist/packages/extension-logger/src/Logger.d.ts +67 -0
- package/dist/packages/extension-logger/src/index.d.ts +1 -0
- package/dist/packages/{monitor → extension-monitor}/src/Collector.d.ts +4 -5
- package/dist/packages/{monitor → extension-monitor}/src/Dashboard.d.ts +2 -2
- package/dist/packages/{monitor → extension-monitor}/src/Storage.d.ts +0 -0
- package/dist/packages/{monitor → extension-monitor}/src/index.d.ts +3 -5
- package/dist/packages/extension-redis/src/Redis.d.ts +98 -0
- package/dist/packages/extension-redis/src/index.d.ts +1 -0
- 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-throttle/src/index.d.ts +24 -0
- package/dist/packages/{webhook → extension-webhook}/src/index.d.ts +5 -11
- package/dist/packages/provider/src/EventEmitter.d.ts +1 -1
- package/dist/packages/provider/src/HocuspocusCloudProvider.d.ts +11 -0
- package/dist/packages/provider/src/HocuspocusProvider.d.ts +116 -33
- package/dist/packages/provider/src/IncomingMessage.d.ts +9 -6
- package/dist/packages/provider/src/MessageReceiver.d.ts +3 -2
- package/dist/packages/provider/src/MessageSender.d.ts +4 -9
- package/dist/packages/provider/src/OutgoingMessage.d.ts +5 -4
- package/dist/packages/provider/src/OutgoingMessages/AuthenticationMessage.d.ts +7 -0
- package/dist/packages/provider/src/OutgoingMessages/UpdateMessage.d.ts +1 -2
- package/dist/packages/provider/src/index.d.ts +1 -1
- package/dist/packages/provider/src/types.d.ts +54 -2
- package/dist/packages/server/src/Connection.d.ts +27 -7
- package/dist/packages/server/src/Debugger.d.ts +14 -0
- package/dist/packages/server/src/Document.d.ts +11 -5
- package/dist/packages/server/src/Hocuspocus.d.ts +63 -17
- package/dist/packages/server/src/IncomingMessage.d.ts +11 -7
- package/dist/packages/server/src/MessageReceiver.d.ts +13 -0
- package/dist/packages/server/src/OutgoingMessage.d.ts +6 -0
- package/dist/packages/server/src/index.d.ts +6 -0
- package/dist/packages/server/src/types.d.ts +180 -26
- package/dist/{demos/backend/src/create-document.d.ts → playground/backend/src/default.d.ts} +0 -0
- package/dist/{demos → playground}/backend/src/express.d.ts +0 -0
- package/dist/{demos → 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/dist/tests/extension-database/fetch.d.ts +1 -0
- package/dist/tests/extension-logger/onListen.d.ts +1 -0
- package/dist/tests/extension-redis/closeConnections.d.ts +1 -0
- package/dist/tests/extension-redis/getConnectionCount.d.ts +1 -0
- package/dist/tests/extension-redis/getDocumentsCount.d.ts +1 -0
- package/dist/tests/extension-redis/onAwarenessChange.d.ts +1 -0
- package/dist/tests/extension-redis/onChange.d.ts +1 -0
- package/dist/tests/extension-redis/onStoreDocument.d.ts +1 -0
- package/dist/tests/extension-throttle/configuration.d.ts +1 -0
- package/dist/tests/provider/configuration.d.ts +1 -0
- package/dist/tests/provider/observe.d.ts +1 -0
- package/dist/tests/provider/observeDeep.d.ts +1 -0
- package/dist/tests/provider/onAuthenticated.d.ts +1 -0
- package/dist/tests/provider/onAuthenticationFailed.d.ts +1 -0
- package/dist/tests/provider/onAwarenessChange.d.ts +1 -0
- package/dist/tests/provider/onAwarenessUpdate.d.ts +1 -0
- package/dist/tests/provider/onClose.d.ts +1 -0
- package/dist/tests/provider/onConnect.d.ts +1 -0
- package/dist/tests/provider/onDisconnect.d.ts +1 -0
- package/dist/tests/provider/onMessage.d.ts +1 -0
- package/dist/tests/provider/onOpen.d.ts +1 -0
- package/dist/tests/provider/onSynced.d.ts +1 -0
- package/dist/tests/server/address.d.ts +1 -0
- package/dist/tests/server/afterStoreDocument.d.ts +1 -0
- package/dist/tests/server/beforeHandleMessage.d.ts +1 -0
- package/dist/tests/server/closeConnections.d.ts +1 -0
- package/dist/tests/server/getConnectionsCount.d.ts +1 -0
- package/dist/tests/server/getDocumentName.d.ts +1 -0
- package/dist/tests/server/getDocumentsCount.d.ts +1 -0
- package/dist/tests/server/getMessageLogs.d.ts +1 -0
- package/dist/tests/server/listen.d.ts +1 -0
- package/dist/tests/server/onAuthenticate.d.ts +1 -0
- package/dist/tests/server/onAwarenessUpdate.d.ts +1 -0
- package/dist/tests/server/onChange.d.ts +1 -0
- package/dist/tests/server/onConfigure.d.ts +1 -0
- package/dist/tests/server/onConnect.d.ts +1 -0
- package/dist/tests/server/onDestroy.d.ts +1 -0
- package/dist/tests/server/onDisconnect.d.ts +1 -0
- package/dist/tests/server/onListen.d.ts +1 -0
- package/dist/tests/server/onLoadDocument.d.ts +1 -0
- package/dist/tests/server/onRequest.d.ts +1 -0
- package/dist/tests/server/onStoreDocument.d.ts +1 -0
- package/dist/tests/server/onUpgrade.d.ts +1 -0
- package/dist/tests/server/requiresAuthentication.d.ts +1 -0
- package/dist/tests/server/websocketError.d.ts +1 -0
- package/dist/tests/transformer/TiptapTransformer.d.ts +1 -0
- package/dist/tests/utils/createDirectory.d.ts +1 -0
- package/dist/tests/utils/flushRedis.d.ts +1 -0
- package/dist/tests/utils/index.d.ts +8 -0
- package/dist/tests/utils/newHocuspocus.d.ts +2 -0
- package/dist/tests/utils/newHocuspocusProvider.d.ts +3 -0
- package/dist/tests/utils/randomInteger.d.ts +1 -0
- package/dist/tests/utils/redisConnectionSettings.d.ts +4 -0
- package/dist/tests/utils/removeDirectory.d.ts +1 -0
- package/dist/tests/utils/retryableAssertion.d.ts +2 -0
- package/dist/tests/utils/sleep.d.ts +1 -0
- package/package.json +14 -10
- package/src/EventEmitter.ts +1 -1
- package/src/HocuspocusCloudProvider.ts +34 -0
- package/src/HocuspocusProvider.ts +410 -162
- package/src/IncomingMessage.ts +35 -11
- package/src/MessageReceiver.ts +56 -24
- package/src/MessageSender.ts +5 -17
- package/src/OutgoingMessage.ts +9 -9
- package/src/OutgoingMessages/AuthenticationMessage.ts +21 -0
- package/src/OutgoingMessages/AwarenessMessage.ts +1 -1
- package/src/OutgoingMessages/SyncStepOneMessage.ts +0 -1
- package/src/OutgoingMessages/UpdateMessage.ts +4 -4
- package/src/index.ts +1 -1
- package/src/types.ts +70 -3
- package/CHANGELOG.md +0 -64
- package/LICENSE.md +0 -21
- package/dist/packages/logger/src/index.d.ts +0 -13
- package/dist/packages/provider/src/utils/awarenessStatesToArray.d.ts +0 -4
- package/dist/packages/provider/src/utils/index.d.ts +0 -1
- package/dist/packages/redis/src/Redis.d.ts +0 -22
- package/dist/packages/redis/src/RedisCluster.d.ts +0 -4
- package/dist/packages/redis/src/index.d.ts +0 -2
- package/dist/packages/rocksdb/src/index.d.ts +0 -30
- package/dist/packages/server/src/CloseEvents.d.ts +0 -3
- package/dist/packages/throttle/src/index.d.ts +0 -28
- package/src/utils/awarenessStatesToArray.ts +0 -8
- package/src/utils/index.ts +0 -1
|
@@ -1,14 +1,14 @@
|
|
|
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
|
-
import * as encoding from 'lib0/encoding'
|
|
6
4
|
import { Awareness, removeAwarenessStates } from 'y-protocols/awareness'
|
|
7
5
|
import * as mutex from 'lib0/mutex'
|
|
8
|
-
import * as math from 'lib0/math'
|
|
9
6
|
import * as url from 'lib0/url'
|
|
10
|
-
|
|
11
|
-
import {
|
|
7
|
+
import type { Event, CloseEvent, MessageEvent } from 'ws'
|
|
8
|
+
import { retry } from '@lifeomic/attempt'
|
|
9
|
+
import {
|
|
10
|
+
awarenessStatesToArray, Forbidden, Unauthorized, WsReadyStates,
|
|
11
|
+
} from '@hocuspocus/common'
|
|
12
12
|
import EventEmitter from './EventEmitter'
|
|
13
13
|
import { IncomingMessage } from './IncomingMessage'
|
|
14
14
|
import { MessageReceiver } from './MessageReceiver'
|
|
@@ -16,56 +16,148 @@ import { MessageSender } from './MessageSender'
|
|
|
16
16
|
import { SyncStepOneMessage } from './OutgoingMessages/SyncStepOneMessage'
|
|
17
17
|
import { SyncStepTwoMessage } from './OutgoingMessages/SyncStepTwoMessage'
|
|
18
18
|
import { QueryAwarenessMessage } from './OutgoingMessages/QueryAwarenessMessage'
|
|
19
|
+
import { AuthenticationMessage } from './OutgoingMessages/AuthenticationMessage'
|
|
19
20
|
import { AwarenessMessage } from './OutgoingMessages/AwarenessMessage'
|
|
20
21
|
import { UpdateMessage } from './OutgoingMessages/UpdateMessage'
|
|
21
|
-
import {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
export interface
|
|
31
|
-
|
|
32
|
-
|
|
22
|
+
import {
|
|
23
|
+
ConstructableOutgoingMessage, onAuthenticationFailedParameters, onCloseParameters, onDisconnectParameters, onMessageParameters, onOpenParameters, onOutgoingMessageParameters, onStatusParameters, onSyncedParameters, WebSocketStatus,
|
|
24
|
+
} from './types'
|
|
25
|
+
import { onAwarenessChangeParameters, onAwarenessUpdateParameters } from '.'
|
|
26
|
+
|
|
27
|
+
export type HocuspocusProviderConfiguration =
|
|
28
|
+
Required<Pick<CompleteHocuspocusProviderConfiguration, 'url' | 'name'>>
|
|
29
|
+
& Partial<CompleteHocuspocusProviderConfiguration>
|
|
30
|
+
|
|
31
|
+
export interface CompleteHocuspocusProviderConfiguration {
|
|
32
|
+
/**
|
|
33
|
+
* URL of your @hocuspocus/server instance
|
|
34
|
+
*/
|
|
35
|
+
url: string,
|
|
36
|
+
/**
|
|
37
|
+
* The identifier/name of your document
|
|
38
|
+
*/
|
|
39
|
+
name: string,
|
|
40
|
+
/**
|
|
41
|
+
* The actual Y.js document
|
|
42
|
+
*/
|
|
33
43
|
document: Y.Doc,
|
|
44
|
+
/**
|
|
45
|
+
* Pass `false` to start the connection manually.
|
|
46
|
+
*/
|
|
34
47
|
connect: boolean,
|
|
48
|
+
/**
|
|
49
|
+
* Pass false to disable broadcasting between browser tabs.
|
|
50
|
+
*/
|
|
35
51
|
broadcast: boolean,
|
|
52
|
+
/**
|
|
53
|
+
* An Awareness instance to keep the presence state of all clients.
|
|
54
|
+
*/
|
|
36
55
|
awareness: Awareness,
|
|
56
|
+
/**
|
|
57
|
+
* A token that’s sent to the backend for authentication purposes.
|
|
58
|
+
*/
|
|
59
|
+
token: string | (() => string) | (() => Promise<string>) | null,
|
|
60
|
+
/**
|
|
61
|
+
* URL parameters that should be added.
|
|
62
|
+
*/
|
|
37
63
|
parameters: { [key: string]: any },
|
|
64
|
+
/**
|
|
65
|
+
* An optional WebSocket polyfill, for example for Node.js
|
|
66
|
+
*/
|
|
38
67
|
WebSocketPolyfill: any,
|
|
68
|
+
/**
|
|
69
|
+
* Force syncing the document in the defined interval.
|
|
70
|
+
*/
|
|
39
71
|
forceSyncInterval: false | number,
|
|
40
|
-
|
|
41
|
-
|
|
72
|
+
/**
|
|
73
|
+
* Disconnect when no message is received for the defined amount of milliseconds.
|
|
74
|
+
*/
|
|
42
75
|
messageReconnectTimeout: number,
|
|
43
|
-
|
|
76
|
+
/**
|
|
77
|
+
* The delay between each attempt in milliseconds. You can provide a factor to have the delay grow exponentially.
|
|
78
|
+
*/
|
|
79
|
+
delay: number,
|
|
80
|
+
/**
|
|
81
|
+
* 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.
|
|
82
|
+
*/
|
|
83
|
+
initialDelay: number,
|
|
84
|
+
/**
|
|
85
|
+
* The factor option is used to grow the delay exponentially.
|
|
86
|
+
*/
|
|
87
|
+
factor: number,
|
|
88
|
+
/**
|
|
89
|
+
* The maximum number of attempts or 0 if there is no limit on number of attempts.
|
|
90
|
+
*/
|
|
91
|
+
maxAttempts: number,
|
|
92
|
+
/**
|
|
93
|
+
* minDelay is used to set a lower bound of delay when jitter is enabled. This property has no effect if jitter is disabled.
|
|
94
|
+
*/
|
|
95
|
+
minDelay: number,
|
|
96
|
+
/**
|
|
97
|
+
* 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.
|
|
98
|
+
*/
|
|
99
|
+
maxDelay: number,
|
|
100
|
+
/**
|
|
101
|
+
* If jitter is true then the calculated delay will be a random integer value between minDelay and the calculated delay for the current iteration.
|
|
102
|
+
*/
|
|
103
|
+
jitter: boolean,
|
|
104
|
+
/**
|
|
105
|
+
* 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.
|
|
106
|
+
*/
|
|
107
|
+
timeout: number,
|
|
108
|
+
onAuthenticated: () => void,
|
|
109
|
+
onAuthenticationFailed: (data: onAuthenticationFailedParameters) => void,
|
|
110
|
+
onOpen: (data: onOpenParameters) => void,
|
|
44
111
|
onConnect: () => void,
|
|
45
|
-
onMessage: (
|
|
46
|
-
onOutgoingMessage: (
|
|
47
|
-
onStatus: (
|
|
48
|
-
onSynced: () => void,
|
|
49
|
-
onDisconnect: (
|
|
50
|
-
onClose: (
|
|
112
|
+
onMessage: (data: onMessageParameters) => void,
|
|
113
|
+
onOutgoingMessage: (data: onOutgoingMessageParameters) => void,
|
|
114
|
+
onStatus: (data: onStatusParameters) => void,
|
|
115
|
+
onSynced: (data: onSyncedParameters) => void,
|
|
116
|
+
onDisconnect: (data: onDisconnectParameters) => void,
|
|
117
|
+
onClose: (data: onCloseParameters) => void,
|
|
51
118
|
onDestroy: () => void,
|
|
52
|
-
|
|
53
|
-
|
|
119
|
+
onAwarenessUpdate: (data: onAwarenessUpdateParameters) => void,
|
|
120
|
+
onAwarenessChange: (data: onAwarenessChangeParameters) => void,
|
|
121
|
+
/**
|
|
122
|
+
* Don’t output any warnings.
|
|
123
|
+
*/
|
|
124
|
+
quiet: boolean,
|
|
54
125
|
}
|
|
55
126
|
|
|
56
127
|
export class HocuspocusProvider extends EventEmitter {
|
|
57
|
-
public
|
|
58
|
-
url: '',
|
|
128
|
+
public configuration: CompleteHocuspocusProviderConfiguration = {
|
|
59
129
|
name: '',
|
|
130
|
+
url: '',
|
|
131
|
+
// @ts-ignore
|
|
132
|
+
document: undefined,
|
|
133
|
+
// @ts-ignore
|
|
134
|
+
awareness: undefined,
|
|
135
|
+
WebSocketPolyfill: undefined,
|
|
136
|
+
token: null,
|
|
60
137
|
parameters: {},
|
|
61
|
-
debug: false,
|
|
62
138
|
connect: true,
|
|
63
139
|
broadcast: true,
|
|
64
140
|
forceSyncInterval: false,
|
|
65
|
-
reconnectTimeoutBase: 1200,
|
|
66
|
-
maxReconnectTimeout: 2500,
|
|
67
141
|
// TODO: this should depend on awareness.outdatedTime
|
|
68
142
|
messageReconnectTimeout: 30000,
|
|
143
|
+
// 1 second
|
|
144
|
+
delay: 1000,
|
|
145
|
+
// instant
|
|
146
|
+
initialDelay: 0,
|
|
147
|
+
// double the delay each time
|
|
148
|
+
factor: 2,
|
|
149
|
+
// unlimited retries
|
|
150
|
+
maxAttempts: 0,
|
|
151
|
+
// wait at least 1 second
|
|
152
|
+
minDelay: 1000,
|
|
153
|
+
// at least every 30 seconds
|
|
154
|
+
maxDelay: 30000,
|
|
155
|
+
// randomize
|
|
156
|
+
jitter: true,
|
|
157
|
+
// retry forever
|
|
158
|
+
timeout: 0,
|
|
159
|
+
onAuthenticated: () => null,
|
|
160
|
+
onAuthenticationFailed: () => null,
|
|
69
161
|
onOpen: () => null,
|
|
70
162
|
onConnect: () => null,
|
|
71
163
|
onMessage: () => null,
|
|
@@ -75,23 +167,25 @@ export class HocuspocusProvider extends EventEmitter {
|
|
|
75
167
|
onDisconnect: () => null,
|
|
76
168
|
onClose: () => null,
|
|
77
169
|
onDestroy: () => null,
|
|
170
|
+
onAwarenessUpdate: () => null,
|
|
78
171
|
onAwarenessChange: () => null,
|
|
172
|
+
quiet: false,
|
|
79
173
|
}
|
|
80
174
|
|
|
81
|
-
awareness: Awareness
|
|
82
|
-
|
|
83
175
|
subscribedToBroadcastChannel = false
|
|
84
176
|
|
|
85
|
-
webSocket:
|
|
177
|
+
webSocket: WebSocket | null = null
|
|
86
178
|
|
|
87
179
|
shouldConnect = true
|
|
88
180
|
|
|
89
|
-
status
|
|
90
|
-
|
|
91
|
-
failedConnectionAttempts = 0
|
|
181
|
+
status = WebSocketStatus.Disconnected
|
|
92
182
|
|
|
93
183
|
isSynced = false
|
|
94
184
|
|
|
185
|
+
unsyncedChanges = 0
|
|
186
|
+
|
|
187
|
+
isAuthenticated = false
|
|
188
|
+
|
|
95
189
|
lastMessageReceived = 0
|
|
96
190
|
|
|
97
191
|
mux = mutex.createMutex()
|
|
@@ -101,78 +195,210 @@ export class HocuspocusProvider extends EventEmitter {
|
|
|
101
195
|
connectionChecker: null,
|
|
102
196
|
}
|
|
103
197
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
198
|
+
connectionAttempt: {
|
|
199
|
+
resolve: (value?: any) => void
|
|
200
|
+
reject: (reason?: any) => void
|
|
201
|
+
} | null = null
|
|
108
202
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
this.
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
this.
|
|
115
|
-
this.
|
|
116
|
-
|
|
117
|
-
this.on('
|
|
118
|
-
this.on('
|
|
119
|
-
this.on('
|
|
120
|
-
this.on('
|
|
121
|
-
this.on('
|
|
122
|
-
this.on('
|
|
123
|
-
this.on('
|
|
203
|
+
constructor(configuration: HocuspocusProviderConfiguration) {
|
|
204
|
+
super()
|
|
205
|
+
this.setConfiguration(configuration)
|
|
206
|
+
|
|
207
|
+
this.configuration.document = configuration.document ? configuration.document : new Y.Doc()
|
|
208
|
+
this.configuration.awareness = configuration.awareness ? configuration.awareness : new Awareness(this.document)
|
|
209
|
+
this.configuration.WebSocketPolyfill = configuration.WebSocketPolyfill ? configuration.WebSocketPolyfill : WebSocket
|
|
210
|
+
|
|
211
|
+
this.on('open', this.configuration.onOpen)
|
|
212
|
+
this.on('authenticated', this.configuration.onAuthenticated)
|
|
213
|
+
this.on('authenticationFailed', this.configuration.onAuthenticationFailed)
|
|
214
|
+
this.on('connect', this.configuration.onConnect)
|
|
215
|
+
this.on('message', this.configuration.onMessage)
|
|
216
|
+
this.on('outgoingMessage', this.configuration.onOutgoingMessage)
|
|
217
|
+
this.on('synced', this.configuration.onSynced)
|
|
218
|
+
this.on('status', this.configuration.onStatus)
|
|
219
|
+
this.on('disconnect', this.configuration.onDisconnect)
|
|
220
|
+
this.on('close', this.configuration.onClose)
|
|
221
|
+
this.on('destroy', this.configuration.onDestroy)
|
|
222
|
+
this.on('awarenessUpdate', this.configuration.onAwarenessUpdate)
|
|
223
|
+
this.on('awarenessChange', this.configuration.onAwarenessChange)
|
|
224
|
+
|
|
225
|
+
this.awareness.on('update', () => {
|
|
226
|
+
this.emit('awarenessUpdate', { states: awarenessStatesToArray(this.awareness.getStates()) })
|
|
227
|
+
})
|
|
124
228
|
|
|
125
229
|
this.awareness.on('change', () => {
|
|
126
|
-
this.emit('awarenessChange', {
|
|
127
|
-
states: awarenessStatesToArray(this.awareness.getStates()),
|
|
128
|
-
})
|
|
230
|
+
this.emit('awarenessChange', { states: awarenessStatesToArray(this.awareness.getStates()) })
|
|
129
231
|
})
|
|
130
232
|
|
|
233
|
+
this.document.on('update', this.documentUpdateHandler.bind(this))
|
|
234
|
+
this.awareness.on('update', this.awarenessUpdateHandler.bind(this))
|
|
235
|
+
this.registerEventListeners()
|
|
236
|
+
|
|
131
237
|
this.intervals.connectionChecker = setInterval(
|
|
132
238
|
this.checkConnection.bind(this),
|
|
133
|
-
this.
|
|
239
|
+
this.configuration.messageReconnectTimeout / 10,
|
|
134
240
|
)
|
|
135
241
|
|
|
136
|
-
|
|
137
|
-
this.awareness.on('update', this.awarenessUpdateHandler.bind(this))
|
|
138
|
-
this.registerBeforeUnloadEventListener()
|
|
139
|
-
|
|
140
|
-
if (this.options.forceSyncInterval) {
|
|
242
|
+
if (this.configuration.forceSyncInterval) {
|
|
141
243
|
this.intervals.forceSync = setInterval(
|
|
142
244
|
this.forceSync.bind(this),
|
|
143
|
-
this.
|
|
245
|
+
this.configuration.forceSyncInterval,
|
|
144
246
|
)
|
|
145
247
|
}
|
|
146
248
|
|
|
147
|
-
if (
|
|
148
|
-
this.connect
|
|
249
|
+
if (typeof configuration.connect !== 'undefined') {
|
|
250
|
+
this.shouldConnect = configuration.connect
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
if (!this.shouldConnect) {
|
|
254
|
+
return
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
this.connect()
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
public setConfiguration(configuration: Partial<HocuspocusProviderConfiguration> = {}): void {
|
|
261
|
+
this.configuration = { ...this.configuration, ...configuration }
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
boundConnect = this.connect.bind(this)
|
|
265
|
+
|
|
266
|
+
cancelWebsocketRetry?: () => void
|
|
267
|
+
|
|
268
|
+
async connect() {
|
|
269
|
+
if (this.status === WebSocketStatus.Connected) {
|
|
270
|
+
return
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// Always cancel any previously initiated connection retryer instances
|
|
274
|
+
if (this.cancelWebsocketRetry) {
|
|
275
|
+
this.cancelWebsocketRetry()
|
|
276
|
+
this.cancelWebsocketRetry = undefined
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
this.unsyncedChanges = 0 // set to 0 in case we got reconnected
|
|
280
|
+
this.shouldConnect = true
|
|
281
|
+
this.subscribeToBroadcastChannel()
|
|
282
|
+
|
|
283
|
+
const abortableRetry = () => {
|
|
284
|
+
let cancelAttempt = false
|
|
285
|
+
|
|
286
|
+
const retryPromise = retry(this.createWebSocketConnection.bind(this), {
|
|
287
|
+
delay: this.configuration.delay,
|
|
288
|
+
initialDelay: this.configuration.initialDelay,
|
|
289
|
+
factor: this.configuration.factor,
|
|
290
|
+
maxAttempts: this.configuration.maxAttempts,
|
|
291
|
+
minDelay: this.configuration.minDelay,
|
|
292
|
+
maxDelay: this.configuration.maxDelay,
|
|
293
|
+
jitter: this.configuration.jitter,
|
|
294
|
+
timeout: this.configuration.timeout,
|
|
295
|
+
beforeAttempt: context => {
|
|
296
|
+
if (!this.shouldConnect || cancelAttempt) {
|
|
297
|
+
context.abort()
|
|
298
|
+
}
|
|
299
|
+
},
|
|
300
|
+
}).catch((error: any) => {
|
|
301
|
+
// If we aborted the connection attempt then don’t throw an error
|
|
302
|
+
// ref: https://github.com/lifeomic/attempt/blob/master/src/index.ts#L136
|
|
303
|
+
if (error && error.code !== 'ATTEMPT_ABORTED') {
|
|
304
|
+
throw error
|
|
305
|
+
}
|
|
306
|
+
})
|
|
307
|
+
|
|
308
|
+
return {
|
|
309
|
+
retryPromise,
|
|
310
|
+
cancelFunc: () => {
|
|
311
|
+
cancelAttempt = true
|
|
312
|
+
},
|
|
313
|
+
}
|
|
149
314
|
}
|
|
315
|
+
|
|
316
|
+
const { retryPromise, cancelFunc } = abortableRetry()
|
|
317
|
+
this.cancelWebsocketRetry = cancelFunc
|
|
318
|
+
|
|
319
|
+
return retryPromise
|
|
150
320
|
}
|
|
151
321
|
|
|
152
|
-
|
|
153
|
-
|
|
322
|
+
createWebSocketConnection() {
|
|
323
|
+
return new Promise((resolve, reject) => {
|
|
324
|
+
if (this.webSocket) {
|
|
325
|
+
this.webSocket.close()
|
|
326
|
+
this.webSocket = null
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// Init the WebSocket connection
|
|
330
|
+
const ws = new this.configuration.WebSocketPolyfill(this.url)
|
|
331
|
+
ws.binaryType = 'arraybuffer'
|
|
332
|
+
ws.onmessage = this.onMessage.bind(this)
|
|
333
|
+
ws.onclose = this.onClose.bind(this)
|
|
334
|
+
ws.onopen = this.onOpen.bind(this)
|
|
335
|
+
ws.onerror = (err: any) => {
|
|
336
|
+
reject(err)
|
|
337
|
+
}
|
|
338
|
+
this.webSocket = ws
|
|
339
|
+
|
|
340
|
+
// Reset the status
|
|
341
|
+
this.synced = false
|
|
342
|
+
this.status = WebSocketStatus.Connecting
|
|
343
|
+
this.emit('status', { status: WebSocketStatus.Connecting })
|
|
344
|
+
|
|
345
|
+
// Store resolve/reject for later use
|
|
346
|
+
this.connectionAttempt = {
|
|
347
|
+
resolve,
|
|
348
|
+
reject,
|
|
349
|
+
}
|
|
350
|
+
})
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
resolveConnectionAttempt() {
|
|
354
|
+
this.connectionAttempt?.resolve()
|
|
355
|
+
this.connectionAttempt = null
|
|
356
|
+
|
|
357
|
+
this.status = WebSocketStatus.Connected
|
|
358
|
+
this.emit('status', { status: WebSocketStatus.Connected })
|
|
359
|
+
this.emit('connect')
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
stopConnectionAttempt() {
|
|
363
|
+
this.connectionAttempt = null
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
rejectConnectionAttempt() {
|
|
367
|
+
this.connectionAttempt?.reject()
|
|
368
|
+
this.connectionAttempt = null
|
|
154
369
|
}
|
|
155
370
|
|
|
156
371
|
get document() {
|
|
157
|
-
return this.
|
|
372
|
+
return this.configuration.document
|
|
158
373
|
}
|
|
159
374
|
|
|
160
375
|
get awareness() {
|
|
161
|
-
return this.
|
|
376
|
+
return this.configuration.awareness
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
get hasUnsyncedChanges() {
|
|
380
|
+
return this.unsyncedChanges > 0
|
|
162
381
|
}
|
|
163
382
|
|
|
164
383
|
checkConnection() {
|
|
384
|
+
// Don’t check the connection when it’s not even established
|
|
165
385
|
if (this.status !== WebSocketStatus.Connected) {
|
|
166
386
|
return
|
|
167
387
|
}
|
|
168
388
|
|
|
169
|
-
|
|
389
|
+
// Don’t close then connection while waiting for the first message
|
|
390
|
+
if (!this.lastMessageReceived) {
|
|
391
|
+
return
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
// Don’t close the connection when a message was received recently
|
|
395
|
+
if (this.configuration.messageReconnectTimeout >= time.getUnixTime() - this.lastMessageReceived) {
|
|
170
396
|
return
|
|
171
397
|
}
|
|
172
398
|
|
|
173
399
|
// No message received in a long time, not even your own
|
|
174
400
|
// Awareness updates, which are updated every 15 seconds.
|
|
175
|
-
this.webSocket
|
|
401
|
+
this.webSocket?.close()
|
|
176
402
|
}
|
|
177
403
|
|
|
178
404
|
forceSync() {
|
|
@@ -183,14 +409,19 @@ export class HocuspocusProvider extends EventEmitter {
|
|
|
183
409
|
this.send(SyncStepOneMessage, { document: this.document })
|
|
184
410
|
}
|
|
185
411
|
|
|
186
|
-
|
|
412
|
+
boundBeforeUnload = this.beforeUnload.bind(this)
|
|
413
|
+
|
|
414
|
+
beforeUnload() {
|
|
415
|
+
removeAwarenessStates(this.awareness, [this.document.clientID], 'window unload')
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
registerEventListeners() {
|
|
187
419
|
if (typeof window === 'undefined') {
|
|
188
420
|
return
|
|
189
421
|
}
|
|
190
422
|
|
|
191
|
-
window.addEventListener('
|
|
192
|
-
|
|
193
|
-
})
|
|
423
|
+
window.addEventListener('online', this.boundConnect)
|
|
424
|
+
window.addEventListener('beforeunload', this.boundBeforeUnload)
|
|
194
425
|
}
|
|
195
426
|
|
|
196
427
|
documentUpdateHandler(update: Uint8Array, origin: any) {
|
|
@@ -198,6 +429,7 @@ export class HocuspocusProvider extends EventEmitter {
|
|
|
198
429
|
return
|
|
199
430
|
}
|
|
200
431
|
|
|
432
|
+
this.unsyncedChanges += 1
|
|
201
433
|
this.send(UpdateMessage, { update }, true)
|
|
202
434
|
}
|
|
203
435
|
|
|
@@ -210,19 +442,32 @@ export class HocuspocusProvider extends EventEmitter {
|
|
|
210
442
|
}, true)
|
|
211
443
|
}
|
|
212
444
|
|
|
445
|
+
permissionDeniedHandler(reason: string) {
|
|
446
|
+
this.emit('authenticationFailed', { reason })
|
|
447
|
+
this.isAuthenticated = false
|
|
448
|
+
this.shouldConnect = false
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
authenticatedHandler() {
|
|
452
|
+
this.isAuthenticated = true
|
|
453
|
+
|
|
454
|
+
this.emit('authenticated')
|
|
455
|
+
this.startSync()
|
|
456
|
+
}
|
|
457
|
+
|
|
213
458
|
// Ensure that the URL always ends with /
|
|
214
459
|
get serverUrl() {
|
|
215
|
-
while (this.
|
|
216
|
-
return this.
|
|
460
|
+
while (this.configuration.url[this.configuration.url.length - 1] === '/') {
|
|
461
|
+
return this.configuration.url.slice(0, this.configuration.url.length - 1)
|
|
217
462
|
}
|
|
218
463
|
|
|
219
|
-
return this.
|
|
464
|
+
return this.configuration.url
|
|
220
465
|
}
|
|
221
466
|
|
|
222
467
|
get url() {
|
|
223
|
-
const encodedParams = url.encodeQueryParams(this.
|
|
468
|
+
const encodedParams = url.encodeQueryParams(this.configuration.parameters)
|
|
224
469
|
|
|
225
|
-
return `${this.serverUrl}/${this.
|
|
470
|
+
return `${this.serverUrl}/${this.configuration.name}${encodedParams.length === 0 ? '' : `?${encodedParams}`}`
|
|
226
471
|
}
|
|
227
472
|
|
|
228
473
|
get synced(): boolean {
|
|
@@ -239,13 +484,8 @@ export class HocuspocusProvider extends EventEmitter {
|
|
|
239
484
|
this.emit('sync', { state })
|
|
240
485
|
}
|
|
241
486
|
|
|
242
|
-
|
|
243
|
-
this.
|
|
244
|
-
|
|
245
|
-
if (this.status !== WebSocketStatus.Connected) {
|
|
246
|
-
this.createWebSocketConnection()
|
|
247
|
-
this.subscribeToBroadcastChannel()
|
|
248
|
-
}
|
|
487
|
+
get isAuthenticationRequired(): boolean {
|
|
488
|
+
return !!this.configuration.token && !this.isAuthenticated
|
|
249
489
|
}
|
|
250
490
|
|
|
251
491
|
disconnect() {
|
|
@@ -263,34 +503,29 @@ export class HocuspocusProvider extends EventEmitter {
|
|
|
263
503
|
}
|
|
264
504
|
}
|
|
265
505
|
|
|
266
|
-
|
|
267
|
-
|
|
506
|
+
async onOpen(event: Event) {
|
|
507
|
+
this.emit('open', { event })
|
|
508
|
+
|
|
509
|
+
if (this.isAuthenticationRequired) {
|
|
510
|
+
this.send(AuthenticationMessage, {
|
|
511
|
+
token: await this.getToken(),
|
|
512
|
+
})
|
|
268
513
|
return
|
|
269
514
|
}
|
|
270
515
|
|
|
271
|
-
this.
|
|
272
|
-
this.webSocket.binaryType = 'arraybuffer'
|
|
273
|
-
|
|
274
|
-
this.status = WebSocketStatus.Connecting
|
|
275
|
-
this.synced = false
|
|
276
|
-
|
|
277
|
-
this.webSocket.onmessage = this.onMessage.bind(this)
|
|
278
|
-
this.webSocket.onclose = this.onClose.bind(this)
|
|
279
|
-
this.webSocket.onopen = this.onOpen.bind(this)
|
|
280
|
-
|
|
281
|
-
this.emit('status', { status: 'connecting' })
|
|
516
|
+
this.startSync()
|
|
282
517
|
}
|
|
283
518
|
|
|
284
|
-
|
|
285
|
-
this.
|
|
286
|
-
|
|
519
|
+
async getToken() {
|
|
520
|
+
if (typeof this.configuration.token === 'function') {
|
|
521
|
+
const token = await this.configuration.token()
|
|
522
|
+
return token
|
|
523
|
+
}
|
|
287
524
|
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
this.status = WebSocketStatus.Connected
|
|
291
|
-
this.emit('status', { status: 'connected' })
|
|
292
|
-
this.emit('connect')
|
|
525
|
+
return this.configuration.token
|
|
526
|
+
}
|
|
293
527
|
|
|
528
|
+
startSync() {
|
|
294
529
|
this.send(SyncStepOneMessage, { document: this.document })
|
|
295
530
|
|
|
296
531
|
if (this.awareness.getLocalState() !== null) {
|
|
@@ -301,14 +536,12 @@ export class HocuspocusProvider extends EventEmitter {
|
|
|
301
536
|
}
|
|
302
537
|
}
|
|
303
538
|
|
|
304
|
-
send(Message:
|
|
539
|
+
send(Message: ConstructableOutgoingMessage, args: any, broadcast = false) {
|
|
305
540
|
if (broadcast) {
|
|
306
|
-
this.mux(() => {
|
|
307
|
-
this.broadcast(Message, args)
|
|
308
|
-
})
|
|
541
|
+
this.mux(() => { this.broadcast(Message, args) })
|
|
309
542
|
}
|
|
310
543
|
|
|
311
|
-
if (this.
|
|
544
|
+
if (this.webSocket?.readyState === WsReadyStates.Open) {
|
|
312
545
|
const messageSender = new MessageSender(Message, args)
|
|
313
546
|
|
|
314
547
|
this.emit('outgoingMessage', { message: messageSender.message })
|
|
@@ -317,9 +550,7 @@ export class HocuspocusProvider extends EventEmitter {
|
|
|
317
550
|
}
|
|
318
551
|
|
|
319
552
|
onMessage(event: MessageEvent) {
|
|
320
|
-
|
|
321
|
-
this.webSocketConnectionEstablished()
|
|
322
|
-
}
|
|
553
|
+
this.resolveConnectionAttempt()
|
|
323
554
|
|
|
324
555
|
this.lastMessageReceived = time.getUnixTime()
|
|
325
556
|
|
|
@@ -327,22 +558,17 @@ export class HocuspocusProvider extends EventEmitter {
|
|
|
327
558
|
|
|
328
559
|
this.emit('message', { event, message })
|
|
329
560
|
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
// TODO: What’s that doing?
|
|
333
|
-
// if (encoding.length(encoder) > 1) {
|
|
334
|
-
// this.send(encoding.toUint8Array(encoder))
|
|
335
|
-
// }
|
|
561
|
+
new MessageReceiver(message).apply(this)
|
|
336
562
|
}
|
|
337
563
|
|
|
338
564
|
onClose(event: CloseEvent) {
|
|
339
565
|
this.emit('close', { event })
|
|
340
566
|
|
|
341
567
|
this.webSocket = null
|
|
568
|
+
this.isAuthenticated = false
|
|
569
|
+
this.synced = false
|
|
342
570
|
|
|
343
571
|
if (this.status === WebSocketStatus.Connected) {
|
|
344
|
-
this.synced = false
|
|
345
|
-
|
|
346
572
|
// update awareness (all users except local left)
|
|
347
573
|
removeAwarenessStates(
|
|
348
574
|
this.awareness,
|
|
@@ -351,29 +577,46 @@ export class HocuspocusProvider extends EventEmitter {
|
|
|
351
577
|
)
|
|
352
578
|
|
|
353
579
|
this.status = WebSocketStatus.Disconnected
|
|
354
|
-
this.emit('status', { status:
|
|
580
|
+
this.emit('status', { status: WebSocketStatus.Disconnected })
|
|
355
581
|
this.emit('disconnect', { event })
|
|
356
|
-
} else {
|
|
357
|
-
this.failedConnectionAttempts += 1
|
|
358
582
|
}
|
|
359
583
|
|
|
360
|
-
if (
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
584
|
+
if (event.code === Unauthorized.code) {
|
|
585
|
+
if (!this.configuration.quiet) {
|
|
586
|
+
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.')
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
this.shouldConnect = false
|
|
590
|
+
}
|
|
365
591
|
|
|
366
|
-
|
|
367
|
-
|
|
592
|
+
if (event.code === Forbidden.code) {
|
|
593
|
+
if (!this.configuration.quiet) {
|
|
594
|
+
console.warn('[HocuspocusProvider] The provided authentication token isn’t allowed to connect to this server. Will try again.')
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
if (this.connectionAttempt) {
|
|
599
|
+
// That connection attempt failed.
|
|
600
|
+
this.rejectConnectionAttempt()
|
|
601
|
+
} else if (this.shouldConnect) {
|
|
602
|
+
// The connection was closed by the server. Let’s just try again.
|
|
603
|
+
this.connect()
|
|
604
|
+
}
|
|
368
605
|
|
|
606
|
+
// If we’ll reconnect, we’re done for now.
|
|
607
|
+
if (this.shouldConnect) {
|
|
369
608
|
return
|
|
370
609
|
}
|
|
371
610
|
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
this.emit('disconnect', { event })
|
|
611
|
+
// The status is set correctly already.
|
|
612
|
+
if (this.status === WebSocketStatus.Disconnected) {
|
|
613
|
+
return
|
|
376
614
|
}
|
|
615
|
+
|
|
616
|
+
// Let’s update the connection status.
|
|
617
|
+
this.status = WebSocketStatus.Disconnected
|
|
618
|
+
this.emit('status', { status: WebSocketStatus.Disconnected })
|
|
619
|
+
this.emit('disconnect', { event })
|
|
377
620
|
}
|
|
378
621
|
|
|
379
622
|
destroy() {
|
|
@@ -385,33 +628,46 @@ export class HocuspocusProvider extends EventEmitter {
|
|
|
385
628
|
|
|
386
629
|
clearInterval(this.intervals.connectionChecker)
|
|
387
630
|
|
|
631
|
+
removeAwarenessStates(this.awareness, [this.document.clientID], 'provider destroy')
|
|
632
|
+
|
|
633
|
+
// If there is still a connection attempt outstanding then we should stop
|
|
634
|
+
// it before calling disconnect, otherwise it will be rejected in the onClose
|
|
635
|
+
// handler and trigger a retry
|
|
636
|
+
this.stopConnectionAttempt()
|
|
637
|
+
|
|
388
638
|
this.disconnect()
|
|
389
639
|
|
|
390
640
|
this.awareness.off('update', this.awarenessUpdateHandler)
|
|
391
641
|
this.document.off('update', this.documentUpdateHandler)
|
|
392
642
|
|
|
393
643
|
this.removeAllListeners()
|
|
644
|
+
|
|
645
|
+
if (typeof window === 'undefined') {
|
|
646
|
+
return
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
window.removeEventListener('online', this.boundConnect)
|
|
650
|
+
window.removeEventListener('beforeunload', this.boundBeforeUnload)
|
|
394
651
|
}
|
|
395
652
|
|
|
396
653
|
get broadcastChannel() {
|
|
397
|
-
return `${this.serverUrl}/${this.
|
|
654
|
+
return `${this.serverUrl}/${this.configuration.name}`
|
|
398
655
|
}
|
|
399
656
|
|
|
657
|
+
boundBroadcastChannelSubscriber = this.broadcastChannelSubscriber.bind(this)
|
|
658
|
+
|
|
400
659
|
broadcastChannelSubscriber(data: ArrayBuffer) {
|
|
401
660
|
this.mux(() => {
|
|
402
661
|
const message = new IncomingMessage(data)
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
// if (encoding.length(encoder) > 1) {
|
|
407
|
-
// this.broadcast(encoding.toUint8Array(encoder))
|
|
408
|
-
// }
|
|
662
|
+
new MessageReceiver(message)
|
|
663
|
+
.setBroadcasted(true)
|
|
664
|
+
.apply(this, false)
|
|
409
665
|
})
|
|
410
666
|
}
|
|
411
667
|
|
|
412
668
|
subscribeToBroadcastChannel() {
|
|
413
669
|
if (!this.subscribedToBroadcastChannel) {
|
|
414
|
-
bc.subscribe(this.broadcastChannel, this.
|
|
670
|
+
bc.subscribe(this.broadcastChannel, this.boundBroadcastChannelSubscriber)
|
|
415
671
|
this.subscribedToBroadcastChannel = true
|
|
416
672
|
}
|
|
417
673
|
|
|
@@ -432,13 +688,13 @@ export class HocuspocusProvider extends EventEmitter {
|
|
|
432
688
|
}, true)
|
|
433
689
|
|
|
434
690
|
if (this.subscribedToBroadcastChannel) {
|
|
435
|
-
bc.unsubscribe(this.broadcastChannel, this.
|
|
691
|
+
bc.unsubscribe(this.broadcastChannel, this.boundBroadcastChannelSubscriber)
|
|
436
692
|
this.subscribedToBroadcastChannel = false
|
|
437
693
|
}
|
|
438
694
|
}
|
|
439
695
|
|
|
440
|
-
broadcast(Message:
|
|
441
|
-
if (!this.
|
|
696
|
+
broadcast(Message: ConstructableOutgoingMessage, args?: any) {
|
|
697
|
+
if (!this.configuration.broadcast) {
|
|
442
698
|
return
|
|
443
699
|
}
|
|
444
700
|
|
|
@@ -449,14 +705,6 @@ export class HocuspocusProvider extends EventEmitter {
|
|
|
449
705
|
new MessageSender(Message, args).broadcast(this.broadcastChannel)
|
|
450
706
|
}
|
|
451
707
|
|
|
452
|
-
log(message: string): void {
|
|
453
|
-
if (!this.options.debug) {
|
|
454
|
-
return
|
|
455
|
-
}
|
|
456
|
-
|
|
457
|
-
console.log(message)
|
|
458
|
-
}
|
|
459
|
-
|
|
460
708
|
setAwarenessField(key: string, value: any) {
|
|
461
709
|
this.awareness.setLocalStateField(key, value)
|
|
462
710
|
}
|