@hocuspocus/provider 2.5.0 → 2.6.1

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.
@@ -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: WebSocket | null;
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
- getVersions(): AuditHistoryVersion[];
23
- watchVersions(callback: Parameters<AbstractType<YArrayEvent<AuditHistoryVersion>>['observe']>[0]): void;
24
- unwatchVersions(callback: Parameters<AbstractType<YArrayEvent<AuditHistoryVersion>>['unobserve']>[0]): void;
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: onLoadDocumentPayload): Promise<any>;
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: onLoadDocumentPayload): Promise<any>;
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: onLoadDocumentPayload;
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.5.0",
3
+ "version": "2.6.1",
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.5.0",
32
+ "@hocuspocus/common": "^2.6.1",
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
 
@@ -542,11 +546,16 @@ export class HocuspocusProvider extends EventEmitter {
542
546
  }
543
547
 
544
548
  this.mux(() => {
545
- this.broadcast(SyncStepOneMessage, { document: this.document })
546
- this.broadcast(SyncStepTwoMessage, { document: this.document })
547
- this.broadcast(QueryAwarenessMessage, { document: this.document })
549
+ this.broadcast(SyncStepOneMessage, { document: this.document, documentName: this.configuration.name })
550
+ this.broadcast(SyncStepTwoMessage, { document: this.document, documentName: this.configuration.name })
551
+ this.broadcast(QueryAwarenessMessage, { document: this.document, documentName: this.configuration.name })
548
552
  if (this.awareness) {
549
- this.broadcast(AwarenessMessage, { awareness: this.awareness, clients: [this.document.clientID], document: this.document })
553
+ this.broadcast(AwarenessMessage, {
554
+ awareness: this.awareness,
555
+ clients: [this.document.clientID],
556
+ document: this.document,
557
+ documentName: this.configuration.name,
558
+ })
550
559
  }
551
560
  })
552
561
  }
@@ -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: WebSocket | null = null
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 ? configuration.WebSocketPolyfill : WebSocket
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(configuration: Partial<HocuspocusProviderWebsocketConfiguration> = {}): void {
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.webSocket.close()
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.onmessage = (payload: any) => this.emit('message', payload)
305
- ws.onclose = (payload: any) => this.emit('close', { event: payload })
306
- ws.onopen = (payload: any) => this.emit('open', payload)
307
- ws.onerror = (err: any) => {
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 (this.configuration.messageReconnectTimeout >= time.getUnixTime() - this.lastMessageReceived) {
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.webSocket = null
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('[HocuspocusProvider] An authentication token is required, but you didn’t send one. Try adding a `token` to your HocuspocusProvider configuration. Won’t try again.')
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(`[HocuspocusProvider] Connection closed with status Unauthorized: ${event.reason}`)
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('[HocuspocusProvider] The provided authentication token isn’t allowed to connect to this server. Will try again.')
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(`[HocuspocusProvider] Connection closed with status MessageTooBig: ${event.reason}`)
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
- if (typeof window === 'undefined') {
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
- console.error('This doesnt work yet! If you want to join as a beta tester, send an email to humans@tiptap.dev')
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
- getVersions(): AuditHistoryVersion[] {
56
- console.error('This doesnt work yet! If you want to join as a beta tester, send an email to humans@tiptap.dev')
57
- return this.configuration.document.getArray<AuditHistoryVersion>(`${this.tiptapCollabConfigurationPrefix}versions`).toArray()
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<AuditHistoryVersion>>['observe']>[0]) {
61
- console.error('This doesnt work yet! If you want to join as a beta tester, send an email to humans@tiptap.dev')
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<AuditHistoryVersion>>['unobserve']>[0]) {
66
- console.error('This doesnt work yet! If you want to join as a beta tester, send an email to humans@tiptap.dev')
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
+ };