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.
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,219 +1,211 @@
1
- import { DocumentDriveAction } from 'document-model-libs/document-drive';
1
+ import { DocumentDriveAction } from "document-model-libs/document-drive";
2
2
  import {
3
- BaseAction,
4
- Document,
5
- DocumentHeader,
6
- Operation,
7
- OperationScope
8
- } from 'document-model/document';
9
- import {
10
- DocumentDriveStorage,
11
- DocumentStorage,
12
- IDriveStorage,
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
- private documents: Record<string, Record<string, DocumentStorage>>;
19
- private drives: Record<string, DocumentDriveStorage>;
20
- private slugToDriveId: Record<string, string> = {};
21
-
22
- constructor() {
23
- this.documents = {};
24
- this.drives = {};
25
- }
26
-
27
- checkDocumentExists(drive: string, id: string): Promise<boolean> {
28
- return Promise.resolve(this.documents[drive]?.[id] !== undefined);
29
- }
30
-
31
- async getDocuments(drive: string) {
32
- return Object.keys(this.documents[drive] ?? {});
33
- }
34
-
35
- async getDocument(driveId: string, id: string) {
36
- const drive = this.documents[driveId];
37
- if (!drive) {
38
- throw new Error(`Drive with id ${driveId} not found`);
39
- }
40
- const document = drive[id];
41
- if (!document) {
42
- throw new Error(`Document with id ${id} not found`);
43
- }
44
-
45
- return document;
46
- }
47
-
48
- async saveDocument(drive: string, id: string, document: Document) {
49
- this.documents[drive] = this.documents[drive] ?? {};
50
- this.documents[drive]![id] = document;
51
- }
52
-
53
- async clearStorage(): Promise<void> {
54
- this.documents = {};
55
- this.drives = {};
56
- }
57
-
58
- async createDocument(drive: string, id: string, document: DocumentStorage) {
59
- this.documents[drive] = this.documents[drive] ?? {};
60
- const {
61
- operations,
62
- initialState,
63
- name,
64
- revision,
65
- documentType,
66
- created,
67
- lastModified,
68
- clipboard,
69
- state
70
- } = document;
71
- this.documents[drive]![id] = {
72
- operations,
73
- initialState,
74
- name,
75
- revision,
76
- documentType,
77
- created,
78
- lastModified,
79
- clipboard,
80
- state
81
- };
82
- }
83
-
84
- async addDocumentOperations(
85
- drive: string,
86
- id: string,
87
- operations: Operation[],
88
- header: DocumentHeader
89
- ): Promise<void> {
90
- const document = await this.getDocument(drive, id);
91
- if (!document) {
92
- throw new Error(`Document with id ${id} not found`);
93
- }
94
-
95
- const mergedOperations = mergeOperations(
96
- document.operations,
97
- operations
98
- );
99
-
100
- this.documents[drive]![id] = {
101
- ...document,
102
- ...header,
103
- operations: mergedOperations
104
- };
105
- }
106
-
107
- async deleteDocument(drive: string, id: string) {
108
- if (!this.documents[drive]) {
109
- throw new Error(`Drive with id ${drive} not found`);
110
- }
111
- delete this.documents[drive]![id];
112
- }
113
-
114
- async getDrives() {
115
- return Object.keys(this.drives);
116
- }
117
-
118
- async getDrive(id: string) {
119
- const drive = this.drives[id];
120
- if (!drive) {
121
- throw new Error(`Drive with id ${id} not found`);
122
- }
123
- return drive;
124
- }
125
-
126
- async getDriveBySlug(slug: string) {
127
- const driveId = this.slugToDriveId[slug];
128
- if (!driveId) {
129
- throw new Error(`Drive with slug ${slug} not found`);
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
- return this.getDrive(driveId);
132
- }
133
-
134
- async createDrive(id: string, drive: DocumentDriveStorage) {
135
- this.drives[id] = drive;
136
- this.documents[id] = {};
137
- const { slug } = drive.initialState.state.global;
138
- if (slug) {
139
- this.slugToDriveId[slug] = id;
140
- }
141
- }
142
-
143
- async addDriveOperations(
144
- id: string,
145
- operations: Operation<DocumentDriveAction | BaseAction>[],
146
- header: DocumentHeader
147
- ): Promise<void> {
148
- const drive = await this.getDrive(id);
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
  }