@hocuspocus/provider 1.0.0-alpha.36 → 1.0.0-alpha.39

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.
@@ -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: ({ documentName }: {
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({ document, documentName }: onLoadDocumentPayload): Promise<any>;
25
+ onLoadDocument(data: onLoadDocumentPayload): Promise<any>;
28
26
  /**
29
27
  * Store new updates in the database.
30
28
  */
@@ -1,6 +1,6 @@
1
1
  import RedisClient from 'ioredis';
2
2
  import Redlock from 'redlock';
3
- import { Document, Extension, afterLoadDocumentPayload, afterStoreDocumentPayload, onDisconnectPayload, onStoreDocumentPayload, onAwarenessUpdatePayload, Debugger, onConfigurePayload, onListenPayload } from '@hocuspocus/server';
3
+ import { Document, Extension, afterLoadDocumentPayload, afterStoreDocumentPayload, onDisconnectPayload, onStoreDocumentPayload, onAwarenessUpdatePayload, onChangePayload, Debugger, onConfigurePayload, onListenPayload } from '@hocuspocus/server';
4
4
  export interface Configuration {
5
5
  /**
6
6
  * Redis port
@@ -75,13 +75,17 @@ export declare class Redis implements Extension {
75
75
  /**
76
76
  * Handle awareness update messages received directly by this Hocuspocus instance.
77
77
  */
78
- onAwarenessUpdate({ documentName, awareness }: onAwarenessUpdatePayload): Promise<number>;
78
+ onAwarenessUpdate({ documentName, awareness, added, updated, removed, }: onAwarenessUpdatePayload): Promise<number>;
79
79
  /**
80
80
  * Handle incoming messages published on all subscribed document channels.
81
81
  * Note that this will also include messages from ourselves as it is not possible
82
82
  * in Redis to filter these.
83
83
  */
84
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>;
85
89
  /**
86
90
  * Make sure to *not* listen for further changes, when there’s
87
91
  * noone connected anymore.
@@ -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;
@@ -19,7 +19,7 @@ export declare class Document extends Doc {
19
19
  /**
20
20
  * Constructor.
21
21
  */
22
- constructor(name: string, logger: Debugger);
22
+ constructor(name: string, logger: Debugger, yDocOptions: {});
23
23
  /**
24
24
  * Check if the Document is empty
25
25
  */
@@ -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
@@ -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;
@@ -9,7 +9,8 @@ export declare enum MessageType {
9
9
  Sync = 0,
10
10
  Awareness = 1,
11
11
  Auth = 2,
12
- QueryAwareness = 3
12
+ QueryAwareness = 3,
13
+ SyncReply = 4
13
14
  }
14
15
  export interface AwarenessUpdate {
15
16
  added: Array<any>;
@@ -48,7 +49,7 @@ export declare type HookName = 'onConfigure' | 'onListen' | 'onUpgrade' | 'onCon
48
49
  * @deprecated onCreateDocument is deprecated, use onLoadDocument instead
49
50
  */
50
51
  'onCreateDocument' | 'onLoadDocument' | 'afterLoadDocument' | 'onChange' | 'onStoreDocument' | 'afterStoreDocument' | 'onAwarenessUpdate' | 'onRequest' | 'onDisconnect' | 'onDestroy';
51
- export declare type HookPayload = onConfigurePayload | onListenPayload | onUpgradePayload | onConnectPayload | connectedPayload | onAuthenticatePayload | onLoadDocumentPayload | onLoadDocumentPayload | onLoadDocumentPayload | onChangePayload | onStoreDocumentPayload | afterStoreDocumentPayload | onAwarenessUpdatePayload | onRequestPayload | onDisconnectPayload | onDestroyPayload;
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
  }
@@ -0,0 +1 @@
1
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hocuspocus/provider",
3
- "version": "1.0.0-alpha.36",
3
+ "version": "1.0.0-alpha.39",
4
4
  "description": "hocuspocus provider",
5
5
  "homepage": "https://hocuspocus.dev",
6
6
  "keywords": [
@@ -34,5 +34,5 @@
34
34
  "y-protocols": "^1.0.5",
35
35
  "yjs": "^13.5.29"
36
36
  },
37
- "gitHead": "b0a04ffe8d56cfa6b269a2c8ad7f64c92b108de0"
37
+ "gitHead": "02845d914742af88c8ef034adf072fcaadb48208"
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
 
@@ -300,8 +305,8 @@ export class HocuspocusProvider extends EventEmitter {
300
305
  ws.onmessage = this.onMessage.bind(this)
301
306
  ws.onclose = this.onClose.bind(this)
302
307
  ws.onopen = this.onOpen.bind(this)
303
- ws.onerror = () => {
304
- reject()
308
+ ws.onerror = (err: any) => {
309
+ reject(err)
305
310
  }
306
311
  this.webSocket = ws
307
312
 
@@ -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.connect.bind(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.connect.bind(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.broadcastChannelSubscriber.bind(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.broadcastChannelSubscriber.bind(this))
664
+ bc.unsubscribe(this.broadcastChannel, this.boundBroadcastChannelSubscriber)
648
665
  this.subscribedToBroadcastChannel = false
649
666
  }
650
667
  }
@@ -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) {