@hocuspocus/provider 1.0.2 → 1.1.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.
@@ -1,6 +1,7 @@
1
- import RedisClient from 'ioredis';
1
+ import RedisClient, { ClusterNode, ClusterOptions, RedisOptions } from 'ioredis';
2
2
  import Redlock from 'redlock';
3
- import { Document, Extension, afterLoadDocumentPayload, afterStoreDocumentPayload, onDisconnectPayload, onStoreDocumentPayload, onAwarenessUpdatePayload, onChangePayload, Debugger, onConfigurePayload, onListenPayload } from '@hocuspocus/server';
3
+ import { Document, Extension, afterLoadDocumentPayload, afterStoreDocumentPayload, onDisconnectPayload, onStoreDocumentPayload, onAwarenessUpdatePayload, onChangePayload, Debugger, onConfigurePayload, onListenPayload, beforeBroadcastStatelessPayload } from '@hocuspocus/server';
4
+ export declare type RedisInstance = RedisClient.Cluster | RedisClient.Redis;
4
5
  export interface Configuration {
5
6
  /**
6
7
  * Redis port
@@ -10,12 +11,24 @@ export interface Configuration {
10
11
  * Redis host
11
12
  */
12
13
  host: string;
14
+ /**
15
+ * Redis Cluster
16
+ */
17
+ nodes?: ClusterNode[];
18
+ /**
19
+ * Duplicate from an existed Redis instance
20
+ */
21
+ redis?: RedisInstance;
22
+ /**
23
+ * Redis instance creator
24
+ */
25
+ createClient?: () => RedisInstance;
13
26
  /**
14
27
  * Options passed directly to Redis constructor
15
28
  *
16
29
  * https://github.com/luin/ioredis/blob/master/API.md#new-redisport-host-options
17
30
  */
18
- options?: RedisClient.RedisOptions;
31
+ options?: ClusterOptions | RedisOptions;
19
32
  /**
20
33
  * An unique instance name, required to filter messages in Redis.
21
34
  * If none is provided an unique id is generated.
@@ -38,8 +51,8 @@ export declare class Redis implements Extension {
38
51
  */
39
52
  priority: number;
40
53
  configuration: Configuration;
41
- pub: RedisClient.Redis;
42
- sub: RedisClient.Redis;
54
+ pub: RedisInstance;
55
+ sub: RedisInstance;
43
56
  documents: Map<string, Document>;
44
57
  redlock: Redlock;
45
58
  locks: Map<string, Redlock.Lock>;
@@ -91,6 +104,7 @@ export declare class Redis implements Extension {
91
104
  * noone connected anymore.
92
105
  */
93
106
  onDisconnect: ({ documentName, clientsCount }: onDisconnectPayload) => Promise<void>;
107
+ beforeBroadcastStateless(data: beforeBroadcastStatelessPayload): Promise<number>;
94
108
  /**
95
109
  * Kill the Redlock connection immediately.
96
110
  */
@@ -3,7 +3,7 @@ import { Awareness } from 'y-protocols/awareness';
3
3
  import * as mutex from 'lib0/mutex';
4
4
  import type { Event, CloseEvent, MessageEvent } from 'ws';
5
5
  import EventEmitter from './EventEmitter';
6
- import { ConstructableOutgoingMessage, onAuthenticationFailedParameters, onCloseParameters, onDisconnectParameters, onMessageParameters, onOpenParameters, onOutgoingMessageParameters, onStatusParameters, onSyncedParameters, WebSocketStatus } from './types';
6
+ import { ConstructableOutgoingMessage, onAuthenticationFailedParameters, onCloseParameters, onDisconnectParameters, onMessageParameters, onOpenParameters, onOutgoingMessageParameters, onStatelessParameters, onStatusParameters, onSyncedParameters, WebSocketStatus } from './types';
7
7
  import { onAwarenessChangeParameters, onAwarenessUpdateParameters } from '.';
8
8
  export declare type HocuspocusProviderConfiguration = Required<Pick<CompleteHocuspocusProviderConfiguration, 'url' | 'name'>> & Partial<CompleteHocuspocusProviderConfiguration>;
9
9
  export interface CompleteHocuspocusProviderConfiguration {
@@ -98,6 +98,7 @@ export interface CompleteHocuspocusProviderConfiguration {
98
98
  onDestroy: () => void;
99
99
  onAwarenessUpdate: (data: onAwarenessUpdateParameters) => void;
100
100
  onAwarenessChange: (data: onAwarenessChangeParameters) => void;
101
+ onStateless: (data: onStatelessParameters) => void;
101
102
  /**
102
103
  * Don’t output any warnings.
103
104
  */
@@ -136,6 +137,7 @@ export declare class HocuspocusProvider extends EventEmitter {
136
137
  boundBeforeUnload: () => void;
137
138
  beforeUnload(): void;
138
139
  registerEventListeners(): void;
140
+ sendStateless(payload: string): void;
139
141
  documentUpdateHandler(update: Uint8Array, origin: any): void;
140
142
  awarenessUpdateHandler({ added, updated, removed }: any, origin: any): void;
141
143
  permissionDeniedHandler(reason: string): void;
@@ -144,6 +146,7 @@ export declare class HocuspocusProvider extends EventEmitter {
144
146
  get url(): string;
145
147
  get synced(): boolean;
146
148
  set synced(state: boolean);
149
+ receiveStateless(payload: string): void;
147
150
  get isAuthenticationRequired(): boolean;
148
151
  disconnect(): void;
149
152
  onOpen(event: Event): Promise<void>;
@@ -0,0 +1,7 @@
1
+ import { MessageType, OutgoingMessageArguments } from '../types';
2
+ import { OutgoingMessage } from '../OutgoingMessage';
3
+ export declare class StatelessMessage extends OutgoingMessage {
4
+ type: MessageType;
5
+ description: string;
6
+ get(args: Partial<OutgoingMessageArguments>): import("lib0/encoding").Encoder;
7
+ }
@@ -14,7 +14,8 @@ export declare enum MessageType {
14
14
  Sync = 0,
15
15
  Awareness = 1,
16
16
  Auth = 2,
17
- QueryAwareness = 3
17
+ QueryAwareness = 3,
18
+ Stateless = 5
18
19
  }
19
20
  export declare enum WebSocketStatus {
20
21
  Connecting = "connecting",
@@ -34,6 +35,7 @@ export interface OutgoingMessageArguments {
34
35
  [key: string]: any;
35
36
  }>;
36
37
  update: any;
38
+ payload: string;
37
39
  encoder: Encoder;
38
40
  }
39
41
  export interface Constructable<T> {
@@ -71,6 +73,9 @@ export declare type onAwarenessUpdateParameters = {
71
73
  export declare type onAwarenessChangeParameters = {
72
74
  states: StatesArray;
73
75
  };
76
+ export declare type onStatelessParameters = {
77
+ payload: string;
78
+ };
74
79
  export declare type StatesArray = {
75
80
  clientId: number;
76
81
  [key: string | number]: any;
@@ -5,6 +5,7 @@ import { IncomingMessage as HTTPIncomingMessage } from 'http';
5
5
  import { CloseEvent } from '@hocuspocus/common';
6
6
  import Document from './Document';
7
7
  import { Debugger } from './Debugger';
8
+ import { onStatelessPayload } from './types';
8
9
  export declare class Connection {
9
10
  webSocket: WebSocket;
10
11
  context: any;
@@ -26,6 +27,10 @@ export declare class Connection {
26
27
  * Set a callback that will be triggered when the connection is closed
27
28
  */
28
29
  onClose(callback: (document: Document) => void): Connection;
30
+ /**
31
+ * Set a callback that will be triggered when an stateless message is received
32
+ */
33
+ onStatelessCallback(callback: (payload: onStatelessPayload) => Promise<void>): Connection;
29
34
  /**
30
35
  * Set a callback that will be triggered before an message is handled
31
36
  */
@@ -34,6 +39,10 @@ export declare class Connection {
34
39
  * Send the given message
35
40
  */
36
41
  send(message: any): void;
42
+ /**
43
+ * Send a stateless message with payload
44
+ */
45
+ sendStateless(payload: string): void;
37
46
  /**
38
47
  * Graceful wrapper around the WebSocket close method.
39
48
  */
@@ -8,6 +8,7 @@ export declare class Document extends Doc {
8
8
  awareness: Awareness;
9
9
  callbacks: {
10
10
  onUpdate: (document: Document, connection: Connection, update: Uint8Array) => void;
11
+ beforeBroadcastStateless: (document: Document, stateless: string) => void;
11
12
  };
12
13
  connections: Map<WebSocket, {
13
14
  clients: Set<any>;
@@ -33,6 +34,10 @@ export declare class Document extends Doc {
33
34
  * Set a callback that will be triggered when the document is updated
34
35
  */
35
36
  onUpdate(callback: (document: Document, connection: Connection, update: Uint8Array) => void): Document;
37
+ /**
38
+ * Set a callback that will be triggered before a stateless message is broadcasted
39
+ */
40
+ beforeBroadcastStateless(callback: (document: Document, stateless: string) => void): Document;
36
41
  /**
37
42
  * Register a connection and a set of clients on this document keyed by the
38
43
  * underlying websocket connection
@@ -75,5 +80,9 @@ export declare class Document extends Doc {
75
80
  * Handle an updated document and sync changes to clients
76
81
  */
77
82
  private handleUpdate;
83
+ /**
84
+ * Broadcast stateless message to all connections
85
+ */
86
+ broadcastStateless(payload: string): void;
78
87
  }
79
88
  export default Document;
@@ -13,6 +13,7 @@ export declare class IncomingMessage {
13
13
  constructor(input: any);
14
14
  readVarUint8Array(): Uint8Array;
15
15
  readVarUint(): number;
16
+ readVarString(): string;
16
17
  toUint8Array(): Uint8Array;
17
18
  writeVarUint(type: MessageType): void;
18
19
  get length(): number;
@@ -14,5 +14,7 @@ export declare class OutgoingMessage {
14
14
  writePermissionDenied(reason: string): OutgoingMessage;
15
15
  writeFirstSyncStepFor(document: Document): OutgoingMessage;
16
16
  writeUpdate(update: Uint8Array): OutgoingMessage;
17
+ writeStateless(payload: string): OutgoingMessage;
18
+ writeBroadcastStateless(payload: string): OutgoingMessage;
17
19
  toUint8Array(): Uint8Array;
18
20
  }
@@ -4,13 +4,16 @@ import { URLSearchParams } from 'url';
4
4
  import { Awareness } from 'y-protocols/awareness';
5
5
  import Document from './Document';
6
6
  import { Hocuspocus } from './Hocuspocus';
7
+ import Connection from './Connection';
7
8
  export declare enum MessageType {
8
9
  Unknown = -1,
9
10
  Sync = 0,
10
11
  Awareness = 1,
11
12
  Auth = 2,
12
13
  QueryAwareness = 3,
13
- SyncReply = 4
14
+ SyncReply = 4,
15
+ Stateless = 5,
16
+ BroadcastStateless = 6
14
17
  }
15
18
  export interface AwarenessUpdate {
16
19
  added: Array<any>;
@@ -33,6 +36,8 @@ export interface Extension {
33
36
  onLoadDocument?(data: onLoadDocumentPayload): Promise<any>;
34
37
  afterLoadDocument?(data: onLoadDocumentPayload): Promise<any>;
35
38
  beforeHandleMessage?(data: beforeHandleMessagePayload): Promise<any>;
39
+ beforeBroadcastStateless?(data: beforeBroadcastStatelessPayload): Promise<any>;
40
+ onStateless?(payload: onStatelessPayload): Promise<any>;
36
41
  onChange?(data: onChangePayload): Promise<any>;
37
42
  onStoreDocument?(data: onStoreDocumentPayload): Promise<any>;
38
43
  afterStoreDocument?(data: afterStoreDocumentPayload): Promise<any>;
@@ -41,8 +46,8 @@ export interface Extension {
41
46
  onDisconnect?(data: onDisconnectPayload): Promise<any>;
42
47
  onDestroy?(data: onDestroyPayload): Promise<any>;
43
48
  }
44
- export declare type HookName = 'onConfigure' | 'onListen' | 'onUpgrade' | 'onConnect' | 'connected' | 'onAuthenticate' | 'onLoadDocument' | 'afterLoadDocument' | 'beforeHandleMessage' | 'onChange' | 'onStoreDocument' | 'afterStoreDocument' | 'onAwarenessUpdate' | 'onRequest' | 'onDisconnect' | 'onDestroy';
45
- export declare type HookPayload = onConfigurePayload | onListenPayload | onUpgradePayload | onConnectPayload | connectedPayload | onAuthenticatePayload | onLoadDocumentPayload | onChangePayload | onStoreDocumentPayload | afterStoreDocumentPayload | onAwarenessUpdatePayload | onRequestPayload | onDisconnectPayload | onDestroyPayload;
49
+ export declare type HookName = 'onConfigure' | 'onListen' | 'onUpgrade' | 'onConnect' | 'connected' | 'onAuthenticate' | 'onLoadDocument' | 'afterLoadDocument' | 'beforeHandleMessage' | 'beforeBroadcastStateless' | 'onStateless' | 'onChange' | 'onStoreDocument' | 'afterStoreDocument' | 'onAwarenessUpdate' | 'onRequest' | 'onDisconnect' | 'onDestroy';
50
+ export declare type HookPayload = onConfigurePayload | onListenPayload | onUpgradePayload | onConnectPayload | connectedPayload | onAuthenticatePayload | onLoadDocumentPayload | onStatelessPayload | beforeHandleMessagePayload | beforeBroadcastStatelessPayload | onChangePayload | onStoreDocumentPayload | afterStoreDocumentPayload | onAwarenessUpdatePayload | onRequestPayload | onDisconnectPayload | onDestroyPayload;
46
51
  export interface Configuration extends Extension {
47
52
  /**
48
53
  * A name for the instance, used for logging.
@@ -94,6 +99,12 @@ export interface getDocumentNamePayload {
94
99
  request: IncomingMessage;
95
100
  requestParameters: URLSearchParams;
96
101
  }
102
+ export interface onStatelessPayload {
103
+ connection: Connection;
104
+ documentName: string;
105
+ document: Document;
106
+ payload: string;
107
+ }
97
108
  export interface onAuthenticatePayload {
98
109
  documentName: string;
99
110
  instance: Hocuspocus;
@@ -163,6 +174,11 @@ export interface beforeHandleMessagePayload {
163
174
  update: Uint8Array;
164
175
  socketId: string;
165
176
  }
177
+ export interface beforeBroadcastStatelessPayload {
178
+ document: Document;
179
+ documentName: string;
180
+ payload: string;
181
+ }
166
182
  export interface onStoreDocumentPayload {
167
183
  clientsCount: number;
168
184
  context: any;
@@ -1,5 +1,5 @@
1
1
  import { Doc } from 'yjs';
2
- import { Schema } from 'prosemirror-model';
2
+ import { Schema } from '@tiptap/pm/model';
3
3
  import { Transformer } from './types';
4
4
  declare class Prosemirror implements Transformer {
5
5
  defaultSchema: Schema;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hocuspocus/provider",
3
- "version": "1.0.2",
3
+ "version": "1.1.0",
4
4
  "description": "hocuspocus provider",
5
5
  "homepage": "https://hocuspocus.dev",
6
6
  "keywords": [
@@ -28,7 +28,7 @@
28
28
  "dist"
29
29
  ],
30
30
  "dependencies": {
31
- "@hocuspocus/common": "^1.0.2",
31
+ "@hocuspocus/common": "^1.1.0",
32
32
  "@lifeomic/attempt": "^3.0.2",
33
33
  "lib0": "^0.2.46"
34
34
  },
@@ -20,9 +20,10 @@ import { AuthenticationMessage } from './OutgoingMessages/AuthenticationMessage'
20
20
  import { AwarenessMessage } from './OutgoingMessages/AwarenessMessage'
21
21
  import { UpdateMessage } from './OutgoingMessages/UpdateMessage'
22
22
  import {
23
- ConstructableOutgoingMessage, onAuthenticationFailedParameters, onCloseParameters, onDisconnectParameters, onMessageParameters, onOpenParameters, onOutgoingMessageParameters, onStatusParameters, onSyncedParameters, WebSocketStatus,
23
+ ConstructableOutgoingMessage, onAuthenticationFailedParameters, onCloseParameters, onDisconnectParameters, onMessageParameters, onOpenParameters, onOutgoingMessageParameters, onStatelessParameters, onStatusParameters, onSyncedParameters, WebSocketStatus,
24
24
  } from './types'
25
25
  import { onAwarenessChangeParameters, onAwarenessUpdateParameters } from '.'
26
+ import { StatelessMessage } from './OutgoingMessages/StatelessMessage'
26
27
 
27
28
  export type HocuspocusProviderConfiguration =
28
29
  Required<Pick<CompleteHocuspocusProviderConfiguration, 'url' | 'name'>>
@@ -118,6 +119,8 @@ export interface CompleteHocuspocusProviderConfiguration {
118
119
  onDestroy: () => void,
119
120
  onAwarenessUpdate: (data: onAwarenessUpdateParameters) => void,
120
121
  onAwarenessChange: (data: onAwarenessChangeParameters) => void,
122
+ onStateless: (data: onStatelessParameters) => void
123
+
121
124
  /**
122
125
  * Don’t output any warnings.
123
126
  */
@@ -169,6 +172,7 @@ export class HocuspocusProvider extends EventEmitter {
169
172
  onDestroy: () => null,
170
173
  onAwarenessUpdate: () => null,
171
174
  onAwarenessChange: () => null,
175
+ onStateless: () => null,
172
176
  quiet: false,
173
177
  }
174
178
 
@@ -221,6 +225,7 @@ export class HocuspocusProvider extends EventEmitter {
221
225
  this.on('destroy', this.configuration.onDestroy)
222
226
  this.on('awarenessUpdate', this.configuration.onAwarenessUpdate)
223
227
  this.on('awarenessChange', this.configuration.onAwarenessChange)
228
+ this.on('stateless', this.configuration.onStateless)
224
229
 
225
230
  this.awareness.on('update', () => {
226
231
  this.emit('awarenessUpdate', { states: awarenessStatesToArray(this.awareness.getStates()) })
@@ -424,6 +429,10 @@ export class HocuspocusProvider extends EventEmitter {
424
429
  window.addEventListener('beforeunload', this.boundBeforeUnload)
425
430
  }
426
431
 
432
+ sendStateless(payload: string) {
433
+ this.send(StatelessMessage, { payload })
434
+ }
435
+
427
436
  documentUpdateHandler(update: Uint8Array, origin: any) {
428
437
  if (origin === this) {
429
438
  return
@@ -484,6 +493,10 @@ export class HocuspocusProvider extends EventEmitter {
484
493
  this.emit('sync', { state })
485
494
  }
486
495
 
496
+ receiveStateless(payload: string) {
497
+ this.emit('stateless', { payload })
498
+ }
499
+
487
500
  get isAuthenticationRequired(): boolean {
488
501
  return !!this.configuration.token && !this.isAuthenticated
489
502
  }
@@ -1,6 +1,7 @@
1
1
  import * as awarenessProtocol from 'y-protocols/awareness'
2
2
  import { readSyncMessage, messageYjsSyncStep2, messageYjsUpdate } from 'y-protocols/sync'
3
3
  import { readAuthMessage } from '@hocuspocus/common'
4
+ import { readVarString } from 'lib0/decoding'
4
5
  import { MessageType } from './types'
5
6
  import { HocuspocusProvider } from './HocuspocusProvider'
6
7
  import { IncomingMessage } from './IncomingMessage'
@@ -43,6 +44,10 @@ export class MessageReceiver {
43
44
  this.applyQueryAwarenessMessage(provider)
44
45
  break
45
46
 
47
+ case MessageType.Stateless:
48
+ provider.receiveStateless(readVarString(message.decoder))
49
+ break
50
+
46
51
  default:
47
52
  throw new Error(`Can’t apply message of unknown type: ${type}`)
48
53
  }
@@ -0,0 +1,16 @@
1
+ import { writeVarString, writeVarUint } from 'lib0/encoding'
2
+ import { MessageType, OutgoingMessageArguments } from '../types'
3
+ import { OutgoingMessage } from '../OutgoingMessage'
4
+
5
+ export class StatelessMessage extends OutgoingMessage {
6
+ type = MessageType.Stateless
7
+
8
+ description = 'A stateless message'
9
+
10
+ get(args: Partial<OutgoingMessageArguments>) {
11
+ writeVarUint(this.encoder, this.type)
12
+ writeVarString(this.encoder, args.payload ?? '')
13
+
14
+ return this.encoder
15
+ }
16
+ }
package/src/types.ts CHANGED
@@ -16,6 +16,7 @@ export enum MessageType {
16
16
  Awareness = 1,
17
17
  Auth = 2,
18
18
  QueryAwareness = 3,
19
+ Stateless = 5,
19
20
  }
20
21
 
21
22
  export enum WebSocketStatus {
@@ -36,6 +37,7 @@ export interface OutgoingMessageArguments {
36
37
  clients: number[],
37
38
  states: Map<number, { [key: string]: any; }>,
38
39
  update: any,
40
+ payload: string,
39
41
  encoder: Encoder,
40
42
  }
41
43
 
@@ -92,4 +94,8 @@ export type onAwarenessChangeParameters = {
92
94
  states: StatesArray
93
95
  }
94
96
 
97
+ export type onStatelessParameters = {
98
+ payload: string
99
+ }
100
+
95
101
  export type StatesArray = { clientId: number, [key: string | number]: any }[]