@hocuspocus/extension-redis 2.1.0-alpha.0 → 2.2.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.
Files changed (130) hide show
  1. package/dist/hocuspocus-redis.cjs +201 -153
  2. package/dist/hocuspocus-redis.cjs.map +1 -1
  3. package/dist/hocuspocus-redis.esm.js +200 -153
  4. package/dist/hocuspocus-redis.esm.js.map +1 -1
  5. package/dist/packages/common/src/CloseEvents.d.ts +29 -29
  6. package/dist/packages/common/src/auth.d.ts +6 -6
  7. package/dist/packages/common/src/awarenessStatesToArray.d.ts +3 -3
  8. package/dist/packages/common/src/index.d.ts +4 -4
  9. package/dist/packages/common/src/types.d.ts +10 -10
  10. package/dist/packages/extension-database/src/Database.d.ts +30 -30
  11. package/dist/packages/extension-database/src/index.d.ts +1 -1
  12. package/dist/packages/extension-logger/src/Logger.d.ts +67 -67
  13. package/dist/packages/extension-logger/src/index.d.ts +1 -1
  14. package/dist/packages/extension-redis/src/Redis.d.ts +116 -95
  15. package/dist/packages/extension-redis/src/index.d.ts +1 -1
  16. package/dist/packages/extension-sqlite/src/SQLite.d.ts +26 -26
  17. package/dist/packages/extension-sqlite/src/index.d.ts +1 -1
  18. package/dist/packages/extension-throttle/src/index.d.ts +31 -31
  19. package/dist/packages/extension-webhook/src/index.d.ts +57 -57
  20. package/dist/packages/provider/src/EventEmitter.d.ts +9 -9
  21. package/dist/packages/provider/src/HocuspocusProvider.d.ts +110 -110
  22. package/dist/packages/provider/src/HocuspocusProviderWebsocket.d.ts +115 -115
  23. package/dist/packages/provider/src/IncomingMessage.d.ts +16 -16
  24. package/dist/packages/provider/src/MessageReceiver.d.ts +13 -13
  25. package/dist/packages/provider/src/MessageSender.d.ts +10 -10
  26. package/dist/packages/provider/src/OutgoingMessage.d.ts +9 -9
  27. package/dist/packages/provider/src/OutgoingMessages/AuthenticationMessage.d.ts +7 -7
  28. package/dist/packages/provider/src/OutgoingMessages/AwarenessMessage.d.ts +8 -8
  29. package/dist/packages/provider/src/OutgoingMessages/CloseMessage.d.ts +8 -8
  30. package/dist/packages/provider/src/OutgoingMessages/QueryAwarenessMessage.d.ts +8 -8
  31. package/dist/packages/provider/src/OutgoingMessages/StatelessMessage.d.ts +7 -7
  32. package/dist/packages/provider/src/OutgoingMessages/SyncStepOneMessage.d.ts +8 -8
  33. package/dist/packages/provider/src/OutgoingMessages/SyncStepTwoMessage.d.ts +8 -8
  34. package/dist/packages/provider/src/OutgoingMessages/UpdateMessage.d.ts +7 -7
  35. package/dist/packages/provider/src/TiptapCollabProvider.d.ts +11 -11
  36. package/dist/packages/provider/src/TiptapCollabProviderWebsocket.d.ts +11 -11
  37. package/dist/packages/provider/src/index.d.ts +5 -5
  38. package/dist/packages/provider/src/types.d.ts +84 -84
  39. package/dist/packages/server/src/ClientConnection.d.ts +56 -0
  40. package/dist/packages/server/src/Connection.d.ts +71 -71
  41. package/dist/packages/server/src/Debugger.d.ts +14 -14
  42. package/dist/packages/server/src/DirectConnection.d.ts +14 -0
  43. package/dist/packages/server/src/Document.d.ts +91 -88
  44. package/dist/packages/server/src/Hocuspocus.d.ts +103 -108
  45. package/dist/packages/server/src/IncomingMessage.d.ts +21 -21
  46. package/dist/packages/server/src/MessageReceiver.d.ts +12 -12
  47. package/dist/packages/server/src/OutgoingMessage.d.ts +20 -20
  48. package/dist/packages/server/src/index.d.ts +8 -8
  49. package/dist/packages/server/src/types.d.ts +270 -264
  50. package/dist/packages/server/src/util/getParameters.d.ts +8 -0
  51. package/dist/packages/transformer/src/Prosemirror.d.ts +11 -11
  52. package/dist/packages/transformer/src/Tiptap.d.ts +10 -10
  53. package/dist/packages/transformer/src/index.d.ts +3 -3
  54. package/dist/packages/transformer/src/types.d.ts +5 -5
  55. package/dist/playground/backend/src/default.d.ts +1 -1
  56. package/dist/playground/backend/src/express.d.ts +1 -1
  57. package/dist/playground/backend/src/koa.d.ts +1 -1
  58. package/dist/playground/backend/src/load-document.d.ts +1 -1
  59. package/dist/playground/backend/src/redis.d.ts +1 -1
  60. package/dist/playground/backend/src/slow.d.ts +1 -1
  61. package/dist/playground/backend/src/tiptapcollab.d.ts +1 -1
  62. package/dist/playground/backend/src/webhook.d.ts +1 -1
  63. package/dist/playground/frontend/src/main.d.ts +1 -1
  64. package/dist/playground/frontend/vite.config.d.ts +2 -2
  65. package/dist/tests/extension-database/fetch.d.ts +1 -1
  66. package/dist/tests/extension-logger/onListen.d.ts +1 -1
  67. package/dist/tests/extension-redis/closeConnections.d.ts +1 -1
  68. package/dist/tests/extension-redis/getConnectionCount.d.ts +1 -1
  69. package/dist/tests/extension-redis/getDocumentsCount.d.ts +1 -1
  70. package/dist/tests/extension-redis/onAwarenessChange.d.ts +1 -1
  71. package/dist/tests/extension-redis/onChange.d.ts +1 -1
  72. package/dist/tests/extension-redis/onStateless.d.ts +1 -1
  73. package/dist/tests/extension-redis/onStoreDocument.d.ts +1 -1
  74. package/dist/tests/extension-throttle/banning.d.ts +1 -1
  75. package/dist/tests/extension-throttle/configuration.d.ts +1 -1
  76. package/dist/tests/provider/observe.d.ts +1 -1
  77. package/dist/tests/provider/observeDeep.d.ts +1 -1
  78. package/dist/tests/provider/onAuthenticated.d.ts +1 -1
  79. package/dist/tests/provider/onAuthenticationFailed.d.ts +1 -1
  80. package/dist/tests/provider/onAwarenessChange.d.ts +1 -1
  81. package/dist/tests/provider/onAwarenessUpdate.d.ts +1 -1
  82. package/dist/tests/provider/onClose.d.ts +1 -1
  83. package/dist/tests/provider/onConnect.d.ts +1 -1
  84. package/dist/tests/provider/onDisconnect.d.ts +1 -1
  85. package/dist/tests/provider/onMessage.d.ts +1 -1
  86. package/dist/tests/provider/onOpen.d.ts +1 -1
  87. package/dist/tests/provider/onStateless.d.ts +1 -1
  88. package/dist/tests/provider/onSynced.d.ts +1 -1
  89. package/dist/tests/providerwebsocket/configuration.d.ts +1 -1
  90. package/dist/tests/server/address.d.ts +1 -1
  91. package/dist/tests/server/afterStoreDocument.d.ts +1 -1
  92. package/dist/tests/server/beforeBroadcastStateless.d.ts +1 -1
  93. package/dist/tests/server/beforeHandleMessage.d.ts +1 -1
  94. package/dist/tests/server/closeConnections.d.ts +1 -1
  95. package/dist/tests/server/getConnectionsCount.d.ts +1 -1
  96. package/dist/tests/server/getDocumentsCount.d.ts +1 -1
  97. package/dist/tests/server/getMessageLogs.d.ts +1 -1
  98. package/dist/tests/server/listen.d.ts +1 -1
  99. package/dist/tests/server/onAuthenticate.d.ts +1 -1
  100. package/dist/tests/server/onAwarenessUpdate.d.ts +1 -1
  101. package/dist/tests/server/onChange.d.ts +1 -1
  102. package/dist/tests/server/onClose.d.ts +1 -1
  103. package/dist/tests/server/onConfigure.d.ts +1 -1
  104. package/dist/tests/server/onConnect.d.ts +1 -1
  105. package/dist/tests/server/onDestroy.d.ts +1 -1
  106. package/dist/tests/server/onDisconnect.d.ts +1 -1
  107. package/dist/tests/server/onListen.d.ts +1 -1
  108. package/dist/tests/server/onLoadDocument.d.ts +1 -1
  109. package/dist/tests/server/onRequest.d.ts +1 -1
  110. package/dist/tests/server/onStateless.d.ts +1 -1
  111. package/dist/tests/server/onStoreDocument.d.ts +1 -1
  112. package/dist/tests/server/onUpgrade.d.ts +1 -1
  113. package/dist/tests/server/openDirectConnection.d.ts +1 -0
  114. package/dist/tests/server/requiresAuthentication.d.ts +1 -1
  115. package/dist/tests/server/websocketError.d.ts +1 -1
  116. package/dist/tests/transformer/TiptapTransformer.d.ts +1 -1
  117. package/dist/tests/utils/createDirectory.d.ts +1 -1
  118. package/dist/tests/utils/flushRedis.d.ts +1 -1
  119. package/dist/tests/utils/index.d.ts +9 -9
  120. package/dist/tests/utils/newHocuspocus.d.ts +2 -2
  121. package/dist/tests/utils/newHocuspocusProvider.d.ts +3 -3
  122. package/dist/tests/utils/newHocuspocusProviderWebsocket.d.ts +3 -3
  123. package/dist/tests/utils/randomInteger.d.ts +1 -1
  124. package/dist/tests/utils/redisConnectionSettings.d.ts +4 -4
  125. package/dist/tests/utils/removeDirectory.d.ts +1 -1
  126. package/dist/tests/utils/retryableAssertion.d.ts +2 -2
  127. package/dist/tests/utils/sleep.d.ts +1 -1
  128. package/package.json +9 -5
  129. package/src/Redis.ts +74 -13
  130. package/src/index.ts +1 -1
@@ -1,2 +1,2 @@
1
- declare const _default: import("vite").UserConfigExport;
2
- export default _default;
1
+ declare const _default: import("vite").UserConfigExport;
2
+ export default _default;
@@ -1 +1 @@
1
- export {};
1
+ export {};
@@ -1 +1 @@
1
- export {};
1
+ export {};
@@ -1 +1 @@
1
- export {};
1
+ export {};
@@ -1 +1 @@
1
- export {};
1
+ export {};
@@ -1 +1 @@
1
- export {};
1
+ export {};
@@ -1 +1 @@
1
- export {};
1
+ export {};
@@ -1 +1 @@
1
- export {};
1
+ export {};
@@ -1 +1 @@
1
- export {};
1
+ export {};
@@ -1 +1 @@
1
- export {};
1
+ export {};
@@ -1 +1 @@
1
- export {};
1
+ export {};
@@ -1 +1 @@
1
- export {};
1
+ export {};
@@ -1 +1 @@
1
- export {};
1
+ export {};
@@ -1 +1 @@
1
- export {};
1
+ export {};
@@ -1 +1 @@
1
- export {};
1
+ export {};
@@ -1 +1 @@
1
- export {};
1
+ export {};
@@ -1 +1 @@
1
- export {};
1
+ export {};
@@ -1 +1 @@
1
- export {};
1
+ export {};
@@ -1 +1 @@
1
- export {};
1
+ export {};
@@ -1 +1 @@
1
- export {};
1
+ export {};
@@ -1 +1 @@
1
- export {};
1
+ export {};
@@ -1 +1 @@
1
- export {};
1
+ export {};
@@ -1 +1 @@
1
- export {};
1
+ export {};
@@ -1 +1 @@
1
- export {};
1
+ export {};
@@ -1 +1 @@
1
- export {};
1
+ export {};
@@ -1 +1 @@
1
- export {};
1
+ export {};
@@ -1 +1 @@
1
- export {};
1
+ export {};
@@ -1 +1 @@
1
- export {};
1
+ export {};
@@ -1 +1 @@
1
- export {};
1
+ export {};
@@ -1 +1 @@
1
- export {};
1
+ export {};
@@ -1 +1 @@
1
- export {};
1
+ export {};
@@ -1 +1 @@
1
- export {};
1
+ export {};
@@ -1 +1 @@
1
- export {};
1
+ export {};
@@ -1 +1 @@
1
- export {};
1
+ export {};
@@ -1 +1 @@
1
- export {};
1
+ export {};
@@ -1 +1 @@
1
- export {};
1
+ export {};
@@ -1 +1 @@
1
- export {};
1
+ export {};
@@ -1 +1 @@
1
- export {};
1
+ export {};
@@ -1 +1 @@
1
- export {};
1
+ export {};
@@ -1 +1 @@
1
- export {};
1
+ export {};
@@ -1 +1 @@
1
- export {};
1
+ export {};
@@ -1 +1 @@
1
- export {};
1
+ export {};
@@ -1 +1 @@
1
- export {};
1
+ export {};
@@ -1 +1 @@
1
- export {};
1
+ export {};
@@ -1 +1 @@
1
- export {};
1
+ export {};
@@ -1 +1 @@
1
- export {};
1
+ export {};
@@ -1 +1 @@
1
- export {};
1
+ export {};
@@ -1 +1 @@
1
- export {};
1
+ export {};
@@ -1 +1 @@
1
- export {};
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -1 +1 @@
1
- export {};
1
+ export {};
@@ -1 +1 @@
1
- export {};
1
+ export {};
@@ -1 +1 @@
1
- export {};
1
+ export {};
@@ -1 +1 @@
1
- export declare const createDirectory: (dir: string) => void;
1
+ export declare const createDirectory: (dir: string) => void;
@@ -1 +1 @@
1
- export declare const flushRedis: () => Promise<string>;
1
+ export declare const flushRedis: () => Promise<string>;
@@ -1,9 +1,9 @@
1
- export * from './createDirectory';
2
- export * from './flushRedis';
3
- export * from './newHocuspocus';
4
- export * from './newHocuspocusProvider';
5
- export * from './newHocuspocusProviderWebsocket';
6
- export * from './randomInteger';
7
- export * from './redisConnectionSettings';
8
- export * from './removeDirectory';
9
- export * from './sleep';
1
+ export * from './createDirectory.js';
2
+ export * from './flushRedis.js';
3
+ export * from './newHocuspocus.js';
4
+ export * from './newHocuspocusProvider.js';
5
+ export * from './newHocuspocusProviderWebsocket.js';
6
+ export * from './randomInteger.js';
7
+ export * from './redisConnectionSettings.js';
8
+ export * from './removeDirectory.js';
9
+ export * from './sleep.js';
@@ -1,2 +1,2 @@
1
- import { Hocuspocus, Configuration } from '@hocuspocus/server';
2
- export declare const newHocuspocus: (options?: Partial<Configuration>) => Promise<Hocuspocus>;
1
+ import { Hocuspocus, Configuration } from '@hocuspocus/server';
2
+ export declare const newHocuspocus: (options?: Partial<Configuration>) => Promise<Hocuspocus>;
@@ -1,3 +1,3 @@
1
- import { HocuspocusProvider, HocuspocusProviderConfiguration, HocuspocusProviderWebsocketConfiguration } from '@hocuspocus/provider';
2
- import { Hocuspocus } from '@hocuspocus/server';
3
- export declare const newHocuspocusProvider: (server: Hocuspocus, options?: Partial<HocuspocusProviderConfiguration>, websocketOptions?: Partial<HocuspocusProviderWebsocketConfiguration>) => HocuspocusProvider;
1
+ import { HocuspocusProvider, HocuspocusProviderConfiguration, HocuspocusProviderWebsocketConfiguration } from '@hocuspocus/provider';
2
+ import { Hocuspocus } from '@hocuspocus/server';
3
+ export declare const newHocuspocusProvider: (server: Hocuspocus, options?: Partial<HocuspocusProviderConfiguration>, websocketOptions?: Partial<HocuspocusProviderWebsocketConfiguration>) => HocuspocusProvider;
@@ -1,3 +1,3 @@
1
- import { HocuspocusProviderWebsocket, HocuspocusProviderWebsocketConfiguration } from '@hocuspocus/provider';
2
- import { Hocuspocus } from '@hocuspocus/server';
3
- export declare const newHocuspocusProviderWebsocket: (server: Hocuspocus, options?: Partial<Omit<HocuspocusProviderWebsocketConfiguration, 'url'>>) => HocuspocusProviderWebsocket;
1
+ import { HocuspocusProviderWebsocket, HocuspocusProviderWebsocketConfiguration } from '@hocuspocus/provider';
2
+ import { Hocuspocus } from '@hocuspocus/server';
3
+ export declare const newHocuspocusProviderWebsocket: (server: Hocuspocus, options?: Partial<Omit<HocuspocusProviderWebsocketConfiguration, 'url'>>) => HocuspocusProviderWebsocket;
@@ -1 +1 @@
1
- export declare const randomInteger: (min: number, max: number) => number;
1
+ export declare const randomInteger: (min: number, max: number) => number;
@@ -1,4 +1,4 @@
1
- export declare const redisConnectionSettings: {
2
- host: string;
3
- port: number;
4
- };
1
+ export declare const redisConnectionSettings: {
2
+ host: string;
3
+ port: number;
4
+ };
@@ -1 +1 @@
1
- export declare const removeDirectory: (dir: string) => void;
1
+ export declare const removeDirectory: (dir: string) => void;
@@ -1,2 +1,2 @@
1
- import { ExecutionContext } from 'ava';
2
- export declare const retryableAssertion: (t: ExecutionContext, recoverableTry: (tt: ExecutionContext) => void) => Promise<void>;
1
+ import { ExecutionContext } from 'ava';
2
+ export declare const retryableAssertion: (t: ExecutionContext, recoverableTry: (tt: ExecutionContext) => void) => Promise<void>;
@@ -1 +1 @@
1
- export declare const sleep: (time: number) => Promise<unknown>;
1
+ export declare const sleep: (time: number) => Promise<unknown>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hocuspocus/extension-redis",
3
- "version": "2.1.0-alpha.0",
3
+ "version": "2.2.0",
4
4
  "description": "Scale Hocuspocus horizontally with Redis",
5
5
  "homepage": "https://hocuspocus.dev",
6
6
  "keywords": [
@@ -19,7 +19,8 @@
19
19
  },
20
20
  "default": {
21
21
  "import": "./dist/hocuspocus-redis.esm.js",
22
- "require": "./dist/hocuspocus-redis.cjs"
22
+ "require": "./dist/hocuspocus-redis.cjs",
23
+ "types": "./dist/packages/extension-redis/src/index.d.ts"
23
24
  }
24
25
  },
25
26
  "files": [
@@ -27,13 +28,16 @@
27
28
  "dist"
28
29
  ],
29
30
  "devDependencies": {
30
- "@types/lodash.debounce": "^4.0.6"
31
+ "@types/ioredis": "^4.28.7",
32
+ "@types/lodash.debounce": "^4.0.6",
33
+ "@types/redlock": "^4.0.3"
31
34
  },
32
35
  "dependencies": {
33
- "@hocuspocus/server": "^2.1.0-alpha.0",
34
- "ioredis": "^5.0.4",
36
+ "@hocuspocus/server": "^2.2.0",
37
+ "ioredis": "^4.28.2",
35
38
  "kleur": "^4.1.4",
36
39
  "lodash.debounce": "^4.0.8",
40
+ "redlock": "^4.2.0",
37
41
  "uuid": "^9.0.0"
38
42
  },
39
43
  "peerDependencies": {
package/src/Redis.ts CHANGED
@@ -1,6 +1,5 @@
1
- import RedisClient, {
2
- ClusterNode, ClusterOptions, RedisOptions, Cluster as RedisClusterClient,
3
- } from 'ioredis'
1
+ import RedisClient, { ClusterNode, ClusterOptions, RedisOptions } from 'ioredis'
2
+ import Redlock from 'redlock'
4
3
  import { v4 as uuid } from 'uuid'
5
4
  import {
6
5
  IncomingMessage,
@@ -8,7 +7,9 @@ import {
8
7
  Document,
9
8
  Extension,
10
9
  afterLoadDocumentPayload,
10
+ afterStoreDocumentPayload,
11
11
  onDisconnectPayload,
12
+ onStoreDocumentPayload,
12
13
  onAwarenessUpdatePayload,
13
14
  onChangePayload,
14
15
  MessageReceiver,
@@ -17,7 +18,7 @@ import {
17
18
  beforeBroadcastStatelessPayload, Hocuspocus,
18
19
  } from '@hocuspocus/server'
19
20
 
20
- export type RedisInstance = RedisClient | RedisClusterClient
21
+ export type RedisInstance = RedisClient.Cluster | RedisClient.Redis
21
22
 
22
23
  export interface Configuration {
23
24
  /**
@@ -55,6 +56,10 @@ export interface Configuration {
55
56
  * Namespace for Redis keys, if none is provided 'hocuspocus' is used
56
57
  */
57
58
  prefix: string,
59
+ /**
60
+ * The maximum time for the Redis lock in ms (in case it can’t be released).
61
+ */
62
+ lockTimeout: number,
58
63
  /**
59
64
  * A delay before onDisconnect is executed. This allows last minute updates'
60
65
  * sync messages to be received by the subscription before it's closed.
@@ -75,6 +80,7 @@ export class Redis implements Extension {
75
80
  host: '127.0.0.1',
76
81
  prefix: 'hocuspocus',
77
82
  identifier: `host-${uuid()}`,
83
+ lockTimeout: 1000,
78
84
  disconnectDelay: 1000,
79
85
  }
80
86
 
@@ -84,6 +90,10 @@ export class Redis implements Extension {
84
90
 
85
91
  instance!: Hocuspocus
86
92
 
93
+ redlock: Redlock
94
+
95
+ locks = new Map<string, Redlock.Lock>()
96
+
87
97
  logger: Debugger
88
98
 
89
99
  public constructor(configuration: Partial<Configuration>) {
@@ -92,6 +102,9 @@ export class Redis implements Extension {
92
102
  ...configuration,
93
103
  }
94
104
 
105
+ // We’ll replace that in the onConfigure hook with the global instance.
106
+ this.logger = new Debugger()
107
+
95
108
  // Create Redis instance
96
109
  const {
97
110
  port,
@@ -112,13 +125,12 @@ export class Redis implements Extension {
112
125
  this.pub = new RedisClient.Cluster(nodes, options)
113
126
  this.sub = new RedisClient.Cluster(nodes, options)
114
127
  } else {
115
- this.pub = new RedisClient(port, host, options as RedisOptions)
116
- this.sub = new RedisClient(port, host, options as RedisOptions)
128
+ this.pub = new RedisClient(port, host, options)
129
+ this.sub = new RedisClient(port, host, options)
117
130
  }
118
131
  this.sub.on('pmessageBuffer', this.handleIncomingMessage)
119
132
 
120
- // We’ll replace that in the onConfigure hook with the global instance.
121
- this.logger = new Debugger()
133
+ this.redlock = new Redlock([this.pub])
122
134
  }
123
135
 
124
136
  async onConfigure({ instance }: onConfigurePayload) {
@@ -138,6 +150,10 @@ export class Redis implements Extension {
138
150
  return `${this.getKey(documentName)}:*`
139
151
  }
140
152
 
153
+ private lockKey(documentName: string) {
154
+ return `${this.getKey(documentName)}:lock`
155
+ }
156
+
141
157
  /**
142
158
  * Once a document is laoded, subscribe to the channel in Redis.
143
159
  */
@@ -167,7 +183,7 @@ export class Redis implements Extension {
167
183
  .createSyncMessage()
168
184
  .writeFirstSyncStepFor(document)
169
185
 
170
- return this.pub.publish(this.pubKey(documentName), Buffer.from(syncMessage.toUint8Array()))
186
+ return this.pub.publishBuffer(this.pubKey(documentName), Buffer.from(syncMessage.toUint8Array()))
171
187
  }
172
188
 
173
189
  /**
@@ -177,12 +193,49 @@ export class Redis implements Extension {
177
193
  const awarenessMessage = new OutgoingMessage(documentName)
178
194
  .writeQueryAwareness()
179
195
 
180
- return this.pub.publish(
196
+ return this.pub.publishBuffer(
181
197
  this.pubKey(documentName),
182
198
  Buffer.from(awarenessMessage.toUint8Array()),
183
199
  )
184
200
  }
185
201
 
202
+ /**
203
+ * Before the document is stored, make sure to set a lock in Redis.
204
+ * That’s meant to avoid conflicts with other instances trying to store the document.
205
+ */
206
+ async onStoreDocument({ documentName }: onStoreDocumentPayload) {
207
+ // Attempt to acquire a lock and read lastReceivedTimestamp from Redis,
208
+ // to avoid conflict with other instances storing the same document.
209
+ return new Promise((resolve, reject) => {
210
+ this.redlock.lock(this.lockKey(documentName), this.configuration.lockTimeout, async (error, lock) => {
211
+ if (error || !lock) {
212
+ // Expected behavior: Could not acquire lock, another instance locked it already.
213
+ // No further `onStoreDocument` hooks will be executed.
214
+ reject()
215
+ return
216
+ }
217
+
218
+ this.locks.set(this.lockKey(documentName), lock)
219
+
220
+ resolve(undefined)
221
+ })
222
+ })
223
+ }
224
+
225
+ /**
226
+ * Release the Redis lock, so other instances can store documents.
227
+ */
228
+ async afterStoreDocument({ documentName }: afterStoreDocumentPayload) {
229
+ this.locks.get(this.lockKey(documentName))?.unlock()
230
+ .catch(() => {
231
+ // Not able to unlock Redis. The lock will expire after ${lockTimeout} ms.
232
+ // console.error(`Not able to unlock Redis. The lock will expire after ${this.configuration.lockTimeout}ms.`)
233
+ })
234
+ .finally(() => {
235
+ this.locks.delete(this.lockKey(documentName))
236
+ })
237
+ }
238
+
186
239
  /**
187
240
  * Handle awareness update messages received directly by this Hocuspocus instance.
188
241
  */
@@ -193,7 +246,7 @@ export class Redis implements Extension {
193
246
  const message = new OutgoingMessage(documentName)
194
247
  .createAwarenessUpdateMessage(awareness, changedClients)
195
248
 
196
- return this.pub.publish(
249
+ return this.pub.publishBuffer(
197
250
  this.pubKey(documentName),
198
251
  Buffer.from(message.toUint8Array()),
199
252
  )
@@ -226,7 +279,7 @@ export class Redis implements Extension {
226
279
  message,
227
280
  this.logger,
228
281
  ).apply(document, undefined, reply => {
229
- return this.pub.publish(
282
+ return this.pub.publishBuffer(
230
283
  this.pubKey(document.name),
231
284
  Buffer.from(reply),
232
285
  )
@@ -268,10 +321,18 @@ export class Redis implements Extension {
268
321
  const message = new OutgoingMessage(data.documentName)
269
322
  .writeBroadcastStateless(data.payload)
270
323
 
271
- return this.pub.publish(
324
+ return this.pub.publishBuffer(
272
325
  this.pubKey(data.documentName),
273
326
  Buffer.from(message.toUint8Array()),
274
327
  )
275
328
  }
276
329
 
330
+ /**
331
+ * Kill the Redlock connection immediately.
332
+ */
333
+ async onDestroy() {
334
+ await this.redlock.quit()
335
+ this.pub.disconnect(false)
336
+ this.sub.disconnect(false)
337
+ }
277
338
  }
package/src/index.ts CHANGED
@@ -1 +1 @@
1
- export * from './Redis'
1
+ export * from './Redis.js'