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.
- 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/server/utils.ts
CHANGED
|
@@ -1,35 +1,34 @@
|
|
|
1
|
-
import type { Document, OperationScope } from
|
|
2
|
-
import { RevisionsFilter, StrandUpdate } from
|
|
1
|
+
import type { Document, OperationScope } from "document-model/document";
|
|
2
|
+
import { RevisionsFilter, StrandUpdate } from "./types";
|
|
3
3
|
|
|
4
4
|
export function buildRevisionsFilter(
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
strands: StrandUpdate[],
|
|
6
|
+
driveId: string,
|
|
7
|
+
documentId: string,
|
|
8
8
|
): RevisionsFilter {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
9
|
+
return strands.reduce<RevisionsFilter>((acc, s) => {
|
|
10
|
+
if (!(s.driveId === driveId && s.documentId === documentId)) {
|
|
11
|
+
return acc;
|
|
12
|
+
}
|
|
13
|
+
acc[s.scope] = s.operations[s.operations.length - 1]?.index ?? -1;
|
|
14
|
+
return acc;
|
|
15
|
+
}, {});
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
export function filterOperationsByRevision(
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
): Document[
|
|
22
|
-
|
|
23
|
-
|
|
19
|
+
operations: Document["operations"],
|
|
20
|
+
revisions?: RevisionsFilter,
|
|
21
|
+
): Document["operations"] {
|
|
22
|
+
if (!revisions) {
|
|
23
|
+
return operations;
|
|
24
|
+
}
|
|
25
|
+
return (Object.keys(operations) as OperationScope[]).reduce<
|
|
26
|
+
Document["operations"]
|
|
27
|
+
>((acc, scope) => {
|
|
28
|
+
const revision = revisions[scope];
|
|
29
|
+
if (revision !== undefined) {
|
|
30
|
+
acc[scope] = operations[scope].filter((op) => op.index <= revision);
|
|
24
31
|
}
|
|
25
|
-
return
|
|
26
|
-
|
|
27
|
-
>((acc, scope) => {
|
|
28
|
-
const revision = revisions[scope];
|
|
29
|
-
if (revision !== undefined) {
|
|
30
|
-
acc[scope] = operations[scope].filter(op => op.index <= revision);
|
|
31
|
-
}
|
|
32
|
-
return acc;
|
|
33
|
-
}, operations);
|
|
32
|
+
return acc;
|
|
33
|
+
}, operations);
|
|
34
34
|
}
|
|
35
|
-
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { DocumentDriveAction } from "document-model-libs/document-drive";
|
|
2
|
+
import { BaseAction, DocumentHeader, Operation } from "document-model/document";
|
|
3
|
+
import { SynchronizationUnitQuery } from "../server";
|
|
4
|
+
import {
|
|
5
|
+
DocumentDriveStorage,
|
|
6
|
+
DocumentStorage,
|
|
7
|
+
IDriveStorage,
|
|
8
|
+
IStorage,
|
|
9
|
+
IStorageDelegate,
|
|
10
|
+
} from "./types";
|
|
11
|
+
|
|
12
|
+
abstract class BaseStorage implements IStorage {
|
|
13
|
+
abstract checkDocumentExists(drive: string, id: string): Promise<boolean>;
|
|
14
|
+
|
|
15
|
+
abstract getDocuments(drive: string): Promise<string[]>;
|
|
16
|
+
|
|
17
|
+
abstract getDocument(drive: string, id: string): Promise<DocumentStorage>;
|
|
18
|
+
|
|
19
|
+
abstract createDocument(
|
|
20
|
+
drive: string,
|
|
21
|
+
id: string,
|
|
22
|
+
document: DocumentStorage,
|
|
23
|
+
): Promise<void>;
|
|
24
|
+
|
|
25
|
+
abstract addDocumentOperations(
|
|
26
|
+
drive: string,
|
|
27
|
+
id: string,
|
|
28
|
+
operations: Operation[],
|
|
29
|
+
header: DocumentHeader,
|
|
30
|
+
): Promise<void>;
|
|
31
|
+
|
|
32
|
+
abstract addDocumentOperationsWithTransaction?(
|
|
33
|
+
drive: string,
|
|
34
|
+
id: string,
|
|
35
|
+
callback: (document: DocumentStorage) => Promise<{
|
|
36
|
+
operations: Operation[];
|
|
37
|
+
header: DocumentHeader;
|
|
38
|
+
}>,
|
|
39
|
+
): Promise<void>;
|
|
40
|
+
|
|
41
|
+
abstract deleteDocument(drive: string, id: string): Promise<void>;
|
|
42
|
+
|
|
43
|
+
abstract getOperationResultingState?(
|
|
44
|
+
drive: string,
|
|
45
|
+
id: string,
|
|
46
|
+
index: number,
|
|
47
|
+
scope: string,
|
|
48
|
+
branch: string,
|
|
49
|
+
): Promise<unknown>;
|
|
50
|
+
|
|
51
|
+
abstract setStorageDelegate?(delegate: IStorageDelegate): void;
|
|
52
|
+
|
|
53
|
+
abstract getSynchronizationUnitsRevision(
|
|
54
|
+
units: SynchronizationUnitQuery[],
|
|
55
|
+
): Promise<
|
|
56
|
+
{
|
|
57
|
+
driveId: string;
|
|
58
|
+
documentId: string;
|
|
59
|
+
scope: string;
|
|
60
|
+
branch: string;
|
|
61
|
+
lastUpdated: string;
|
|
62
|
+
revision: number;
|
|
63
|
+
}[]
|
|
64
|
+
>;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export abstract class BaseDriveStorage
|
|
68
|
+
extends BaseStorage
|
|
69
|
+
implements IDriveStorage
|
|
70
|
+
{
|
|
71
|
+
abstract getDrives(): Promise<string[]>;
|
|
72
|
+
abstract getDrive(id: string): Promise<DocumentDriveStorage>;
|
|
73
|
+
abstract getDriveBySlug(slug: string): Promise<DocumentDriveStorage>;
|
|
74
|
+
abstract createDrive(id: string, drive: DocumentDriveStorage): Promise<void>;
|
|
75
|
+
abstract deleteDrive(id: string): Promise<void>;
|
|
76
|
+
abstract addDriveOperations(
|
|
77
|
+
id: string,
|
|
78
|
+
operations: Operation<DocumentDriveAction | BaseAction>[],
|
|
79
|
+
header: DocumentHeader,
|
|
80
|
+
): Promise<void>;
|
|
81
|
+
}
|
package/src/storage/browser.ts
CHANGED
|
@@ -1,221 +1,238 @@
|
|
|
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
|
-
import {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
} from
|
|
3
|
+
BaseAction,
|
|
4
|
+
Document,
|
|
5
|
+
DocumentHeader,
|
|
6
|
+
Operation,
|
|
7
|
+
OperationScope,
|
|
8
|
+
} from "document-model/document";
|
|
9
|
+
import LocalForage from "localforage";
|
|
10
|
+
import { DriveNotFoundError } from "../server/error";
|
|
11
|
+
import { SynchronizationUnitQuery } from "../server/types";
|
|
12
|
+
import { mergeOperations } from "../utils";
|
|
13
|
+
import { migrateDocumentOperationSigatures } from "../utils/migrations";
|
|
14
|
+
import { DocumentDriveStorage, DocumentStorage, IDriveStorage } from "./types";
|
|
15
15
|
|
|
16
16
|
export class BrowserStorage implements IDriveStorage {
|
|
17
|
-
|
|
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
|
-
|
|
17
|
+
private db: Promise<LocalForage>;
|
|
18
|
+
|
|
19
|
+
static DBName = "DOCUMENT_DRIVES";
|
|
20
|
+
static SEP = ":";
|
|
21
|
+
static DRIVES_KEY = "DRIVES";
|
|
22
|
+
|
|
23
|
+
constructor(namespace?: string) {
|
|
24
|
+
this.db = LocalForage.ready().then(() =>
|
|
25
|
+
LocalForage.createInstance({
|
|
26
|
+
name: namespace
|
|
27
|
+
? `${namespace}:${BrowserStorage.DBName}`
|
|
28
|
+
: BrowserStorage.DBName,
|
|
29
|
+
}),
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
buildKey(...args: string[]) {
|
|
34
|
+
return args.join(BrowserStorage.SEP);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
async checkDocumentExists(drive: string, id: string): Promise<boolean> {
|
|
38
|
+
const document = await (
|
|
39
|
+
await this.db
|
|
40
|
+
).getItem<Document>(this.buildKey(drive, id));
|
|
41
|
+
return !!document;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
async getDocuments(drive: string) {
|
|
45
|
+
const db = await this.db;
|
|
46
|
+
const keys = await db.keys();
|
|
47
|
+
const driveKey = `${drive}${BrowserStorage.SEP}`;
|
|
48
|
+
return keys
|
|
49
|
+
.filter((key) => key.startsWith(driveKey))
|
|
50
|
+
.map((key) => key.slice(driveKey.length));
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
async getDocument(driveId: string, id: string) {
|
|
54
|
+
const document = await (
|
|
55
|
+
await this.db
|
|
56
|
+
).getItem<Document>(this.buildKey(driveId, id));
|
|
57
|
+
if (!document) {
|
|
58
|
+
throw new Error(`Document with id ${id} not found`);
|
|
59
|
+
}
|
|
60
|
+
return document;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
async createDocument(drive: string, id: string, document: DocumentStorage) {
|
|
64
|
+
await (await this.db).setItem(this.buildKey(drive, id), document);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
async deleteDocument(drive: string, id: string) {
|
|
68
|
+
await (await this.db).removeItem(this.buildKey(drive, id));
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
async clearStorage(): Promise<void> {
|
|
72
|
+
return (await this.db).clear();
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
async addDocumentOperations(
|
|
76
|
+
drive: string,
|
|
77
|
+
id: string,
|
|
78
|
+
operations: Operation[],
|
|
79
|
+
header: DocumentHeader,
|
|
80
|
+
): Promise<void> {
|
|
81
|
+
const document = await this.getDocument(drive, id);
|
|
82
|
+
if (!document) {
|
|
83
|
+
throw new Error(`Document with id ${id} not found`);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const mergedOperations = mergeOperations(document.operations, operations);
|
|
87
|
+
|
|
88
|
+
const db = await this.db;
|
|
89
|
+
await db.setItem(this.buildKey(drive, id), {
|
|
90
|
+
...document,
|
|
91
|
+
...header,
|
|
92
|
+
operations: mergedOperations,
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
async getDrives() {
|
|
97
|
+
const db = await this.db;
|
|
98
|
+
const keys = await db.keys();
|
|
99
|
+
return keys
|
|
100
|
+
.filter((key) => key.startsWith(BrowserStorage.DRIVES_KEY))
|
|
101
|
+
.map((key) =>
|
|
102
|
+
key.slice(BrowserStorage.DRIVES_KEY.length + BrowserStorage.SEP.length),
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
async getDrive(id: string) {
|
|
107
|
+
const db = await this.db;
|
|
108
|
+
const drive = await db.getItem<DocumentDriveStorage>(
|
|
109
|
+
this.buildKey(BrowserStorage.DRIVES_KEY, id),
|
|
110
|
+
);
|
|
111
|
+
if (!drive) {
|
|
112
|
+
throw new DriveNotFoundError(id);
|
|
113
|
+
}
|
|
114
|
+
return drive;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
async getDriveBySlug(slug: string) {
|
|
118
|
+
// get oldes drives first
|
|
119
|
+
const drives = (await this.getDrives()).reverse();
|
|
120
|
+
for (const drive of drives) {
|
|
121
|
+
const driveData = await this.getDrive(drive);
|
|
122
|
+
if (driveData.initialState.state.global.slug === slug) {
|
|
123
|
+
return this.getDrive(drive);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
throw new Error(`Drive with slug ${slug} not found`);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
async createDrive(id: string, drive: DocumentDriveStorage) {
|
|
131
|
+
const db = await this.db;
|
|
132
|
+
await db.setItem(this.buildKey(BrowserStorage.DRIVES_KEY, id), drive);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
async deleteDrive(id: string) {
|
|
136
|
+
const documents = await this.getDocuments(id);
|
|
137
|
+
await Promise.all(documents.map((doc) => this.deleteDocument(id, doc)));
|
|
138
|
+
return (await this.db).removeItem(
|
|
139
|
+
this.buildKey(BrowserStorage.DRIVES_KEY, 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
|
+
const db = await this.db;
|
|
151
|
+
|
|
152
|
+
await db.setItem(this.buildKey(BrowserStorage.DRIVES_KEY, id), {
|
|
153
|
+
...drive,
|
|
154
|
+
...header,
|
|
155
|
+
operations: mergedOperations,
|
|
156
|
+
});
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
async getSynchronizationUnitsRevision(
|
|
161
|
+
units: SynchronizationUnitQuery[],
|
|
162
|
+
): Promise<
|
|
163
|
+
{
|
|
164
|
+
driveId: string;
|
|
165
|
+
documentId: string;
|
|
166
|
+
scope: string;
|
|
167
|
+
branch: string;
|
|
168
|
+
lastUpdated: string;
|
|
169
|
+
revision: number;
|
|
170
|
+
}[]
|
|
171
|
+
> {
|
|
172
|
+
const results = await Promise.allSettled(
|
|
173
|
+
units.map(async (unit) => {
|
|
174
|
+
try {
|
|
175
|
+
const document = await (unit.documentId
|
|
176
|
+
? this.getDocument(unit.driveId, unit.documentId)
|
|
177
|
+
: this.getDrive(unit.driveId));
|
|
178
|
+
if (!document) {
|
|
179
|
+
return undefined;
|
|
180
|
+
}
|
|
181
|
+
const operation =
|
|
182
|
+
document.operations[unit.scope as OperationScope].at(-1);
|
|
183
|
+
if (operation) {
|
|
184
|
+
return {
|
|
185
|
+
driveId: unit.driveId,
|
|
186
|
+
documentId: unit.documentId,
|
|
187
|
+
scope: unit.scope,
|
|
188
|
+
branch: unit.branch,
|
|
189
|
+
lastUpdated: operation.timestamp,
|
|
190
|
+
revision: operation.index,
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
} catch {
|
|
194
|
+
return undefined;
|
|
58
195
|
}
|
|
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
|
-
return keys
|
|
102
|
-
.filter(key => key.startsWith(BrowserStorage.DRIVES_KEY))
|
|
103
|
-
.map(key =>
|
|
104
|
-
key.slice(
|
|
105
|
-
BrowserStorage.DRIVES_KEY.length + BrowserStorage.SEP.length
|
|
106
|
-
)
|
|
107
|
-
);
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
async getDrive(id: string) {
|
|
111
|
-
const drive = await (
|
|
112
|
-
await this.db
|
|
113
|
-
).getItem<DocumentDriveStorage>(
|
|
114
|
-
this.buildKey(BrowserStorage.DRIVES_KEY, id)
|
|
115
|
-
);
|
|
116
|
-
if (!drive) {
|
|
117
|
-
throw new Error(`Drive with id ${id} not found`);
|
|
118
|
-
}
|
|
119
|
-
return drive;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
async getDriveBySlug(slug: string) {
|
|
123
|
-
// get oldes drives first
|
|
124
|
-
const drives = (await this.getDrives()).reverse();
|
|
125
|
-
for (const drive of drives) {
|
|
126
|
-
const driveData = await this.getDrive(drive);
|
|
127
|
-
if (driveData.initialState.state.global.slug === slug) {
|
|
128
|
-
return this.getDrive(drive);
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
throw new Error(`Drive with slug ${slug} not found`);
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
async createDrive(id: string, drive: DocumentDriveStorage) {
|
|
136
|
-
const db = await this.db;
|
|
137
|
-
await db.setItem(this.buildKey(BrowserStorage.DRIVES_KEY, id), drive);
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
async deleteDrive(id: string) {
|
|
141
|
-
const documents = await this.getDocuments(id);
|
|
142
|
-
await Promise.all(documents.map(doc => this.deleteDocument(id, doc)));
|
|
143
|
-
return (await this.db).removeItem(
|
|
144
|
-
this.buildKey(BrowserStorage.DRIVES_KEY, id)
|
|
145
|
-
);
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
async addDriveOperations(
|
|
149
|
-
id: string,
|
|
150
|
-
operations: Operation<DocumentDriveAction | BaseAction>[],
|
|
151
|
-
header: DocumentHeader
|
|
152
|
-
): Promise<void> {
|
|
153
|
-
const drive = await this.getDrive(id);
|
|
154
|
-
const mergedOperations = mergeOperations(drive.operations, operations);
|
|
155
|
-
const db = await this.db;
|
|
156
|
-
|
|
157
|
-
await db.setItem(this.buildKey(BrowserStorage.DRIVES_KEY, id), {
|
|
158
|
-
...drive,
|
|
159
|
-
...header,
|
|
160
|
-
operations: mergedOperations
|
|
161
|
-
});
|
|
162
|
-
return;
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
async getSynchronizationUnitsRevision(
|
|
166
|
-
units: SynchronizationUnitQuery[]
|
|
167
|
-
): Promise<
|
|
168
|
-
{
|
|
169
|
-
driveId: string;
|
|
170
|
-
documentId: string;
|
|
171
|
-
scope: string;
|
|
172
|
-
branch: string;
|
|
173
|
-
lastUpdated: string;
|
|
174
|
-
revision: number;
|
|
175
|
-
}[]
|
|
176
|
-
> {
|
|
177
|
-
const results = await Promise.allSettled(
|
|
178
|
-
units.map(async unit => {
|
|
179
|
-
try {
|
|
180
|
-
const document = await (unit.documentId
|
|
181
|
-
? this.getDocument(unit.driveId, unit.documentId)
|
|
182
|
-
: this.getDrive(unit.driveId));
|
|
183
|
-
if (!document) {
|
|
184
|
-
return undefined;
|
|
185
|
-
}
|
|
186
|
-
const operation =
|
|
187
|
-
document.operations[unit.scope as OperationScope]?.at(
|
|
188
|
-
-1
|
|
189
|
-
);
|
|
190
|
-
if (operation) {
|
|
191
|
-
return {
|
|
192
|
-
driveId: unit.driveId,
|
|
193
|
-
documentId: unit.documentId,
|
|
194
|
-
scope: unit.scope,
|
|
195
|
-
branch: unit.branch,
|
|
196
|
-
lastUpdated: operation.timestamp,
|
|
197
|
-
revision: operation.index
|
|
198
|
-
};
|
|
199
|
-
}
|
|
200
|
-
} catch {
|
|
201
|
-
return undefined;
|
|
202
|
-
}
|
|
203
|
-
})
|
|
204
|
-
);
|
|
205
|
-
return results.reduce<
|
|
206
|
-
{
|
|
207
|
-
driveId: string;
|
|
208
|
-
documentId: string;
|
|
209
|
-
scope: string;
|
|
210
|
-
branch: string;
|
|
211
|
-
lastUpdated: string;
|
|
212
|
-
revision: number;
|
|
213
|
-
}[]
|
|
214
|
-
>((acc, curr) => {
|
|
215
|
-
if (curr.status === 'fulfilled' && curr.value !== undefined) {
|
|
216
|
-
acc.push(curr.value);
|
|
217
|
-
}
|
|
218
|
-
return acc;
|
|
219
|
-
}, []);
|
|
220
|
-
}
|
|
196
|
+
}),
|
|
197
|
+
);
|
|
198
|
+
return results.reduce<
|
|
199
|
+
{
|
|
200
|
+
driveId: string;
|
|
201
|
+
documentId: string;
|
|
202
|
+
scope: string;
|
|
203
|
+
branch: string;
|
|
204
|
+
lastUpdated: string;
|
|
205
|
+
revision: number;
|
|
206
|
+
}[]
|
|
207
|
+
>((acc, curr) => {
|
|
208
|
+
if (curr.status === "fulfilled" && curr.value !== undefined) {
|
|
209
|
+
acc.push(curr.value);
|
|
210
|
+
}
|
|
211
|
+
return acc;
|
|
212
|
+
}, []);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// migrates all stored operations from legacy signature to signatures array
|
|
216
|
+
async migrateOperationSignatures() {
|
|
217
|
+
const drives = await this.getDrives();
|
|
218
|
+
for (const drive of drives) {
|
|
219
|
+
await this.migrateDocument(BrowserStorage.DRIVES_KEY, drive);
|
|
220
|
+
|
|
221
|
+
const documents = await this.getDocuments(drive);
|
|
222
|
+
await Promise.all(
|
|
223
|
+
documents.map(async (docId) => this.migrateDocument(drive, docId)),
|
|
224
|
+
);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
private async migrateDocument(drive: string, id: string) {
|
|
229
|
+
const document = await this.getDocument(drive, id);
|
|
230
|
+
const migratedDocument = migrateDocumentOperationSigatures(document);
|
|
231
|
+
if (migratedDocument !== document) {
|
|
232
|
+
return (await this.db).setItem(
|
|
233
|
+
this.buildKey(drive, id),
|
|
234
|
+
migratedDocument,
|
|
235
|
+
);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
221
238
|
}
|