document-drive 1.0.0-websockets → 1.0.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/README.md +1 -0
- package/package.json +74 -88
- package/src/cache/index.ts +2 -2
- package/src/cache/memory.ts +22 -13
- package/src/cache/redis.ts +43 -16
- package/src/cache/types.ts +4 -4
- package/src/index.ts +6 -3
- package/src/queue/base.ts +276 -214
- package/src/queue/index.ts +2 -2
- package/src/queue/redis.ts +138 -127
- package/src/queue/types.ts +44 -38
- package/src/read-mode/errors.ts +19 -0
- package/src/read-mode/index.ts +125 -0
- package/src/read-mode/service.ts +207 -0
- package/src/read-mode/types.ts +108 -0
- package/src/server/error.ts +61 -26
- package/src/server/index.ts +2160 -1785
- package/src/server/listener/index.ts +2 -2
- package/src/server/listener/manager.ts +475 -437
- package/src/server/listener/transmitter/index.ts +4 -5
- package/src/server/listener/transmitter/internal.ts +77 -79
- package/src/server/listener/transmitter/pull-responder.ts +363 -329
- package/src/server/listener/transmitter/switchboard-push.ts +72 -55
- package/src/server/listener/transmitter/types.ts +19 -25
- package/src/server/types.ts +536 -349
- package/src/server/utils.ts +26 -27
- package/src/storage/base.ts +81 -0
- package/src/storage/browser.ts +233 -216
- package/src/storage/filesystem.ts +257 -256
- package/src/storage/index.ts +2 -1
- package/src/storage/memory.ts +206 -214
- package/src/storage/prisma.ts +575 -568
- package/src/storage/sequelize.ts +460 -471
- package/src/storage/types.ts +83 -67
- package/src/utils/default-drives-manager.ts +341 -0
- package/src/utils/document-helpers.ts +19 -18
- package/src/utils/graphql.ts +288 -34
- package/src/utils/index.ts +61 -59
- package/src/utils/logger.ts +39 -37
- package/src/utils/migrations.ts +58 -0
- package/src/utils/run-asap.ts +156 -0
- package/CHANGELOG.md +0 -818
- package/src/server/listener/transmitter/subscription.ts +0 -364
package/src/storage/memory.ts
CHANGED
|
@@ -1,219 +1,211 @@
|
|
|
1
|
-
import { DocumentDriveAction } from
|
|
1
|
+
import { DocumentDriveAction } from "document-model-libs/document-drive";
|
|
2
2
|
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
} from
|
|
9
|
-
import {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
} from './types';
|
|
14
|
-
import type { SynchronizationUnitQuery } from '../server/types';
|
|
15
|
-
import { mergeOperations } from '../utils';
|
|
3
|
+
BaseAction,
|
|
4
|
+
Document,
|
|
5
|
+
DocumentHeader,
|
|
6
|
+
Operation,
|
|
7
|
+
OperationScope,
|
|
8
|
+
} from "document-model/document";
|
|
9
|
+
import { DriveNotFoundError } from "../server/error";
|
|
10
|
+
import type { SynchronizationUnitQuery } from "../server/types";
|
|
11
|
+
import { mergeOperations } from "../utils";
|
|
12
|
+
import { DocumentDriveStorage, DocumentStorage, IDriveStorage } from "./types";
|
|
16
13
|
|
|
17
14
|
export class MemoryStorage implements IDriveStorage {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
15
|
+
private documents: Record<string, Record<string, DocumentStorage>>;
|
|
16
|
+
private drives: Record<string, DocumentDriveStorage>;
|
|
17
|
+
private slugToDriveId: Record<string, string> = {};
|
|
18
|
+
|
|
19
|
+
constructor() {
|
|
20
|
+
this.documents = {};
|
|
21
|
+
this.drives = {};
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
checkDocumentExists(drive: string, id: string): Promise<boolean> {
|
|
25
|
+
return Promise.resolve(this.documents[drive]?.[id] !== undefined);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async getDocuments(drive: string) {
|
|
29
|
+
return Object.keys(this.documents[drive] ?? {});
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
async getDocument(driveId: string, id: string) {
|
|
33
|
+
const drive = this.documents[driveId];
|
|
34
|
+
if (!drive) {
|
|
35
|
+
throw new DriveNotFoundError(driveId);
|
|
36
|
+
}
|
|
37
|
+
const document = drive[id];
|
|
38
|
+
if (!document) {
|
|
39
|
+
throw new Error(`Document with id ${id} not found`);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return document;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async saveDocument(drive: string, id: string, document: Document) {
|
|
46
|
+
this.documents[drive] = this.documents[drive] ?? {};
|
|
47
|
+
this.documents[drive][id] = document;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
async clearStorage(): Promise<void> {
|
|
51
|
+
this.documents = {};
|
|
52
|
+
this.drives = {};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
async createDocument(drive: string, id: string, document: DocumentStorage) {
|
|
56
|
+
this.documents[drive] = this.documents[drive] ?? {};
|
|
57
|
+
const {
|
|
58
|
+
operations,
|
|
59
|
+
initialState,
|
|
60
|
+
name,
|
|
61
|
+
revision,
|
|
62
|
+
documentType,
|
|
63
|
+
created,
|
|
64
|
+
lastModified,
|
|
65
|
+
clipboard,
|
|
66
|
+
state,
|
|
67
|
+
} = document;
|
|
68
|
+
this.documents[drive][id] = {
|
|
69
|
+
operations,
|
|
70
|
+
initialState,
|
|
71
|
+
name,
|
|
72
|
+
revision,
|
|
73
|
+
documentType,
|
|
74
|
+
created,
|
|
75
|
+
lastModified,
|
|
76
|
+
clipboard,
|
|
77
|
+
state,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
async addDocumentOperations(
|
|
82
|
+
drive: string,
|
|
83
|
+
id: string,
|
|
84
|
+
operations: Operation[],
|
|
85
|
+
header: DocumentHeader,
|
|
86
|
+
): Promise<void> {
|
|
87
|
+
const document = await this.getDocument(drive, id);
|
|
88
|
+
if (!document) {
|
|
89
|
+
throw new Error(`Document with id ${id} not found`);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const mergedOperations = mergeOperations(document.operations, operations);
|
|
93
|
+
|
|
94
|
+
this.documents[drive]![id] = {
|
|
95
|
+
...document,
|
|
96
|
+
...header,
|
|
97
|
+
operations: mergedOperations,
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
async deleteDocument(drive: string, id: string) {
|
|
102
|
+
if (!this.documents[drive]) {
|
|
103
|
+
throw new DriveNotFoundError(drive);
|
|
104
|
+
}
|
|
105
|
+
delete this.documents[drive][id];
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
async getDrives() {
|
|
109
|
+
return Object.keys(this.drives);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
async getDrive(id: string) {
|
|
113
|
+
const drive = this.drives[id];
|
|
114
|
+
if (!drive) {
|
|
115
|
+
throw new DriveNotFoundError(id);
|
|
116
|
+
}
|
|
117
|
+
return drive;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
async getDriveBySlug(slug: string) {
|
|
121
|
+
const driveId = this.slugToDriveId[slug];
|
|
122
|
+
if (!driveId) {
|
|
123
|
+
throw new Error(`Drive with slug ${slug} not found`);
|
|
124
|
+
}
|
|
125
|
+
return this.getDrive(driveId);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
async createDrive(id: string, drive: DocumentDriveStorage) {
|
|
129
|
+
this.drives[id] = drive;
|
|
130
|
+
this.documents[id] = {};
|
|
131
|
+
const { slug } = drive.initialState.state.global;
|
|
132
|
+
if (slug) {
|
|
133
|
+
this.slugToDriveId[slug] = id;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
async addDriveOperations(
|
|
138
|
+
id: string,
|
|
139
|
+
operations: Operation<DocumentDriveAction | BaseAction>[],
|
|
140
|
+
header: DocumentHeader,
|
|
141
|
+
): Promise<void> {
|
|
142
|
+
const drive = await this.getDrive(id);
|
|
143
|
+
const mergedOperations = mergeOperations(drive.operations, operations);
|
|
144
|
+
|
|
145
|
+
this.drives[id] = {
|
|
146
|
+
...drive,
|
|
147
|
+
...header,
|
|
148
|
+
operations: mergedOperations,
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
async deleteDrive(id: string) {
|
|
153
|
+
delete this.documents[id];
|
|
154
|
+
delete this.drives[id];
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
async getSynchronizationUnitsRevision(
|
|
158
|
+
units: SynchronizationUnitQuery[],
|
|
159
|
+
): Promise<
|
|
160
|
+
{
|
|
161
|
+
driveId: string;
|
|
162
|
+
documentId: string;
|
|
163
|
+
scope: string;
|
|
164
|
+
branch: string;
|
|
165
|
+
lastUpdated: string;
|
|
166
|
+
revision: number;
|
|
167
|
+
}[]
|
|
168
|
+
> {
|
|
169
|
+
const results = await Promise.allSettled(
|
|
170
|
+
units.map(async (unit) => {
|
|
171
|
+
try {
|
|
172
|
+
const document = await (unit.documentId
|
|
173
|
+
? this.getDocument(unit.driveId, unit.documentId)
|
|
174
|
+
: this.getDrive(unit.driveId));
|
|
175
|
+
if (!document) {
|
|
176
|
+
return undefined;
|
|
177
|
+
}
|
|
178
|
+
const operation =
|
|
179
|
+
document.operations[unit.scope as OperationScope].at(-1);
|
|
180
|
+
if (operation) {
|
|
181
|
+
return {
|
|
182
|
+
driveId: unit.driveId,
|
|
183
|
+
documentId: unit.documentId,
|
|
184
|
+
scope: unit.scope,
|
|
185
|
+
branch: unit.branch,
|
|
186
|
+
lastUpdated: operation.timestamp,
|
|
187
|
+
revision: operation.index,
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
} catch {
|
|
191
|
+
return undefined;
|
|
130
192
|
}
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
)
|
|
148
|
-
|
|
149
|
-
const mergedOperations = mergeOperations(drive.operations, operations);
|
|
150
|
-
|
|
151
|
-
this.drives[id] = {
|
|
152
|
-
...drive,
|
|
153
|
-
...header,
|
|
154
|
-
operations: mergedOperations
|
|
155
|
-
};
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
async deleteDrive(id: string) {
|
|
159
|
-
delete this.documents[id];
|
|
160
|
-
delete this.drives[id];
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
async getSynchronizationUnitsRevision(
|
|
164
|
-
units: SynchronizationUnitQuery[]
|
|
165
|
-
): Promise<
|
|
166
|
-
{
|
|
167
|
-
driveId: string;
|
|
168
|
-
documentId: string;
|
|
169
|
-
scope: string;
|
|
170
|
-
branch: string;
|
|
171
|
-
lastUpdated: string;
|
|
172
|
-
revision: number;
|
|
173
|
-
}[]
|
|
174
|
-
> {
|
|
175
|
-
const results = await Promise.allSettled(
|
|
176
|
-
units.map(async unit => {
|
|
177
|
-
try {
|
|
178
|
-
const document = await (unit.documentId
|
|
179
|
-
? this.getDocument(unit.driveId, unit.documentId)
|
|
180
|
-
: this.getDrive(unit.driveId));
|
|
181
|
-
if (!document) {
|
|
182
|
-
return undefined;
|
|
183
|
-
}
|
|
184
|
-
const operation =
|
|
185
|
-
document.operations[unit.scope as OperationScope]?.at(
|
|
186
|
-
-1
|
|
187
|
-
);
|
|
188
|
-
if (operation) {
|
|
189
|
-
return {
|
|
190
|
-
driveId: unit.driveId,
|
|
191
|
-
documentId: unit.documentId,
|
|
192
|
-
scope: unit.scope,
|
|
193
|
-
branch: unit.branch,
|
|
194
|
-
lastUpdated: operation.timestamp,
|
|
195
|
-
revision: operation.index
|
|
196
|
-
};
|
|
197
|
-
}
|
|
198
|
-
} catch {
|
|
199
|
-
return undefined;
|
|
200
|
-
}
|
|
201
|
-
})
|
|
202
|
-
);
|
|
203
|
-
return results.reduce<
|
|
204
|
-
{
|
|
205
|
-
driveId: string;
|
|
206
|
-
documentId: string;
|
|
207
|
-
scope: string;
|
|
208
|
-
branch: string;
|
|
209
|
-
lastUpdated: string;
|
|
210
|
-
revision: number;
|
|
211
|
-
}[]
|
|
212
|
-
>((acc, curr) => {
|
|
213
|
-
if (curr.status === 'fulfilled' && curr.value !== undefined) {
|
|
214
|
-
acc.push(curr.value);
|
|
215
|
-
}
|
|
216
|
-
return acc;
|
|
217
|
-
}, []);
|
|
218
|
-
}
|
|
193
|
+
}),
|
|
194
|
+
);
|
|
195
|
+
return results.reduce<
|
|
196
|
+
{
|
|
197
|
+
driveId: string;
|
|
198
|
+
documentId: string;
|
|
199
|
+
scope: string;
|
|
200
|
+
branch: string;
|
|
201
|
+
lastUpdated: string;
|
|
202
|
+
revision: number;
|
|
203
|
+
}[]
|
|
204
|
+
>((acc, curr) => {
|
|
205
|
+
if (curr.status === "fulfilled" && curr.value !== undefined) {
|
|
206
|
+
acc.push(curr.value);
|
|
207
|
+
}
|
|
208
|
+
return acc;
|
|
209
|
+
}, []);
|
|
210
|
+
}
|
|
219
211
|
}
|