@verdant-web/store 3.12.1 → 4.0.0-next.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/dist/bundle/index.js +11 -13
- package/dist/bundle/index.js.map +4 -4
- package/dist/esm/__tests__/batching.test.js +5 -5
- package/dist/esm/__tests__/batching.test.js.map +1 -1
- package/dist/esm/__tests__/entities.test.js +1 -1
- package/dist/esm/__tests__/entities.test.js.map +1 -1
- package/dist/esm/__tests__/fixtures/testStorage.d.ts +1 -3
- package/dist/esm/__tests__/fixtures/testStorage.js +3 -3
- package/dist/esm/__tests__/fixtures/testStorage.js.map +1 -1
- package/dist/esm/__tests__/queries.test.js.map +1 -1
- package/dist/esm/backup.d.ts +3 -4
- package/dist/esm/backup.js.map +1 -1
- package/dist/esm/client/Client.d.ts +28 -33
- package/dist/esm/client/Client.js +50 -161
- package/dist/esm/client/Client.js.map +1 -1
- package/dist/esm/client/ClientDescriptor.d.ts +8 -11
- package/dist/esm/client/ClientDescriptor.js +39 -141
- package/dist/esm/client/ClientDescriptor.js.map +1 -1
- package/dist/esm/context/Time.d.ts +13 -0
- package/dist/esm/context/Time.js +27 -0
- package/dist/esm/context/Time.js.map +1 -0
- package/dist/esm/context/context.d.ts +170 -0
- package/dist/esm/{context.js.map → context/context.js.map} +1 -1
- package/dist/esm/entities/DocumentManager.js.map +1 -1
- package/dist/esm/entities/Entity.d.ts +4 -5
- package/dist/esm/entities/Entity.js +5 -3
- package/dist/esm/entities/Entity.js.map +1 -1
- package/dist/esm/entities/Entity.test.js +4 -3
- package/dist/esm/entities/Entity.test.js.map +1 -1
- package/dist/esm/entities/EntityCache.d.ts +0 -3
- package/dist/esm/entities/EntityCache.js +0 -9
- package/dist/esm/entities/EntityCache.js.map +1 -1
- package/dist/esm/entities/EntityMetadata.d.ts +1 -1
- package/dist/esm/entities/EntityMetadata.js +6 -5
- package/dist/esm/entities/EntityMetadata.js.map +1 -1
- package/dist/esm/entities/EntityStore.d.ts +2 -6
- package/dist/esm/entities/EntityStore.js +22 -16
- package/dist/esm/entities/EntityStore.js.map +1 -1
- package/dist/esm/entities/OperationBatcher.d.ts +2 -5
- package/dist/esm/entities/OperationBatcher.js +9 -7
- package/dist/esm/entities/OperationBatcher.js.map +1 -1
- package/dist/esm/errors.d.ts +8 -0
- package/dist/esm/errors.js +12 -0
- package/dist/esm/errors.js.map +1 -0
- package/dist/esm/files/EntityFile.d.ts +6 -3
- package/dist/esm/files/EntityFile.js +22 -19
- package/dist/esm/files/EntityFile.js.map +1 -1
- package/dist/esm/files/FileManager.d.ts +8 -39
- package/dist/esm/files/FileManager.js +15 -170
- package/dist/esm/files/FileManager.js.map +1 -1
- package/dist/esm/files/utils.d.ts +0 -1
- package/dist/esm/files/utils.js +0 -14
- package/dist/esm/files/utils.js.map +1 -1
- package/dist/esm/index.d.ts +1 -2
- package/dist/esm/index.js +0 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/{metadata → persistence}/MessageCreator.d.ts +5 -6
- package/dist/esm/{metadata → persistence}/MessageCreator.js +31 -38
- package/dist/esm/persistence/MessageCreator.js.map +1 -0
- package/dist/esm/persistence/PersistenceFiles.d.ts +48 -0
- package/dist/esm/persistence/PersistenceFiles.js +160 -0
- package/dist/esm/persistence/PersistenceFiles.js.map +1 -0
- package/dist/esm/persistence/PersistenceMetadata.d.ts +69 -0
- package/dist/esm/persistence/PersistenceMetadata.js +302 -0
- package/dist/esm/persistence/PersistenceMetadata.js.map +1 -0
- package/dist/esm/persistence/PersistenceQueries.d.ts +34 -0
- package/dist/esm/persistence/PersistenceQueries.js +15 -0
- package/dist/esm/persistence/PersistenceQueries.js.map +1 -0
- package/dist/esm/persistence/PersistenceRebaser.d.ts +32 -0
- package/dist/esm/persistence/PersistenceRebaser.js +120 -0
- package/dist/esm/persistence/PersistenceRebaser.js.map +1 -0
- package/dist/esm/{IDBService.d.ts → persistence/idb/IdbService.d.ts} +9 -7
- package/dist/esm/{IDBService.js → persistence/idb/IdbService.js} +29 -8
- package/dist/esm/persistence/idb/IdbService.js.map +1 -0
- package/dist/esm/persistence/idb/files/IdbPersistenceFileDb.d.ts +58 -0
- package/dist/esm/{files/FileStorage.js → persistence/idb/files/IdbPersistenceFileDb.js} +85 -50
- package/dist/esm/persistence/idb/files/IdbPersistenceFileDb.js.map +1 -0
- package/dist/esm/persistence/idb/idbPersistence.d.ts +19 -0
- package/dist/esm/persistence/idb/idbPersistence.js +80 -0
- package/dist/esm/persistence/idb/idbPersistence.js.map +1 -0
- package/dist/esm/persistence/idb/metadata/IdbMetadataDb.d.ts +72 -0
- package/dist/esm/persistence/idb/metadata/IdbMetadataDb.js +235 -0
- package/dist/esm/persistence/idb/metadata/IdbMetadataDb.js.map +1 -0
- package/dist/esm/{metadata → persistence/idb/metadata}/openMetadataDatabase.d.ts +3 -1
- package/dist/esm/{metadata → persistence/idb/metadata}/openMetadataDatabase.js +12 -3
- package/dist/esm/persistence/idb/metadata/openMetadataDatabase.js.map +1 -0
- package/dist/esm/persistence/idb/queries/IdbQueryDb.d.ts +41 -0
- package/dist/esm/persistence/idb/queries/IdbQueryDb.js +174 -0
- package/dist/esm/persistence/idb/queries/IdbQueryDb.js.map +1 -0
- package/dist/esm/{migration → persistence/idb/queries/migration}/db.d.ts +1 -1
- package/dist/esm/{migration → persistence/idb/queries/migration}/db.js +10 -48
- package/dist/esm/persistence/idb/queries/migration/db.js.map +1 -0
- package/dist/esm/persistence/idb/queries/migration/engine.d.ts +12 -0
- package/dist/esm/{migration → persistence/idb/queries/migration}/engine.js +29 -46
- package/dist/esm/persistence/idb/queries/migration/engine.js.map +1 -0
- package/dist/esm/{migration → persistence/idb/queries/migration}/migrations.d.ts +1 -3
- package/dist/esm/{migration → persistence/idb/queries/migration}/migrations.js +11 -10
- package/dist/esm/persistence/idb/queries/migration/migrations.js.map +1 -0
- package/dist/esm/{migration → persistence/idb/queries/migration}/openQueryDatabase.d.ts +1 -3
- package/dist/esm/{migration → persistence/idb/queries/migration}/openQueryDatabase.js +4 -7
- package/dist/esm/persistence/idb/queries/migration/openQueryDatabase.js.map +1 -0
- package/dist/esm/{migration → persistence/idb/queries/migration}/paths.js +2 -2
- package/dist/esm/persistence/idb/queries/migration/paths.js.map +1 -0
- package/dist/esm/persistence/idb/queries/migration/paths.test.js.map +1 -0
- package/dist/esm/persistence/idb/queries/migration/types.d.ts +6 -0
- package/dist/esm/persistence/idb/queries/migration/types.js.map +1 -0
- package/dist/esm/persistence/idb/queries/ranges.d.ts +2 -0
- package/dist/esm/persistence/idb/queries/ranges.js +66 -0
- package/dist/esm/persistence/idb/queries/ranges.js.map +1 -0
- package/dist/esm/{idb.d.ts → persistence/idb/util.d.ts} +11 -0
- package/dist/esm/{idb.js → persistence/idb/util.js} +58 -1
- package/dist/esm/persistence/idb/util.js.map +1 -0
- package/dist/esm/persistence/interfaces.d.ts +181 -0
- package/dist/esm/persistence/interfaces.js +2 -0
- package/dist/esm/persistence/interfaces.js.map +1 -0
- package/dist/esm/persistence/persistence.d.ts +4 -0
- package/dist/esm/persistence/persistence.js +126 -0
- package/dist/esm/persistence/persistence.js.map +1 -0
- package/dist/esm/queries/BaseQuery.d.ts +2 -1
- package/dist/esm/queries/BaseQuery.js +3 -0
- package/dist/esm/queries/BaseQuery.js.map +1 -1
- package/dist/esm/queries/CollectionQueries.d.ts +1 -1
- package/dist/esm/queries/FindAllQuery.js +1 -3
- package/dist/esm/queries/FindAllQuery.js.map +1 -1
- package/dist/esm/queries/FindInfiniteQuery.js +2 -5
- package/dist/esm/queries/FindInfiniteQuery.js.map +1 -1
- package/dist/esm/queries/FindOneQuery.js +1 -3
- package/dist/esm/queries/FindOneQuery.js.map +1 -1
- package/dist/esm/queries/FindPageQuery.js +1 -3
- package/dist/esm/queries/FindPageQuery.js.map +1 -1
- package/dist/esm/queries/QueryCache.d.ts +1 -1
- package/dist/esm/queries/QueryCache.js +4 -0
- package/dist/esm/queries/QueryCache.js.map +1 -1
- package/dist/esm/sync/FileSync.d.ts +23 -8
- package/dist/esm/sync/FileSync.js +76 -28
- package/dist/esm/sync/FileSync.js.map +1 -1
- package/dist/esm/sync/PresenceManager.d.ts +4 -3
- package/dist/esm/sync/PresenceManager.js +2 -2
- package/dist/esm/sync/PresenceManager.js.map +1 -1
- package/dist/esm/sync/PushPullSync.d.ts +4 -6
- package/dist/esm/sync/PushPullSync.js +13 -12
- package/dist/esm/sync/PushPullSync.js.map +1 -1
- package/dist/esm/sync/Sync.d.ts +9 -11
- package/dist/esm/sync/Sync.js +34 -29
- package/dist/esm/sync/Sync.js.map +1 -1
- package/dist/esm/sync/WebSocketSync.d.ts +4 -6
- package/dist/esm/sync/WebSocketSync.js +20 -22
- package/dist/esm/sync/WebSocketSync.js.map +1 -1
- package/dist/esm/utils/Disposable.d.ts +5 -2
- package/dist/esm/utils/Disposable.js +3 -2
- package/dist/esm/utils/Disposable.js.map +1 -1
- package/dist/esm/utils/wip.d.ts +2 -0
- package/dist/esm/utils/wip.js +5 -0
- package/dist/esm/utils/wip.js.map +1 -0
- package/package.json +2 -2
- package/src/__tests__/batching.test.ts +6 -6
- package/src/__tests__/entities.test.ts +1 -1
- package/src/__tests__/fixtures/testStorage.ts +2 -10
- package/src/__tests__/queries.test.ts +1 -1
- package/src/backup.ts +3 -4
- package/src/client/Client.ts +69 -226
- package/src/client/ClientDescriptor.ts +53 -184
- package/src/context/Time.ts +35 -0
- package/src/context/context.ts +200 -0
- package/src/entities/DocumentManager.ts +0 -3
- package/src/entities/Entity.test.ts +9 -9
- package/src/entities/Entity.ts +6 -12
- package/src/entities/EntityCache.ts +0 -9
- package/src/entities/EntityMetadata.ts +4 -4
- package/src/entities/EntityStore.ts +26 -29
- package/src/entities/OperationBatcher.ts +9 -11
- package/src/errors.ts +13 -0
- package/src/files/EntityFile.ts +16 -5
- package/src/files/FileManager.ts +18 -245
- package/src/files/utils.ts +0 -15
- package/src/index.ts +2 -1
- package/src/{metadata → persistence}/MessageCreator.ts +46 -36
- package/src/persistence/PersistenceFiles.ts +227 -0
- package/src/persistence/PersistenceMetadata.ts +425 -0
- package/src/persistence/PersistenceQueries.ts +22 -0
- package/src/persistence/PersistenceRebaser.ts +171 -0
- package/src/{IDBService.ts → persistence/idb/IdbService.ts} +45 -12
- package/src/{files/FileStorage.ts → persistence/idb/files/IdbPersistenceFileDb.ts} +128 -86
- package/src/persistence/idb/idbPersistence.ts +116 -0
- package/src/persistence/idb/metadata/IdbMetadataDb.ts +460 -0
- package/src/{metadata → persistence/idb/metadata}/openMetadataDatabase.ts +21 -4
- package/src/persistence/idb/queries/IdbQueryDb.ts +251 -0
- package/src/{migration → persistence/idb/queries/migration}/db.ts +18 -72
- package/src/{migration → persistence/idb/queries/migration}/engine.ts +39 -62
- package/src/{migration → persistence/idb/queries/migration}/migrations.ts +13 -18
- package/src/{migration → persistence/idb/queries/migration}/openQueryDatabase.ts +5 -14
- package/src/{migration → persistence/idb/queries/migration}/paths.ts +4 -3
- package/src/persistence/idb/queries/migration/types.ts +8 -0
- package/src/persistence/idb/queries/ranges.ts +107 -0
- package/src/{idb.ts → persistence/idb/util.ts} +75 -0
- package/src/persistence/interfaces.ts +240 -0
- package/src/persistence/persistence.ts +223 -0
- package/src/queries/BaseQuery.ts +5 -1
- package/src/queries/CollectionQueries.ts +2 -2
- package/src/queries/FindAllQuery.ts +1 -3
- package/src/queries/FindInfiniteQuery.ts +2 -5
- package/src/queries/FindOneQuery.ts +1 -3
- package/src/queries/FindPageQuery.ts +1 -3
- package/src/queries/QueryCache.ts +20 -1
- package/src/sync/FileSync.ts +93 -30
- package/src/sync/PresenceManager.ts +5 -7
- package/src/sync/PushPullSync.ts +23 -19
- package/src/sync/Sync.ts +45 -36
- package/src/sync/WebSocketSync.ts +41 -27
- package/src/utils/Disposable.ts +7 -4
- package/src/utils/wip.ts +5 -0
- package/dist/esm/IDBService.js.map +0 -1
- package/dist/esm/__tests__/legacyOids.test.d.ts +0 -1
- package/dist/esm/__tests__/legacyOids.test.js +0 -352
- package/dist/esm/__tests__/legacyOids.test.js.map +0 -1
- package/dist/esm/context.d.ts +0 -45
- package/dist/esm/files/FileStorage.d.ts +0 -47
- package/dist/esm/files/FileStorage.js.map +0 -1
- package/dist/esm/idb.js.map +0 -1
- package/dist/esm/metadata/AckInfoStore.d.ts +0 -10
- package/dist/esm/metadata/AckInfoStore.js +0 -22
- package/dist/esm/metadata/AckInfoStore.js.map +0 -1
- package/dist/esm/metadata/BaselinesStore.d.ts +0 -40
- package/dist/esm/metadata/BaselinesStore.js +0 -102
- package/dist/esm/metadata/BaselinesStore.js.map +0 -1
- package/dist/esm/metadata/LocalReplicaStore.d.ts +0 -19
- package/dist/esm/metadata/LocalReplicaStore.js +0 -56
- package/dist/esm/metadata/LocalReplicaStore.js.map +0 -1
- package/dist/esm/metadata/MessageCreator.js.map +0 -1
- package/dist/esm/metadata/Metadata.d.ts +0 -146
- package/dist/esm/metadata/Metadata.js +0 -452
- package/dist/esm/metadata/Metadata.js.map +0 -1
- package/dist/esm/metadata/OperationsStore.d.ts +0 -62
- package/dist/esm/metadata/OperationsStore.js +0 -175
- package/dist/esm/metadata/OperationsStore.js.map +0 -1
- package/dist/esm/metadata/SchemaStore.d.ts +0 -9
- package/dist/esm/metadata/SchemaStore.js +0 -35
- package/dist/esm/metadata/SchemaStore.js.map +0 -1
- package/dist/esm/metadata/openMetadataDatabase.js.map +0 -1
- package/dist/esm/migration/db.js.map +0 -1
- package/dist/esm/migration/engine.d.ts +0 -15
- package/dist/esm/migration/engine.js.map +0 -1
- package/dist/esm/migration/errors.d.ts +0 -5
- package/dist/esm/migration/errors.js +0 -8
- package/dist/esm/migration/errors.js.map +0 -1
- package/dist/esm/migration/migrations.js.map +0 -1
- package/dist/esm/migration/openQueryDatabase.js.map +0 -1
- package/dist/esm/migration/openWIPDatabase.d.ts +0 -11
- package/dist/esm/migration/openWIPDatabase.js +0 -65
- package/dist/esm/migration/openWIPDatabase.js.map +0 -1
- package/dist/esm/migration/paths.js.map +0 -1
- package/dist/esm/migration/paths.test.js.map +0 -1
- package/dist/esm/migration/types.d.ts +0 -3
- package/dist/esm/migration/types.js.map +0 -1
- package/dist/esm/queries/QueryableStorage.d.ts +0 -20
- package/dist/esm/queries/QueryableStorage.js +0 -90
- package/dist/esm/queries/QueryableStorage.js.map +0 -1
- package/dist/esm/queries/dbQueries.d.ts +0 -22
- package/dist/esm/queries/dbQueries.js +0 -130
- package/dist/esm/queries/dbQueries.js.map +0 -1
- package/src/__tests__/legacyOids.test.ts +0 -375
- package/src/context.ts +0 -55
- package/src/metadata/AckInfoStore.ts +0 -30
- package/src/metadata/BaselinesStore.ts +0 -188
- package/src/metadata/LocalReplicaStore.ts +0 -79
- package/src/metadata/Metadata.ts +0 -685
- package/src/metadata/OperationsStore.ts +0 -332
- package/src/metadata/SchemaStore.ts +0 -47
- package/src/migration/errors.ts +0 -7
- package/src/migration/openWIPDatabase.ts +0 -97
- package/src/migration/types.ts +0 -4
- package/src/queries/QueryableStorage.ts +0 -122
- package/src/queries/dbQueries.ts +0 -161
- /package/dist/esm/{context.js → context/context.js} +0 -0
- /package/dist/esm/{migration → persistence/idb/queries/migration}/paths.d.ts +0 -0
- /package/dist/esm/{migration → persistence/idb/queries/migration}/paths.test.d.ts +0 -0
- /package/dist/esm/{migration → persistence/idb/queries/migration}/paths.test.js +0 -0
- /package/dist/esm/{migration → persistence/idb/queries/migration}/types.js +0 -0
- /package/src/{migration → persistence/idb/queries/migration}/paths.test.ts +0 -0
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { Migration } from '@verdant-web/common';
|
|
2
|
-
import { Metadata } from '../metadata/Metadata.js';
|
|
3
2
|
import { getDatabaseVersion, openDatabase } from './db.js';
|
|
4
3
|
import { runMigrations } from './migrations.js';
|
|
5
4
|
import { getMigrationPath } from './paths.js';
|
|
@@ -12,32 +11,24 @@ export async function openQueryDatabase({
|
|
|
12
11
|
version,
|
|
13
12
|
indexedDB = globalIDB,
|
|
14
13
|
migrations,
|
|
15
|
-
meta,
|
|
16
14
|
context,
|
|
17
15
|
}: {
|
|
18
16
|
version: number;
|
|
19
17
|
migrations: Migration<any>[];
|
|
20
18
|
indexedDB?: IDBFactory;
|
|
21
|
-
meta: Metadata;
|
|
22
19
|
context: OpenDocumentDbContext;
|
|
23
20
|
}) {
|
|
24
|
-
|
|
25
|
-
throw new Error('Cannot open a production client with a WIP schema!');
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
const currentVersion = await getDatabaseVersion(
|
|
29
|
-
indexedDB,
|
|
30
|
-
context.namespace,
|
|
31
|
-
version,
|
|
32
|
-
context.log,
|
|
33
|
-
);
|
|
21
|
+
const currentVersion = await getDatabaseVersion(indexedDB, context.namespace);
|
|
34
22
|
|
|
35
23
|
context.log(
|
|
36
24
|
'debug',
|
|
25
|
+
'Opening index database',
|
|
26
|
+
context.namespace,
|
|
37
27
|
'Current database version:',
|
|
38
28
|
currentVersion,
|
|
39
29
|
'target version:',
|
|
40
30
|
version,
|
|
31
|
+
context.schema.wip ? '(wip)' : '',
|
|
41
32
|
);
|
|
42
33
|
|
|
43
34
|
const toRun = getMigrationPath({
|
|
@@ -52,7 +43,7 @@ export async function openQueryDatabase({
|
|
|
52
43
|
'Migrations to run:',
|
|
53
44
|
toRun.map((m) => m.version),
|
|
54
45
|
);
|
|
55
|
-
await runMigrations({ context, toRun,
|
|
46
|
+
await runMigrations({ context, toRun, indexedDB });
|
|
56
47
|
}
|
|
57
48
|
return openDatabase({
|
|
58
49
|
indexedDB,
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import { Migration } from '@verdant-web/common';
|
|
2
|
-
import { MigrationPathError } from './errors.js';
|
|
1
|
+
import { Migration, VerdantError } from '@verdant-web/common';
|
|
3
2
|
|
|
4
3
|
export function getMigrationPath({
|
|
5
4
|
currentVersion,
|
|
@@ -16,7 +15,9 @@ export function getMigrationPath({
|
|
|
16
15
|
migrations,
|
|
17
16
|
});
|
|
18
17
|
if (!path) {
|
|
19
|
-
throw new
|
|
18
|
+
throw new VerdantError(
|
|
19
|
+
VerdantError.Code.MigrationPathNotFound,
|
|
20
|
+
undefined,
|
|
20
21
|
`No migration path found from ${currentVersion} to ${targetVersion}! This is a bug. If you're seeing this, contact the developer and provide them with the full contents of this message.`,
|
|
21
22
|
);
|
|
22
23
|
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import {
|
|
2
|
+
assert,
|
|
3
|
+
CollectionCompoundIndexFilter,
|
|
4
|
+
CollectionFilter,
|
|
5
|
+
createCompoundIndexValue,
|
|
6
|
+
createLowerBoundIndexValue,
|
|
7
|
+
createUpperBoundIndexValue,
|
|
8
|
+
isMatchIndexFilter,
|
|
9
|
+
isRangeIndexFilter,
|
|
10
|
+
isSortIndexFilter,
|
|
11
|
+
isStartsWithIndexFilter,
|
|
12
|
+
MatchCollectionIndexFilter,
|
|
13
|
+
RangeCollectionIndexFilter,
|
|
14
|
+
sanitizeIndexValue,
|
|
15
|
+
SortIndexFilter,
|
|
16
|
+
StartsWithIndexFilter,
|
|
17
|
+
StorageSchema,
|
|
18
|
+
} from '@verdant-web/common';
|
|
19
|
+
|
|
20
|
+
const matchIndexToIdbKeyRange = (filter: MatchCollectionIndexFilter) => {
|
|
21
|
+
return IDBKeyRange.only(sanitizeIndexValue(filter.equals));
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const sortIndexToIdbKeyRange = (filter: SortIndexFilter) => {
|
|
25
|
+
return undefined;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const rangeIndexToIdbKeyRange = (filter: RangeCollectionIndexFilter) => {
|
|
29
|
+
const lower = filter.gte || filter.gt;
|
|
30
|
+
const upper = filter.lte || filter.lt;
|
|
31
|
+
if (lower === upper) {
|
|
32
|
+
return IDBKeyRange.only(sanitizeIndexValue(lower));
|
|
33
|
+
}
|
|
34
|
+
if (!lower) {
|
|
35
|
+
return IDBKeyRange.upperBound(sanitizeIndexValue(upper), !!filter.lt);
|
|
36
|
+
} else if (!upper) {
|
|
37
|
+
return IDBKeyRange.lowerBound(sanitizeIndexValue(lower), !!filter.gt);
|
|
38
|
+
} else {
|
|
39
|
+
return IDBKeyRange.bound(
|
|
40
|
+
sanitizeIndexValue(lower),
|
|
41
|
+
sanitizeIndexValue(upper),
|
|
42
|
+
!!filter.gt,
|
|
43
|
+
!!filter.lt,
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const compoundIndexToIdbKeyRange = (
|
|
49
|
+
// FIXME:
|
|
50
|
+
schema: any,
|
|
51
|
+
collection: string,
|
|
52
|
+
filter: CollectionCompoundIndexFilter,
|
|
53
|
+
) => {
|
|
54
|
+
// validate the usage of the compound index:
|
|
55
|
+
// - all match fields must be contiguous at the start of the compound order
|
|
56
|
+
const indexDefinition =
|
|
57
|
+
schema.collections[collection].compounds[filter.where];
|
|
58
|
+
assert(
|
|
59
|
+
indexDefinition,
|
|
60
|
+
`Index ${filter.where} does not exist on collection ${collection}`,
|
|
61
|
+
);
|
|
62
|
+
const matchedKeys = Object.keys(filter.match).sort(
|
|
63
|
+
(a, b) => indexDefinition.of.indexOf(a) - indexDefinition.of.indexOf(b),
|
|
64
|
+
);
|
|
65
|
+
for (const key of matchedKeys) {
|
|
66
|
+
if (indexDefinition.of.indexOf(key) !== matchedKeys.indexOf(key)) {
|
|
67
|
+
throw new Error(
|
|
68
|
+
`Compound index ${filter.where} does not have ${key} at the start of its order`,
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const matchedValues = matchedKeys.map(
|
|
74
|
+
(key) => filter.match[key as keyof typeof filter.match] as string | number,
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
// special case: all match fields are specified - we don't need a range
|
|
78
|
+
// query, just a single key query
|
|
79
|
+
if (matchedKeys.length === indexDefinition.of.length) {
|
|
80
|
+
return IDBKeyRange.only(createCompoundIndexValue(...matchedValues));
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// create our bounds for the matched values
|
|
84
|
+
const lower = createLowerBoundIndexValue(...matchedValues);
|
|
85
|
+
const upper = createUpperBoundIndexValue(...matchedValues);
|
|
86
|
+
return IDBKeyRange.bound(lower, upper);
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
function startsWithIndexToIdbKeyRange(filter: StartsWithIndexFilter) {
|
|
90
|
+
const lower = filter.startsWith;
|
|
91
|
+
const upper = filter.startsWith + '\uffff';
|
|
92
|
+
return IDBKeyRange.bound(lower, upper);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export function getRange(
|
|
96
|
+
schema: StorageSchema,
|
|
97
|
+
collection: string,
|
|
98
|
+
index?: CollectionFilter,
|
|
99
|
+
) {
|
|
100
|
+
if (!index) return undefined;
|
|
101
|
+
if (isRangeIndexFilter(index)) return rangeIndexToIdbKeyRange(index);
|
|
102
|
+
if (isMatchIndexFilter(index)) return matchIndexToIdbKeyRange(index);
|
|
103
|
+
if (isSortIndexFilter(index)) return sortIndexToIdbKeyRange(index);
|
|
104
|
+
if (isStartsWithIndexFilter(index))
|
|
105
|
+
return startsWithIndexToIdbKeyRange(index);
|
|
106
|
+
return compoundIndexToIdbKeyRange(schema, collection, index);
|
|
107
|
+
}
|
|
@@ -93,6 +93,20 @@ export function getSizeOfObjectStore(
|
|
|
93
93
|
});
|
|
94
94
|
}
|
|
95
95
|
|
|
96
|
+
export async function getSizesOfAllObjectStores(
|
|
97
|
+
database: IDBDatabase,
|
|
98
|
+
): Promise<{ [storeName: string]: { count: number; size: number } }> {
|
|
99
|
+
const storeNames = Array.from(database.objectStoreNames);
|
|
100
|
+
const promises = storeNames.map(async (storeName) => {
|
|
101
|
+
const result = await getSizeOfObjectStore(database, storeName);
|
|
102
|
+
return { [storeName]: result };
|
|
103
|
+
});
|
|
104
|
+
const results = await Promise.all(promises);
|
|
105
|
+
return results.reduce((acc, result_1) => {
|
|
106
|
+
return { ...acc, ...result_1 };
|
|
107
|
+
}, {});
|
|
108
|
+
}
|
|
109
|
+
|
|
96
110
|
export function getAllFromObjectStores(db: IDBDatabase, stores: string[]) {
|
|
97
111
|
const transaction = db.transaction(stores, 'readonly');
|
|
98
112
|
const promises = stores.map((store) => {
|
|
@@ -182,3 +196,64 @@ export function emptyDatabase(db: IDBDatabase) {
|
|
|
182
196
|
tx.onerror = () => reject(tx.error);
|
|
183
197
|
});
|
|
184
198
|
}
|
|
199
|
+
|
|
200
|
+
export async function copyDatabase(from: IDBDatabase, to: IDBDatabase) {
|
|
201
|
+
await emptyDatabase(to);
|
|
202
|
+
const records = await getAllFromObjectStores(
|
|
203
|
+
from,
|
|
204
|
+
Array.from(from.objectStoreNames),
|
|
205
|
+
);
|
|
206
|
+
const writeTx = to.transaction(Array.from(to.objectStoreNames), 'readwrite');
|
|
207
|
+
for (let i = 0; i < records.length; i++) {
|
|
208
|
+
const store = writeTx.objectStore(from.objectStoreNames[i]);
|
|
209
|
+
for (const record of records[i]) {
|
|
210
|
+
store.add(record);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
return new Promise<void>((resolve, reject) => {
|
|
214
|
+
writeTx.oncomplete = () => resolve();
|
|
215
|
+
writeTx.onerror = () => reject(writeTx.error);
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
export function openDatabase(
|
|
220
|
+
name: string,
|
|
221
|
+
expectedVersion: number,
|
|
222
|
+
indexedDB: IDBFactory = window.indexedDB,
|
|
223
|
+
) {
|
|
224
|
+
return new Promise<IDBDatabase>((resolve, reject) => {
|
|
225
|
+
const req = indexedDB.open(name, expectedVersion);
|
|
226
|
+
req.onsuccess = () => {
|
|
227
|
+
resolve(req.result);
|
|
228
|
+
};
|
|
229
|
+
req.onerror = () => {
|
|
230
|
+
reject(req.error);
|
|
231
|
+
};
|
|
232
|
+
req.onblocked = () => {
|
|
233
|
+
reject(new Error('Database blocked'));
|
|
234
|
+
};
|
|
235
|
+
req.onupgradeneeded = (event) => {
|
|
236
|
+
const db = req.result;
|
|
237
|
+
if (db.version !== expectedVersion) {
|
|
238
|
+
db.close();
|
|
239
|
+
reject(
|
|
240
|
+
new Error(
|
|
241
|
+
`Migration error: database version changed unexpectedly when reading current data. Expected ${expectedVersion}, got ${db.version}`,
|
|
242
|
+
),
|
|
243
|
+
);
|
|
244
|
+
}
|
|
245
|
+
};
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
export function getMetadataDbName(namespace: string) {
|
|
250
|
+
return [namespace, 'meta'].join('_');
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
export function getDocumentDbName(namespace: string) {
|
|
254
|
+
return [namespace, 'collections'].join('_');
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
export function getNamespaceFromDatabaseInfo(info: IDBDatabaseInfo) {
|
|
258
|
+
return info.name?.split('_')[0];
|
|
259
|
+
}
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CollectionFilter,
|
|
3
|
+
DocumentBaseline,
|
|
4
|
+
FileData,
|
|
5
|
+
ObjectIdentifier,
|
|
6
|
+
Operation,
|
|
7
|
+
} from '@verdant-web/common';
|
|
8
|
+
import { Context, InitialContext } from '../context/context.js';
|
|
9
|
+
|
|
10
|
+
export interface AckInfo {
|
|
11
|
+
type: 'ack';
|
|
12
|
+
globalAckTimestamp: string | null;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface LocalReplicaInfo {
|
|
16
|
+
type: 'localReplicaInfo';
|
|
17
|
+
id: string;
|
|
18
|
+
userId: string | undefined;
|
|
19
|
+
ackedLogicalTime: string | null;
|
|
20
|
+
lastSyncedLogicalTime: string | null;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export type ClientOperation = Operation & {
|
|
24
|
+
isLocal: boolean;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export interface MetadataExport {
|
|
28
|
+
operations: Operation[];
|
|
29
|
+
baselines: DocumentBaseline[];
|
|
30
|
+
localReplica?: LocalReplicaInfo;
|
|
31
|
+
schemaVersion: number;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface ExportedData {
|
|
35
|
+
data: MetadataExport;
|
|
36
|
+
fileData: Array<Omit<PersistedFileData, 'file'>>;
|
|
37
|
+
files: File[];
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export type AbstractTransaction = unknown;
|
|
41
|
+
export type QueryMode = 'readwrite' | 'readonly';
|
|
42
|
+
export interface CommonQueryOptions {
|
|
43
|
+
transaction?: AbstractTransaction;
|
|
44
|
+
mode?: QueryMode;
|
|
45
|
+
}
|
|
46
|
+
export type Iterator<T> = (item: T) => void | boolean;
|
|
47
|
+
|
|
48
|
+
export interface PersistenceMetadataDb {
|
|
49
|
+
transaction(opts: {
|
|
50
|
+
mode?: QueryMode;
|
|
51
|
+
storeNames: string[];
|
|
52
|
+
abort?: AbortSignal;
|
|
53
|
+
}): AbstractTransaction;
|
|
54
|
+
dispose(): void | Promise<void>;
|
|
55
|
+
|
|
56
|
+
// infos
|
|
57
|
+
getAckInfo(): Promise<AckInfo>;
|
|
58
|
+
setGlobalAck(ack: string): Promise<void>;
|
|
59
|
+
getLocalReplica(opts?: CommonQueryOptions): Promise<LocalReplicaInfo>;
|
|
60
|
+
updateLocalReplica(
|
|
61
|
+
data: Partial<LocalReplicaInfo>,
|
|
62
|
+
opts?: CommonQueryOptions,
|
|
63
|
+
): Promise<void>;
|
|
64
|
+
|
|
65
|
+
// baselines
|
|
66
|
+
iterateDocumentBaselines(
|
|
67
|
+
rootOid: string,
|
|
68
|
+
iterator: Iterator<DocumentBaseline>,
|
|
69
|
+
opts?: CommonQueryOptions,
|
|
70
|
+
): Promise<void>;
|
|
71
|
+
iterateCollectionBaselines(
|
|
72
|
+
collection: string,
|
|
73
|
+
iterator: Iterator<DocumentBaseline>,
|
|
74
|
+
opts?: CommonQueryOptions,
|
|
75
|
+
): Promise<void>;
|
|
76
|
+
iterateAllBaselines(
|
|
77
|
+
iterator: Iterator<DocumentBaseline>,
|
|
78
|
+
opts?: CommonQueryOptions,
|
|
79
|
+
): Promise<void>;
|
|
80
|
+
getBaseline(
|
|
81
|
+
oid: string,
|
|
82
|
+
opts?: CommonQueryOptions,
|
|
83
|
+
): Promise<DocumentBaseline>;
|
|
84
|
+
setBaselines(
|
|
85
|
+
baselines: DocumentBaseline[],
|
|
86
|
+
opts?: CommonQueryOptions,
|
|
87
|
+
): Promise<void>;
|
|
88
|
+
deleteBaseline(oid: string, opts?: CommonQueryOptions): Promise<void>;
|
|
89
|
+
|
|
90
|
+
// operations
|
|
91
|
+
iterateDocumentOperations(
|
|
92
|
+
rootOid: string,
|
|
93
|
+
iterator: Iterator<ClientOperation>,
|
|
94
|
+
opts?: CommonQueryOptions & {
|
|
95
|
+
to?: string | null;
|
|
96
|
+
},
|
|
97
|
+
): Promise<void>;
|
|
98
|
+
iterateEntityOperations(
|
|
99
|
+
oid: string,
|
|
100
|
+
iterator: Iterator<ClientOperation>,
|
|
101
|
+
opts?: CommonQueryOptions & { to?: string | null },
|
|
102
|
+
): Promise<void>;
|
|
103
|
+
iterateCollectionOperations(
|
|
104
|
+
collection: string,
|
|
105
|
+
iterator: Iterator<ClientOperation>,
|
|
106
|
+
opts?: CommonQueryOptions,
|
|
107
|
+
): Promise<void>;
|
|
108
|
+
iterateLocalOperations(
|
|
109
|
+
iterator: Iterator<ClientOperation>,
|
|
110
|
+
opts?: CommonQueryOptions & {
|
|
111
|
+
before?: string | null;
|
|
112
|
+
after?: string | null;
|
|
113
|
+
},
|
|
114
|
+
): Promise<void>;
|
|
115
|
+
/** Iterates over operations for an entity for processing and deletes them as it goes. */
|
|
116
|
+
consumeEntityOperations(
|
|
117
|
+
oid: string,
|
|
118
|
+
iterator: Iterator<ClientOperation>,
|
|
119
|
+
opts?: CommonQueryOptions & { to?: string | null },
|
|
120
|
+
): Promise<void>;
|
|
121
|
+
iterateAllOperations(
|
|
122
|
+
iterator: Iterator<ClientOperation>,
|
|
123
|
+
opts?: CommonQueryOptions & {
|
|
124
|
+
before?: string | null;
|
|
125
|
+
from?: string | null;
|
|
126
|
+
},
|
|
127
|
+
): Promise<void>;
|
|
128
|
+
/**
|
|
129
|
+
* @returns a list of all document (root) OIDs affected by the adds.
|
|
130
|
+
*/
|
|
131
|
+
addOperations(
|
|
132
|
+
ops: ClientOperation[],
|
|
133
|
+
opts?: CommonQueryOptions,
|
|
134
|
+
): Promise<ObjectIdentifier[]>;
|
|
135
|
+
|
|
136
|
+
/* WARNING: deletes all data */
|
|
137
|
+
reset(opts?: {
|
|
138
|
+
clearReplica?: boolean;
|
|
139
|
+
transaction?: AbstractTransaction;
|
|
140
|
+
}): Promise<void>;
|
|
141
|
+
|
|
142
|
+
stats(): Promise<{
|
|
143
|
+
operationsSize: { count: number; size: number };
|
|
144
|
+
baselinesSize: { count: number; size: number };
|
|
145
|
+
}>;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
export interface PersistenceQueryDb {
|
|
149
|
+
transaction(opts: {
|
|
150
|
+
mode?: QueryMode;
|
|
151
|
+
storeNames: string[];
|
|
152
|
+
abort?: AbortSignal;
|
|
153
|
+
}): AbstractTransaction;
|
|
154
|
+
dispose(): void | Promise<void>;
|
|
155
|
+
|
|
156
|
+
findOneOid(opts: {
|
|
157
|
+
collection: string;
|
|
158
|
+
index?: CollectionFilter;
|
|
159
|
+
}): Promise<ObjectIdentifier | null>;
|
|
160
|
+
findAllOids(opts: {
|
|
161
|
+
collection: string;
|
|
162
|
+
index?: CollectionFilter;
|
|
163
|
+
limit?: number;
|
|
164
|
+
offset?: number;
|
|
165
|
+
}): Promise<{ result: ObjectIdentifier[]; hasNextPage: boolean }>;
|
|
166
|
+
|
|
167
|
+
saveEntities(
|
|
168
|
+
entities: { oid: ObjectIdentifier; getSnapshot: () => any }[],
|
|
169
|
+
opts?: CommonQueryOptions & { abort?: AbortSignal },
|
|
170
|
+
): Promise<void>;
|
|
171
|
+
|
|
172
|
+
reset(opts?: { transaction?: AbstractTransaction }): Promise<void>;
|
|
173
|
+
|
|
174
|
+
stats(): Promise<Record<string, { count: number; size: number }>>;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
export interface PersistedFileData extends FileData {
|
|
178
|
+
deletedAt: number | null;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
export interface PersistenceFileDb {
|
|
182
|
+
transaction(opts: {
|
|
183
|
+
mode?: QueryMode;
|
|
184
|
+
storeNames: string[];
|
|
185
|
+
abort?: AbortSignal;
|
|
186
|
+
}): AbstractTransaction;
|
|
187
|
+
dispose(): void | Promise<void>;
|
|
188
|
+
|
|
189
|
+
add(
|
|
190
|
+
file: FileData,
|
|
191
|
+
options?: { transaction?: AbstractTransaction; downloadRemote?: boolean },
|
|
192
|
+
): Promise<void>;
|
|
193
|
+
markUploaded(
|
|
194
|
+
fileId: string,
|
|
195
|
+
options?: { transaction?: AbstractTransaction },
|
|
196
|
+
): Promise<void>;
|
|
197
|
+
get(
|
|
198
|
+
fileId: string,
|
|
199
|
+
options?: { transaction?: AbstractTransaction },
|
|
200
|
+
): Promise<PersistedFileData | null>;
|
|
201
|
+
delete(
|
|
202
|
+
fileId: string,
|
|
203
|
+
options?: { transaction?: AbstractTransaction },
|
|
204
|
+
): Promise<void>;
|
|
205
|
+
markPendingDelete(
|
|
206
|
+
fileId: string,
|
|
207
|
+
options?: { transaction?: AbstractTransaction },
|
|
208
|
+
): Promise<void>;
|
|
209
|
+
listUnsynced(options?: {
|
|
210
|
+
transaction?: AbstractTransaction;
|
|
211
|
+
}): Promise<PersistedFileData[]>;
|
|
212
|
+
resetSyncedStatusSince(
|
|
213
|
+
since: string | null,
|
|
214
|
+
options?: { transaction?: AbstractTransaction },
|
|
215
|
+
): Promise<void>;
|
|
216
|
+
iterateOverPendingDelete(
|
|
217
|
+
iterator: (file: PersistedFileData, store: IDBObjectStore) => void,
|
|
218
|
+
options?: { transaction?: IDBTransaction },
|
|
219
|
+
): Promise<void>;
|
|
220
|
+
getAll(options?: {
|
|
221
|
+
transaction?: AbstractTransaction;
|
|
222
|
+
}): Promise<PersistedFileData[]>;
|
|
223
|
+
stats(): Promise<{ size: { count: number; size: number } }>;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
export interface PersistenceImplementation {
|
|
227
|
+
openMetadata(ctx: InitialContext): Promise<PersistenceMetadataDb>;
|
|
228
|
+
openQueries(ctx: Omit<Context, 'queries'>): Promise<PersistenceQueryDb>;
|
|
229
|
+
openFiles(
|
|
230
|
+
ctx: Omit<Context, 'files' | 'queries'>,
|
|
231
|
+
): Promise<PersistenceFileDb>;
|
|
232
|
+
/** Copies all data (metadata/document queries/files) from one namespace to another. */
|
|
233
|
+
copyNamespace(from: string, to: string, ctx: InitialContext): Promise<void>;
|
|
234
|
+
/** Returns a list of all persisted namespaces visible to this app. */
|
|
235
|
+
getNamespaces(): Promise<string[]>;
|
|
236
|
+
/** Deletes all data from a particular namespace. */
|
|
237
|
+
deleteNamespace(namespace: string, ctx: InitialContext): Promise<void>;
|
|
238
|
+
/** Gets the schema version of the given namespace */
|
|
239
|
+
getNamespaceVersion(namespace: string): Promise<number>;
|
|
240
|
+
}
|