@hocuspocus/provider 2.5.0 → 2.6.0
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 +67 -31
- package/dist/hocuspocus-provider.cjs.map +1 -1
- package/dist/hocuspocus-provider.esm.js +67 -31
- package/dist/hocuspocus-provider.esm.js.map +1 -1
- package/dist/packages/provider/src/HocuspocusProviderWebsocket.d.ts +10 -2
- package/dist/packages/provider/src/TiptapCollabProvider.d.ts +19 -8
- package/dist/packages/provider/src/types.d.ts +45 -0
- package/dist/packages/server/src/types.d.ts +4 -3
- package/package.json +2 -2
- package/src/HocuspocusProvider.ts +4 -0
- package/src/HocuspocusProviderWebsocket.ts +77 -37
- package/src/TiptapCollabProvider.ts +26 -21
- package/src/types.ts +63 -0
|
@@ -4,6 +4,9 @@ import { Event } from 'ws';
|
|
|
4
4
|
import EventEmitter from './EventEmitter.js';
|
|
5
5
|
import { HocuspocusProvider } from './HocuspocusProvider.js';
|
|
6
6
|
import { WebSocketStatus, onAwarenessChangeParameters, onAwarenessUpdateParameters, onCloseParameters, onDisconnectParameters, onMessageParameters, onOpenParameters, onOutgoingMessageParameters, onStatusParameters } from './types.js';
|
|
7
|
+
export type HocusPocusWebSocket = WebSocket & {
|
|
8
|
+
identifier: string;
|
|
9
|
+
};
|
|
7
10
|
export type HocuspocusProviderWebsocketConfiguration = Required<Pick<CompleteHocuspocusProviderWebsocketConfiguration, 'url'>> & Partial<CompleteHocuspocusProviderWebsocketConfiguration>;
|
|
8
11
|
export interface CompleteHocuspocusProviderWebsocketConfiguration {
|
|
9
12
|
/**
|
|
@@ -79,10 +82,14 @@ export declare class HocuspocusProviderWebsocket extends EventEmitter {
|
|
|
79
82
|
private messageQueue;
|
|
80
83
|
configuration: CompleteHocuspocusProviderWebsocketConfiguration;
|
|
81
84
|
subscribedToBroadcastChannel: boolean;
|
|
82
|
-
webSocket:
|
|
85
|
+
webSocket: HocusPocusWebSocket | null;
|
|
86
|
+
webSocketHandlers: {
|
|
87
|
+
[key: string]: any;
|
|
88
|
+
};
|
|
83
89
|
shouldConnect: boolean;
|
|
84
90
|
status: WebSocketStatus;
|
|
85
91
|
lastMessageReceived: number;
|
|
92
|
+
identifier: number;
|
|
86
93
|
mux: mutex.mutex;
|
|
87
94
|
intervals: any;
|
|
88
95
|
connectionAttempt: {
|
|
@@ -100,6 +107,8 @@ export declare class HocuspocusProviderWebsocket extends EventEmitter {
|
|
|
100
107
|
boundConnect: () => Promise<unknown>;
|
|
101
108
|
cancelWebsocketRetry?: () => void;
|
|
102
109
|
connect(): Promise<unknown>;
|
|
110
|
+
attachWebSocketListeners(ws: HocusPocusWebSocket, reject: Function): void;
|
|
111
|
+
cleanupWebSocket(): void;
|
|
103
112
|
createWebSocketConnection(): Promise<unknown>;
|
|
104
113
|
onMessage(event: MessageEvent): void;
|
|
105
114
|
resolveConnectionAttempt(): void;
|
|
@@ -107,7 +116,6 @@ export declare class HocuspocusProviderWebsocket extends EventEmitter {
|
|
|
107
116
|
rejectConnectionAttempt(): void;
|
|
108
117
|
closeTries: number;
|
|
109
118
|
checkConnection(): void;
|
|
110
|
-
registerEventListeners(): void;
|
|
111
119
|
get serverUrl(): string;
|
|
112
120
|
get url(): string;
|
|
113
121
|
disconnect(): void;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { AbstractType, YArrayEvent } from 'yjs';
|
|
2
2
|
import { HocuspocusProvider, HocuspocusProviderConfiguration } from './HocuspocusProvider.js';
|
|
3
3
|
import { TiptapCollabProviderWebsocket } from './TiptapCollabProviderWebsocket.js';
|
|
4
|
+
import type { THistoryVersion } from './types';
|
|
4
5
|
export type TiptapCollabProviderConfiguration = Required<Pick<HocuspocusProviderConfiguration, 'name'>> & Partial<HocuspocusProviderConfiguration> & (Required<Pick<AdditionalTiptapCollabProviderConfiguration, 'websocketProvider'>> | Required<Pick<AdditionalTiptapCollabProviderConfiguration, 'appId'>>);
|
|
5
6
|
export interface AdditionalTiptapCollabProviderConfiguration {
|
|
6
7
|
/**
|
|
@@ -9,19 +10,29 @@ export interface AdditionalTiptapCollabProviderConfiguration {
|
|
|
9
10
|
appId?: string;
|
|
10
11
|
websocketProvider?: TiptapCollabProviderWebsocket;
|
|
11
12
|
}
|
|
12
|
-
export type AuditHistoryVersion = {
|
|
13
|
-
name?: string;
|
|
14
|
-
version: number;
|
|
15
|
-
date: number;
|
|
16
|
-
};
|
|
17
13
|
export declare class TiptapCollabProvider extends HocuspocusProvider {
|
|
18
14
|
tiptapCollabConfigurationPrefix: string;
|
|
19
15
|
constructor(configuration: TiptapCollabProviderConfiguration);
|
|
16
|
+
/**
|
|
17
|
+
* note: this will only work if your server loaded @hocuspocus-pro/extension-history, or if you are on a Tiptap business plan.
|
|
18
|
+
*/
|
|
20
19
|
createVersion(name?: string): void;
|
|
20
|
+
/**
|
|
21
|
+
* note: this will only work if your server loaded @hocuspocus-pro/extension-history, or if you are on a Tiptap business plan.
|
|
22
|
+
*/
|
|
21
23
|
revertToVersion(targetVersion: number): void;
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
24
|
+
/**
|
|
25
|
+
* note: this will only work if your server loaded @hocuspocus-pro/extension-history, or if you are on a Tiptap business plan.
|
|
26
|
+
*
|
|
27
|
+
* The server will reply with a stateless message (THistoryVersionPreviewEvent)
|
|
28
|
+
*/
|
|
29
|
+
previewVersion(targetVersion: number): void;
|
|
30
|
+
/**
|
|
31
|
+
* note: this will only work if your server loaded @hocuspocus-pro/extension-history, or if you are on a Tiptap business plan.
|
|
32
|
+
*/
|
|
33
|
+
getVersions(): THistoryVersion[];
|
|
34
|
+
watchVersions(callback: Parameters<AbstractType<YArrayEvent<THistoryVersion>>['observe']>[0]): void;
|
|
35
|
+
unwatchVersions(callback: Parameters<AbstractType<YArrayEvent<THistoryVersion>>['unobserve']>[0]): void;
|
|
25
36
|
isAutoVersioning(): boolean;
|
|
26
37
|
enableAutoVersioning(): 1;
|
|
27
38
|
disableAutoVersioning(): 0;
|
|
@@ -84,3 +84,48 @@ export type StatesArray = {
|
|
|
84
84
|
clientId: number;
|
|
85
85
|
[key: string | number]: any;
|
|
86
86
|
}[];
|
|
87
|
+
export type THistoryVersion = {
|
|
88
|
+
name?: string;
|
|
89
|
+
version: number;
|
|
90
|
+
date: number;
|
|
91
|
+
};
|
|
92
|
+
export type THistoryConfiguration = {
|
|
93
|
+
autoVersioning: boolean;
|
|
94
|
+
currentVersion: number;
|
|
95
|
+
stateCaptured: number;
|
|
96
|
+
};
|
|
97
|
+
export type THistoryAction = THistoryDocumentRevertAction | THistoryVersionCreateAction | THistoryVersionPreviewAction;
|
|
98
|
+
export type THistoryDocumentRevertAction = {
|
|
99
|
+
action: 'document.revert';
|
|
100
|
+
/**
|
|
101
|
+
* if changes havent been persisted to a version yet, we'll create one with the specified name,
|
|
102
|
+
* expect when `false` is passed.
|
|
103
|
+
*/
|
|
104
|
+
currentVersionName?: string | false;
|
|
105
|
+
/**
|
|
106
|
+
* Name of the version that is created after the revert. Pass `false` to avoid generating a new version.
|
|
107
|
+
*/
|
|
108
|
+
newVersionName?: string | false;
|
|
109
|
+
};
|
|
110
|
+
export type THistoryVersionCreateAction = {
|
|
111
|
+
action: 'version.create';
|
|
112
|
+
name?: string;
|
|
113
|
+
};
|
|
114
|
+
export type THistoryVersionPreviewAction = {
|
|
115
|
+
action: 'version.preview';
|
|
116
|
+
version: number;
|
|
117
|
+
};
|
|
118
|
+
export type THistoryEvent = THistoryVersionPreviewEvent | THistoryVersionCreatedEvent | THistoryDocumentRevertedEvent;
|
|
119
|
+
export type THistoryVersionCreatedEvent = {
|
|
120
|
+
event: 'version.created';
|
|
121
|
+
version: number;
|
|
122
|
+
};
|
|
123
|
+
export type THistoryVersionPreviewEvent = {
|
|
124
|
+
event: 'version.preview';
|
|
125
|
+
version: number;
|
|
126
|
+
ydoc: string;
|
|
127
|
+
};
|
|
128
|
+
export type THistoryDocumentRevertedEvent = {
|
|
129
|
+
event: 'document.reverted';
|
|
130
|
+
version: number;
|
|
131
|
+
};
|
|
@@ -39,7 +39,7 @@ export interface Extension {
|
|
|
39
39
|
connected?(data: connectedPayload): Promise<any>;
|
|
40
40
|
onAuthenticate?(data: onAuthenticatePayload): Promise<any>;
|
|
41
41
|
onLoadDocument?(data: onLoadDocumentPayload): Promise<any>;
|
|
42
|
-
afterLoadDocument?(data:
|
|
42
|
+
afterLoadDocument?(data: afterLoadDocumentPayload): Promise<any>;
|
|
43
43
|
beforeHandleMessage?(data: beforeHandleMessagePayload): Promise<any>;
|
|
44
44
|
beforeBroadcastStateless?(data: beforeBroadcastStatelessPayload): Promise<any>;
|
|
45
45
|
onStateless?(payload: onStatelessPayload): Promise<any>;
|
|
@@ -49,7 +49,7 @@ export interface Extension {
|
|
|
49
49
|
onAwarenessUpdate?(data: onAwarenessUpdatePayload): Promise<any>;
|
|
50
50
|
onRequest?(data: onRequestPayload): Promise<any>;
|
|
51
51
|
onDisconnect?(data: onDisconnectPayload): Promise<any>;
|
|
52
|
-
afterUnloadDocument?(data:
|
|
52
|
+
afterUnloadDocument?(data: afterUnloadDocumentPayload): Promise<any>;
|
|
53
53
|
onDestroy?(data: onDestroyPayload): Promise<any>;
|
|
54
54
|
}
|
|
55
55
|
export type HookName = 'onConfigure' | 'onListen' | 'onUpgrade' | 'onConnect' | 'connected' | 'onAuthenticate' | 'onLoadDocument' | 'afterLoadDocument' | 'beforeHandleMessage' | 'beforeBroadcastStateless' | 'onStateless' | 'onChange' | 'onStoreDocument' | 'afterStoreDocument' | 'onAwarenessUpdate' | 'onRequest' | 'onDisconnect' | 'afterUnloadDocument' | 'onDestroy';
|
|
@@ -61,7 +61,7 @@ export type HookPayloadByName = {
|
|
|
61
61
|
connected: connectedPayload;
|
|
62
62
|
onAuthenticate: onAuthenticatePayload;
|
|
63
63
|
onLoadDocument: onLoadDocumentPayload;
|
|
64
|
-
afterLoadDocument:
|
|
64
|
+
afterLoadDocument: afterLoadDocumentPayload;
|
|
65
65
|
beforeHandleMessage: beforeHandleMessagePayload;
|
|
66
66
|
beforeBroadcastStateless: beforeBroadcastStatelessPayload;
|
|
67
67
|
onStateless: onStatelessPayload;
|
|
@@ -140,6 +140,7 @@ export interface onAuthenticatePayload {
|
|
|
140
140
|
connection: ConnectionConfiguration;
|
|
141
141
|
}
|
|
142
142
|
export interface onConnectPayload {
|
|
143
|
+
context: any;
|
|
143
144
|
documentName: string;
|
|
144
145
|
instance: Hocuspocus;
|
|
145
146
|
request: IncomingMessage;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hocuspocus/provider",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.6.0",
|
|
4
4
|
"description": "hocuspocus provider",
|
|
5
5
|
"homepage": "https://hocuspocus.dev",
|
|
6
6
|
"keywords": [
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
"dist"
|
|
30
30
|
],
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"@hocuspocus/common": "^2.
|
|
32
|
+
"@hocuspocus/common": "^2.6.0",
|
|
33
33
|
"@lifeomic/attempt": "^3.0.2",
|
|
34
34
|
"lib0": "^0.2.47",
|
|
35
35
|
"ws": "^7.5.9"
|
|
@@ -367,6 +367,10 @@ export class HocuspocusProvider extends EventEmitter {
|
|
|
367
367
|
|
|
368
368
|
// not needed, but provides backward compatibility with e.g. lexicla/yjs
|
|
369
369
|
async connect() {
|
|
370
|
+
if (this.configuration.broadcast) {
|
|
371
|
+
this.subscribeToBroadcastChannel()
|
|
372
|
+
}
|
|
373
|
+
|
|
370
374
|
return this.configuration.websocketProvider.connect()
|
|
371
375
|
}
|
|
372
376
|
|
|
@@ -15,6 +15,8 @@ import {
|
|
|
15
15
|
onCloseParameters, onDisconnectParameters, onMessageParameters, onOpenParameters, onOutgoingMessageParameters, onStatusParameters,
|
|
16
16
|
} from './types.js'
|
|
17
17
|
|
|
18
|
+
export type HocusPocusWebSocket = WebSocket & { identifier: string };
|
|
19
|
+
|
|
18
20
|
export type HocuspocusProviderWebsocketConfiguration =
|
|
19
21
|
Required<Pick<CompleteHocuspocusProviderWebsocketConfiguration, 'url'>>
|
|
20
22
|
& Partial<CompleteHocuspocusProviderWebsocketConfiguration>
|
|
@@ -136,7 +138,9 @@ export class HocuspocusProviderWebsocket extends EventEmitter {
|
|
|
136
138
|
|
|
137
139
|
subscribedToBroadcastChannel = false
|
|
138
140
|
|
|
139
|
-
webSocket:
|
|
141
|
+
webSocket: HocusPocusWebSocket | null = null
|
|
142
|
+
|
|
143
|
+
webSocketHandlers: { [key: string]: any } = {}
|
|
140
144
|
|
|
141
145
|
shouldConnect = true
|
|
142
146
|
|
|
@@ -144,6 +148,8 @@ export class HocuspocusProviderWebsocket extends EventEmitter {
|
|
|
144
148
|
|
|
145
149
|
lastMessageReceived = 0
|
|
146
150
|
|
|
151
|
+
identifier = 0
|
|
152
|
+
|
|
147
153
|
mux = mutex.createMutex()
|
|
148
154
|
|
|
149
155
|
intervals: any = {
|
|
@@ -152,15 +158,17 @@ export class HocuspocusProviderWebsocket extends EventEmitter {
|
|
|
152
158
|
}
|
|
153
159
|
|
|
154
160
|
connectionAttempt: {
|
|
155
|
-
resolve: (value?: any) => void
|
|
156
|
-
reject: (reason?: any) => void
|
|
161
|
+
resolve: (value?: any) => void;
|
|
162
|
+
reject: (reason?: any) => void;
|
|
157
163
|
} | null = null
|
|
158
164
|
|
|
159
165
|
constructor(configuration: HocuspocusProviderWebsocketConfiguration) {
|
|
160
166
|
super()
|
|
161
167
|
this.setConfiguration(configuration)
|
|
162
168
|
|
|
163
|
-
this.configuration.WebSocketPolyfill = configuration.WebSocketPolyfill
|
|
169
|
+
this.configuration.WebSocketPolyfill = configuration.WebSocketPolyfill
|
|
170
|
+
? configuration.WebSocketPolyfill
|
|
171
|
+
: WebSocket
|
|
164
172
|
|
|
165
173
|
this.on('open', this.configuration.onOpen)
|
|
166
174
|
this.on('open', this.onOpen.bind(this))
|
|
@@ -178,8 +186,6 @@ export class HocuspocusProviderWebsocket extends EventEmitter {
|
|
|
178
186
|
this.on('close', this.onClose.bind(this))
|
|
179
187
|
this.on('message', this.onMessage.bind(this))
|
|
180
188
|
|
|
181
|
-
this.registerEventListeners()
|
|
182
|
-
|
|
183
189
|
this.intervals.connectionChecker = setInterval(
|
|
184
190
|
this.checkConnection.bind(this),
|
|
185
191
|
this.configuration.messageReconnectTimeout / 10,
|
|
@@ -224,10 +230,11 @@ export class HocuspocusProviderWebsocket extends EventEmitter {
|
|
|
224
230
|
|
|
225
231
|
detach(provider: HocuspocusProvider) {
|
|
226
232
|
// tell the server to remove the listener
|
|
227
|
-
|
|
228
233
|
}
|
|
229
234
|
|
|
230
|
-
public setConfiguration(
|
|
235
|
+
public setConfiguration(
|
|
236
|
+
configuration: Partial<HocuspocusProviderWebsocketConfiguration> = {},
|
|
237
|
+
): void {
|
|
231
238
|
this.configuration = { ...this.configuration, ...configuration }
|
|
232
239
|
}
|
|
233
240
|
|
|
@@ -289,24 +296,60 @@ export class HocuspocusProviderWebsocket extends EventEmitter {
|
|
|
289
296
|
return retryPromise
|
|
290
297
|
}
|
|
291
298
|
|
|
299
|
+
attachWebSocketListeners(ws: HocusPocusWebSocket, reject: Function) {
|
|
300
|
+
const { identifier } = ws
|
|
301
|
+
const onMessageHandler = (payload: any) => this.emit('message', payload)
|
|
302
|
+
const onCloseHandler = (payload: any) => this.emit('close', { event: payload })
|
|
303
|
+
const onOpenHandler = (payload: any) => this.emit('open', payload)
|
|
304
|
+
const onErrorHandler = (err: any) => {
|
|
305
|
+
reject(err)
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
this.webSocketHandlers[identifier] = {
|
|
309
|
+
message: onMessageHandler,
|
|
310
|
+
close: onCloseHandler,
|
|
311
|
+
open: onOpenHandler,
|
|
312
|
+
error: onErrorHandler,
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
const handlers = this.webSocketHandlers[ws.identifier]
|
|
316
|
+
|
|
317
|
+
Object.keys(handlers).forEach(name => {
|
|
318
|
+
ws.addEventListener(name, handlers[name])
|
|
319
|
+
})
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
cleanupWebSocket() {
|
|
323
|
+
if (!this.webSocket) {
|
|
324
|
+
return
|
|
325
|
+
}
|
|
326
|
+
const { identifier } = this.webSocket
|
|
327
|
+
const handlers = this.webSocketHandlers[identifier]
|
|
328
|
+
|
|
329
|
+
Object.keys(handlers).forEach(name => {
|
|
330
|
+
this.webSocket?.removeEventListener(name, handlers[name])
|
|
331
|
+
delete this.webSocketHandlers[identifier]
|
|
332
|
+
})
|
|
333
|
+
this.webSocket.close()
|
|
334
|
+
this.webSocket = null
|
|
335
|
+
}
|
|
336
|
+
|
|
292
337
|
createWebSocketConnection() {
|
|
293
338
|
return new Promise((resolve, reject) => {
|
|
294
339
|
if (this.webSocket) {
|
|
295
340
|
this.messageQueue = []
|
|
296
|
-
this.
|
|
297
|
-
this.webSocket = null
|
|
341
|
+
this.cleanupWebSocket()
|
|
298
342
|
}
|
|
299
343
|
this.lastMessageReceived = 0
|
|
344
|
+
this.identifier += 1
|
|
300
345
|
|
|
301
346
|
// Init the WebSocket connection
|
|
302
347
|
const ws = new this.configuration.WebSocketPolyfill(this.url)
|
|
303
348
|
ws.binaryType = 'arraybuffer'
|
|
304
|
-
ws.
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
reject(err)
|
|
309
|
-
}
|
|
349
|
+
ws.identifier = this.identifier
|
|
350
|
+
|
|
351
|
+
this.attachWebSocketListeners(ws, reject)
|
|
352
|
+
|
|
310
353
|
this.webSocket = ws
|
|
311
354
|
|
|
312
355
|
// Reset the status
|
|
@@ -363,7 +406,10 @@ export class HocuspocusProviderWebsocket extends EventEmitter {
|
|
|
363
406
|
}
|
|
364
407
|
|
|
365
408
|
// Don’t close the connection when a message was received recently
|
|
366
|
-
if (
|
|
409
|
+
if (
|
|
410
|
+
this.configuration.messageReconnectTimeout
|
|
411
|
+
>= time.getUnixTime() - this.lastMessageReceived
|
|
412
|
+
) {
|
|
367
413
|
return
|
|
368
414
|
}
|
|
369
415
|
|
|
@@ -384,15 +430,6 @@ export class HocuspocusProviderWebsocket extends EventEmitter {
|
|
|
384
430
|
this.webSocket?.close()
|
|
385
431
|
this.messageQueue = []
|
|
386
432
|
}
|
|
387
|
-
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
registerEventListeners() {
|
|
391
|
-
if (typeof window === 'undefined') {
|
|
392
|
-
return
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
window.addEventListener('online', this.boundConnect)
|
|
396
433
|
}
|
|
397
434
|
|
|
398
435
|
// Ensure that the URL always ends with /
|
|
@@ -435,7 +472,7 @@ export class HocuspocusProviderWebsocket extends EventEmitter {
|
|
|
435
472
|
|
|
436
473
|
onClose({ event }: onCloseParameters) {
|
|
437
474
|
this.closeTries = 0
|
|
438
|
-
this.
|
|
475
|
+
this.cleanupWebSocket()
|
|
439
476
|
|
|
440
477
|
if (this.status === WebSocketStatus.Connected) {
|
|
441
478
|
this.status = WebSocketStatus.Disconnected
|
|
@@ -445,9 +482,13 @@ export class HocuspocusProviderWebsocket extends EventEmitter {
|
|
|
445
482
|
|
|
446
483
|
if (event.code === Unauthorized.code) {
|
|
447
484
|
if (event.reason === Unauthorized.reason) {
|
|
448
|
-
console.warn(
|
|
485
|
+
console.warn(
|
|
486
|
+
'[HocuspocusProvider] An authentication token is required, but you didn’t send one. Try adding a `token` to your HocuspocusProvider configuration. Won’t try again.',
|
|
487
|
+
)
|
|
449
488
|
} else {
|
|
450
|
-
console.warn(
|
|
489
|
+
console.warn(
|
|
490
|
+
`[HocuspocusProvider] Connection closed with status Unauthorized: ${event.reason}`,
|
|
491
|
+
)
|
|
451
492
|
}
|
|
452
493
|
|
|
453
494
|
this.shouldConnect = false
|
|
@@ -455,13 +496,17 @@ export class HocuspocusProviderWebsocket extends EventEmitter {
|
|
|
455
496
|
|
|
456
497
|
if (event.code === Forbidden.code) {
|
|
457
498
|
if (!this.configuration.quiet) {
|
|
458
|
-
console.warn(
|
|
499
|
+
console.warn(
|
|
500
|
+
'[HocuspocusProvider] The provided authentication token isn’t allowed to connect to this server. Will try again.',
|
|
501
|
+
)
|
|
459
502
|
return // TODO REMOVE ME
|
|
460
503
|
}
|
|
461
504
|
}
|
|
462
505
|
|
|
463
506
|
if (event.code === MessageTooBig.code) {
|
|
464
|
-
console.warn(
|
|
507
|
+
console.warn(
|
|
508
|
+
`[HocuspocusProvider] Connection closed with status MessageTooBig: ${event.reason}`,
|
|
509
|
+
)
|
|
465
510
|
this.shouldConnect = false
|
|
466
511
|
}
|
|
467
512
|
|
|
@@ -507,11 +552,6 @@ export class HocuspocusProviderWebsocket extends EventEmitter {
|
|
|
507
552
|
|
|
508
553
|
this.removeAllListeners()
|
|
509
554
|
|
|
510
|
-
|
|
511
|
-
return
|
|
512
|
-
}
|
|
513
|
-
|
|
514
|
-
window.removeEventListener('online', this.boundConnect)
|
|
555
|
+
this.cleanupWebSocket()
|
|
515
556
|
}
|
|
516
|
-
|
|
517
557
|
}
|
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
} from './HocuspocusProvider.js'
|
|
6
6
|
|
|
7
7
|
import { TiptapCollabProviderWebsocket } from './TiptapCollabProviderWebsocket.js'
|
|
8
|
+
import type { THistoryVersion } from './types'
|
|
8
9
|
|
|
9
10
|
export type TiptapCollabProviderConfiguration =
|
|
10
11
|
Required<Pick<HocuspocusProviderConfiguration, 'name'>> &
|
|
@@ -21,12 +22,6 @@ export interface AdditionalTiptapCollabProviderConfiguration {
|
|
|
21
22
|
websocketProvider?: TiptapCollabProviderWebsocket
|
|
22
23
|
}
|
|
23
24
|
|
|
24
|
-
export type AuditHistoryVersion = {
|
|
25
|
-
name?: string;
|
|
26
|
-
version: number;
|
|
27
|
-
date: number;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
25
|
export class TiptapCollabProvider extends HocuspocusProvider {
|
|
31
26
|
tiptapCollabConfigurationPrefix = '__tiptapcollab__'
|
|
32
27
|
|
|
@@ -42,43 +37,53 @@ export class TiptapCollabProvider extends HocuspocusProvider {
|
|
|
42
37
|
super(configuration as HocuspocusProviderConfiguration)
|
|
43
38
|
}
|
|
44
39
|
|
|
40
|
+
/**
|
|
41
|
+
* note: this will only work if your server loaded @hocuspocus-pro/extension-history, or if you are on a Tiptap business plan.
|
|
42
|
+
*/
|
|
45
43
|
createVersion(name?: string) {
|
|
46
|
-
console.error('This doesnt work yet! If you want to join as a beta tester, send an email to humans@tiptap.dev')
|
|
47
44
|
return this.sendStateless(JSON.stringify({ action: 'version.create', name }))
|
|
48
45
|
}
|
|
49
46
|
|
|
47
|
+
/**
|
|
48
|
+
* note: this will only work if your server loaded @hocuspocus-pro/extension-history, or if you are on a Tiptap business plan.
|
|
49
|
+
*/
|
|
50
50
|
revertToVersion(targetVersion: number) {
|
|
51
|
-
|
|
52
|
-
return this.sendStateless(JSON.stringify({ action: 'version.revert', version: targetVersion }))
|
|
51
|
+
return this.sendStateless(JSON.stringify({ action: 'document.revert', version: targetVersion }))
|
|
53
52
|
}
|
|
54
53
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
54
|
+
/**
|
|
55
|
+
* note: this will only work if your server loaded @hocuspocus-pro/extension-history, or if you are on a Tiptap business plan.
|
|
56
|
+
*
|
|
57
|
+
* The server will reply with a stateless message (THistoryVersionPreviewEvent)
|
|
58
|
+
*/
|
|
59
|
+
previewVersion(targetVersion: number) {
|
|
60
|
+
return this.sendStateless(JSON.stringify({ action: 'version.preview', version: targetVersion }))
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* note: this will only work if your server loaded @hocuspocus-pro/extension-history, or if you are on a Tiptap business plan.
|
|
65
|
+
*/
|
|
66
|
+
getVersions(): THistoryVersion[] {
|
|
67
|
+
return this.configuration.document.getArray<THistoryVersion>(`${this.tiptapCollabConfigurationPrefix}versions`).toArray()
|
|
58
68
|
}
|
|
59
69
|
|
|
60
|
-
watchVersions(callback: Parameters<AbstractType<YArrayEvent<
|
|
61
|
-
|
|
62
|
-
return this.configuration.document.getArray<AuditHistoryVersion>('__tiptapcollab__versions').observe(callback)
|
|
70
|
+
watchVersions(callback: Parameters<AbstractType<YArrayEvent<THistoryVersion>>['observe']>[0]) {
|
|
71
|
+
return this.configuration.document.getArray<THistoryVersion>('__tiptapcollab__versions').observe(callback)
|
|
63
72
|
}
|
|
64
73
|
|
|
65
|
-
unwatchVersions(callback: Parameters<AbstractType<YArrayEvent<
|
|
66
|
-
|
|
67
|
-
return this.configuration.document.getArray<AuditHistoryVersion>('__tiptapcollab__versions').unobserve(callback)
|
|
74
|
+
unwatchVersions(callback: Parameters<AbstractType<YArrayEvent<THistoryVersion>>['unobserve']>[0]) {
|
|
75
|
+
return this.configuration.document.getArray<THistoryVersion>('__tiptapcollab__versions').unobserve(callback)
|
|
68
76
|
}
|
|
69
77
|
|
|
70
78
|
isAutoVersioning(): boolean {
|
|
71
|
-
console.error('This doesnt work yet! If you want to join as a beta tester, send an email to humans@tiptap.dev')
|
|
72
79
|
return !!this.configuration.document.getMap<number>(`${this.tiptapCollabConfigurationPrefix}config`).get('autoVersioning')
|
|
73
80
|
}
|
|
74
81
|
|
|
75
82
|
enableAutoVersioning() {
|
|
76
|
-
console.error('This doesnt work yet! If you want to join as a beta tester, send an email to humans@tiptap.dev')
|
|
77
83
|
return this.configuration.document.getMap<number>(`${this.tiptapCollabConfigurationPrefix}config`).set('autoVersioning', 1)
|
|
78
84
|
}
|
|
79
85
|
|
|
80
86
|
disableAutoVersioning() {
|
|
81
|
-
console.error('This doesnt work yet! If you want to join as a beta tester, send an email to humans@tiptap.dev')
|
|
82
87
|
return this.configuration.document.getMap<number>(`${this.tiptapCollabConfigurationPrefix}config`).set('autoVersioning', 0)
|
|
83
88
|
}
|
|
84
89
|
|
package/src/types.ts
CHANGED
|
@@ -103,3 +103,66 @@ export type onStatelessParameters = {
|
|
|
103
103
|
}
|
|
104
104
|
|
|
105
105
|
export type StatesArray = { clientId: number, [key: string | number]: any }[]
|
|
106
|
+
|
|
107
|
+
// hocuspocus-pro types
|
|
108
|
+
|
|
109
|
+
export type THistoryVersion = {
|
|
110
|
+
name?: string;
|
|
111
|
+
version: number;
|
|
112
|
+
date: number;
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
export type THistoryConfiguration = {
|
|
116
|
+
autoVersioning: boolean;
|
|
117
|
+
currentVersion: number;
|
|
118
|
+
stateCaptured: number; // indicates whether changes have been made since the last version
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
export type THistoryAction =
|
|
122
|
+
| THistoryDocumentRevertAction
|
|
123
|
+
| THistoryVersionCreateAction
|
|
124
|
+
| THistoryVersionPreviewAction;
|
|
125
|
+
|
|
126
|
+
export type THistoryDocumentRevertAction = {
|
|
127
|
+
action: 'document.revert';
|
|
128
|
+
/**
|
|
129
|
+
* if changes havent been persisted to a version yet, we'll create one with the specified name,
|
|
130
|
+
* expect when `false` is passed.
|
|
131
|
+
*/
|
|
132
|
+
currentVersionName?: string | false;
|
|
133
|
+
/**
|
|
134
|
+
* Name of the version that is created after the revert. Pass `false` to avoid generating a new version.
|
|
135
|
+
*/
|
|
136
|
+
newVersionName?: string | false;
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
export type THistoryVersionCreateAction = {
|
|
140
|
+
action: 'version.create';
|
|
141
|
+
name?: string;
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
export type THistoryVersionPreviewAction = {
|
|
145
|
+
action: 'version.preview';
|
|
146
|
+
version: number;
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
export type THistoryEvent =
|
|
150
|
+
| THistoryVersionPreviewEvent
|
|
151
|
+
| THistoryVersionCreatedEvent
|
|
152
|
+
| THistoryDocumentRevertedEvent;
|
|
153
|
+
|
|
154
|
+
export type THistoryVersionCreatedEvent = {
|
|
155
|
+
event: 'version.created';
|
|
156
|
+
version: number;
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
export type THistoryVersionPreviewEvent = {
|
|
160
|
+
event: 'version.preview';
|
|
161
|
+
version: number;
|
|
162
|
+
ydoc: string; // base64-encoded Uint8Array
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
export type THistoryDocumentRevertedEvent = {
|
|
166
|
+
event: 'document.reverted';
|
|
167
|
+
version: number;
|
|
168
|
+
};
|