@hocuspocus/provider 1.0.0-alpha.35 → 1.0.0-alpha.38
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 +24 -8
- package/dist/hocuspocus-provider.cjs.map +1 -1
- package/dist/hocuspocus-provider.esm.js +24 -8
- package/dist/hocuspocus-provider.esm.js.map +1 -1
- package/dist/packages/extension-database/src/Database.d.ts +3 -5
- package/dist/packages/extension-monitor/src/Collector.d.ts +2 -1
- package/dist/packages/extension-redis/src/Redis.d.ts +91 -9
- package/dist/packages/extension-redis/src/index.d.ts +0 -1
- package/dist/packages/provider/src/HocuspocusProvider.d.ts +6 -0
- package/dist/packages/server/src/Document.d.ts +1 -1
- package/dist/packages/server/src/Hocuspocus.d.ts +6 -2
- package/dist/packages/server/src/MessageReceiver.d.ts +1 -1
- package/dist/packages/server/src/OutgoingMessage.d.ts +1 -0
- package/dist/packages/server/src/index.d.ts +3 -4
- package/dist/packages/server/src/types.d.ts +26 -6
- package/dist/tests/{extension-redis-rewrite/closeConnections.d.ts → extension-database/fetch.d.ts} +0 -0
- package/dist/tests/{extension-redis-rewrite/getConnectionCount.d.ts → extension-redis/closeConnections.d.ts} +0 -0
- package/dist/tests/{extension-redis-rewrite/getDocumentsCount.d.ts → extension-redis/getConnectionCount.d.ts} +0 -0
- package/dist/tests/{extension-redis-rewrite/onAwarenessChange.d.ts → extension-redis/getDocumentsCount.d.ts} +0 -0
- package/dist/tests/{extension-redis-rewrite/onChange.d.ts → extension-redis/onAwarenessChange.d.ts} +0 -0
- package/dist/tests/{extension-redis-rewrite/onStoreDocument.d.ts → extension-redis/onChange.d.ts} +0 -0
- package/dist/tests/extension-redis/{onLoadDocument.d.ts → onStoreDocument.d.ts} +0 -0
- package/dist/tests/utils/index.d.ts +1 -0
- package/dist/tests/utils/randomInteger.d.ts +1 -0
- package/package.json +4 -4
- package/src/HocuspocusProvider.ts +24 -7
- package/src/MessageReceiver.ts +8 -2
- package/dist/packages/extension-redis/src/RedisCluster.d.ts +0 -4
- package/dist/tests/extension-redis/onSynced.d.ts +0 -1
|
@@ -1,12 +1,10 @@
|
|
|
1
|
-
import { Extension, onChangePayload, onLoadDocumentPayload, storePayload } from '@hocuspocus/server';
|
|
1
|
+
import { Extension, onChangePayload, onLoadDocumentPayload, storePayload, fetchPayload } from '@hocuspocus/server';
|
|
2
2
|
export interface DatabaseConfiguration {
|
|
3
3
|
/**
|
|
4
4
|
* Pass a Promise to retrieve updates from your database. The Promise should resolve to
|
|
5
5
|
* an array of items with Y.js-compatible binary data.
|
|
6
6
|
*/
|
|
7
|
-
fetch: (
|
|
8
|
-
documentName: string;
|
|
9
|
-
}) => Promise<Uint8Array | null>;
|
|
7
|
+
fetch: (data: fetchPayload) => Promise<Uint8Array | null>;
|
|
10
8
|
/**
|
|
11
9
|
* Pass a function to store updates in your database.
|
|
12
10
|
*/
|
|
@@ -24,7 +22,7 @@ export declare class Database implements Extension {
|
|
|
24
22
|
/**
|
|
25
23
|
* Get stored data from the database.
|
|
26
24
|
*/
|
|
27
|
-
onLoadDocument(
|
|
25
|
+
onLoadDocument(data: onLoadDocumentPayload): Promise<any>;
|
|
28
26
|
/**
|
|
29
27
|
* Store new updates in the database.
|
|
30
28
|
*/
|
|
@@ -50,11 +50,12 @@ export declare class Collector {
|
|
|
50
50
|
documents(): {};
|
|
51
51
|
info(): Promise<{
|
|
52
52
|
configuration: Partial<Configuration>;
|
|
53
|
-
ipAddress: string;
|
|
53
|
+
ipAddress: string | null;
|
|
54
54
|
nodeVersion: string;
|
|
55
55
|
platform: NodeJS.Platform;
|
|
56
56
|
started: string;
|
|
57
57
|
version: string;
|
|
58
58
|
}>;
|
|
59
|
+
private getIpAddress;
|
|
59
60
|
private static readableYDoc;
|
|
60
61
|
}
|
|
@@ -1,16 +1,98 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
1
|
+
import RedisClient from 'ioredis';
|
|
2
|
+
import Redlock from 'redlock';
|
|
3
|
+
import { Document, Extension, afterLoadDocumentPayload, afterStoreDocumentPayload, onDisconnectPayload, onStoreDocumentPayload, onAwarenessUpdatePayload, onChangePayload, Debugger, onConfigurePayload, onListenPayload } from '@hocuspocus/server';
|
|
3
4
|
export interface Configuration {
|
|
5
|
+
/**
|
|
6
|
+
* Redis port
|
|
7
|
+
*/
|
|
8
|
+
port: number;
|
|
9
|
+
/**
|
|
10
|
+
* Redis host
|
|
11
|
+
*/
|
|
12
|
+
host: string;
|
|
13
|
+
/**
|
|
14
|
+
* Options passed directly to Redis constructor
|
|
15
|
+
*
|
|
16
|
+
* https://github.com/luin/ioredis/blob/master/API.md#new-redisport-host-options
|
|
17
|
+
*/
|
|
18
|
+
options?: RedisClient.RedisOptions;
|
|
19
|
+
/**
|
|
20
|
+
* An unique instance name, required to filter messages in Redis.
|
|
21
|
+
* If none is provided an unique id is generated.
|
|
22
|
+
*/
|
|
23
|
+
identifier: string;
|
|
24
|
+
/**
|
|
25
|
+
* Namespace for Redis keys, if none is provided 'hocuspocus' is used
|
|
26
|
+
*/
|
|
27
|
+
prefix: string;
|
|
28
|
+
/**
|
|
29
|
+
* The maximum time for the Redis lock in ms (in case it can’t be released).
|
|
30
|
+
*/
|
|
31
|
+
lockTimeout: number;
|
|
4
32
|
}
|
|
5
33
|
export declare class Redis implements Extension {
|
|
34
|
+
/**
|
|
35
|
+
* Make sure to give that extension a higher priority, so
|
|
36
|
+
* the `onStoreDocument` hook is able to intercept the chain,
|
|
37
|
+
* before documents are stored to the database.
|
|
38
|
+
*/
|
|
39
|
+
priority: number;
|
|
6
40
|
configuration: Configuration;
|
|
7
|
-
|
|
8
|
-
|
|
41
|
+
pub: RedisClient.Redis;
|
|
42
|
+
sub: RedisClient.Redis;
|
|
43
|
+
documents: Map<string, Document>;
|
|
44
|
+
redlock: Redlock;
|
|
45
|
+
locks: Map<string, Redlock.Lock>;
|
|
46
|
+
logger: Debugger;
|
|
47
|
+
constructor(configuration: Partial<Configuration>);
|
|
48
|
+
onConfigure({ instance }: onConfigurePayload): Promise<void>;
|
|
49
|
+
onListen({ configuration }: onListenPayload): Promise<void>;
|
|
50
|
+
private getKey;
|
|
51
|
+
private pubKey;
|
|
52
|
+
private subKey;
|
|
53
|
+
private lockKey;
|
|
54
|
+
/**
|
|
55
|
+
* Once a document is laoded, subscribe to the channel in Redis.
|
|
56
|
+
*/
|
|
57
|
+
afterLoadDocument({ documentName, document }: afterLoadDocumentPayload): Promise<unknown>;
|
|
58
|
+
/**
|
|
59
|
+
* Publish the first sync step through Redis.
|
|
60
|
+
*/
|
|
61
|
+
private publishFirstSyncStep;
|
|
62
|
+
/**
|
|
63
|
+
* Let’s ask Redis who is connected already.
|
|
64
|
+
*/
|
|
65
|
+
private requestAwarenessFromOtherInstances;
|
|
66
|
+
/**
|
|
67
|
+
* Before the document is stored, make sure to set a lock in Redis.
|
|
68
|
+
* That’s meant to avoid conflicts with other instances trying to store the document.
|
|
69
|
+
*/
|
|
70
|
+
onStoreDocument({ documentName }: onStoreDocumentPayload): Promise<unknown>;
|
|
71
|
+
/**
|
|
72
|
+
* Release the Redis lock, so other instances can store documents.
|
|
73
|
+
*/
|
|
74
|
+
afterStoreDocument({ documentName }: afterStoreDocumentPayload): Promise<void>;
|
|
75
|
+
/**
|
|
76
|
+
* Handle awareness update messages received directly by this Hocuspocus instance.
|
|
77
|
+
*/
|
|
78
|
+
onAwarenessUpdate({ documentName, awareness }: onAwarenessUpdatePayload): Promise<number>;
|
|
79
|
+
/**
|
|
80
|
+
* Handle incoming messages published on all subscribed document channels.
|
|
81
|
+
* Note that this will also include messages from ourselves as it is not possible
|
|
82
|
+
* in Redis to filter these.
|
|
83
|
+
*/
|
|
84
|
+
private handleIncomingMessage;
|
|
85
|
+
/**
|
|
86
|
+
* if the ydoc changed, we'll need to inform other Hocuspocus servers about it.
|
|
87
|
+
*/
|
|
88
|
+
onChange(data: onChangePayload): Promise<any>;
|
|
89
|
+
/**
|
|
90
|
+
* Make sure to *not* listen for further changes, when there’s
|
|
91
|
+
* noone connected anymore.
|
|
92
|
+
*/
|
|
93
|
+
onDisconnect: ({ documentName, clientsCount }: onDisconnectPayload) => Promise<void>;
|
|
9
94
|
/**
|
|
10
|
-
*
|
|
95
|
+
* Kill the Redlock connection immediately.
|
|
11
96
|
*/
|
|
12
|
-
|
|
13
|
-
onLoadDocument(data: onLoadDocumentPayload): Promise<import("yjs").Doc | undefined>;
|
|
14
|
-
onConnect(data: onConnectPayload): Promise<void>;
|
|
15
|
-
onDisconnect(data: onDisconnectPayload): Promise<void>;
|
|
97
|
+
onDestroy(): Promise<void>;
|
|
16
98
|
}
|
|
@@ -110,6 +110,7 @@ export declare class HocuspocusProvider extends EventEmitter {
|
|
|
110
110
|
shouldConnect: boolean;
|
|
111
111
|
status: WebSocketStatus;
|
|
112
112
|
isSynced: boolean;
|
|
113
|
+
unsyncedChanges: number;
|
|
113
114
|
isAuthenticated: boolean;
|
|
114
115
|
lastMessageReceived: number;
|
|
115
116
|
mux: mutex.mutex;
|
|
@@ -120,6 +121,7 @@ export declare class HocuspocusProvider extends EventEmitter {
|
|
|
120
121
|
} | null;
|
|
121
122
|
constructor(configuration: HocuspocusProviderConfiguration);
|
|
122
123
|
setConfiguration(configuration?: Partial<HocuspocusProviderConfiguration>): void;
|
|
124
|
+
boundConnect: () => Promise<void>;
|
|
123
125
|
connect(): Promise<void>;
|
|
124
126
|
createWebSocketConnection(): Promise<unknown>;
|
|
125
127
|
resolveConnectionAttempt(): void;
|
|
@@ -127,8 +129,11 @@ export declare class HocuspocusProvider extends EventEmitter {
|
|
|
127
129
|
rejectConnectionAttempt(): void;
|
|
128
130
|
get document(): Y.Doc;
|
|
129
131
|
get awareness(): Awareness;
|
|
132
|
+
get hasUnsyncedChanges(): boolean;
|
|
130
133
|
checkConnection(): void;
|
|
131
134
|
forceSync(): void;
|
|
135
|
+
boundBeforeUnload: () => void;
|
|
136
|
+
beforeUnload(): void;
|
|
132
137
|
registerEventListeners(): void;
|
|
133
138
|
documentUpdateHandler(update: Uint8Array, origin: any): void;
|
|
134
139
|
awarenessUpdateHandler({ added, updated, removed }: any, origin: any): void;
|
|
@@ -148,6 +153,7 @@ export declare class HocuspocusProvider extends EventEmitter {
|
|
|
148
153
|
onClose(event: CloseEvent): void;
|
|
149
154
|
destroy(): void;
|
|
150
155
|
get broadcastChannel(): string;
|
|
156
|
+
boundBroadcastChannelSubscriber: (data: ArrayBuffer) => void;
|
|
151
157
|
broadcastChannelSubscriber(data: ArrayBuffer): void;
|
|
152
158
|
subscribeToBroadcastChannel(): void;
|
|
153
159
|
disconnectBroadcastChannel(): void;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
2
|
import WebSocket, { AddressInfo, WebSocketServer } from 'ws';
|
|
3
3
|
import { IncomingMessage, Server as HTTPServer } from 'http';
|
|
4
|
-
import { Configuration,
|
|
4
|
+
import { Configuration, HookName, HookPayload } from './types';
|
|
5
5
|
import Document from './Document';
|
|
6
6
|
import { Debugger } from './Debugger';
|
|
7
7
|
import { onListenPayload } from '.';
|
|
@@ -12,6 +12,10 @@ export declare const defaultConfiguration: {
|
|
|
12
12
|
debounce: number;
|
|
13
13
|
maxDebounce: number;
|
|
14
14
|
quiet: boolean;
|
|
15
|
+
yDocOptions: {
|
|
16
|
+
gc: boolean;
|
|
17
|
+
gcFilter: () => boolean;
|
|
18
|
+
};
|
|
15
19
|
};
|
|
16
20
|
/**
|
|
17
21
|
* Hocuspocus Server
|
|
@@ -88,7 +92,7 @@ export declare class Hocuspocus {
|
|
|
88
92
|
* Run the given hook on all configured extensions.
|
|
89
93
|
* Runs the given callback after each hook.
|
|
90
94
|
*/
|
|
91
|
-
hooks(name:
|
|
95
|
+
hooks(name: HookName, payload: HookPayload, callback?: Function | null): Promise<any>;
|
|
92
96
|
/**
|
|
93
97
|
* Get parameters by the given request
|
|
94
98
|
*/
|
|
@@ -8,6 +8,6 @@ export declare class MessageReceiver {
|
|
|
8
8
|
logger: Debugger;
|
|
9
9
|
constructor(message: IncomingMessage, logger: Debugger);
|
|
10
10
|
apply(document: Document, connection?: Connection, reply?: (message: Uint8Array) => void): void;
|
|
11
|
-
readSyncMessage(message: IncomingMessage, document: Document, connection?: Connection, reply?: (message: Uint8Array) => void): 0 | 2 | 1;
|
|
11
|
+
readSyncMessage(message: IncomingMessage, document: Document, connection?: Connection, reply?: (message: Uint8Array) => void, requestFirstSync?: boolean): 0 | 2 | 1;
|
|
12
12
|
applyQueryAwarenessMessage(awareness: Awareness, reply?: (message: Uint8Array) => void): void;
|
|
13
13
|
}
|
|
@@ -7,6 +7,7 @@ export declare class OutgoingMessage {
|
|
|
7
7
|
category?: string;
|
|
8
8
|
constructor();
|
|
9
9
|
createSyncMessage(): OutgoingMessage;
|
|
10
|
+
createSyncReplyMessage(): OutgoingMessage;
|
|
10
11
|
createAwarenessUpdateMessage(awareness: Awareness, changedClients?: Array<any>): OutgoingMessage;
|
|
11
12
|
writeQueryAwareness(): OutgoingMessage;
|
|
12
13
|
writeAuthenticated(): OutgoingMessage;
|
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
export * from './Hocuspocus';
|
|
2
1
|
export * from './Connection';
|
|
2
|
+
export * from './Debugger';
|
|
3
3
|
export * from './Document';
|
|
4
|
+
export * from './Hocuspocus';
|
|
4
5
|
export * from './IncomingMessage';
|
|
6
|
+
export * from './MessageReceiver';
|
|
5
7
|
export * from './OutgoingMessage';
|
|
6
8
|
export * from './types';
|
|
7
|
-
export * from './MessageReceiver';
|
|
8
|
-
export * from './Document';
|
|
9
|
-
export * from './Connection';
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
2
|
import { IncomingHttpHeaders, IncomingMessage, ServerResponse } from 'http';
|
|
3
3
|
import { URLSearchParams } from 'url';
|
|
4
|
-
import { Socket } from 'net';
|
|
5
4
|
import { Awareness } from 'y-protocols/awareness';
|
|
6
5
|
import Document from './Document';
|
|
7
6
|
import { Hocuspocus } from './Hocuspocus';
|
|
@@ -10,7 +9,8 @@ export declare enum MessageType {
|
|
|
10
9
|
Sync = 0,
|
|
11
10
|
Awareness = 1,
|
|
12
11
|
Auth = 2,
|
|
13
|
-
QueryAwareness = 3
|
|
12
|
+
QueryAwareness = 3,
|
|
13
|
+
SyncReply = 4
|
|
14
14
|
}
|
|
15
15
|
export interface AwarenessUpdate {
|
|
16
16
|
added: Array<any>;
|
|
@@ -44,11 +44,12 @@ export interface Extension {
|
|
|
44
44
|
onDisconnect?(data: onDisconnectPayload): Promise<any>;
|
|
45
45
|
onDestroy?(data: onDestroyPayload): Promise<any>;
|
|
46
46
|
}
|
|
47
|
-
export declare type
|
|
47
|
+
export declare type HookName = 'onConfigure' | 'onListen' | 'onUpgrade' | 'onConnect' | 'connected' | 'onAuthenticate' |
|
|
48
48
|
/**
|
|
49
49
|
* @deprecated onCreateDocument is deprecated, use onLoadDocument instead
|
|
50
50
|
*/
|
|
51
51
|
'onCreateDocument' | 'onLoadDocument' | 'afterLoadDocument' | 'onChange' | 'onStoreDocument' | 'afterStoreDocument' | 'onAwarenessUpdate' | 'onRequest' | 'onDisconnect' | 'onDestroy';
|
|
52
|
+
export declare type HookPayload = onConfigurePayload | onListenPayload | onUpgradePayload | onConnectPayload | connectedPayload | onAuthenticatePayload | onLoadDocumentPayload | onChangePayload | onStoreDocumentPayload | afterStoreDocumentPayload | onAwarenessUpdatePayload | onRequestPayload | onDisconnectPayload | onDestroyPayload;
|
|
52
53
|
export interface Configuration extends Extension {
|
|
53
54
|
/**
|
|
54
55
|
* A name for the instance, used for logging.
|
|
@@ -79,6 +80,13 @@ export interface Configuration extends Extension {
|
|
|
79
80
|
* By default, the servers show a start screen. If passed false, the server will start quietly.
|
|
80
81
|
*/
|
|
81
82
|
quiet: boolean;
|
|
83
|
+
/**
|
|
84
|
+
* options to pass to the ydoc document
|
|
85
|
+
*/
|
|
86
|
+
yDocOptions: {
|
|
87
|
+
gc: boolean;
|
|
88
|
+
gcFilter: () => boolean;
|
|
89
|
+
};
|
|
82
90
|
/**
|
|
83
91
|
* Function which returns the (customized) document name based on the request
|
|
84
92
|
*/
|
|
@@ -179,6 +187,16 @@ export declare type StatesArray = {
|
|
|
179
187
|
clientId: number;
|
|
180
188
|
[key: string | number]: any;
|
|
181
189
|
}[];
|
|
190
|
+
export interface fetchPayload {
|
|
191
|
+
context: any;
|
|
192
|
+
document: Document;
|
|
193
|
+
documentName: string;
|
|
194
|
+
instance: Hocuspocus;
|
|
195
|
+
requestHeaders: IncomingHttpHeaders;
|
|
196
|
+
requestParameters: URLSearchParams;
|
|
197
|
+
socketId: string;
|
|
198
|
+
connection: ConnectionConfiguration;
|
|
199
|
+
}
|
|
182
200
|
export interface storePayload extends onStoreDocumentPayload {
|
|
183
201
|
state: Buffer;
|
|
184
202
|
}
|
|
@@ -198,19 +216,21 @@ export interface onRequestPayload {
|
|
|
198
216
|
instance: Hocuspocus;
|
|
199
217
|
}
|
|
200
218
|
export interface onUpgradePayload {
|
|
201
|
-
head: any;
|
|
202
219
|
request: IncomingMessage;
|
|
203
|
-
socket:
|
|
220
|
+
socket: any;
|
|
221
|
+
head: any;
|
|
204
222
|
instance: Hocuspocus;
|
|
205
223
|
}
|
|
206
224
|
export interface onListenPayload {
|
|
225
|
+
instance: Hocuspocus;
|
|
226
|
+
configuration: Configuration;
|
|
207
227
|
port: number;
|
|
208
228
|
}
|
|
209
229
|
export interface onDestroyPayload {
|
|
210
230
|
instance: Hocuspocus;
|
|
211
231
|
}
|
|
212
232
|
export interface onConfigurePayload {
|
|
233
|
+
instance: Hocuspocus;
|
|
213
234
|
configuration: Configuration;
|
|
214
235
|
version: string;
|
|
215
|
-
instance: Hocuspocus;
|
|
216
236
|
}
|
package/dist/tests/{extension-redis-rewrite/closeConnections.d.ts → extension-database/fetch.d.ts}
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
package/dist/tests/{extension-redis-rewrite/onChange.d.ts → extension-redis/onAwarenessChange.d.ts}
RENAMED
|
File without changes
|
package/dist/tests/{extension-redis-rewrite/onStoreDocument.d.ts → extension-redis/onChange.d.ts}
RENAMED
|
File without changes
|
|
File without changes
|
|
@@ -2,6 +2,7 @@ export * from './createDirectory';
|
|
|
2
2
|
export * from './flushRedis';
|
|
3
3
|
export * from './newHocuspocus';
|
|
4
4
|
export * from './newHocuspocusProvider';
|
|
5
|
+
export * from './randomInteger';
|
|
5
6
|
export * from './redisConnectionSettings';
|
|
6
7
|
export * from './removeDirectory';
|
|
7
8
|
export * from './sleep';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const randomInteger: (min: number, max: number) => number;
|
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.38",
|
|
4
4
|
"description": "hocuspocus provider",
|
|
5
5
|
"homepage": "https://hocuspocus.dev",
|
|
6
6
|
"keywords": [
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
],
|
|
12
12
|
"license": "MIT",
|
|
13
13
|
"type": "module",
|
|
14
|
-
"main": "dist/hocuspocus-provider.
|
|
14
|
+
"main": "dist/hocuspocus-provider.cjs",
|
|
15
15
|
"module": "dist/hocuspocus-provider.esm.js",
|
|
16
16
|
"types": "dist/packages/provider/src/index.d.ts",
|
|
17
17
|
"exports": {
|
|
@@ -28,11 +28,11 @@
|
|
|
28
28
|
"dist"
|
|
29
29
|
],
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"@hocuspocus/common": "^1.0.0-alpha.
|
|
31
|
+
"@hocuspocus/common": "^1.0.0-alpha.11",
|
|
32
32
|
"@lifeomic/attempt": "^3.0.2",
|
|
33
33
|
"lib0": "^0.2.46",
|
|
34
34
|
"y-protocols": "^1.0.5",
|
|
35
35
|
"yjs": "^13.5.29"
|
|
36
36
|
},
|
|
37
|
-
"gitHead": "
|
|
37
|
+
"gitHead": "fe784d26fb1b281ab6d0bc7b5fca500be3b62684"
|
|
38
38
|
}
|
|
@@ -182,6 +182,8 @@ export class HocuspocusProvider extends EventEmitter {
|
|
|
182
182
|
|
|
183
183
|
isSynced = false
|
|
184
184
|
|
|
185
|
+
unsyncedChanges = 0
|
|
186
|
+
|
|
185
187
|
isAuthenticated = false
|
|
186
188
|
|
|
187
189
|
lastMessageReceived = 0
|
|
@@ -259,11 +261,14 @@ export class HocuspocusProvider extends EventEmitter {
|
|
|
259
261
|
this.configuration = { ...this.configuration, ...configuration }
|
|
260
262
|
}
|
|
261
263
|
|
|
264
|
+
boundConnect = this.connect.bind(this)
|
|
265
|
+
|
|
262
266
|
async connect() {
|
|
263
267
|
if (this.status === WebSocketStatus.Connected) {
|
|
264
268
|
return
|
|
265
269
|
}
|
|
266
270
|
|
|
271
|
+
this.unsyncedChanges = 0 // set to 0 in case we got reconnected
|
|
267
272
|
this.shouldConnect = true
|
|
268
273
|
this.subscribeToBroadcastChannel()
|
|
269
274
|
|
|
@@ -344,6 +349,10 @@ export class HocuspocusProvider extends EventEmitter {
|
|
|
344
349
|
return this.configuration.awareness
|
|
345
350
|
}
|
|
346
351
|
|
|
352
|
+
get hasUnsyncedChanges() {
|
|
353
|
+
return this.unsyncedChanges > 0
|
|
354
|
+
}
|
|
355
|
+
|
|
347
356
|
checkConnection() {
|
|
348
357
|
// Don’t check the connection when it’s not even established
|
|
349
358
|
if (this.status !== WebSocketStatus.Connected) {
|
|
@@ -373,15 +382,19 @@ export class HocuspocusProvider extends EventEmitter {
|
|
|
373
382
|
this.send(SyncStepOneMessage, { document: this.document })
|
|
374
383
|
}
|
|
375
384
|
|
|
385
|
+
boundBeforeUnload = this.beforeUnload.bind(this)
|
|
386
|
+
|
|
387
|
+
beforeUnload() {
|
|
388
|
+
removeAwarenessStates(this.awareness, [this.document.clientID], 'window unload')
|
|
389
|
+
}
|
|
390
|
+
|
|
376
391
|
registerEventListeners() {
|
|
377
392
|
if (typeof window === 'undefined') {
|
|
378
393
|
return
|
|
379
394
|
}
|
|
380
395
|
|
|
381
|
-
window.addEventListener('online', this.
|
|
382
|
-
window.addEventListener('beforeunload',
|
|
383
|
-
removeAwarenessStates(this.awareness, [this.document.clientID], 'window unload')
|
|
384
|
-
})
|
|
396
|
+
window.addEventListener('online', this.boundConnect)
|
|
397
|
+
window.addEventListener('beforeunload', this.boundBeforeUnload)
|
|
385
398
|
}
|
|
386
399
|
|
|
387
400
|
documentUpdateHandler(update: Uint8Array, origin: any) {
|
|
@@ -389,6 +402,7 @@ export class HocuspocusProvider extends EventEmitter {
|
|
|
389
402
|
return
|
|
390
403
|
}
|
|
391
404
|
|
|
405
|
+
this.unsyncedChanges += 1
|
|
392
406
|
this.send(UpdateMessage, { update }, true)
|
|
393
407
|
}
|
|
394
408
|
|
|
@@ -605,13 +619,16 @@ export class HocuspocusProvider extends EventEmitter {
|
|
|
605
619
|
return
|
|
606
620
|
}
|
|
607
621
|
|
|
608
|
-
window.removeEventListener('online', this.
|
|
622
|
+
window.removeEventListener('online', this.boundConnect)
|
|
623
|
+
window.removeEventListener('beforeunload', this.boundBeforeUnload)
|
|
609
624
|
}
|
|
610
625
|
|
|
611
626
|
get broadcastChannel() {
|
|
612
627
|
return `${this.serverUrl}/${this.configuration.name}`
|
|
613
628
|
}
|
|
614
629
|
|
|
630
|
+
boundBroadcastChannelSubscriber = this.broadcastChannelSubscriber.bind(this)
|
|
631
|
+
|
|
615
632
|
broadcastChannelSubscriber(data: ArrayBuffer) {
|
|
616
633
|
this.mux(() => {
|
|
617
634
|
const message = new IncomingMessage(data)
|
|
@@ -623,7 +640,7 @@ export class HocuspocusProvider extends EventEmitter {
|
|
|
623
640
|
|
|
624
641
|
subscribeToBroadcastChannel() {
|
|
625
642
|
if (!this.subscribedToBroadcastChannel) {
|
|
626
|
-
bc.subscribe(this.broadcastChannel, this.
|
|
643
|
+
bc.subscribe(this.broadcastChannel, this.boundBroadcastChannelSubscriber)
|
|
627
644
|
this.subscribedToBroadcastChannel = true
|
|
628
645
|
}
|
|
629
646
|
|
|
@@ -644,7 +661,7 @@ export class HocuspocusProvider extends EventEmitter {
|
|
|
644
661
|
}, true)
|
|
645
662
|
|
|
646
663
|
if (this.subscribedToBroadcastChannel) {
|
|
647
|
-
bc.unsubscribe(this.broadcastChannel, this.
|
|
664
|
+
bc.unsubscribe(this.broadcastChannel, this.boundBroadcastChannelSubscriber)
|
|
648
665
|
this.subscribedToBroadcastChannel = false
|
|
649
666
|
}
|
|
650
667
|
}
|
package/src/MessageReceiver.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as awarenessProtocol from 'y-protocols/awareness'
|
|
2
|
-
import { readSyncMessage, messageYjsSyncStep2 } from 'y-protocols/sync'
|
|
2
|
+
import { readSyncMessage, messageYjsSyncStep2, messageYjsUpdate } from 'y-protocols/sync'
|
|
3
3
|
import { readAuthMessage } from '@hocuspocus/common'
|
|
4
4
|
import { MessageType } from './types'
|
|
5
5
|
import { HocuspocusProvider } from './HocuspocusProvider'
|
|
@@ -75,9 +75,15 @@ export class MessageReceiver {
|
|
|
75
75
|
)
|
|
76
76
|
|
|
77
77
|
// Synced once we receive Step2
|
|
78
|
-
if (emitSynced && syncMessageType === messageYjsSyncStep2) {
|
|
78
|
+
if (emitSynced && (syncMessageType === messageYjsSyncStep2)) {
|
|
79
79
|
provider.synced = true
|
|
80
80
|
}
|
|
81
|
+
|
|
82
|
+
if (syncMessageType === messageYjsUpdate || syncMessageType === messageYjsSyncStep2) {
|
|
83
|
+
if (provider.unsyncedChanges > 0) {
|
|
84
|
+
provider.unsyncedChanges -= 1
|
|
85
|
+
}
|
|
86
|
+
}
|
|
81
87
|
}
|
|
82
88
|
|
|
83
89
|
private applyAwarenessMessage(provider: HocuspocusProvider) {
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|