@hocuspocus/provider 3.2.5 → 3.3.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,12 @@
1
1
  import * as encoding from "lib0/encoding";
2
2
  import * as decoding from "lib0/decoding";
3
+ export declare enum AuthMessageType {
4
+ Token = 0,
5
+ PermissionDenied = 1,
6
+ Authenticated = 2
7
+ }
3
8
  export declare const writeAuthentication: (encoder: encoding.Encoder, auth: string) => void;
4
9
  export declare const writePermissionDenied: (encoder: encoding.Encoder, reason: string) => void;
5
10
  export declare const writeAuthenticated: (encoder: encoding.Encoder, scope: "readonly" | "read-write") => void;
6
- export declare const readAuthMessage: (decoder: decoding.Decoder, permissionDeniedHandler: (reason: string) => void, authenticatedHandler: (scope: string) => void) => void;
11
+ export declare const writeTokenSyncRequest: (encoder: encoding.Encoder) => void;
12
+ export declare const readAuthMessage: (decoder: decoding.Decoder, sendToken: () => void, permissionDeniedHandler: (reason: string) => void, authenticatedHandler: (scope: string) => void) => void;
@@ -1,4 +1,4 @@
1
- import type { Extension, Hocuspocus, afterLoadDocumentPayload, afterStoreDocumentPayload, beforeBroadcastStatelessPayload, onAwarenessUpdatePayload, onChangePayload, onConfigurePayload, onDisconnectPayload, onStoreDocumentPayload } from "@hocuspocus/server";
1
+ import type { Extension, Hocuspocus, afterLoadDocumentPayload, afterStoreDocumentPayload, afterUnloadDocumentPayload, beforeBroadcastStatelessPayload, beforeUnloadDocumentPayload, onAwarenessUpdatePayload, onChangePayload, onConfigurePayload, onStoreDocumentPayload } from "@hocuspocus/server";
2
2
  import { type ExecutionResult, type Lock, Redlock } from "@sesamecare-oss/redlock";
3
3
  import type { Cluster, ClusterNode, ClusterOptions, RedisOptions } from "ioredis";
4
4
  import RedisClient from "ioredis";
@@ -67,11 +67,6 @@ export declare class Redis implements Extension {
67
67
  release?: Promise<ExecutionResult>;
68
68
  }>;
69
69
  messagePrefix: Buffer;
70
- /**
71
- * When we have a high frequency of updates to a document we don't need tons of setTimeouts
72
- * piling up, so we'll track them to keep it to the most recent per document.
73
- */
74
- private pendingDisconnects;
75
70
  private pendingAfterStoreDocumentResolves;
76
71
  constructor(configuration: Partial<Configuration>);
77
72
  onConfigure({ instance }: onConfigurePayload): Promise<void>;
@@ -117,10 +112,10 @@ export declare class Redis implements Extension {
117
112
  */
118
113
  onChange(data: onChangePayload): Promise<any>;
119
114
  /**
120
- * Make sure to *not* listen for further changes, when there’s
121
- * no one connected anymore.
115
+ * Delay unloading to allow syncs to finish
122
116
  */
123
- onDisconnect: ({ documentName }: onDisconnectPayload) => Promise<void>;
117
+ beforeUnloadDocument(data: beforeUnloadDocumentPayload): Promise<void>;
118
+ afterUnloadDocument(data: afterUnloadDocumentPayload): Promise<void>;
124
119
  beforeBroadcastStateless(data: beforeBroadcastStatelessPayload): Promise<number>;
125
120
  /**
126
121
  * Kill the Redlock connection immediately.
@@ -87,6 +87,7 @@ export declare class HocuspocusProvider extends EventEmitter {
87
87
  pageHide(): void;
88
88
  registerEventListeners(): void;
89
89
  sendStateless(payload: string): void;
90
+ sendToken(): Promise<void>;
90
91
  documentUpdateHandler(update: Uint8Array, origin: any): void;
91
92
  awarenessUpdateHandler({ added, updated, removed }: any, origin: any): void;
92
93
  /**
@@ -2,7 +2,7 @@ import type { IncomingMessage as HTTPIncomingMessage } from "node:http";
2
2
  import { type CloseEvent } from "@hocuspocus/common";
3
3
  import type WebSocket from "ws";
4
4
  import type Document from "./Document.ts";
5
- import type { beforeSyncPayload, onStatelessPayload } from "./types.ts";
5
+ import type { beforeSyncPayload, onTokenSyncPayload, onStatelessPayload } from "./types.ts";
6
6
  export declare class Connection {
7
7
  webSocket: WebSocket;
8
8
  context: any;
@@ -13,6 +13,7 @@ export declare class Connection {
13
13
  beforeHandleMessage: (connection: Connection, update: Uint8Array) => Promise<void>;
14
14
  beforeSync: (connection: Connection, payload: Pick<beforeSyncPayload, "type" | "payload">) => Promise<void>;
15
15
  statelessCallback: (payload: onStatelessPayload) => Promise<void>;
16
+ onTokenSyncCallback: (payload: Partial<onTokenSyncPayload>) => Promise<void>;
16
17
  };
17
18
  socketId: string;
18
19
  readOnly: boolean;
@@ -36,6 +37,10 @@ export declare class Connection {
36
37
  * Set a callback that will be triggered before a sync message is handled
37
38
  */
38
39
  beforeSync(callback: (connection: Connection, payload: Pick<beforeSyncPayload, "type" | "payload">) => Promise<any>): Connection;
40
+ /**
41
+ * Set a callback that will be triggered when on token sync message is received
42
+ */
43
+ onTokenSyncCallback(callback: (payload: onTokenSyncPayload) => Promise<void>): Connection;
39
44
  /**
40
45
  * Send the given message
41
46
  */
@@ -44,6 +49,10 @@ export declare class Connection {
44
49
  * Send a stateless message with payload
45
50
  */
46
51
  sendStateless(payload: string): void;
52
+ /**
53
+ * Request current token from the client
54
+ */
55
+ requestToken(): void;
47
56
  /**
48
57
  * Graceful wrapper around the WebSocket close method.
49
58
  */
@@ -19,6 +19,7 @@ export declare const defaultConfiguration: {
19
19
  export declare class Hocuspocus {
20
20
  configuration: Configuration;
21
21
  loadingDocuments: Map<string, Promise<Document>>;
22
+ unloadingDocuments: Map<string, Promise<void>>;
22
23
  documents: Map<string, Document>;
23
24
  server?: Server;
24
25
  debouncer: {
@@ -10,6 +10,7 @@ export declare class OutgoingMessage {
10
10
  createSyncReplyMessage(): OutgoingMessage;
11
11
  createAwarenessUpdateMessage(awareness: Awareness, changedClients?: Array<any>): OutgoingMessage;
12
12
  writeQueryAwareness(): OutgoingMessage;
13
+ writeTokenSyncRequest(): OutgoingMessage;
13
14
  writeAuthenticated(readonly: boolean): OutgoingMessage;
14
15
  writePermissionDenied(reason: string): OutgoingMessage;
15
16
  writeFirstSyncStepFor(document: Document): OutgoingMessage;
@@ -34,6 +34,7 @@ export interface Extension {
34
34
  onConnect?(data: onConnectPayload): Promise<any>;
35
35
  connected?(data: connectedPayload): Promise<any>;
36
36
  onAuthenticate?(data: onAuthenticatePayload): Promise<any>;
37
+ onTokenSync?(data: onTokenSyncPayload): Promise<any>;
37
38
  onCreateDocument?(data: onCreateDocumentPayload): Promise<any>;
38
39
  onLoadDocument?(data: onLoadDocumentPayload): Promise<any>;
39
40
  afterLoadDocument?(data: afterLoadDocumentPayload): Promise<any>;
@@ -51,7 +52,7 @@ export interface Extension {
51
52
  afterUnloadDocument?(data: afterUnloadDocumentPayload): Promise<any>;
52
53
  onDestroy?(data: onDestroyPayload): Promise<any>;
53
54
  }
54
- export type HookName = "onConfigure" | "onListen" | "onUpgrade" | "onConnect" | "connected" | "onAuthenticate" | "onCreateDocument" | "onLoadDocument" | "afterLoadDocument" | "beforeHandleMessage" | "beforeBroadcastStateless" | "beforeSync" | "onStateless" | "onChange" | "onStoreDocument" | "afterStoreDocument" | "onAwarenessUpdate" | "onRequest" | "onDisconnect" | "beforeUnloadDocument" | "afterUnloadDocument" | "onDestroy";
55
+ export type HookName = "onConfigure" | "onListen" | "onUpgrade" | "onConnect" | "connected" | "onAuthenticate" | "onTokenSync" | "onCreateDocument" | "onLoadDocument" | "afterLoadDocument" | "beforeHandleMessage" | "beforeBroadcastStateless" | "beforeSync" | "onStateless" | "onChange" | "onStoreDocument" | "afterStoreDocument" | "onAwarenessUpdate" | "onRequest" | "onDisconnect" | "beforeUnloadDocument" | "afterUnloadDocument" | "onDestroy";
55
56
  export type HookPayloadByName = {
56
57
  onConfigure: onConfigurePayload;
57
58
  onListen: onListenPayload;
@@ -59,6 +60,7 @@ export type HookPayloadByName = {
59
60
  onConnect: onConnectPayload;
60
61
  connected: connectedPayload;
61
62
  onAuthenticate: onAuthenticatePayload;
63
+ onTokenSync: onTokenSyncPayload;
62
64
  onCreateDocument: onCreateDocumentPayload;
63
65
  onLoadDocument: onLoadDocumentPayload;
64
66
  afterLoadDocument: afterLoadDocumentPayload;
@@ -135,6 +137,18 @@ export interface onAuthenticatePayload {
135
137
  token: string;
136
138
  connectionConfig: ConnectionConfiguration;
137
139
  }
140
+ export interface onTokenSyncPayload {
141
+ context: any;
142
+ document: Document;
143
+ documentName: string;
144
+ instance: Hocuspocus;
145
+ requestHeaders: IncomingHttpHeaders;
146
+ requestParameters: URLSearchParams;
147
+ socketId: string;
148
+ token: string;
149
+ connectionConfig: ConnectionConfiguration;
150
+ connection: Connection;
151
+ }
138
152
  export interface onCreateDocumentPayload {
139
153
  context: any;
140
154
  documentName: string;
@@ -0,0 +1 @@
1
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hocuspocus/provider",
3
- "version": "3.2.5",
3
+ "version": "3.3.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": "^3.2.5",
32
+ "@hocuspocus/common": "^3.3.0",
33
33
  "@lifeomic/attempt": "^3.0.2",
34
34
  "lib0": "^0.2.87",
35
35
  "ws": "^8.17.1"
@@ -304,6 +304,21 @@ export class HocuspocusProvider extends EventEmitter {
304
304
  });
305
305
  }
306
306
 
307
+ async sendToken() {
308
+ let token: string | null;
309
+ try {
310
+ token = await this.getToken();
311
+ } catch (error) {
312
+ this.permissionDeniedHandler(`Failed to get token during sendToken(): ${error}`);
313
+ return;
314
+ }
315
+
316
+ this.send(AuthenticationMessage, {
317
+ token: token ?? "",
318
+ documentName: this.configuration.name,
319
+ });
320
+ }
321
+
307
322
  documentUpdateHandler(update: Uint8Array, origin: any) {
308
323
  if (origin === this) {
309
324
  return;
@@ -374,20 +389,7 @@ export class HocuspocusProvider extends EventEmitter {
374
389
  this.isAuthenticated = false;
375
390
 
376
391
  this.emit("open", { event });
377
-
378
- let token: string | null;
379
- try {
380
- token = await this.getToken();
381
- } catch (error) {
382
- this.permissionDeniedHandler(`Failed to get token: ${error}`);
383
- return;
384
- }
385
-
386
- this.send(AuthenticationMessage, {
387
- token: token ?? "",
388
- documentName: this.configuration.name,
389
- });
390
-
392
+ await this.sendToken();
391
393
  this.startSync();
392
394
  }
393
395
 
@@ -117,6 +117,7 @@ export class MessageReceiver {
117
117
 
118
118
  readAuthMessage(
119
119
  message.decoder,
120
+ provider.sendToken.bind(provider),
120
121
  provider.permissionDeniedHandler.bind(provider),
121
122
  provider.authenticatedHandler.bind(provider),
122
123
  );