document-drive 1.0.0-websockets.1 → 1.0.1

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 (43) hide show
  1. package/README.md +1 -0
  2. package/package.json +74 -88
  3. package/src/cache/index.ts +2 -2
  4. package/src/cache/memory.ts +22 -13
  5. package/src/cache/redis.ts +43 -16
  6. package/src/cache/types.ts +4 -4
  7. package/src/index.ts +6 -3
  8. package/src/queue/base.ts +276 -214
  9. package/src/queue/index.ts +2 -2
  10. package/src/queue/redis.ts +138 -127
  11. package/src/queue/types.ts +44 -38
  12. package/src/read-mode/errors.ts +19 -0
  13. package/src/read-mode/index.ts +125 -0
  14. package/src/read-mode/service.ts +207 -0
  15. package/src/read-mode/types.ts +108 -0
  16. package/src/server/error.ts +61 -26
  17. package/src/server/index.ts +2160 -1785
  18. package/src/server/listener/index.ts +2 -2
  19. package/src/server/listener/manager.ts +475 -437
  20. package/src/server/listener/transmitter/index.ts +4 -5
  21. package/src/server/listener/transmitter/internal.ts +77 -79
  22. package/src/server/listener/transmitter/pull-responder.ts +363 -329
  23. package/src/server/listener/transmitter/switchboard-push.ts +72 -55
  24. package/src/server/listener/transmitter/types.ts +19 -25
  25. package/src/server/types.ts +536 -349
  26. package/src/server/utils.ts +26 -27
  27. package/src/storage/base.ts +81 -0
  28. package/src/storage/browser.ts +233 -216
  29. package/src/storage/filesystem.ts +257 -256
  30. package/src/storage/index.ts +2 -1
  31. package/src/storage/memory.ts +206 -214
  32. package/src/storage/prisma.ts +575 -568
  33. package/src/storage/sequelize.ts +460 -471
  34. package/src/storage/types.ts +83 -67
  35. package/src/utils/default-drives-manager.ts +341 -0
  36. package/src/utils/document-helpers.ts +19 -18
  37. package/src/utils/graphql.ts +288 -34
  38. package/src/utils/index.ts +61 -59
  39. package/src/utils/logger.ts +39 -37
  40. package/src/utils/migrations.ts +58 -0
  41. package/src/utils/run-asap.ts +156 -0
  42. package/CHANGELOG.md +0 -818
  43. package/src/server/listener/transmitter/subscription.ts +0 -364
@@ -1,509 +1,498 @@
1
1
  import {
2
- DocumentDriveLocalState,
3
- DocumentDriveState
4
- } from 'document-model-libs/document-drive';
2
+ DocumentDriveLocalState,
3
+ DocumentDriveState,
4
+ } from "document-model-libs/document-drive";
5
5
  import {
6
- AttachmentInput,
7
- DocumentHeader,
8
- ExtendedState,
9
- Operation,
10
- OperationScope
11
- } from 'document-model/document';
12
- import { DataTypes, Options, Sequelize } from 'sequelize';
13
- import { DocumentDriveStorage, DocumentStorage, IDriveStorage } from './types';
14
- import type { SynchronizationUnitQuery } from '../server/types';
6
+ AttachmentInput,
7
+ DocumentHeader,
8
+ ExtendedState,
9
+ Operation,
10
+ OperationScope,
11
+ } from "document-model/document";
12
+ import { DataTypes, Options, Sequelize } from "sequelize";
13
+ import type { SynchronizationUnitQuery } from "../server/types";
14
+ import { DocumentDriveStorage, DocumentStorage, IDriveStorage } from "./types";
15
15
 
16
16
  export class SequelizeStorage implements IDriveStorage {
17
- private db: Sequelize;
18
-
19
- constructor(options: Options) {
20
- this.db = new Sequelize(options);
17
+ private db: Sequelize;
18
+
19
+ constructor(options: Options) {
20
+ this.db = new Sequelize(options);
21
+ }
22
+
23
+ public syncModels() {
24
+ const Drive = this.db.define("drive", {
25
+ slug: {
26
+ type: DataTypes.STRING,
27
+ primaryKey: true,
28
+ },
29
+ id: DataTypes.STRING,
30
+ });
31
+ const Document = this.db.define("document", {
32
+ id: {
33
+ type: DataTypes.STRING,
34
+ primaryKey: true,
35
+ },
36
+ driveId: {
37
+ type: DataTypes.STRING,
38
+ primaryKey: true,
39
+ },
40
+ name: DataTypes.STRING,
41
+ documentType: DataTypes.STRING,
42
+ initialState: DataTypes.JSON,
43
+ lastModified: DataTypes.DATE,
44
+ revision: DataTypes.JSON,
45
+ });
46
+
47
+ const Operation = this.db.define("operation", {
48
+ driveId: {
49
+ type: DataTypes.STRING,
50
+ primaryKey: true,
51
+ unique: "unique_operation",
52
+ },
53
+ documentId: {
54
+ type: DataTypes.STRING,
55
+ primaryKey: true,
56
+ unique: "unique_operation",
57
+ },
58
+ hash: DataTypes.STRING,
59
+ index: {
60
+ type: DataTypes.INTEGER,
61
+ primaryKey: true,
62
+ unique: "unique_operation",
63
+ },
64
+ input: DataTypes.JSON,
65
+ timestamp: DataTypes.DATE,
66
+ type: DataTypes.STRING,
67
+ scope: {
68
+ type: DataTypes.STRING,
69
+ primaryKey: true,
70
+ unique: "unique_operation",
71
+ },
72
+ branch: {
73
+ type: DataTypes.STRING,
74
+ primaryKey: true,
75
+ unique: "unique_operation",
76
+ },
77
+ skip: {
78
+ type: DataTypes.INTEGER,
79
+ defaultValue: 0,
80
+ },
81
+ });
82
+
83
+ const Attachment = this.db.define("attachment", {
84
+ driveId: {
85
+ type: DataTypes.STRING,
86
+ primaryKey: true,
87
+ },
88
+ documentId: {
89
+ type: DataTypes.STRING,
90
+ primaryKey: true,
91
+ },
92
+ scope: {
93
+ type: DataTypes.STRING,
94
+ primaryKey: true,
95
+ },
96
+ branch: {
97
+ type: DataTypes.STRING,
98
+ primaryKey: true,
99
+ },
100
+ index: {
101
+ type: DataTypes.STRING,
102
+ primaryKey: true,
103
+ },
104
+ hash: {
105
+ type: DataTypes.STRING,
106
+ primaryKey: true,
107
+ },
108
+ mimeType: DataTypes.STRING,
109
+ fileName: DataTypes.STRING,
110
+ extension: DataTypes.STRING,
111
+ data: DataTypes.BLOB,
112
+ });
113
+
114
+ Operation.hasMany(Attachment, {
115
+ onDelete: "CASCADE",
116
+ });
117
+ Attachment.belongsTo(Operation);
118
+ Document.hasMany(Operation, {
119
+ onDelete: "CASCADE",
120
+ });
121
+ Operation.belongsTo(Document);
122
+
123
+ return this.db.sync({ force: true });
124
+ }
125
+
126
+ async createDrive(id: string, drive: DocumentDriveStorage): Promise<void> {
127
+ await this.createDocument("drives", id, drive as DocumentStorage);
128
+ const Drive = this.db.models.drive;
129
+ await Drive?.upsert({ id, slug: drive.initialState.state.global.slug });
130
+ }
131
+ async addDriveOperations(
132
+ id: string,
133
+ operations: Operation[],
134
+ header: DocumentHeader,
135
+ ): Promise<void> {
136
+ await this.addDocumentOperations("drives", id, operations, header);
137
+ }
138
+ async createDocument(
139
+ drive: string,
140
+ id: string,
141
+ document: DocumentStorage,
142
+ ): Promise<void> {
143
+ const Document = this.db.models.document;
144
+
145
+ if (!Document) {
146
+ throw new Error("Document model not found");
21
147
  }
22
148
 
23
- public syncModels() {
24
- const Drive = this.db.define('drive', {
25
- slug: {
26
- type: DataTypes.STRING,
27
- primaryKey: true
28
- },
29
- id: DataTypes.STRING
30
- });
31
- const Document = this.db.define('document', {
32
- id: {
33
- type: DataTypes.STRING,
34
- primaryKey: true
35
- },
36
- driveId: {
37
- type: DataTypes.STRING,
38
- primaryKey: true
39
- },
40
- name: DataTypes.STRING,
41
- documentType: DataTypes.STRING,
42
- initialState: DataTypes.JSON,
43
- lastModified: DataTypes.DATE,
44
- revision: DataTypes.JSON
45
- });
46
-
47
- const Operation = this.db.define('operation', {
48
- driveId: {
49
- type: DataTypes.STRING,
50
- primaryKey: true,
51
- unique: 'unique_operation'
52
- },
53
- documentId: {
54
- type: DataTypes.STRING,
55
- primaryKey: true,
56
- unique: 'unique_operation'
57
- },
58
- hash: DataTypes.STRING,
59
- index: {
60
- type: DataTypes.INTEGER,
61
- primaryKey: true,
62
- unique: 'unique_operation'
63
- },
64
- input: DataTypes.JSON,
65
- timestamp: DataTypes.DATE,
66
- type: DataTypes.STRING,
67
- scope: {
68
- type: DataTypes.STRING,
69
- primaryKey: true,
70
- unique: 'unique_operation'
71
- },
72
- branch: {
73
- type: DataTypes.STRING,
74
- primaryKey: true,
75
- unique: 'unique_operation'
76
- }
77
- });
78
-
79
- const Attachment = this.db.define('attachment', {
80
- driveId: {
81
- type: DataTypes.STRING,
82
- primaryKey: true
83
- },
84
- documentId: {
85
- type: DataTypes.STRING,
86
- primaryKey: true
87
- },
88
- scope: {
89
- type: DataTypes.STRING,
90
- primaryKey: true
91
- },
92
- branch: {
93
- type: DataTypes.STRING,
94
- primaryKey: true
95
- },
96
- index: {
97
- type: DataTypes.STRING,
98
- primaryKey: true
99
- },
100
- hash: {
101
- type: DataTypes.STRING,
102
- primaryKey: true
103
- },
104
- mimeType: DataTypes.STRING,
105
- fileName: DataTypes.STRING,
106
- extension: DataTypes.STRING,
107
- data: DataTypes.BLOB
108
- });
109
-
110
- Operation.hasMany(Attachment, {
111
- onDelete: 'CASCADE'
112
- });
113
- Attachment.belongsTo(Operation);
114
- Document.hasMany(Operation, {
115
- onDelete: 'CASCADE'
116
- });
117
- Operation.belongsTo(Document);
118
-
119
- return this.db.sync({ force: true });
149
+ await Document.create({
150
+ id: id,
151
+ driveId: drive,
152
+ name: document.name,
153
+ documentType: document.documentType,
154
+ initialState: document.initialState,
155
+ lastModified: document.lastModified,
156
+ revision: document.revision,
157
+ });
158
+ }
159
+ async addDocumentOperations(
160
+ drive: string,
161
+ id: string,
162
+ operations: Operation[],
163
+ header: DocumentHeader,
164
+ ): Promise<void> {
165
+ const document = await this.getDocument(drive, id);
166
+ if (!document) {
167
+ throw new Error(`Document with id ${id} not found`);
120
168
  }
121
169
 
122
- async createDrive(id: string, drive: DocumentDriveStorage): Promise<void> {
123
- await this.createDocument('drives', id, drive as DocumentStorage);
124
- const Drive = this.db.models.drive;
125
- await Drive?.upsert({ id, slug: drive.initialState.state.global.slug });
126
- }
127
- async addDriveOperations(
128
- id: string,
129
- operations: Operation[],
130
- header: DocumentHeader
131
- ): Promise<void> {
132
- await this.addDocumentOperations('drives', id, operations, header);
170
+ const Operation = this.db.models.operation;
171
+ if (!Operation) {
172
+ throw new Error("Operation model not found");
133
173
  }
134
- async createDocument(
135
- drive: string,
136
- id: string,
137
- document: DocumentStorage
138
- ): Promise<void> {
139
- const Document = this.db.models.document;
140
-
141
- if (!Document) {
142
- throw new Error('Document model not found');
143
- }
144
174
 
145
- await Document.create({
146
- id: id,
175
+ await Operation.bulkCreate(
176
+ operations.map((op) => ({
177
+ driveId: drive,
178
+ documentId: id,
179
+ hash: op.hash,
180
+ index: op.index,
181
+ input: op.input,
182
+ timestamp: op.timestamp,
183
+ type: op.type,
184
+ scope: op.scope,
185
+ branch: "main",
186
+ opId: op.id,
187
+ })),
188
+ );
189
+
190
+ const attachments = operations.reduce<AttachmentInput[]>((acc, op) => {
191
+ if (op.attachments?.length) {
192
+ return acc.concat(
193
+ op.attachments.map((attachment) => ({
147
194
  driveId: drive,
148
- name: document.name,
149
- documentType: document.documentType,
150
- initialState: document.initialState,
151
- lastModified: document.lastModified,
152
- revision: document.revision
153
- });
154
- }
155
- async addDocumentOperations(
156
- drive: string,
157
- id: string,
158
- operations: Operation[],
159
- header: DocumentHeader
160
- ): Promise<void> {
161
- const document = await this.getDocument(drive, id);
162
- if (!document) {
163
- throw new Error(`Document with id ${id} not found`);
164
- }
165
-
166
- const Operation = this.db.models.operation;
167
- if (!Operation) {
168
- throw new Error('Operation model not found');
169
- }
170
-
171
- await Operation.bulkCreate(
172
- operations.map(op => ({
173
- driveId: drive,
174
- documentId: id,
175
- hash: op.hash,
176
- index: op.index,
177
- input: op.input,
178
- timestamp: op.timestamp,
179
- type: op.type,
180
- scope: op.scope,
181
- branch: 'main',
182
- opId: op.id
183
- }))
184
- );
185
-
186
- const attachments = operations.reduce<AttachmentInput[]>((acc, op) => {
187
- if (op.attachments?.length) {
188
- return acc.concat(
189
- op.attachments.map(attachment => ({
190
- driveId: drive,
191
- documentId: id,
192
- scope: op.scope,
193
- branch: 'main',
194
- index: op.index,
195
- mimeType: attachment.mimeType,
196
- fileName: attachment.fileName,
197
- extension: attachment.extension,
198
- data: attachment.data,
199
- hash: attachment.hash
200
- }))
201
- );
202
- }
203
- return acc;
204
- }, []);
205
- if (attachments.length) {
206
- const Attachment = this.db.models.attachment;
207
- if (!Attachment) {
208
- throw new Error('Attachment model not found');
209
- }
210
-
211
- await Attachment.bulkCreate(attachments);
212
- }
213
-
214
- const Document = this.db.models.document;
215
- if (!Document) {
216
- throw new Error('Document model not found');
217
- }
218
-
219
- await Document.update(
220
- {
221
- lastModified: header.lastModified,
222
- revision: header.revision
223
- },
224
- {
225
- where: {
226
- id: id,
227
- driveId: drive
228
- }
229
- }
195
+ documentId: id,
196
+ scope: op.scope,
197
+ branch: "main",
198
+ index: op.index,
199
+ mimeType: attachment.mimeType,
200
+ fileName: attachment.fileName,
201
+ extension: attachment.extension,
202
+ data: attachment.data,
203
+ hash: attachment.hash,
204
+ })),
230
205
  );
206
+ }
207
+ return acc;
208
+ }, []);
209
+ if (attachments.length) {
210
+ const Attachment = this.db.models.attachment;
211
+ if (!Attachment) {
212
+ throw new Error("Attachment model not found");
213
+ }
214
+
215
+ await Attachment.bulkCreate(attachments);
231
216
  }
232
217
 
233
- async _addDocumentOperationAttachments(
234
- driveId: string,
235
- documentId: string,
236
- operation: Operation,
237
- attachments: AttachmentInput[]
238
- ) {
239
- const Attachment = this.db.models.attachment;
240
- if (!Attachment) {
241
- throw new Error('Attachment model not found');
242
- }
243
-
244
- return Attachment.bulkCreate(
245
- attachments.map(attachment => ({
246
- driveId: driveId,
247
- documentId: documentId,
248
- scope: operation.scope,
249
- branch: 'main',
250
- index: operation.index,
251
- mimeType: attachment.mimeType,
252
- fileName: attachment.fileName,
253
- extension: attachment.extension,
254
- data: attachment.data,
255
- hash: attachment.hash
256
- }))
257
- );
218
+ const Document = this.db.models.document;
219
+ if (!Document) {
220
+ throw new Error("Document model not found");
258
221
  }
259
222
 
260
- async getDocuments(drive: string) {
261
- const Document = this.db.models.document;
262
- if (!Document) {
263
- throw new Error('Document model not found');
264
- }
265
-
266
- const result = await Document.findAll({
267
- attributes: ['id'],
268
- where: {
269
- driveId: drive
270
- }
271
- });
272
-
273
- const ids = result.map((e: { dataValues: { id: string } }) => {
274
- const { id } = e.dataValues;
275
- return id;
276
- });
277
- return ids;
223
+ await Document.update(
224
+ {
225
+ lastModified: header.lastModified,
226
+ revision: header.revision,
227
+ },
228
+ {
229
+ where: {
230
+ id: id,
231
+ driveId: drive,
232
+ },
233
+ },
234
+ );
235
+ }
236
+
237
+ async _addDocumentOperationAttachments(
238
+ driveId: string,
239
+ documentId: string,
240
+ operation: Operation,
241
+ attachments: AttachmentInput[],
242
+ ) {
243
+ const Attachment = this.db.models.attachment;
244
+ if (!Attachment) {
245
+ throw new Error("Attachment model not found");
278
246
  }
279
247
 
280
- async checkDocumentExists(driveId: string, id: string): Promise<boolean> {
281
- const Document = this.db.models.document;
282
- if (!Document) {
283
- throw new Error('Document model not found');
284
- }
285
- const count = await Document.count({
286
- where: {
287
- id: id,
288
- driveId: driveId
289
- }
290
- });
291
-
292
- return count > 0;
248
+ return Attachment.bulkCreate(
249
+ attachments.map((attachment) => ({
250
+ driveId: driveId,
251
+ documentId: documentId,
252
+ scope: operation.scope,
253
+ branch: "main",
254
+ index: operation.index,
255
+ mimeType: attachment.mimeType,
256
+ fileName: attachment.fileName,
257
+ extension: attachment.extension,
258
+ data: attachment.data,
259
+ hash: attachment.hash,
260
+ })),
261
+ );
262
+ }
263
+
264
+ async getDocuments(drive: string) {
265
+ const Document = this.db.models.document;
266
+ if (!Document) {
267
+ throw new Error("Document model not found");
293
268
  }
294
269
 
295
- async getDocument(driveId: string, id: string) {
296
- const Document = this.db.models.document;
297
- if (!Document) {
298
- throw new Error('Document model not found');
299
- }
300
-
301
- const entry = await Document.findOne({
302
- where: {
303
- id: id,
304
- driveId: driveId
305
- },
306
- include: [
307
- {
308
- model: this.db.models.operation,
309
- as: 'operations'
310
- }
311
- ]
312
- });
313
-
314
- if (entry === null) {
315
- throw new Error(`Document with id ${id} not found`);
316
- }
317
-
318
- // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
319
- const document: {
320
- operations: [
321
- {
322
- hash: string;
323
- index: number;
324
- timestamp: Date;
325
- input: JSON;
326
- type: string;
327
- scope: string;
328
- opId?: string;
329
- }
330
- ];
331
- revision: Required<Record<OperationScope, number>>;
332
- createdAt: Date;
333
- name: string;
334
- updatedAt: Date;
335
- documentType: string;
336
- initialState: ExtendedState<
337
- DocumentDriveState,
338
- DocumentDriveLocalState
339
- >;
340
- } = entry.dataValues;
341
- const Operation = this.db.models.operation;
342
- if (!Operation) {
343
- throw new Error('Operation model not found');
344
- }
345
-
346
- const operations = document.operations.map(
347
- (op: {
348
- hash: string;
349
- index: number;
350
- timestamp: Date;
351
- input: JSON;
352
- type: string;
353
- scope: string;
354
- opId?: string;
355
- }) => ({
356
- hash: op.hash,
357
- index: op.index,
358
- timestamp: new Date(op.timestamp).toISOString(),
359
- input: op.input,
360
- type: op.type,
361
- scope: op.scope as OperationScope,
362
- id: op.opId
363
- // attachments: fileRegistry
364
- })
365
- );
366
-
367
- const doc = {
368
- created: document.createdAt.toISOString(),
369
- name: document.name ? document.name : '',
370
- documentType: document.documentType,
371
- initialState: document.initialState,
372
- lastModified: document.updatedAt.toISOString(),
373
- operations: {
374
- global: operations.filter(
375
- (op: Operation) => op.scope === 'global'
376
- ),
377
- local: operations.filter(
378
- (op: Operation) => op.scope === 'local'
379
- )
380
- },
381
- revision: document.revision
382
- };
383
-
384
- return doc;
270
+ const result = await Document.findAll({
271
+ attributes: ["id"],
272
+ where: {
273
+ driveId: drive,
274
+ },
275
+ });
276
+
277
+ const ids = result.map((e: { dataValues: { id: string } }) => {
278
+ const { id } = e.dataValues;
279
+ return id;
280
+ });
281
+ return ids;
282
+ }
283
+
284
+ async checkDocumentExists(driveId: string, id: string): Promise<boolean> {
285
+ const Document = this.db.models.document;
286
+ if (!Document) {
287
+ throw new Error("Document model not found");
385
288
  }
386
-
387
- async deleteDocument(drive: string, id: string) {
388
- const Document = this.db.models.document;
389
- if (!Document) {
390
- throw new Error('Document model not found');
391
- }
392
-
393
- await Document.destroy({
394
- where: {
395
- id: id,
396
- driveId: drive
397
- }
398
- });
289
+ const count = await Document.count({
290
+ where: {
291
+ id: id,
292
+ driveId: driveId,
293
+ },
294
+ });
295
+
296
+ return count > 0;
297
+ }
298
+
299
+ // @ts-expect-error TODO: fix as this should not be undefined
300
+ async getDocument(driveId: string, id: string) {
301
+ const Document = this.db.models.document;
302
+ if (!Document) {
303
+ throw new Error("Document model not found");
399
304
  }
400
305
 
401
- async getDrives() {
402
- return this.getDocuments('drives');
306
+ const entry = await Document.findOne({
307
+ where: {
308
+ id: id,
309
+ driveId: driveId,
310
+ },
311
+ include: [
312
+ {
313
+ model: this.db.models.operation,
314
+ as: "operations",
315
+ },
316
+ ],
317
+ });
318
+
319
+ if (entry === null) {
320
+ throw new Error(`Document with id ${id} not found`);
403
321
  }
404
322
 
405
- async getDrive(id: string) {
406
- const doc = await this.getDocument('drives', id);
407
- return doc as DocumentDriveStorage;
323
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
324
+ const document: {
325
+ operations: [
326
+ {
327
+ hash: string;
328
+ index: number;
329
+ timestamp: Date;
330
+ input: JSON;
331
+ type: string;
332
+ scope: string;
333
+ opId?: string;
334
+ skip: number;
335
+ },
336
+ ];
337
+ revision: Required<Record<OperationScope, number>>;
338
+ createdAt: Date;
339
+ name: string;
340
+ updatedAt: Date;
341
+ documentType: string;
342
+ initialState: ExtendedState<DocumentDriveState, DocumentDriveLocalState>;
343
+ } = entry.dataValues;
344
+ const Operation = this.db.models.operation;
345
+ if (!Operation) {
346
+ throw new Error("Operation model not found");
408
347
  }
409
348
 
410
- async getDriveBySlug(slug: string) {
411
- const Drive = this.db.models.drive;
412
- if (!Drive) {
413
- throw new Error('Drive model not found');
414
- }
349
+ const operations: Operation[] = document.operations.map((op) => ({
350
+ hash: op.hash,
351
+ index: op.index,
352
+ timestamp: new Date(op.timestamp).toISOString(),
353
+ input: op.input,
354
+ type: op.type,
355
+ scope: op.scope as OperationScope,
356
+ id: op.opId,
357
+ skip: op.skip,
358
+ // attachments: fileRegistry
359
+ }));
360
+
361
+ const doc = {
362
+ created: document.createdAt.toISOString(),
363
+ name: document.name ? document.name : "",
364
+ documentType: document.documentType,
365
+ initialState: document.initialState,
366
+ lastModified: document.updatedAt.toISOString(),
367
+ operations: {
368
+ global: operations.filter((op: Operation) => op.scope === "global"),
369
+ local: operations.filter((op: Operation) => op.scope === "local"),
370
+ },
371
+ revision: document.revision,
372
+ };
373
+
374
+ return doc;
375
+ }
376
+
377
+ async deleteDocument(drive: string, id: string) {
378
+ const Document = this.db.models.document;
379
+ if (!Document) {
380
+ throw new Error("Document model not found");
381
+ }
415
382
 
416
- const driveEntity = await Drive.findOne({
417
- where: {
418
- slug
419
- }
420
- });
383
+ await Document.destroy({
384
+ where: {
385
+ id: id,
386
+ driveId: drive,
387
+ },
388
+ });
389
+ }
390
+
391
+ async getDrives() {
392
+ return this.getDocuments("drives");
393
+ }
394
+
395
+ async getDrive(id: string) {
396
+ const doc = await this.getDocument("drives", id);
397
+ return doc as DocumentDriveStorage;
398
+ }
399
+
400
+ async getDriveBySlug(slug: string) {
401
+ const Drive = this.db.models.drive;
402
+ if (!Drive) {
403
+ throw new Error("Drive model not found");
404
+ }
421
405
 
422
- if (!driveEntity) {
423
- throw new Error(`Drive with slug ${slug} not found`);
424
- }
406
+ const driveEntity = await Drive.findOne({
407
+ where: {
408
+ slug,
409
+ },
410
+ });
425
411
 
426
- return this.getDrive(driveEntity.dataValues.id);
412
+ if (!driveEntity) {
413
+ throw new Error(`Drive with slug ${slug} not found`);
427
414
  }
428
415
 
429
- async deleteDrive(id: string) {
430
- await this.deleteDocument('drives', id);
416
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-member-access
417
+ return this.getDrive(driveEntity.dataValues.id);
418
+ }
431
419
 
432
- const Document = this.db.models.document;
433
- if (!Document) {
434
- throw new Error('Document model not found');
435
- }
420
+ async deleteDrive(id: string) {
421
+ await this.deleteDocument("drives", id);
436
422
 
437
- await Document.destroy({
438
- where: {
439
- driveId: id
440
- }
441
- });
442
-
443
- const Drive = this.db.models.drive;
444
- if (Drive) {
445
- await Drive.destroy({
446
- where: {
447
- id: id
448
- }
449
- });
450
- }
423
+ const Document = this.db.models.document;
424
+ if (!Document) {
425
+ throw new Error("Document model not found");
451
426
  }
452
427
 
453
- async getSynchronizationUnitsRevision(
454
- units: SynchronizationUnitQuery[]
455
- ): Promise<
456
- {
457
- driveId: string;
458
- documentId: string;
459
- scope: string;
460
- branch: string;
461
- lastUpdated: string;
462
- revision: number;
463
- }[]
464
- > {
465
- const results = await Promise.allSettled(
466
- units.map(async unit => {
467
- try {
468
- const document = await (unit.documentId
469
- ? this.getDocument(unit.driveId, unit.documentId)
470
- : this.getDrive(unit.driveId));
471
- if (!document) {
472
- return undefined;
473
- }
474
- const operation =
475
- document.operations[unit.scope as OperationScope]?.at(
476
- -1
477
- );
478
- if (operation) {
479
- return {
480
- driveId: unit.driveId,
481
- documentId: unit.documentId,
482
- scope: unit.scope,
483
- branch: unit.branch,
484
- lastUpdated: operation.timestamp,
485
- revision: operation.index
486
- };
487
- }
488
- } catch {
489
- return undefined;
490
- }
491
- })
492
- );
493
- return results.reduce<
494
- {
495
- driveId: string;
496
- documentId: string;
497
- scope: string;
498
- branch: string;
499
- lastUpdated: string;
500
- revision: number;
501
- }[]
502
- >((acc, curr) => {
503
- if (curr.status === 'fulfilled' && curr.value !== undefined) {
504
- acc.push(curr.value);
505
- }
506
- return acc;
507
- }, []);
428
+ await Document.destroy({
429
+ where: {
430
+ driveId: id,
431
+ },
432
+ });
433
+
434
+ const Drive = this.db.models.drive;
435
+ if (Drive) {
436
+ await Drive.destroy({
437
+ where: {
438
+ id: id,
439
+ },
440
+ });
508
441
  }
442
+ }
443
+
444
+ async getSynchronizationUnitsRevision(
445
+ units: SynchronizationUnitQuery[],
446
+ ): Promise<
447
+ {
448
+ driveId: string;
449
+ documentId: string;
450
+ scope: string;
451
+ branch: string;
452
+ lastUpdated: string;
453
+ revision: number;
454
+ }[]
455
+ > {
456
+ const results = await Promise.allSettled(
457
+ units.map(async (unit) => {
458
+ try {
459
+ const document = await (unit.documentId
460
+ ? this.getDocument(unit.driveId, unit.documentId)
461
+ : this.getDrive(unit.driveId));
462
+ if (!document) {
463
+ return undefined;
464
+ }
465
+ const operation =
466
+ document.operations[unit.scope as OperationScope].at(-1);
467
+ if (operation) {
468
+ return {
469
+ driveId: unit.driveId,
470
+ documentId: unit.documentId,
471
+ scope: unit.scope,
472
+ branch: unit.branch,
473
+ lastUpdated: operation.timestamp,
474
+ revision: operation.index,
475
+ };
476
+ }
477
+ } catch {
478
+ return undefined;
479
+ }
480
+ }),
481
+ );
482
+ return results.reduce<
483
+ {
484
+ driveId: string;
485
+ documentId: string;
486
+ scope: string;
487
+ branch: string;
488
+ lastUpdated: string;
489
+ revision: number;
490
+ }[]
491
+ >((acc, curr) => {
492
+ if (curr.status === "fulfilled" && curr.value !== undefined) {
493
+ acc.push(curr.value);
494
+ }
495
+ return acc;
496
+ }, []);
497
+ }
509
498
  }