@verdant-web/store 4.0.0 → 4.1.0-alpha.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/LICENSE +21 -650
- package/dist/bundle/index.js +11 -11
- package/dist/bundle/index.js.map +4 -4
- package/dist/esm/__tests__/fixtures/testStorage.d.ts +1 -2
- package/dist/esm/__tests__/fixtures/testStorage.js +3 -5
- package/dist/esm/__tests__/fixtures/testStorage.js.map +1 -1
- package/dist/esm/client/Client.d.ts +6 -2
- package/dist/esm/client/Client.js +18 -6
- package/dist/esm/client/Client.js.map +1 -1
- package/dist/esm/client/ClientDescriptor.d.ts +7 -5
- package/dist/esm/client/ClientDescriptor.js +18 -4
- package/dist/esm/client/ClientDescriptor.js.map +1 -1
- package/dist/esm/context/ShutdownHandler.d.ts +8 -0
- package/dist/esm/context/ShutdownHandler.js +24 -0
- package/dist/esm/context/ShutdownHandler.js.map +1 -0
- package/dist/esm/context/context.d.ts +15 -4
- package/dist/esm/entities/EntityStore.js +6 -3
- package/dist/esm/entities/EntityStore.js.map +1 -1
- package/dist/esm/files/EntityFile.d.ts +1 -0
- package/dist/esm/files/EntityFile.js +16 -11
- package/dist/esm/files/EntityFile.js.map +1 -1
- package/dist/esm/files/FileManager.d.ts +1 -3
- package/dist/esm/files/FileManager.js +12 -10
- package/dist/esm/files/FileManager.js.map +1 -1
- package/dist/esm/index.d.ts +4 -5
- package/dist/esm/index.js +2 -3
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/internal.d.ts +6 -0
- package/dist/esm/internal.js +5 -0
- package/dist/esm/internal.js.map +1 -0
- package/dist/esm/persistence/MessageCreator.d.ts +3 -1
- package/dist/esm/persistence/MessageCreator.js +58 -55
- package/dist/esm/persistence/MessageCreator.js.map +1 -1
- package/dist/esm/persistence/PersistenceFiles.d.ts +8 -21
- package/dist/esm/persistence/PersistenceFiles.js +44 -30
- package/dist/esm/persistence/PersistenceFiles.js.map +1 -1
- package/dist/esm/persistence/PersistenceMetadata.d.ts +12 -11
- package/dist/esm/persistence/PersistenceMetadata.js +201 -137
- package/dist/esm/persistence/PersistenceMetadata.js.map +1 -1
- package/dist/esm/persistence/PersistenceQueries.d.ts +10 -11
- package/dist/esm/persistence/PersistenceQueries.js +33 -5
- package/dist/esm/persistence/PersistenceQueries.js.map +1 -1
- package/dist/esm/persistence/PersistenceRebaser.d.ts +5 -9
- package/dist/esm/persistence/PersistenceRebaser.js +63 -47
- package/dist/esm/persistence/PersistenceRebaser.js.map +1 -1
- package/dist/esm/persistence/idb/IdbService.d.ts +0 -1
- package/dist/esm/persistence/idb/IdbService.js +28 -16
- package/dist/esm/persistence/idb/IdbService.js.map +1 -1
- package/dist/esm/persistence/idb/files/IdbPersistenceFileDb.d.ts +11 -31
- package/dist/esm/persistence/idb/files/IdbPersistenceFileDb.js +31 -36
- package/dist/esm/persistence/idb/files/IdbPersistenceFileDb.js.map +1 -1
- package/dist/esm/persistence/idb/idbPersistence.d.ts +17 -9
- package/dist/esm/persistence/idb/idbPersistence.js +80 -39
- package/dist/esm/persistence/idb/idbPersistence.js.map +1 -1
- package/dist/esm/persistence/idb/metadata/IdbMetadataDb.d.ts +7 -10
- package/dist/esm/persistence/idb/metadata/IdbMetadataDb.js +45 -71
- package/dist/esm/persistence/idb/metadata/IdbMetadataDb.js.map +1 -1
- package/dist/esm/persistence/idb/metadata/openMetadataDatabase.d.ts +1 -12
- package/dist/esm/persistence/idb/metadata/openMetadataDatabase.js +3 -56
- package/dist/esm/persistence/idb/metadata/openMetadataDatabase.js.map +1 -1
- package/dist/esm/persistence/idb/queries/{IdbQueryDb.d.ts → IdbDocumentDb.d.ts} +7 -13
- package/dist/esm/persistence/idb/queries/{IdbQueryDb.js → IdbDocumentDb.js} +15 -32
- package/dist/esm/persistence/idb/queries/IdbDocumentDb.js.map +1 -0
- package/dist/esm/persistence/idb/queries/migration/db.d.ts +3 -5
- package/dist/esm/persistence/idb/queries/migration/db.js +13 -28
- package/dist/esm/persistence/idb/queries/migration/db.js.map +1 -1
- package/dist/esm/persistence/idb/util.d.ts +8 -4
- package/dist/esm/persistence/idb/util.js +64 -21
- package/dist/esm/persistence/idb/util.js.map +1 -1
- package/dist/esm/persistence/interfaces.d.ts +68 -75
- package/dist/esm/persistence/{idb/queries/migration → migration}/engine.d.ts +4 -7
- package/dist/esm/persistence/{idb/queries/migration → migration}/engine.js +18 -10
- package/dist/esm/persistence/migration/engine.js.map +1 -0
- package/dist/esm/persistence/migration/finalize.d.ts +9 -0
- package/dist/esm/persistence/migration/finalize.js +75 -0
- package/dist/esm/persistence/migration/finalize.js.map +1 -0
- package/dist/esm/persistence/migration/migrate.d.ts +12 -0
- package/dist/esm/persistence/migration/migrate.js +89 -0
- package/dist/esm/persistence/migration/migrate.js.map +1 -0
- package/dist/esm/persistence/migration/paths.js.map +1 -0
- package/dist/esm/persistence/migration/paths.test.js.map +1 -0
- package/dist/esm/persistence/migration/types.d.ts +3 -0
- package/dist/esm/persistence/migration/types.js.map +1 -0
- package/dist/esm/persistence/persistence.js +25 -15
- package/dist/esm/persistence/persistence.js.map +1 -1
- package/dist/esm/queries/FindAllQuery.js +1 -1
- package/dist/esm/queries/FindAllQuery.js.map +1 -1
- package/dist/esm/queries/FindInfiniteQuery.js +2 -2
- package/dist/esm/queries/FindInfiniteQuery.js.map +1 -1
- package/dist/esm/queries/FindOneQuery.js +1 -1
- package/dist/esm/queries/FindOneQuery.js.map +1 -1
- package/dist/esm/queries/FindPageQuery.js +1 -1
- package/dist/esm/queries/FindPageQuery.js.map +1 -1
- package/dist/esm/sync/FileSync.js +3 -3
- package/dist/esm/sync/FileSync.js.map +1 -1
- package/dist/esm/sync/PushPullSync.d.ts +2 -3
- package/dist/esm/sync/PushPullSync.js +4 -2
- package/dist/esm/sync/PushPullSync.js.map +1 -1
- package/dist/esm/sync/ServerSyncEndpointProvider.d.ts +3 -7
- package/dist/esm/sync/ServerSyncEndpointProvider.js +3 -2
- package/dist/esm/sync/ServerSyncEndpointProvider.js.map +1 -1
- package/dist/esm/sync/Sync.d.ts +6 -1
- package/dist/esm/sync/Sync.js +12 -4
- package/dist/esm/sync/Sync.js.map +1 -1
- package/dist/esm/sync/WebSocketSync.js +10 -4
- package/dist/esm/sync/WebSocketSync.js.map +1 -1
- package/package.json +6 -2
- package/src/__tests__/fixtures/testStorage.ts +6 -6
- package/src/client/Client.ts +26 -8
- package/src/client/ClientDescriptor.ts +27 -9
- package/src/context/ShutdownHandler.ts +26 -0
- package/src/context/context.ts +16 -4
- package/src/entities/EntityStore.ts +9 -3
- package/src/files/EntityFile.ts +11 -6
- package/src/files/FileManager.ts +13 -10
- package/src/index.ts +8 -9
- package/src/internal.ts +27 -0
- package/src/persistence/MessageCreator.ts +79 -73
- package/src/persistence/PersistenceFiles.ts +57 -31
- package/src/persistence/PersistenceMetadata.ts +287 -195
- package/src/persistence/PersistenceQueries.ts +45 -9
- package/src/persistence/PersistenceRebaser.ts +105 -70
- package/src/persistence/idb/IdbService.ts +40 -22
- package/src/persistence/idb/files/IdbPersistenceFileDb.ts +30 -62
- package/src/persistence/idb/idbPersistence.ts +123 -47
- package/src/persistence/idb/metadata/IdbMetadataDb.ts +75 -97
- package/src/persistence/idb/metadata/openMetadataDatabase.ts +2 -96
- package/src/persistence/idb/queries/{IdbQueryDb.ts → IdbDocumentDb.ts} +17 -57
- package/src/persistence/idb/queries/migration/db.ts +20 -39
- package/src/persistence/idb/util.ts +84 -21
- package/src/persistence/interfaces.ts +89 -90
- package/src/persistence/{idb/queries/migration → migration}/engine.ts +30 -15
- package/src/persistence/migration/finalize.ts +126 -0
- package/src/persistence/migration/migrate.ts +169 -0
- package/src/persistence/migration/types.ts +4 -0
- package/src/persistence/persistence.ts +37 -14
- package/src/queries/FindAllQuery.ts +1 -1
- package/src/queries/FindInfiniteQuery.ts +2 -2
- package/src/queries/FindOneQuery.ts +1 -1
- package/src/queries/FindPageQuery.ts +1 -1
- package/src/sync/FileSync.ts +21 -15
- package/src/sync/PushPullSync.ts +3 -4
- package/src/sync/ServerSyncEndpointProvider.ts +6 -8
- package/src/sync/Sync.ts +20 -7
- package/src/sync/WebSocketSync.ts +10 -4
- package/dist/esm/client/constants.d.ts +0 -1
- package/dist/esm/client/constants.js +0 -2
- package/dist/esm/client/constants.js.map +0 -1
- package/dist/esm/persistence/idb/queries/IdbQueryDb.js.map +0 -1
- package/dist/esm/persistence/idb/queries/migration/engine.js.map +0 -1
- package/dist/esm/persistence/idb/queries/migration/migrations.d.ts +0 -15
- package/dist/esm/persistence/idb/queries/migration/migrations.js +0 -243
- package/dist/esm/persistence/idb/queries/migration/migrations.js.map +0 -1
- package/dist/esm/persistence/idb/queries/migration/openQueryDatabase.d.ts +0 -8
- package/dist/esm/persistence/idb/queries/migration/openQueryDatabase.js +0 -24
- package/dist/esm/persistence/idb/queries/migration/openQueryDatabase.js.map +0 -1
- package/dist/esm/persistence/idb/queries/migration/paths.js.map +0 -1
- package/dist/esm/persistence/idb/queries/migration/paths.test.js.map +0 -1
- package/dist/esm/persistence/idb/queries/migration/types.d.ts +0 -6
- package/dist/esm/persistence/idb/queries/migration/types.js.map +0 -1
- package/src/client/constants.ts +0 -1
- package/src/persistence/idb/queries/migration/migrations.ts +0 -345
- package/src/persistence/idb/queries/migration/openQueryDatabase.ts +0 -54
- package/src/persistence/idb/queries/migration/types.ts +0 -8
- /package/dist/esm/persistence/{idb/queries/migration → migration}/paths.d.ts +0 -0
- /package/dist/esm/persistence/{idb/queries/migration → migration}/paths.js +0 -0
- /package/dist/esm/persistence/{idb/queries/migration → migration}/paths.test.d.ts +0 -0
- /package/dist/esm/persistence/{idb/queries/migration → migration}/paths.test.js +0 -0
- /package/dist/esm/persistence/{idb/queries/migration → migration}/types.js +0 -0
- /package/src/persistence/{idb/queries/migration → migration}/paths.test.ts +0 -0
- /package/src/persistence/{idb/queries/migration → migration}/paths.ts +0 -0
|
@@ -1,22 +1,27 @@
|
|
|
1
1
|
import { Context, InitialContext } from '../../context/context.js';
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
PersistenceImplementation,
|
|
4
|
+
PersistenceFileDb,
|
|
5
|
+
PersistenceNamespace,
|
|
6
|
+
} from '../interfaces.js';
|
|
3
7
|
import { IdbPersistenceFileDb } from './files/IdbPersistenceFileDb.js';
|
|
4
8
|
import { IdbMetadataDb } from './metadata/IdbMetadataDb.js';
|
|
5
9
|
import { openMetadataDatabase } from './metadata/openMetadataDatabase.js';
|
|
6
|
-
import {
|
|
7
|
-
import { openQueryDatabase } from './queries/migration/openQueryDatabase.js';
|
|
8
|
-
import { PersistenceMetadata } from '../PersistenceMetadata.js';
|
|
10
|
+
import { IdbDocumentDb } from './queries/IdbDocumentDb.js';
|
|
9
11
|
import {
|
|
10
12
|
closeDatabase,
|
|
11
13
|
deleteDatabase,
|
|
14
|
+
getDocumentDbName,
|
|
12
15
|
getMetadataDbName,
|
|
13
16
|
getNamespaceFromDatabaseInfo,
|
|
14
|
-
|
|
17
|
+
overwriteDatabase,
|
|
15
18
|
} from './util.js';
|
|
16
|
-
import {
|
|
19
|
+
import { openDatabase, upgradeDatabase } from './queries/migration/db.js';
|
|
20
|
+
import { Migration } from '@verdant-web/common';
|
|
21
|
+
import { OpenDocumentDbContext } from '../migration/types.js';
|
|
17
22
|
|
|
18
23
|
export class IdbPersistence implements PersistenceImplementation {
|
|
19
|
-
|
|
24
|
+
name = 'IdbPersistence';
|
|
20
25
|
constructor(private indexedDB: IDBFactory = window.indexedDB) {}
|
|
21
26
|
|
|
22
27
|
getNamespaces = async (): Promise<string[]> => {
|
|
@@ -30,7 +35,14 @@ export class IdbPersistence implements PersistenceImplementation {
|
|
|
30
35
|
};
|
|
31
36
|
|
|
32
37
|
getNamespaceVersion = async (namespace: string): Promise<number> => {
|
|
33
|
-
|
|
38
|
+
const databaseName = getDocumentDbName(namespace);
|
|
39
|
+
const dbInfo = await this.indexedDB.databases();
|
|
40
|
+
const existingDb = dbInfo.find((info) => info.name === databaseName);
|
|
41
|
+
if (existingDb) {
|
|
42
|
+
return existingDb.version ?? 0;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return 0;
|
|
34
46
|
};
|
|
35
47
|
|
|
36
48
|
deleteNamespace = async (
|
|
@@ -43,8 +55,64 @@ export class IdbPersistence implements PersistenceImplementation {
|
|
|
43
55
|
]);
|
|
44
56
|
};
|
|
45
57
|
|
|
58
|
+
openNamespace = async (
|
|
59
|
+
namespace: string,
|
|
60
|
+
): Promise<IdbPersistenceNamespace> => {
|
|
61
|
+
return new IdbPersistenceNamespace(this.indexedDB, namespace);
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
copyNamespace = async (
|
|
65
|
+
from: string,
|
|
66
|
+
to: string,
|
|
67
|
+
ctx: InitialContext,
|
|
68
|
+
): Promise<void> => {
|
|
69
|
+
const fromCtx = { ...ctx, namespace: from };
|
|
70
|
+
const toCtx = { ...ctx, namespace: to };
|
|
71
|
+
const { db: fromMetaDb } = await openMetadataDatabase({
|
|
72
|
+
indexedDB: this.indexedDB,
|
|
73
|
+
log: fromCtx.log,
|
|
74
|
+
namespace: fromCtx.namespace,
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
// no need to involve files, as they store all data
|
|
78
|
+
// in the metadata database.
|
|
79
|
+
|
|
80
|
+
const fromDocumentsDb = await openDatabase({
|
|
81
|
+
indexedDB: this.indexedDB,
|
|
82
|
+
namespace: fromCtx.namespace,
|
|
83
|
+
version: fromCtx.schema.version,
|
|
84
|
+
log: fromCtx.log,
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
fromCtx.log(
|
|
88
|
+
'info',
|
|
89
|
+
`Copying data from ${fromCtx.namespace} to ${toCtx.namespace}`,
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
await overwriteDatabase(
|
|
93
|
+
fromMetaDb,
|
|
94
|
+
getMetadataDbName(toCtx.namespace),
|
|
95
|
+
toCtx,
|
|
96
|
+
this.indexedDB,
|
|
97
|
+
);
|
|
98
|
+
await overwriteDatabase(
|
|
99
|
+
fromDocumentsDb,
|
|
100
|
+
getDocumentDbName(toCtx.namespace),
|
|
101
|
+
toCtx,
|
|
102
|
+
this.indexedDB,
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
await closeDatabase(fromMetaDb);
|
|
106
|
+
await closeDatabase(fromDocumentsDb);
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
class IdbPersistenceNamespace implements PersistenceNamespace {
|
|
111
|
+
constructor(private indexedDB: IDBFactory, private namespace: string) {}
|
|
112
|
+
private metadataDb: IDBDatabase | undefined;
|
|
113
|
+
|
|
46
114
|
openFiles(
|
|
47
|
-
ctx: Omit<Context, 'files' | '
|
|
115
|
+
ctx: Omit<Context, 'files' | 'documents'>,
|
|
48
116
|
): Promise<PersistenceFileDb> {
|
|
49
117
|
if (!this.metadataDb) {
|
|
50
118
|
throw new Error(
|
|
@@ -58,59 +126,67 @@ export class IdbPersistence implements PersistenceImplementation {
|
|
|
58
126
|
const { db } = await openMetadataDatabase({
|
|
59
127
|
indexedDB: this.indexedDB,
|
|
60
128
|
log: ctx.log,
|
|
61
|
-
namespace:
|
|
129
|
+
namespace: this.namespace,
|
|
62
130
|
});
|
|
63
131
|
this.metadataDb = db;
|
|
132
|
+
ctx.persistenceShutdownHandler.register(() => closeDatabase(db));
|
|
64
133
|
return new IdbMetadataDb(db, ctx);
|
|
65
134
|
};
|
|
66
135
|
|
|
67
|
-
|
|
68
|
-
const db = await
|
|
136
|
+
openDocuments = async (ctx: OpenDocumentDbContext) => {
|
|
137
|
+
const db = await openDatabase({
|
|
69
138
|
version: ctx.schema.version,
|
|
70
139
|
indexedDB: this.indexedDB,
|
|
71
|
-
|
|
72
|
-
|
|
140
|
+
log: ctx.log,
|
|
141
|
+
namespace: this.namespace,
|
|
73
142
|
});
|
|
74
|
-
|
|
143
|
+
ctx.persistenceShutdownHandler.register(() => closeDatabase(db));
|
|
144
|
+
return new IdbDocumentDb(db, ctx);
|
|
75
145
|
};
|
|
76
146
|
|
|
77
|
-
|
|
78
|
-
from: string,
|
|
79
|
-
to: string,
|
|
147
|
+
applyMigration = async (
|
|
80
148
|
ctx: InitialContext,
|
|
149
|
+
migration: Migration<any>,
|
|
81
150
|
): Promise<void> => {
|
|
82
|
-
const fromCtx = { ...ctx, namespace: from, originalNamespace: from };
|
|
83
|
-
const fromMetaDb = await this.openMetadata(fromCtx);
|
|
84
|
-
const fromMeta = new PersistenceMetadata(fromMetaDb, fromCtx);
|
|
85
|
-
const fromQueries = await this.openQueries({ ...fromCtx, meta: fromMeta });
|
|
86
|
-
ctx.log('info', `Copying data from ${from} to ${to}`);
|
|
87
|
-
|
|
88
|
-
const { db: toMetaDb } = await openMetadataDatabase({
|
|
89
|
-
indexedDB: this.indexedDB,
|
|
90
|
-
log: ctx.log,
|
|
91
|
-
namespace: to,
|
|
92
|
-
});
|
|
93
|
-
ctx.log('debug', 'Metadata database opened');
|
|
94
|
-
await fromMetaDb.cloneTo(toMetaDb);
|
|
95
|
-
ctx.log('debug', 'Metadata copied');
|
|
96
|
-
|
|
97
|
-
const toQueryDb = await openQueryDatabase({
|
|
98
|
-
version: ctx.schema.version,
|
|
99
|
-
indexedDB: this.indexedDB,
|
|
100
|
-
migrations: ctx.migrations,
|
|
101
|
-
context: { ...ctx, namespace: to, originalNamespace: to, meta: fromMeta },
|
|
102
|
-
});
|
|
103
|
-
await fromQueries.cloneTo(toQueryDb);
|
|
104
|
-
ctx.log('debug', 'Indexes copied');
|
|
105
151
|
ctx.log(
|
|
106
152
|
'debug',
|
|
107
|
-
'
|
|
108
|
-
|
|
153
|
+
'Applying migration',
|
|
154
|
+
migration.newSchema.version,
|
|
155
|
+
migration,
|
|
109
156
|
);
|
|
157
|
+
await upgradeDatabase(
|
|
158
|
+
this.indexedDB,
|
|
159
|
+
this.namespace,
|
|
160
|
+
migration.newSchema.version,
|
|
161
|
+
(transaction, db) => {
|
|
162
|
+
for (const newCollection of migration.addedCollections) {
|
|
163
|
+
db.createObjectStore(newCollection, {
|
|
164
|
+
keyPath: migration.newSchema.collections[newCollection].primaryKey,
|
|
165
|
+
autoIncrement: false,
|
|
166
|
+
});
|
|
167
|
+
}
|
|
110
168
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
169
|
+
for (const collection of migration.allCollections) {
|
|
170
|
+
const store = transaction.objectStore(collection);
|
|
171
|
+
// apply new indexes
|
|
172
|
+
for (const newIndex of migration.addedIndexes[collection] || []) {
|
|
173
|
+
store.createIndex(newIndex.name, newIndex.name, {
|
|
174
|
+
multiEntry: newIndex.multiEntry,
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
// remove old indexes
|
|
178
|
+
for (const oldIndex of migration.removedIndexes[collection] || []) {
|
|
179
|
+
store.deleteIndex(oldIndex.name);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
for (const removedCollection of migration.removedCollections) {
|
|
183
|
+
// !! can't delete the store, because old operations that relate to
|
|
184
|
+
// this store may still exist in history. instead, we can clear it out
|
|
185
|
+
// and leave it in place
|
|
186
|
+
transaction.objectStore(removedCollection).clear();
|
|
187
|
+
}
|
|
188
|
+
},
|
|
189
|
+
ctx.log,
|
|
190
|
+
);
|
|
115
191
|
};
|
|
116
192
|
}
|
|
@@ -18,7 +18,6 @@ import {
|
|
|
18
18
|
PersistenceMetadataDb,
|
|
19
19
|
} from '../../interfaces.js';
|
|
20
20
|
import { IdbService } from '../IdbService.js';
|
|
21
|
-
import cuid from 'cuid';
|
|
22
21
|
import { closeDatabase, getSizeOfObjectStore } from '../util.js';
|
|
23
22
|
import { Context } from '../../../context/context.js';
|
|
24
23
|
|
|
@@ -34,24 +33,35 @@ export type StoredSchema = {
|
|
|
34
33
|
schema: string;
|
|
35
34
|
};
|
|
36
35
|
|
|
37
|
-
export class IdbMetadataDb
|
|
36
|
+
export class IdbMetadataDb
|
|
37
|
+
extends IdbService
|
|
38
|
+
implements PersistenceMetadataDb<IDBTransaction>
|
|
39
|
+
{
|
|
38
40
|
constructor(
|
|
39
41
|
db: IDBDatabase,
|
|
40
42
|
private ctx: Pick<Context, 'log' | 'namespace'>,
|
|
41
43
|
) {
|
|
42
44
|
super(db, ctx);
|
|
43
|
-
this.addDispose(() =>
|
|
45
|
+
this.addDispose(() => {
|
|
46
|
+
this.ctx.log('info', `Closing metadata DB for`, this.ctx.namespace);
|
|
47
|
+
return closeDatabase(db);
|
|
48
|
+
});
|
|
44
49
|
}
|
|
45
50
|
|
|
46
|
-
transaction = (
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
51
|
+
transaction = async <T>(
|
|
52
|
+
opts: {
|
|
53
|
+
mode?: 'readwrite' | 'readonly';
|
|
54
|
+
storeNames: string[];
|
|
55
|
+
abort?: AbortSignal;
|
|
56
|
+
},
|
|
57
|
+
procedure: (tx: IDBTransaction) => Promise<T>,
|
|
58
|
+
) => {
|
|
59
|
+
const tx = this.createTransaction(opts.storeNames, {
|
|
52
60
|
mode: opts.mode,
|
|
53
61
|
abort: opts.abort,
|
|
54
62
|
});
|
|
63
|
+
const result = await procedure(tx);
|
|
64
|
+
return result;
|
|
55
65
|
};
|
|
56
66
|
|
|
57
67
|
getAckInfo = async (): Promise<AckInfo> => {
|
|
@@ -61,7 +71,6 @@ export class IdbMetadataDb extends IdbService implements PersistenceMetadataDb {
|
|
|
61
71
|
} else {
|
|
62
72
|
return {
|
|
63
73
|
globalAckTimestamp: null,
|
|
64
|
-
type: 'ack',
|
|
65
74
|
};
|
|
66
75
|
}
|
|
67
76
|
};
|
|
@@ -74,62 +83,37 @@ export class IdbMetadataDb extends IdbService implements PersistenceMetadataDb {
|
|
|
74
83
|
);
|
|
75
84
|
};
|
|
76
85
|
|
|
77
|
-
private _creatingLocalReplica: Promise<void> | undefined;
|
|
78
|
-
private cachedLocalReplica: LocalReplicaInfo | undefined;
|
|
79
|
-
|
|
80
86
|
getLocalReplica = async (
|
|
81
87
|
opts?: CommonQueryOptions,
|
|
82
|
-
): Promise<LocalReplicaInfo> => {
|
|
83
|
-
|
|
84
|
-
return this.cachedLocalReplica;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
const lookup = await this.run<LocalReplicaInfo>(
|
|
88
|
+
): Promise<LocalReplicaInfo | undefined> => {
|
|
89
|
+
return this.run<LocalReplicaInfo | undefined>(
|
|
88
90
|
'info',
|
|
89
91
|
(store) => store.get('localReplicaInfo'),
|
|
90
|
-
|
|
92
|
+
opts,
|
|
91
93
|
);
|
|
92
|
-
|
|
93
|
-
// not cached, not in db, create it
|
|
94
|
-
if (!lookup) {
|
|
95
|
-
// prevent a race condition if get() is called again while we are
|
|
96
|
-
// creating the replica info
|
|
97
|
-
if (!this._creatingLocalReplica) {
|
|
98
|
-
this._creatingLocalReplica = (async () => {
|
|
99
|
-
// create our own replica info now
|
|
100
|
-
const replicaId = cuid();
|
|
101
|
-
const replicaInfo: LocalReplicaInfo = {
|
|
102
|
-
type: 'localReplicaInfo',
|
|
103
|
-
id: replicaId,
|
|
104
|
-
userId: undefined,
|
|
105
|
-
ackedLogicalTime: null,
|
|
106
|
-
lastSyncedLogicalTime: null,
|
|
107
|
-
};
|
|
108
|
-
await this.run('info', (store) => store.put(replicaInfo), {
|
|
109
|
-
mode: 'readwrite',
|
|
110
|
-
});
|
|
111
|
-
this.cachedLocalReplica = replicaInfo;
|
|
112
|
-
})();
|
|
113
|
-
}
|
|
114
|
-
await this._creatingLocalReplica;
|
|
115
|
-
|
|
116
|
-
return this.getLocalReplica(opts);
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
this.cachedLocalReplica = lookup;
|
|
120
|
-
return lookup;
|
|
121
94
|
};
|
|
122
95
|
|
|
123
96
|
updateLocalReplica = async (
|
|
124
|
-
data:
|
|
125
|
-
opts
|
|
97
|
+
data: LocalReplicaInfo,
|
|
98
|
+
opts?: CommonQueryOptions,
|
|
126
99
|
): Promise<void> => {
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
100
|
+
try {
|
|
101
|
+
await this.run(
|
|
102
|
+
'info',
|
|
103
|
+
(store) =>
|
|
104
|
+
store.put({
|
|
105
|
+
...data,
|
|
106
|
+
type: 'localReplicaInfo',
|
|
107
|
+
}),
|
|
108
|
+
{
|
|
109
|
+
mode: 'readwrite',
|
|
110
|
+
transaction: opts?.transaction,
|
|
111
|
+
},
|
|
112
|
+
);
|
|
113
|
+
} catch (e) {
|
|
114
|
+
this.ctx.log('critical', 'Error updating local replica', data, e);
|
|
115
|
+
throw e;
|
|
116
|
+
}
|
|
133
117
|
};
|
|
134
118
|
|
|
135
119
|
iterateDocumentBaselines = async (
|
|
@@ -151,7 +135,7 @@ export class IdbMetadataDb extends IdbService implements PersistenceMetadataDb {
|
|
|
151
135
|
];
|
|
152
136
|
},
|
|
153
137
|
iterator,
|
|
154
|
-
|
|
138
|
+
opts,
|
|
155
139
|
);
|
|
156
140
|
};
|
|
157
141
|
|
|
@@ -170,7 +154,7 @@ export class IdbMetadataDb extends IdbService implements PersistenceMetadataDb {
|
|
|
170
154
|
];
|
|
171
155
|
},
|
|
172
156
|
iterator,
|
|
173
|
-
|
|
157
|
+
opts,
|
|
174
158
|
);
|
|
175
159
|
};
|
|
176
160
|
|
|
@@ -182,7 +166,7 @@ export class IdbMetadataDb extends IdbService implements PersistenceMetadataDb {
|
|
|
182
166
|
'baselines',
|
|
183
167
|
(store) => store.index('timestamp').openCursor(),
|
|
184
168
|
iterator,
|
|
185
|
-
|
|
169
|
+
opts,
|
|
186
170
|
);
|
|
187
171
|
};
|
|
188
172
|
|
|
@@ -193,7 +177,7 @@ export class IdbMetadataDb extends IdbService implements PersistenceMetadataDb {
|
|
|
193
177
|
return this.run<DocumentBaseline>(
|
|
194
178
|
'baselines',
|
|
195
179
|
(store) => store.get(oid),
|
|
196
|
-
|
|
180
|
+
opts,
|
|
197
181
|
);
|
|
198
182
|
};
|
|
199
183
|
|
|
@@ -204,7 +188,7 @@ export class IdbMetadataDb extends IdbService implements PersistenceMetadataDb {
|
|
|
204
188
|
await this.runAll<any>(
|
|
205
189
|
'baselines',
|
|
206
190
|
(store) => baselines.map((b) => store.put(b)),
|
|
207
|
-
|
|
191
|
+
opts,
|
|
208
192
|
);
|
|
209
193
|
};
|
|
210
194
|
|
|
@@ -212,11 +196,7 @@ export class IdbMetadataDb extends IdbService implements PersistenceMetadataDb {
|
|
|
212
196
|
oid: string,
|
|
213
197
|
opts: CommonQueryOptions = writeOpts,
|
|
214
198
|
): Promise<void> => {
|
|
215
|
-
await this.run(
|
|
216
|
-
'baselines',
|
|
217
|
-
(store) => store.delete(oid),
|
|
218
|
-
this.convertOpts(opts),
|
|
219
|
-
);
|
|
199
|
+
await this.run('baselines', (store) => store.delete(oid), opts);
|
|
220
200
|
};
|
|
221
201
|
|
|
222
202
|
iterateDocumentOperations = (
|
|
@@ -237,7 +217,7 @@ export class IdbMetadataDb extends IdbService implements PersistenceMetadataDb {
|
|
|
237
217
|
return index.openCursor(range);
|
|
238
218
|
},
|
|
239
219
|
iterator,
|
|
240
|
-
|
|
220
|
+
opts,
|
|
241
221
|
);
|
|
242
222
|
};
|
|
243
223
|
|
|
@@ -262,14 +242,13 @@ export class IdbMetadataDb extends IdbService implements PersistenceMetadataDb {
|
|
|
262
242
|
return store.openCursor(range);
|
|
263
243
|
},
|
|
264
244
|
iterator,
|
|
265
|
-
|
|
245
|
+
opts,
|
|
266
246
|
);
|
|
267
247
|
};
|
|
268
248
|
|
|
269
|
-
|
|
249
|
+
deleteEntityOperations = (
|
|
270
250
|
oid: string,
|
|
271
|
-
|
|
272
|
-
opts: CommonQueryOptions & { to?: string | null } = writeOpts,
|
|
251
|
+
opts: CommonQueryOptions & { to: string | null },
|
|
273
252
|
): Promise<void> => {
|
|
274
253
|
return this.iterate<StoredClientOperation>(
|
|
275
254
|
'operations',
|
|
@@ -283,10 +262,9 @@ export class IdbMetadataDb extends IdbService implements PersistenceMetadataDb {
|
|
|
283
262
|
return store.openCursor(range);
|
|
284
263
|
},
|
|
285
264
|
(op, store) => {
|
|
286
|
-
iterator(op);
|
|
287
265
|
store.delete(op.oid_timestamp);
|
|
288
266
|
},
|
|
289
|
-
|
|
267
|
+
opts,
|
|
290
268
|
);
|
|
291
269
|
};
|
|
292
270
|
|
|
@@ -304,7 +282,7 @@ export class IdbMetadataDb extends IdbService implements PersistenceMetadataDb {
|
|
|
304
282
|
);
|
|
305
283
|
},
|
|
306
284
|
iterator,
|
|
307
|
-
|
|
285
|
+
opts,
|
|
308
286
|
);
|
|
309
287
|
};
|
|
310
288
|
|
|
@@ -359,7 +337,7 @@ export class IdbMetadataDb extends IdbService implements PersistenceMetadataDb {
|
|
|
359
337
|
return store.index('timestamp').openCursor(range, 'next');
|
|
360
338
|
},
|
|
361
339
|
iterator,
|
|
362
|
-
|
|
340
|
+
opts,
|
|
363
341
|
);
|
|
364
342
|
};
|
|
365
343
|
|
|
@@ -375,7 +353,7 @@ export class IdbMetadataDb extends IdbService implements PersistenceMetadataDb {
|
|
|
375
353
|
affected.add(getOidRoot(op.oid));
|
|
376
354
|
return store.put(this.addOperationIndexes(op));
|
|
377
355
|
}),
|
|
378
|
-
|
|
356
|
+
opts,
|
|
379
357
|
);
|
|
380
358
|
return Array.from(affected);
|
|
381
359
|
};
|
|
@@ -414,15 +392,27 @@ export class IdbMetadataDb extends IdbService implements PersistenceMetadataDb {
|
|
|
414
392
|
mode: 'readwrite',
|
|
415
393
|
transaction: tx,
|
|
416
394
|
});
|
|
395
|
+
} else {
|
|
396
|
+
const localInfo = await this.getLocalReplica({
|
|
397
|
+
transaction: tx,
|
|
398
|
+
});
|
|
399
|
+
if (localInfo) {
|
|
400
|
+
localInfo.ackedLogicalTime = null;
|
|
401
|
+
localInfo.lastSyncedLogicalTime = null;
|
|
402
|
+
await this.run(
|
|
403
|
+
'info',
|
|
404
|
+
(store) =>
|
|
405
|
+
store.put({
|
|
406
|
+
...localInfo,
|
|
407
|
+
type: 'localReplicaInfo',
|
|
408
|
+
}),
|
|
409
|
+
{
|
|
410
|
+
mode: 'readwrite',
|
|
411
|
+
transaction: tx,
|
|
412
|
+
},
|
|
413
|
+
);
|
|
414
|
+
}
|
|
417
415
|
}
|
|
418
|
-
|
|
419
|
-
const localInfo = await this.getLocalReplica();
|
|
420
|
-
localInfo.ackedLogicalTime = null;
|
|
421
|
-
localInfo.lastSyncedLogicalTime = null;
|
|
422
|
-
await this.run('info', (store) => store.put(localInfo), {
|
|
423
|
-
mode: 'readwrite',
|
|
424
|
-
transaction: tx,
|
|
425
|
-
});
|
|
426
416
|
};
|
|
427
417
|
|
|
428
418
|
private resetBaselines = async (tx: IDBTransaction) => {
|
|
@@ -443,18 +433,6 @@ export class IdbMetadataDb extends IdbService implements PersistenceMetadataDb {
|
|
|
443
433
|
d_t: createCompoundIndexValue(getOidRoot(op.oid), op.timestamp) as string,
|
|
444
434
|
};
|
|
445
435
|
};
|
|
446
|
-
|
|
447
|
-
private convertOpts = (opts?: CommonQueryOptions) => {
|
|
448
|
-
if (opts?.transaction && !(opts.transaction instanceof IDBTransaction)) {
|
|
449
|
-
throw new Error(
|
|
450
|
-
`Invalid IndexedDB transaction. You cannot mix persistence providers. ${typeof opts.transaction}`,
|
|
451
|
-
);
|
|
452
|
-
}
|
|
453
|
-
return {
|
|
454
|
-
mode: opts?.mode,
|
|
455
|
-
transaction: opts?.transaction as IDBTransaction | undefined,
|
|
456
|
-
};
|
|
457
|
-
};
|
|
458
436
|
}
|
|
459
437
|
|
|
460
438
|
const writeOpts = { mode: 'readwrite' } as const;
|
|
@@ -1,9 +1,5 @@
|
|
|
1
1
|
import { replaceLegacyOidsInObject } from '@verdant-web/common';
|
|
2
|
-
import {
|
|
3
|
-
closeDatabase,
|
|
4
|
-
getMetadataDbName,
|
|
5
|
-
storeRequestPromise,
|
|
6
|
-
} from '../util.js';
|
|
2
|
+
import { getMetadataDbName } from '../util.js';
|
|
7
3
|
import { Context } from '../../../context/context.js';
|
|
8
4
|
|
|
9
5
|
const migrations = [version1, version2, version3, version4, version5, version6];
|
|
@@ -13,18 +9,16 @@ export function openMetadataDatabase({
|
|
|
13
9
|
indexedDB = window.indexedDB,
|
|
14
10
|
namespace,
|
|
15
11
|
log,
|
|
16
|
-
metadataVersion = CURRENT_METADATA_VERSION,
|
|
17
12
|
}: {
|
|
18
13
|
indexedDB?: IDBFactory;
|
|
19
14
|
namespace: string;
|
|
20
15
|
log?: Context['log'];
|
|
21
|
-
metadataVersion?: number;
|
|
22
16
|
}): Promise<{ wasInitialized: boolean; db: IDBDatabase }> {
|
|
23
17
|
return new Promise<{ wasInitialized: boolean; db: IDBDatabase }>(
|
|
24
18
|
(resolve, reject) => {
|
|
25
19
|
const request = indexedDB.open(
|
|
26
20
|
getMetadataDbName(namespace),
|
|
27
|
-
|
|
21
|
+
CURRENT_METADATA_VERSION,
|
|
28
22
|
);
|
|
29
23
|
let wasInitialized = false;
|
|
30
24
|
request.onupgradeneeded = async (event) => {
|
|
@@ -56,94 +50,6 @@ export function openMetadataDatabase({
|
|
|
56
50
|
);
|
|
57
51
|
}
|
|
58
52
|
|
|
59
|
-
export async function openWIPMetadataDatabase({
|
|
60
|
-
wipNamespace,
|
|
61
|
-
namespace,
|
|
62
|
-
indexedDB,
|
|
63
|
-
log,
|
|
64
|
-
metadataVersion,
|
|
65
|
-
}: {
|
|
66
|
-
indexedDB?: IDBFactory;
|
|
67
|
-
namespace: string;
|
|
68
|
-
wipNamespace: string;
|
|
69
|
-
log?: (...args: any[]) => void;
|
|
70
|
-
metadataVersion?: number;
|
|
71
|
-
}): Promise<{ wasInitialized: boolean; db: IDBDatabase }> {
|
|
72
|
-
const result = await openMetadataDatabase({
|
|
73
|
-
namespace: wipNamespace,
|
|
74
|
-
indexedDB,
|
|
75
|
-
log,
|
|
76
|
-
metadataVersion,
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
// this WIP database was already set up.
|
|
80
|
-
if (!result.wasInitialized) {
|
|
81
|
-
return result;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
log?.('debug', 'Beginning copy of production metadata database to WIP');
|
|
85
|
-
// copy all data from production metadata database
|
|
86
|
-
const { db: prodDb } = await openMetadataDatabase({
|
|
87
|
-
namespace,
|
|
88
|
-
indexedDB,
|
|
89
|
-
log,
|
|
90
|
-
metadataVersion,
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
const tx = prodDb.transaction(
|
|
94
|
-
['baselines', 'operations', 'info'],
|
|
95
|
-
'readonly',
|
|
96
|
-
);
|
|
97
|
-
const [baselines, operations, info] = await Promise.all([
|
|
98
|
-
storeRequestPromise(tx.objectStore('baselines').getAll()),
|
|
99
|
-
storeRequestPromise(tx.objectStore('operations').getAll()),
|
|
100
|
-
storeRequestPromise(tx.objectStore('info').getAll()),
|
|
101
|
-
]);
|
|
102
|
-
|
|
103
|
-
const wipTx = result.db.transaction(
|
|
104
|
-
['baselines', 'operations', 'info'],
|
|
105
|
-
'readwrite',
|
|
106
|
-
);
|
|
107
|
-
const wipBaselines = wipTx.objectStore('baselines');
|
|
108
|
-
const wipOperations = wipTx.objectStore('operations');
|
|
109
|
-
const wipInfo = wipTx.objectStore('info');
|
|
110
|
-
|
|
111
|
-
for (const baseline of baselines) {
|
|
112
|
-
wipBaselines.put(baseline);
|
|
113
|
-
}
|
|
114
|
-
for (const operation of operations) {
|
|
115
|
-
wipOperations.put(operation);
|
|
116
|
-
}
|
|
117
|
-
for (const infoItem of info) {
|
|
118
|
-
wipInfo.put(infoItem);
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
await new Promise<void>((resolve, reject) => {
|
|
122
|
-
wipTx.oncomplete = () => {
|
|
123
|
-
resolve();
|
|
124
|
-
};
|
|
125
|
-
wipTx.onerror = (event) => {
|
|
126
|
-
reject(event);
|
|
127
|
-
};
|
|
128
|
-
wipTx.onabort = (event) => {
|
|
129
|
-
reject(event);
|
|
130
|
-
};
|
|
131
|
-
});
|
|
132
|
-
|
|
133
|
-
await closeDatabase(prodDb);
|
|
134
|
-
|
|
135
|
-
log?.(
|
|
136
|
-
'debug',
|
|
137
|
-
'Finished copy of production metadata database to WIP. Copied:',
|
|
138
|
-
baselines.length,
|
|
139
|
-
'baselines,',
|
|
140
|
-
operations.length,
|
|
141
|
-
'operations',
|
|
142
|
-
);
|
|
143
|
-
|
|
144
|
-
return result;
|
|
145
|
-
}
|
|
146
|
-
|
|
147
53
|
async function version1(db: IDBDatabase, tx: IDBTransaction) {
|
|
148
54
|
const baselinesStore = db.createObjectStore('baselines', {
|
|
149
55
|
keyPath: 'oid',
|