@hocuspocus/provider 3.0.0-rc.0 → 3.0.4-rc.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.
- package/dist/hocuspocus-provider.cjs +773 -11306
- package/dist/hocuspocus-provider.cjs.map +1 -1
- package/dist/hocuspocus-provider.esm.js +753 -11303
- package/dist/hocuspocus-provider.esm.js.map +1 -1
- package/dist/packages/common/src/auth.d.ts +1 -1
- package/dist/packages/extension-redis/src/Redis.d.ts +8 -2
- package/dist/packages/extension-throttle/src/index.d.ts +1 -0
- package/dist/packages/extension-webhook/src/index.d.ts +1 -0
- package/dist/packages/provider/src/HocuspocusProvider.d.ts +11 -33
- package/dist/packages/provider/src/HocuspocusProviderWebsocket.d.ts +0 -4
- package/dist/packages/provider/src/MessageReceiver.d.ts +0 -2
- package/dist/packages/provider/src/index.d.ts +0 -2
- package/dist/packages/provider/src/types.d.ts +42 -2
- package/dist/packages/server/src/ClientConnection.d.ts +15 -5
- package/dist/packages/server/src/Connection.d.ts +3 -17
- package/dist/packages/server/src/DirectConnection.d.ts +1 -1
- package/dist/packages/server/src/Document.d.ts +1 -5
- package/dist/packages/server/src/Hocuspocus.d.ts +3 -11
- package/dist/packages/server/src/MessageReceiver.d.ts +1 -3
- package/dist/packages/server/src/OutgoingMessage.d.ts +1 -0
- package/dist/packages/server/src/Server.d.ts +3 -2
- package/dist/packages/server/src/index.d.ts +0 -1
- package/dist/packages/server/src/types.d.ts +20 -11
- package/dist/packages/server/src/util/getParameters.d.ts +2 -0
- package/dist/tests/utils/newHocuspocusProvider.d.ts +2 -2
- package/dist/tests/utils/newHocuspocusProviderWebsocket.d.ts +1 -1
- package/package.json +2 -2
- package/src/HocuspocusProvider.ts +76 -198
- package/src/HocuspocusProviderWebsocket.ts +18 -68
- package/src/MessageReceiver.ts +18 -17
- package/src/index.ts +0 -2
- package/src/types.ts +48 -2
- package/dist/packages/provider/src/TiptapCollabProvider.d.ts +0 -64
- package/dist/packages/provider/src/TiptapCollabProviderWebsocket.d.ts +0 -20
- package/dist/packages/server/src/Debugger.d.ts +0 -14
- package/dist/tests/server/getMessageLogs.d.ts +0 -1
- package/dist/tests/server/requiresAuthentication.d.ts +0 -1
- package/src/TiptapCollabProvider.ts +0 -321
- package/src/TiptapCollabProviderWebsocket.ts +0 -39
package/src/MessageReceiver.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { readAuthMessage } from '@hocuspocus/common'
|
|
|
2
2
|
import { readVarInt, readVarString } from 'lib0/decoding'
|
|
3
3
|
import * as awarenessProtocol from 'y-protocols/awareness'
|
|
4
4
|
import { messageYjsSyncStep2, readSyncMessage } from 'y-protocols/sync'
|
|
5
|
+
import type { CloseEvent } from 'ws'
|
|
5
6
|
import type { HocuspocusProvider } from './HocuspocusProvider.js'
|
|
6
7
|
import type { IncomingMessage } from './IncomingMessage.js'
|
|
7
8
|
import { OutgoingMessage } from './OutgoingMessage.js'
|
|
@@ -11,18 +12,10 @@ export class MessageReceiver {
|
|
|
11
12
|
|
|
12
13
|
message: IncomingMessage
|
|
13
14
|
|
|
14
|
-
broadcasted = false
|
|
15
|
-
|
|
16
15
|
constructor(message: IncomingMessage) {
|
|
17
16
|
this.message = message
|
|
18
17
|
}
|
|
19
18
|
|
|
20
|
-
public setBroadcasted(value: boolean) {
|
|
21
|
-
this.broadcasted = value
|
|
22
|
-
|
|
23
|
-
return this
|
|
24
|
-
}
|
|
25
|
-
|
|
26
19
|
public apply(provider: HocuspocusProvider, emitSynced: boolean) {
|
|
27
20
|
const { message } = this
|
|
28
21
|
const type = message.readVarUint()
|
|
@@ -53,21 +46,29 @@ export class MessageReceiver {
|
|
|
53
46
|
case MessageType.SyncStatus:
|
|
54
47
|
this.applySyncStatusMessage(provider, readVarInt(message.decoder) === 1)
|
|
55
48
|
break
|
|
49
|
+
|
|
50
|
+
case MessageType.CLOSE:
|
|
51
|
+
// eslint-disable-next-line no-case-declarations
|
|
52
|
+
const event: CloseEvent = {
|
|
53
|
+
code: 1000,
|
|
54
|
+
reason: readVarString(message.decoder),
|
|
55
|
+
// @ts-ignore
|
|
56
|
+
target: provider.configuration.websocketProvider.webSocket!,
|
|
57
|
+
type: 'close',
|
|
58
|
+
}
|
|
59
|
+
provider.onClose()
|
|
60
|
+
provider.configuration.onClose({ event })
|
|
61
|
+
provider.forwardClose(event)
|
|
62
|
+
break
|
|
63
|
+
|
|
56
64
|
default:
|
|
57
65
|
throw new Error(`Can’t apply message of unknown type: ${type}`)
|
|
58
66
|
}
|
|
59
67
|
|
|
60
68
|
// Reply
|
|
61
69
|
if (message.length() > emptyMessageLength + 1) { // length of documentName (considered in emptyMessageLength plus length of yjs sync type, set in applySyncMessage)
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
// @ts-ignore
|
|
65
|
-
provider.broadcast(OutgoingMessage, { encoder: message.encoder })
|
|
66
|
-
} else {
|
|
67
|
-
// TODO: Some weird TypeScript error
|
|
68
|
-
// @ts-ignore
|
|
69
|
-
provider.send(OutgoingMessage, { encoder: message.encoder })
|
|
70
|
-
}
|
|
70
|
+
// @ts-ignore
|
|
71
|
+
provider.send(OutgoingMessage, { encoder: message.encoder })
|
|
71
72
|
}
|
|
72
73
|
}
|
|
73
74
|
|
package/src/index.ts
CHANGED
package/src/types.ts
CHANGED
|
@@ -110,15 +110,18 @@ export type TCollabThread<Data = any, CommentData = any> = {
|
|
|
110
110
|
id: string;
|
|
111
111
|
createdAt: number;
|
|
112
112
|
updatedAt: number;
|
|
113
|
+
deletedAt: number | null;
|
|
113
114
|
resolvedAt?: string; // (new Date()).toISOString()
|
|
114
115
|
comments: TCollabComment<CommentData>[];
|
|
116
|
+
deletedComments: TCollabComment<CommentData>[];
|
|
115
117
|
data: Data
|
|
116
118
|
}
|
|
117
119
|
|
|
118
120
|
export type TCollabComment<Data = any> = {
|
|
119
121
|
id: string;
|
|
120
|
-
createdAt:
|
|
121
|
-
updatedAt:
|
|
122
|
+
createdAt: string;
|
|
123
|
+
updatedAt: string;
|
|
124
|
+
deletedAt?: string;
|
|
122
125
|
data: Data
|
|
123
126
|
content: any
|
|
124
127
|
}
|
|
@@ -183,3 +186,46 @@ export type THistoryDocumentRevertedEvent = {
|
|
|
183
186
|
event: 'document.reverted';
|
|
184
187
|
version: number;
|
|
185
188
|
};
|
|
189
|
+
|
|
190
|
+
export type DeleteCommentOptions = {
|
|
191
|
+
/**
|
|
192
|
+
* If `true`, the thread will also be deleted if the deleted comment was the first comment in the thread.
|
|
193
|
+
*/
|
|
194
|
+
deleteThread?: boolean
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* If `true`, will remove the content of the deleted comment
|
|
198
|
+
*/
|
|
199
|
+
deleteContent?: boolean
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
export type DeleteThreadOptions = {
|
|
203
|
+
/**
|
|
204
|
+
* If `true`, will remove the comments on the thread,
|
|
205
|
+
* otherwise will only mark the thread as deleted
|
|
206
|
+
* and keep the comments
|
|
207
|
+
* @default false
|
|
208
|
+
*/
|
|
209
|
+
deleteComments?: boolean
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* If `true`, will forcefully remove the thread and all comments,
|
|
213
|
+
* otherwise will only mark the thread as deleted
|
|
214
|
+
* and keep the comments
|
|
215
|
+
* @default false
|
|
216
|
+
*/
|
|
217
|
+
force?: boolean,
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* The type of thread
|
|
222
|
+
*/
|
|
223
|
+
export type ThreadType = 'archived' | 'unarchived'
|
|
224
|
+
|
|
225
|
+
export type GetThreadsOptions = {
|
|
226
|
+
/**
|
|
227
|
+
* The types of threads to get
|
|
228
|
+
* @default ['unarchived']
|
|
229
|
+
*/
|
|
230
|
+
types?: Array<ThreadType>
|
|
231
|
+
}
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
import type { AbstractType, YArrayEvent } from 'yjs';
|
|
2
|
-
import * as Y from 'yjs';
|
|
3
|
-
import type { HocuspocusProviderConfiguration } from './HocuspocusProvider.js';
|
|
4
|
-
import { HocuspocusProvider } from './HocuspocusProvider.js';
|
|
5
|
-
import { TiptapCollabProviderWebsocket } from './TiptapCollabProviderWebsocket.js';
|
|
6
|
-
import type { TCollabComment, TCollabThread, THistoryVersion } from './types.js';
|
|
7
|
-
export type TiptapCollabProviderConfiguration = Required<Pick<HocuspocusProviderConfiguration, 'name'>> & Partial<HocuspocusProviderConfiguration> & (Required<Pick<AdditionalTiptapCollabProviderConfiguration, 'websocketProvider'>> | Required<Pick<AdditionalTiptapCollabProviderConfiguration, 'appId'>> | Required<Pick<AdditionalTiptapCollabProviderConfiguration, 'baseUrl'>>) & Pick<AdditionalTiptapCollabProviderConfiguration, 'user'>;
|
|
8
|
-
export interface AdditionalTiptapCollabProviderConfiguration {
|
|
9
|
-
/**
|
|
10
|
-
* A Hocuspocus Cloud App ID, get one here: https://cloud.tiptap.dev
|
|
11
|
-
*/
|
|
12
|
-
appId?: string;
|
|
13
|
-
/**
|
|
14
|
-
* If you are using the on-premise version of TiptapCollab, put your baseUrl here (e.g. https://collab.yourdomain.com)
|
|
15
|
-
*/
|
|
16
|
-
baseUrl?: string;
|
|
17
|
-
websocketProvider?: TiptapCollabProviderWebsocket;
|
|
18
|
-
user?: string;
|
|
19
|
-
}
|
|
20
|
-
export declare class TiptapCollabProvider extends HocuspocusProvider {
|
|
21
|
-
tiptapCollabConfigurationPrefix: string;
|
|
22
|
-
userData?: Y.PermanentUserData;
|
|
23
|
-
constructor(configuration: TiptapCollabProviderConfiguration);
|
|
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
|
-
createVersion(name?: string): void;
|
|
28
|
-
/**
|
|
29
|
-
* note: this will only work if your server loaded @hocuspocus-pro/extension-history, or if you are on a Tiptap business plan.
|
|
30
|
-
*/
|
|
31
|
-
revertToVersion(targetVersion: number): void;
|
|
32
|
-
/**
|
|
33
|
-
* note: this will only work if your server loaded @hocuspocus-pro/extension-history, or if you are on a Tiptap business plan.
|
|
34
|
-
*
|
|
35
|
-
* The server will reply with a stateless message (THistoryVersionPreviewEvent)
|
|
36
|
-
*/
|
|
37
|
-
previewVersion(targetVersion: number): void;
|
|
38
|
-
/**
|
|
39
|
-
* note: this will only work if your server loaded @hocuspocus-pro/extension-history, or if you are on a Tiptap business plan.
|
|
40
|
-
*/
|
|
41
|
-
getVersions(): THistoryVersion[];
|
|
42
|
-
watchVersions(callback: Parameters<AbstractType<YArrayEvent<THistoryVersion>>['observe']>[0]): void;
|
|
43
|
-
unwatchVersions(callback: Parameters<AbstractType<YArrayEvent<THistoryVersion>>['unobserve']>[0]): void;
|
|
44
|
-
isAutoVersioning(): boolean;
|
|
45
|
-
enableAutoVersioning(): 1;
|
|
46
|
-
disableAutoVersioning(): 0;
|
|
47
|
-
private getYThreads;
|
|
48
|
-
getThreads<Data, CommentData>(): TCollabThread<Data, CommentData>[];
|
|
49
|
-
private getThreadIndex;
|
|
50
|
-
getThread<Data, CommentData>(id: string): TCollabThread<Data, CommentData> | null;
|
|
51
|
-
private getYThread;
|
|
52
|
-
createThread(data: Omit<TCollabThread, 'id' | 'createdAt' | 'updatedAt' | 'comments'>): TCollabThread;
|
|
53
|
-
updateThread(id: TCollabThread['id'], data: Partial<Pick<TCollabThread, 'data'> & {
|
|
54
|
-
resolvedAt: TCollabThread['resolvedAt'] | null;
|
|
55
|
-
}>): TCollabThread;
|
|
56
|
-
deleteThread(id: TCollabThread['id']): void;
|
|
57
|
-
getThreadComments(threadId: TCollabThread['id']): TCollabComment[] | null;
|
|
58
|
-
getThreadComment(threadId: TCollabThread['id'], commentId: TCollabComment['id']): TCollabComment | null;
|
|
59
|
-
addComment(threadId: TCollabThread['id'], data: Omit<TCollabComment, 'id' | 'updatedAt' | 'createdAt'>): TCollabThread;
|
|
60
|
-
updateComment(threadId: TCollabThread['id'], commentId: TCollabComment['id'], data: Partial<Pick<TCollabComment, 'data' | 'content'>>): TCollabThread;
|
|
61
|
-
deleteComment(threadId: TCollabThread['id'], commentId: TCollabComment['id']): TCollabThread | null | undefined;
|
|
62
|
-
watchThreads(callback: () => void): void;
|
|
63
|
-
unwatchThreads(callback: () => void): void;
|
|
64
|
-
}
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import type { CompleteHocuspocusProviderWebsocketConfiguration } from './HocuspocusProviderWebsocket.js';
|
|
2
|
-
import { HocuspocusProviderWebsocket } from './HocuspocusProviderWebsocket.js';
|
|
3
|
-
export type TiptapCollabProviderWebsocketConfiguration = Partial<CompleteHocuspocusProviderWebsocketConfiguration> & AdditionalTiptapCollabProviderWebsocketConfiguration;
|
|
4
|
-
export interface AdditionalTiptapCollabProviderWebsocketConfiguration {
|
|
5
|
-
/**
|
|
6
|
-
* A Hocuspocus Cloud App ID, get one here: https://cloud.tiptap.dev
|
|
7
|
-
*/
|
|
8
|
-
appId?: string;
|
|
9
|
-
/**
|
|
10
|
-
* If you are using the on-premise version of TiptapCollab, put your baseUrl here (e.g. https://collab.yourdomain.com)
|
|
11
|
-
*/
|
|
12
|
-
baseUrl?: string;
|
|
13
|
-
/**
|
|
14
|
-
* Only fill this if you are using Tiptap Collab HA.
|
|
15
|
-
*/
|
|
16
|
-
shardKey?: string;
|
|
17
|
-
}
|
|
18
|
-
export declare class TiptapCollabProviderWebsocket extends HocuspocusProviderWebsocket {
|
|
19
|
-
constructor(configuration: TiptapCollabProviderWebsocketConfiguration);
|
|
20
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,321 +0,0 @@
|
|
|
1
|
-
import type { AbstractType, YArrayEvent } from 'yjs'
|
|
2
|
-
import * as Y from 'yjs'
|
|
3
|
-
import { uuidv4 } from 'lib0/random'
|
|
4
|
-
import type {
|
|
5
|
-
HocuspocusProviderConfiguration} from './HocuspocusProvider.js'
|
|
6
|
-
import {
|
|
7
|
-
HocuspocusProvider,
|
|
8
|
-
} from './HocuspocusProvider.js'
|
|
9
|
-
|
|
10
|
-
import { TiptapCollabProviderWebsocket } from './TiptapCollabProviderWebsocket.js'
|
|
11
|
-
import type {
|
|
12
|
-
TCollabComment, TCollabThread, THistoryVersion,
|
|
13
|
-
} from './types.js'
|
|
14
|
-
|
|
15
|
-
export type TiptapCollabProviderConfiguration =
|
|
16
|
-
Required<Pick<HocuspocusProviderConfiguration, 'name'>> &
|
|
17
|
-
Partial<HocuspocusProviderConfiguration> &
|
|
18
|
-
(Required<Pick<AdditionalTiptapCollabProviderConfiguration, 'websocketProvider'>> |
|
|
19
|
-
Required<Pick<AdditionalTiptapCollabProviderConfiguration, 'appId'>>|
|
|
20
|
-
Required<Pick<AdditionalTiptapCollabProviderConfiguration, 'baseUrl'>>) &
|
|
21
|
-
Pick<AdditionalTiptapCollabProviderConfiguration, 'user'>
|
|
22
|
-
|
|
23
|
-
export interface AdditionalTiptapCollabProviderConfiguration {
|
|
24
|
-
/**
|
|
25
|
-
* A Hocuspocus Cloud App ID, get one here: https://cloud.tiptap.dev
|
|
26
|
-
*/
|
|
27
|
-
appId?: string,
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* If you are using the on-premise version of TiptapCollab, put your baseUrl here (e.g. https://collab.yourdomain.com)
|
|
31
|
-
*/
|
|
32
|
-
baseUrl?: string
|
|
33
|
-
|
|
34
|
-
websocketProvider?: TiptapCollabProviderWebsocket
|
|
35
|
-
|
|
36
|
-
user?: string
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
export class TiptapCollabProvider extends HocuspocusProvider {
|
|
40
|
-
tiptapCollabConfigurationPrefix = '__tiptapcollab__'
|
|
41
|
-
|
|
42
|
-
userData?: Y.PermanentUserData
|
|
43
|
-
|
|
44
|
-
constructor(configuration: TiptapCollabProviderConfiguration) {
|
|
45
|
-
if (!configuration.websocketProvider) {
|
|
46
|
-
configuration.websocketProvider = new TiptapCollabProviderWebsocket({ appId: (configuration as Required<Pick<AdditionalTiptapCollabProviderConfiguration, 'appId'>>).appId, baseUrl: (configuration as Required<Pick<AdditionalTiptapCollabProviderConfiguration, 'baseUrl'>>).baseUrl })
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
if (!configuration.token) {
|
|
50
|
-
configuration.token = 'notoken' // need to send a token anyway (which will be ignored)
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
super(configuration as HocuspocusProviderConfiguration)
|
|
54
|
-
|
|
55
|
-
if (configuration.user) {
|
|
56
|
-
this.userData = new Y.PermanentUserData(this.document, this.document.getMap('__tiptapcollab__users'))
|
|
57
|
-
this.userData.setUserMapping(this.document, this.document.clientID, configuration.user)
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* note: this will only work if your server loaded @hocuspocus-pro/extension-history, or if you are on a Tiptap business plan.
|
|
63
|
-
*/
|
|
64
|
-
createVersion(name?: string) {
|
|
65
|
-
return this.sendStateless(JSON.stringify({ action: 'version.create', name }))
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
* note: this will only work if your server loaded @hocuspocus-pro/extension-history, or if you are on a Tiptap business plan.
|
|
70
|
-
*/
|
|
71
|
-
revertToVersion(targetVersion: number) {
|
|
72
|
-
return this.sendStateless(JSON.stringify({ action: 'document.revert', version: targetVersion }))
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* note: this will only work if your server loaded @hocuspocus-pro/extension-history, or if you are on a Tiptap business plan.
|
|
77
|
-
*
|
|
78
|
-
* The server will reply with a stateless message (THistoryVersionPreviewEvent)
|
|
79
|
-
*/
|
|
80
|
-
previewVersion(targetVersion: number) {
|
|
81
|
-
return this.sendStateless(JSON.stringify({ action: 'version.preview', version: targetVersion }))
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
/**
|
|
85
|
-
* note: this will only work if your server loaded @hocuspocus-pro/extension-history, or if you are on a Tiptap business plan.
|
|
86
|
-
*/
|
|
87
|
-
getVersions(): THistoryVersion[] {
|
|
88
|
-
return this.configuration.document.getArray<THistoryVersion>(`${this.tiptapCollabConfigurationPrefix}versions`).toArray()
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
watchVersions(callback: Parameters<AbstractType<YArrayEvent<THistoryVersion>>['observe']>[0]) {
|
|
92
|
-
return this.configuration.document.getArray<THistoryVersion>('__tiptapcollab__versions').observe(callback)
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
unwatchVersions(callback: Parameters<AbstractType<YArrayEvent<THistoryVersion>>['unobserve']>[0]) {
|
|
96
|
-
return this.configuration.document.getArray<THistoryVersion>('__tiptapcollab__versions').unobserve(callback)
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
isAutoVersioning(): boolean {
|
|
100
|
-
return !!this.configuration.document.getMap<number>(`${this.tiptapCollabConfigurationPrefix}config`).get('autoVersioning')
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
enableAutoVersioning() {
|
|
104
|
-
return this.configuration.document.getMap<number>(`${this.tiptapCollabConfigurationPrefix}config`).set('autoVersioning', 1)
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
disableAutoVersioning() {
|
|
108
|
-
return this.configuration.document.getMap<number>(`${this.tiptapCollabConfigurationPrefix}config`).set('autoVersioning', 0)
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
private getYThreads() {
|
|
112
|
-
return this.configuration.document.getArray<Y.Map<any>>(`${this.tiptapCollabConfigurationPrefix}threads`)
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
getThreads<Data, CommentData>(): TCollabThread<Data, CommentData>[] {
|
|
116
|
-
return this.getYThreads().toJSON() as TCollabThread<Data, CommentData>[]
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
private getThreadIndex(id: string): number | null {
|
|
120
|
-
let index = null
|
|
121
|
-
|
|
122
|
-
let i = 0
|
|
123
|
-
// eslint-disable-next-line no-restricted-syntax
|
|
124
|
-
for (const thread of this.getThreads()) {
|
|
125
|
-
if (thread.id === id) {
|
|
126
|
-
index = i
|
|
127
|
-
break
|
|
128
|
-
}
|
|
129
|
-
i += 1
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
return index
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
getThread<Data, CommentData>(id: string): TCollabThread<Data, CommentData> | null {
|
|
136
|
-
const index = this.getThreadIndex(id)
|
|
137
|
-
|
|
138
|
-
if (index === null) {
|
|
139
|
-
return null
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
return this.getYThreads().get(index).toJSON() as TCollabThread<Data, CommentData>
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
private getYThread(id: string) {
|
|
146
|
-
const index = this.getThreadIndex(id)
|
|
147
|
-
|
|
148
|
-
if (index === null) {
|
|
149
|
-
return null
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
return this.getYThreads().get(index)
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
createThread(data: Omit<TCollabThread, 'id' | 'createdAt' | 'updatedAt' | 'comments'>) {
|
|
156
|
-
let createdThread: TCollabThread = {} as TCollabThread
|
|
157
|
-
|
|
158
|
-
this.document.transact(() => {
|
|
159
|
-
const thread = new Y.Map()
|
|
160
|
-
thread.set('id', uuidv4())
|
|
161
|
-
thread.set('createdAt', (new Date()).toISOString())
|
|
162
|
-
thread.set('comments', new Y.Array())
|
|
163
|
-
|
|
164
|
-
this.getYThreads().push([thread])
|
|
165
|
-
createdThread = this.updateThread(String(thread.get('id')), data)
|
|
166
|
-
})
|
|
167
|
-
|
|
168
|
-
return createdThread
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
updateThread(id: TCollabThread['id'], data: Partial<Pick<TCollabThread, 'data'> & {
|
|
172
|
-
resolvedAt: TCollabThread['resolvedAt'] | null
|
|
173
|
-
}>) {
|
|
174
|
-
let updatedThread: TCollabThread = {} as TCollabThread
|
|
175
|
-
|
|
176
|
-
this.document.transact(() => {
|
|
177
|
-
const thread = this.getYThread(id)
|
|
178
|
-
|
|
179
|
-
if (thread === null) {
|
|
180
|
-
return null
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
thread.set('updatedAt', (new Date()).toISOString())
|
|
184
|
-
|
|
185
|
-
if (data.data) {
|
|
186
|
-
thread.set('data', data.data)
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
if (data.resolvedAt || data.resolvedAt === null) {
|
|
190
|
-
thread.set('resolvedAt', data.resolvedAt)
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
updatedThread = thread.toJSON() as TCollabThread
|
|
194
|
-
})
|
|
195
|
-
|
|
196
|
-
return updatedThread
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
deleteThread(id: TCollabThread['id']) {
|
|
200
|
-
const index = this.getThreadIndex(id)
|
|
201
|
-
|
|
202
|
-
if (index === null) {
|
|
203
|
-
return
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
this.getYThreads().delete(index, 1)
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
getThreadComments(threadId: TCollabThread['id']): TCollabComment[] | null {
|
|
210
|
-
const index = this.getThreadIndex(threadId)
|
|
211
|
-
|
|
212
|
-
if (index === null) {
|
|
213
|
-
return null
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
return this.getThread(threadId)?.comments ?? []
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
getThreadComment(threadId: TCollabThread['id'], commentId: TCollabComment['id']): TCollabComment | null {
|
|
220
|
-
const index = this.getThreadIndex(threadId)
|
|
221
|
-
|
|
222
|
-
if (index === null) {
|
|
223
|
-
return null
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
return this.getThread(threadId)?.comments.find(comment => comment.id === commentId) ?? null
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
addComment(threadId: TCollabThread['id'], data: Omit<TCollabComment, 'id' | 'updatedAt' | 'createdAt'>) {
|
|
230
|
-
let updatedThread: TCollabThread = {} as TCollabThread
|
|
231
|
-
|
|
232
|
-
this.document.transact(() => {
|
|
233
|
-
const thread = this.getYThread(threadId)
|
|
234
|
-
|
|
235
|
-
if (thread === null) return null
|
|
236
|
-
|
|
237
|
-
const commentMap = new Y.Map()
|
|
238
|
-
commentMap.set('id', uuidv4())
|
|
239
|
-
commentMap.set('createdAt', (new Date()).toISOString())
|
|
240
|
-
thread.get('comments').push([commentMap])
|
|
241
|
-
|
|
242
|
-
this.updateComment(threadId, String(commentMap.get('id')), data)
|
|
243
|
-
|
|
244
|
-
updatedThread = thread.toJSON() as TCollabThread
|
|
245
|
-
})
|
|
246
|
-
|
|
247
|
-
return updatedThread
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
updateComment(threadId: TCollabThread['id'], commentId: TCollabComment['id'], data: Partial<Pick<TCollabComment, 'data' | 'content'>>) {
|
|
251
|
-
let updatedThread: TCollabThread = {} as TCollabThread
|
|
252
|
-
|
|
253
|
-
this.document.transact(() => {
|
|
254
|
-
const thread = this.getYThread(threadId)
|
|
255
|
-
|
|
256
|
-
if (thread === null) return null
|
|
257
|
-
|
|
258
|
-
let comment = null
|
|
259
|
-
// eslint-disable-next-line no-restricted-syntax
|
|
260
|
-
for (const c of thread.get('comments')) {
|
|
261
|
-
if (c.get('id') === commentId) {
|
|
262
|
-
comment = c
|
|
263
|
-
break
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
if (comment === null) return null
|
|
268
|
-
|
|
269
|
-
comment.set('updatedAt', (new Date()).toISOString())
|
|
270
|
-
|
|
271
|
-
if (data.data) {
|
|
272
|
-
comment.set('data', data.data)
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
if (data.content) {
|
|
276
|
-
comment.set('content', data.content)
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
updatedThread = thread.toJSON() as TCollabThread
|
|
280
|
-
})
|
|
281
|
-
|
|
282
|
-
return updatedThread
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
deleteComment(threadId: TCollabThread['id'], commentId: TCollabComment['id']) {
|
|
286
|
-
const thread = this.getYThread(threadId)
|
|
287
|
-
|
|
288
|
-
if (thread === null) return null
|
|
289
|
-
|
|
290
|
-
let commentIndex = 0
|
|
291
|
-
// eslint-disable-next-line no-restricted-syntax
|
|
292
|
-
for (const c of thread.get('comments')) {
|
|
293
|
-
if (c.get('id') === commentId) {
|
|
294
|
-
break
|
|
295
|
-
}
|
|
296
|
-
commentIndex += 1
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
// if the first comment of a thread is deleted we also
|
|
300
|
-
// delete the thread itself as the source comment is gone
|
|
301
|
-
if (commentIndex === 0) {
|
|
302
|
-
this.deleteThread(threadId)
|
|
303
|
-
return
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
if (commentIndex > 0) {
|
|
307
|
-
thread.get('comments').delete(commentIndex)
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
return thread.toJSON() as TCollabThread
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
watchThreads(callback: () => void) {
|
|
314
|
-
this.getYThreads().observeDeep(callback)
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
unwatchThreads(callback: () => void) {
|
|
318
|
-
this.getYThreads().unobserveDeep(callback)
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
}
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
CompleteHocuspocusProviderWebsocketConfiguration, HocuspocusProviderWebsocketConfiguration} from './HocuspocusProviderWebsocket.js'
|
|
3
|
-
import {
|
|
4
|
-
HocuspocusProviderWebsocket,
|
|
5
|
-
} from './HocuspocusProviderWebsocket.js'
|
|
6
|
-
|
|
7
|
-
export type TiptapCollabProviderWebsocketConfiguration =
|
|
8
|
-
Partial<CompleteHocuspocusProviderWebsocketConfiguration> &
|
|
9
|
-
AdditionalTiptapCollabProviderWebsocketConfiguration
|
|
10
|
-
|
|
11
|
-
export interface AdditionalTiptapCollabProviderWebsocketConfiguration {
|
|
12
|
-
/**
|
|
13
|
-
* A Hocuspocus Cloud App ID, get one here: https://cloud.tiptap.dev
|
|
14
|
-
*/
|
|
15
|
-
appId?: string,
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* If you are using the on-premise version of TiptapCollab, put your baseUrl here (e.g. https://collab.yourdomain.com)
|
|
19
|
-
*/
|
|
20
|
-
baseUrl?: string
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Only fill this if you are using Tiptap Collab HA.
|
|
24
|
-
*/
|
|
25
|
-
shardKey?: string
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export class TiptapCollabProviderWebsocket extends HocuspocusProviderWebsocket {
|
|
29
|
-
constructor(configuration: TiptapCollabProviderWebsocketConfiguration) {
|
|
30
|
-
let url = configuration.baseUrl ?? `wss://${configuration.appId}.collab.tiptap.cloud`
|
|
31
|
-
|
|
32
|
-
if (configuration.shardKey) {
|
|
33
|
-
url += url.includes('?') ? '&' : '?'
|
|
34
|
-
url += `shard=${configuration.shardKey}`
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
super({ ...configuration as HocuspocusProviderWebsocketConfiguration, url })
|
|
38
|
-
}
|
|
39
|
-
}
|