@verdant-web/store 3.8.0 → 3.8.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/LICENSE +650 -21
- package/dist/bundle/index.js +12 -12
- package/dist/bundle/index.js.map +4 -4
- package/dist/esm/client/Client.d.ts +3 -3
- package/dist/esm/client/Client.js +5 -5
- package/dist/esm/client/Client.js.map +1 -1
- package/dist/esm/client/ClientDescriptor.d.ts +1 -0
- package/dist/esm/client/ClientDescriptor.js +6 -3
- package/dist/esm/client/ClientDescriptor.js.map +1 -1
- package/dist/esm/context.d.ts +1 -0
- package/dist/esm/entities/EntityStore.d.ts +3 -3
- package/dist/esm/entities/EntityStore.js +5 -0
- package/dist/esm/entities/EntityStore.js.map +1 -1
- package/dist/esm/idb.d.ts +6 -0
- package/dist/esm/idb.js +17 -1
- package/dist/esm/idb.js.map +1 -1
- package/dist/esm/migration/db.d.ts +9 -3
- package/dist/esm/migration/db.js +23 -11
- package/dist/esm/migration/db.js.map +1 -1
- package/dist/esm/migration/engine.d.ts +15 -0
- package/dist/esm/migration/engine.js +159 -0
- package/dist/esm/migration/engine.js.map +1 -0
- package/dist/esm/migration/migrations.d.ts +17 -0
- package/dist/esm/migration/migrations.js +242 -0
- package/dist/esm/migration/migrations.js.map +1 -0
- package/dist/esm/migration/openQueryDatabase.d.ts +10 -0
- package/dist/esm/migration/openQueryDatabase.js +27 -0
- package/dist/esm/migration/openQueryDatabase.js.map +1 -0
- package/dist/esm/migration/openWIPDatabase.d.ts +11 -0
- package/dist/esm/migration/openWIPDatabase.js +65 -0
- package/dist/esm/migration/openWIPDatabase.js.map +1 -0
- package/dist/esm/migration/types.d.ts +3 -0
- package/dist/esm/migration/types.js +2 -0
- package/dist/esm/migration/types.js.map +1 -0
- package/dist/esm/queries/QueryableStorage.js +1 -1
- package/dist/esm/queries/QueryableStorage.js.map +1 -1
- package/dist/esm/sync/PushPullSync.d.ts +3 -2
- package/dist/esm/sync/PushPullSync.js +7 -1
- package/dist/esm/sync/PushPullSync.js.map +1 -1
- package/package.json +2 -2
- package/src/client/Client.ts +6 -8
- package/src/client/ClientDescriptor.ts +7 -6
- package/src/context.ts +1 -0
- package/src/entities/EntityStore.ts +8 -2
- package/src/idb.ts +20 -1
- package/src/migration/db.ts +62 -20
- package/src/migration/engine.ts +248 -0
- package/src/migration/migrations.ts +347 -0
- package/src/migration/openQueryDatabase.ts +63 -0
- package/src/migration/openWIPDatabase.ts +97 -0
- package/src/migration/types.ts +4 -0
- package/src/queries/QueryableStorage.ts +1 -1
- package/src/sync/PushPullSync.ts +10 -0
- package/dist/esm/migration/openDatabase.d.ts +0 -20
- package/dist/esm/migration/openDatabase.js +0 -463
- package/dist/esm/migration/openDatabase.js.map +0 -1
- package/src/migration/openDatabase.ts +0 -749
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { EventSubscriber, VerdantErrorCode, isVerdantErrorResponse, } from '@verdant-web/common';
|
|
1
|
+
import { EventSubscriber, VerdantErrorCode, isVerdantErrorResponse, throttle, } from '@verdant-web/common';
|
|
2
2
|
import { Heartbeat } from './Heartbeat.js';
|
|
3
3
|
export class PushPullSync extends EventSubscriber {
|
|
4
4
|
constructor({ endpointProvider, meta, presence, interval = 15 * 1000, log = () => { }, fetch = window.fetch.bind(window), }) {
|
|
@@ -81,10 +81,16 @@ export class PushPullSync extends EventSubscriber {
|
|
|
81
81
|
}
|
|
82
82
|
this.emit('message', message);
|
|
83
83
|
};
|
|
84
|
+
// reduce rate of presence messages sent; each one would trigger an HTTP
|
|
85
|
+
// request, which is not ideal if presence is updating rapidly.
|
|
86
|
+
this.throttledPresenceUpdate = throttle((message) => {
|
|
87
|
+
this.sendRequest([message]);
|
|
88
|
+
}, 3000);
|
|
84
89
|
this.send = (message) => {
|
|
85
90
|
// only certain messages are sent for pull-based sync.
|
|
86
91
|
switch (message.type) {
|
|
87
92
|
case 'presence-update':
|
|
93
|
+
return this.throttledPresenceUpdate(message);
|
|
88
94
|
case 'sync':
|
|
89
95
|
case 'heartbeat':
|
|
90
96
|
return this.sendRequest([message]);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PushPullSync.js","sourceRoot":"","sources":["../../../src/sync/PushPullSync.ts"],"names":[],"mappings":"AAAA,OAAO,EAEN,eAAe,
|
|
1
|
+
{"version":3,"file":"PushPullSync.js","sourceRoot":"","sources":["../../../src/sync/PushPullSync.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@verdant-web/store",
|
|
3
|
-
"version": "3.8.
|
|
3
|
+
"version": "3.8.1",
|
|
4
4
|
"access": "public",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"jwt-decode": "^3.1.2",
|
|
28
28
|
"kysely": "^0.27.3",
|
|
29
29
|
"weak-event": "^2.0.5",
|
|
30
|
-
"@verdant-web/common": "2.3.
|
|
30
|
+
"@verdant-web/common": "2.3.4"
|
|
31
31
|
},
|
|
32
32
|
"devDependencies": {
|
|
33
33
|
"@types/node": "20.10.5",
|
package/src/client/Client.ts
CHANGED
|
@@ -1,27 +1,25 @@
|
|
|
1
1
|
import {
|
|
2
|
-
assert,
|
|
3
2
|
debounce,
|
|
4
3
|
DocumentBaseline,
|
|
5
4
|
EventSubscriber,
|
|
6
5
|
Migration,
|
|
7
6
|
Operation,
|
|
8
|
-
SchemaCollection,
|
|
9
7
|
} from '@verdant-web/common';
|
|
10
8
|
import { Context } from '../context.js';
|
|
11
9
|
import { DocumentManager } from '../DocumentManager.js';
|
|
10
|
+
import { EntityStore } from '../entities/EntityStore.js';
|
|
12
11
|
import { FileManager, FileManagerConfig } from '../files/FileManager.js';
|
|
12
|
+
import { ReturnedFileData } from '../files/FileStorage.js';
|
|
13
13
|
import {
|
|
14
14
|
closeDatabase,
|
|
15
15
|
deleteAllDatabases,
|
|
16
16
|
getSizeOfObjectStore,
|
|
17
17
|
} from '../idb.js';
|
|
18
18
|
import { ExportData, Metadata } from '../metadata/Metadata.js';
|
|
19
|
-
import {
|
|
20
|
-
import { EntityStore } from '../entities/EntityStore.js';
|
|
21
|
-
import { NoSync, ServerSync, ServerSyncOptions, Sync } from '../sync/Sync.js';
|
|
19
|
+
import { openQueryDatabase } from '../migration/openQueryDatabase.js';
|
|
22
20
|
import { CollectionQueries } from '../queries/CollectionQueries.js';
|
|
23
21
|
import { QueryCache } from '../queries/QueryCache.js';
|
|
24
|
-
import {
|
|
22
|
+
import { NoSync, ServerSync, ServerSyncOptions, Sync } from '../sync/Sync.js';
|
|
25
23
|
|
|
26
24
|
interface ClientConfig<Presence = any> {
|
|
27
25
|
syncConfig?: ServerSyncOptions<Presence>;
|
|
@@ -382,7 +380,7 @@ export class Client<Presence = any, Profile = any> extends EventSubscriber<{
|
|
|
382
380
|
}
|
|
383
381
|
// now open the document DB empty at the specified version
|
|
384
382
|
// and initialize it from the meta DB
|
|
385
|
-
this.context.documentDb = await
|
|
383
|
+
this.context.documentDb = await openQueryDatabase({
|
|
386
384
|
meta: this.meta,
|
|
387
385
|
migrations: this.config.migrations,
|
|
388
386
|
context: this.context,
|
|
@@ -402,7 +400,7 @@ export class Client<Presence = any, Profile = any> extends EventSubscriber<{
|
|
|
402
400
|
this.context.log('Migrating up to latest schema...');
|
|
403
401
|
// put the schema back
|
|
404
402
|
this.context.schema = currentSchema;
|
|
405
|
-
this.context.documentDb = await
|
|
403
|
+
this.context.documentDb = await openQueryDatabase({
|
|
406
404
|
meta: this.meta,
|
|
407
405
|
migrations: this.config.migrations,
|
|
408
406
|
context: this.context,
|
|
@@ -12,10 +12,7 @@ import {
|
|
|
12
12
|
openMetadataDatabase,
|
|
13
13
|
openWIPMetadataDatabase,
|
|
14
14
|
} from '../metadata/openMetadataDatabase.js';
|
|
15
|
-
import {
|
|
16
|
-
openDocumentDatabase,
|
|
17
|
-
openWIPDocumentDatabase,
|
|
18
|
-
} from '../migration/openDatabase.js';
|
|
15
|
+
import { openWIPDatabase } from '../migration/openWIPDatabase.js';
|
|
19
16
|
import { ServerSyncOptions } from '../sync/Sync.js';
|
|
20
17
|
import { UndoHistory } from '../UndoHistory.js';
|
|
21
18
|
import { Client } from './Client.js';
|
|
@@ -26,10 +23,12 @@ import {
|
|
|
26
23
|
} from '../idb.js';
|
|
27
24
|
import { FakeWeakRef } from '../FakeWeakRef.js';
|
|
28
25
|
import { METADATA_VERSION_KEY } from './constants.js';
|
|
26
|
+
import { openQueryDatabase } from '../migration/openQueryDatabase.js';
|
|
29
27
|
|
|
30
28
|
export interface ClientDescriptorOptions<Presence = any, Profile = any> {
|
|
31
29
|
/** The schema used to create this client */
|
|
32
30
|
schema: StorageSchema<any>;
|
|
31
|
+
oldSchemas?: StorageSchema<any>[];
|
|
33
32
|
/** Migrations, in order, to upgrade to each successive version of the schema */
|
|
34
33
|
migrations: Migration<any>[];
|
|
35
34
|
/** Provide a sync config to turn on synchronization with a server */
|
|
@@ -177,6 +176,7 @@ export class ClientDescriptor<
|
|
|
177
176
|
}
|
|
178
177
|
},
|
|
179
178
|
migrations: init.migrations,
|
|
179
|
+
oldSchemas: init.oldSchemas,
|
|
180
180
|
};
|
|
181
181
|
const meta = new Metadata({
|
|
182
182
|
context,
|
|
@@ -191,7 +191,7 @@ export class ClientDescriptor<
|
|
|
191
191
|
getNow: () => meta.now,
|
|
192
192
|
});
|
|
193
193
|
|
|
194
|
-
const documentDb = await
|
|
194
|
+
const documentDb = await openQueryDatabase({
|
|
195
195
|
context: contextWithNow,
|
|
196
196
|
version: init.schema.version,
|
|
197
197
|
meta,
|
|
@@ -245,6 +245,7 @@ export class ClientDescriptor<
|
|
|
245
245
|
}
|
|
246
246
|
},
|
|
247
247
|
migrations: init.migrations,
|
|
248
|
+
oldSchemas: init.oldSchemas,
|
|
248
249
|
};
|
|
249
250
|
const meta = new Metadata({
|
|
250
251
|
context,
|
|
@@ -258,7 +259,7 @@ export class ClientDescriptor<
|
|
|
258
259
|
// verify schema integrity
|
|
259
260
|
await meta.updateSchema(init.schema, init.overrideSchemaConflict);
|
|
260
261
|
|
|
261
|
-
const documentDb = await
|
|
262
|
+
const documentDb = await openWIPDatabase({
|
|
262
263
|
context: contextWithNow,
|
|
263
264
|
version: init.schema.version,
|
|
264
265
|
meta,
|
package/src/context.ts
CHANGED
|
@@ -42,12 +42,12 @@ export type EntityStoreEvents = {
|
|
|
42
42
|
resetAll: WeakEvent<EntityStore, void>;
|
|
43
43
|
};
|
|
44
44
|
|
|
45
|
-
|
|
45
|
+
export interface IncomingData {
|
|
46
46
|
operations?: Operation[];
|
|
47
47
|
baselines?: DocumentBaseline[];
|
|
48
48
|
reset?: boolean;
|
|
49
49
|
isLocal?: boolean;
|
|
50
|
-
}
|
|
50
|
+
}
|
|
51
51
|
|
|
52
52
|
export class EntityStore extends Disposable {
|
|
53
53
|
private ctx;
|
|
@@ -137,6 +137,12 @@ export class EntityStore extends Disposable {
|
|
|
137
137
|
await this.processData(data);
|
|
138
138
|
};
|
|
139
139
|
|
|
140
|
+
empty = async () => {
|
|
141
|
+
await this.queryableStorage.reset();
|
|
142
|
+
this.events.resetAll.invoke(this);
|
|
143
|
+
this.cache.clear();
|
|
144
|
+
};
|
|
145
|
+
|
|
140
146
|
private resetData = async () => {
|
|
141
147
|
if (this.disposed) {
|
|
142
148
|
this.ctx.log('warn', 'EntityStore is disposed, not resetting local data');
|
package/src/idb.ts
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { roughSizeOfObject } from '@verdant-web/common';
|
|
2
2
|
|
|
3
|
+
export const globalIDB =
|
|
4
|
+
typeof window !== 'undefined' ? window.indexedDB : (undefined as any);
|
|
5
|
+
|
|
3
6
|
export function isAbortError(err: unknown) {
|
|
4
7
|
return err instanceof Error && err.name === 'AbortError';
|
|
5
8
|
}
|
|
@@ -109,7 +112,7 @@ export async function closeDatabase(db: IDBDatabase) {
|
|
|
109
112
|
|
|
110
113
|
export async function deleteAllDatabases(
|
|
111
114
|
namespace: string,
|
|
112
|
-
indexedDB: IDBFactory =
|
|
115
|
+
indexedDB: IDBFactory = globalIDB,
|
|
113
116
|
) {
|
|
114
117
|
const req1 = indexedDB.deleteDatabase([namespace, 'meta'].join('_'));
|
|
115
118
|
const req2 = indexedDB.deleteDatabase([namespace, 'collections'].join('_'));
|
|
@@ -163,3 +166,19 @@ export function createAbortableTransaction(
|
|
|
163
166
|
}
|
|
164
167
|
return tx;
|
|
165
168
|
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Empties all data in a database without changing
|
|
172
|
+
* its structure.
|
|
173
|
+
*/
|
|
174
|
+
export function emptyDatabase(db: IDBDatabase) {
|
|
175
|
+
const storeNames = Array.from(db.objectStoreNames);
|
|
176
|
+
const tx = db.transaction(storeNames, 'readwrite');
|
|
177
|
+
for (const storeName of storeNames) {
|
|
178
|
+
tx.objectStore(storeName).clear();
|
|
179
|
+
}
|
|
180
|
+
return new Promise<void>((resolve, reject) => {
|
|
181
|
+
tx.oncomplete = () => resolve();
|
|
182
|
+
tx.onerror = () => reject(tx.error);
|
|
183
|
+
});
|
|
184
|
+
}
|
package/src/migration/db.ts
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { closeDatabase, globalIDB, storeRequestPromise } from '../idb.js';
|
|
2
|
+
import { OpenDocumentDbContext } from './types.js';
|
|
3
|
+
|
|
1
4
|
export async function getDatabaseVersion(
|
|
2
5
|
indexedDB: IDBFactory,
|
|
3
6
|
namespace: string,
|
|
@@ -42,12 +45,6 @@ export async function getDatabaseVersion(
|
|
|
42
45
|
return currentVersion;
|
|
43
46
|
}
|
|
44
47
|
|
|
45
|
-
export async function closeDatabase(db: IDBDatabase) {
|
|
46
|
-
db.close();
|
|
47
|
-
// FIXME: this isn't right!!!!
|
|
48
|
-
await new Promise<void>((resolve) => resolve());
|
|
49
|
-
}
|
|
50
|
-
|
|
51
48
|
/**
|
|
52
49
|
* Upgrades the database to the given version, using the given upgrader function.
|
|
53
50
|
*/
|
|
@@ -61,8 +58,11 @@ export async function upgradeDatabase(
|
|
|
61
58
|
event: IDBVersionChangeEvent,
|
|
62
59
|
) => void,
|
|
63
60
|
log?: (...args: any[]) => void,
|
|
64
|
-
): Promise<
|
|
65
|
-
function openAndUpgrade(
|
|
61
|
+
): Promise<IDBDatabase> {
|
|
62
|
+
function openAndUpgrade(
|
|
63
|
+
resolve: (db: IDBDatabase) => void,
|
|
64
|
+
reject: (err: Error) => void,
|
|
65
|
+
) {
|
|
66
66
|
const request = indexedDb.open(
|
|
67
67
|
[namespace, 'collections'].join('_'),
|
|
68
68
|
version,
|
|
@@ -74,9 +74,8 @@ export async function upgradeDatabase(
|
|
|
74
74
|
wasUpgraded = true;
|
|
75
75
|
};
|
|
76
76
|
request.onsuccess = (event) => {
|
|
77
|
-
request.result.close();
|
|
78
77
|
if (wasUpgraded) {
|
|
79
|
-
resolve();
|
|
78
|
+
resolve(request.result);
|
|
80
79
|
} else {
|
|
81
80
|
reject(
|
|
82
81
|
new Error(
|
|
@@ -95,7 +94,7 @@ export async function upgradeDatabase(
|
|
|
95
94
|
// }, 200);
|
|
96
95
|
};
|
|
97
96
|
}
|
|
98
|
-
return new Promise(openAndUpgrade);
|
|
97
|
+
return new Promise<IDBDatabase>(openAndUpgrade);
|
|
99
98
|
}
|
|
100
99
|
|
|
101
100
|
export async function acquireLock(
|
|
@@ -110,15 +109,20 @@ export async function acquireLock(
|
|
|
110
109
|
}
|
|
111
110
|
}
|
|
112
111
|
|
|
113
|
-
export async function openDatabase(
|
|
114
|
-
|
|
115
|
-
namespace
|
|
116
|
-
version
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
112
|
+
export async function openDatabase({
|
|
113
|
+
indexedDB = globalIDB,
|
|
114
|
+
namespace,
|
|
115
|
+
version,
|
|
116
|
+
context,
|
|
117
|
+
}: {
|
|
118
|
+
indexedDB?: IDBFactory;
|
|
119
|
+
namespace: string;
|
|
120
|
+
version: number;
|
|
121
|
+
context: OpenDocumentDbContext;
|
|
122
|
+
}): Promise<IDBDatabase> {
|
|
123
|
+
context.log('debug', 'Opening database', namespace, 'at version', version);
|
|
120
124
|
const db = await new Promise<IDBDatabase>((resolve, reject) => {
|
|
121
|
-
const request =
|
|
125
|
+
const request = indexedDB.open(
|
|
122
126
|
[namespace, 'collections'].join('_'),
|
|
123
127
|
version,
|
|
124
128
|
);
|
|
@@ -126,7 +130,7 @@ export async function openDatabase(
|
|
|
126
130
|
const transaction = request.transaction!;
|
|
127
131
|
transaction.abort();
|
|
128
132
|
|
|
129
|
-
log
|
|
133
|
+
context.log(
|
|
130
134
|
'error',
|
|
131
135
|
'Database upgrade needed, but not expected',
|
|
132
136
|
'Expected',
|
|
@@ -158,3 +162,41 @@ export async function openDatabase(
|
|
|
158
162
|
|
|
159
163
|
return db;
|
|
160
164
|
}
|
|
165
|
+
|
|
166
|
+
export async function copyAll(
|
|
167
|
+
sourceDatabase: IDBDatabase,
|
|
168
|
+
targetDatabase: IDBDatabase,
|
|
169
|
+
) {
|
|
170
|
+
// DOMStringList... doesn't have iterable... why
|
|
171
|
+
const sourceStoreNames = new Array<string>();
|
|
172
|
+
for (let i = 0; i < sourceDatabase.objectStoreNames.length; i++) {
|
|
173
|
+
sourceStoreNames.push(sourceDatabase.objectStoreNames[i]);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const copyFromTransaction = sourceDatabase.transaction(
|
|
177
|
+
sourceStoreNames,
|
|
178
|
+
'readonly',
|
|
179
|
+
);
|
|
180
|
+
const copyFromStores = sourceStoreNames.map((name) =>
|
|
181
|
+
copyFromTransaction.objectStore(name),
|
|
182
|
+
);
|
|
183
|
+
const allObjects = await Promise.all(
|
|
184
|
+
copyFromStores.map((store) => storeRequestPromise(store.getAll())),
|
|
185
|
+
);
|
|
186
|
+
|
|
187
|
+
const copyToTransaction = targetDatabase.transaction(
|
|
188
|
+
sourceStoreNames,
|
|
189
|
+
'readwrite',
|
|
190
|
+
);
|
|
191
|
+
const copyToStores = sourceStoreNames.map((name) =>
|
|
192
|
+
copyToTransaction.objectStore(name),
|
|
193
|
+
);
|
|
194
|
+
|
|
195
|
+
for (let i = 0; i < copyToStores.length; i++) {
|
|
196
|
+
await Promise.all(
|
|
197
|
+
allObjects[i].map((obj) => {
|
|
198
|
+
return storeRequestPromise(copyToStores[i].put(obj));
|
|
199
|
+
}),
|
|
200
|
+
);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CollectionFilter,
|
|
3
|
+
Migration,
|
|
4
|
+
MigrationEngine,
|
|
5
|
+
ObjectIdentifier,
|
|
6
|
+
addFieldDefaults,
|
|
7
|
+
assert,
|
|
8
|
+
assignOidsToAllSubObjects,
|
|
9
|
+
cloneDeep,
|
|
10
|
+
createOid,
|
|
11
|
+
diffToPatches,
|
|
12
|
+
getOid,
|
|
13
|
+
initialToPatches,
|
|
14
|
+
removeOidPropertiesFromAllSubObjects,
|
|
15
|
+
} from '@verdant-web/common';
|
|
16
|
+
import { Context } from '../context.js';
|
|
17
|
+
import { Metadata } from '../metadata/Metadata.js';
|
|
18
|
+
import { findAllOids, findOneOid } from '../queries/dbQueries.js';
|
|
19
|
+
import { OpenDocumentDbContext } from './types.js';
|
|
20
|
+
|
|
21
|
+
function getMigrationMutations({
|
|
22
|
+
migration,
|
|
23
|
+
meta,
|
|
24
|
+
getMigrationNow,
|
|
25
|
+
newOids,
|
|
26
|
+
}: {
|
|
27
|
+
migration: Migration<any>;
|
|
28
|
+
newOids: string[];
|
|
29
|
+
getMigrationNow: () => string;
|
|
30
|
+
meta: Metadata;
|
|
31
|
+
}) {
|
|
32
|
+
return migration.allCollections.reduce((acc, collectionName) => {
|
|
33
|
+
acc[collectionName] = {
|
|
34
|
+
put: async (doc: any) => {
|
|
35
|
+
// add defaults
|
|
36
|
+
addFieldDefaults(migration.newSchema.collections[collectionName], doc);
|
|
37
|
+
const primaryKey =
|
|
38
|
+
doc[migration.newSchema.collections[collectionName].primaryKey];
|
|
39
|
+
const oid = createOid(collectionName, primaryKey);
|
|
40
|
+
newOids.push(oid);
|
|
41
|
+
await meta.insertLocalOperations(
|
|
42
|
+
initialToPatches(doc, oid, getMigrationNow),
|
|
43
|
+
);
|
|
44
|
+
return doc;
|
|
45
|
+
},
|
|
46
|
+
delete: async (id: string) => {
|
|
47
|
+
const rootOid = createOid(collectionName, id);
|
|
48
|
+
const allOids = await meta.getAllDocumentRelatedOids(rootOid);
|
|
49
|
+
return meta.insertLocalOperations(
|
|
50
|
+
allOids.map((oid) => ({
|
|
51
|
+
oid,
|
|
52
|
+
timestamp: getMigrationNow(),
|
|
53
|
+
data: { op: 'delete' },
|
|
54
|
+
})),
|
|
55
|
+
);
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
return acc;
|
|
59
|
+
}, {} as any);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function getMigrationQueries({
|
|
63
|
+
migration,
|
|
64
|
+
context,
|
|
65
|
+
meta,
|
|
66
|
+
}: {
|
|
67
|
+
migration: Migration<any>;
|
|
68
|
+
context: Context;
|
|
69
|
+
meta: Metadata;
|
|
70
|
+
}) {
|
|
71
|
+
return migration.oldCollections.reduce((acc, collectionName) => {
|
|
72
|
+
acc[collectionName] = {
|
|
73
|
+
get: async (id: string) => {
|
|
74
|
+
const oid = createOid(collectionName, id);
|
|
75
|
+
const doc = await meta.getDocumentSnapshot(oid, {
|
|
76
|
+
// only get the snapshot up to the previous version (newer operations may have synced)
|
|
77
|
+
to: meta.time.now(migration.oldSchema.version),
|
|
78
|
+
});
|
|
79
|
+
return doc;
|
|
80
|
+
},
|
|
81
|
+
findOne: async (filter: CollectionFilter) => {
|
|
82
|
+
const oid = await findOneOid({
|
|
83
|
+
collection: collectionName,
|
|
84
|
+
index: filter,
|
|
85
|
+
context,
|
|
86
|
+
});
|
|
87
|
+
if (!oid) return null;
|
|
88
|
+
const doc = await meta.getDocumentSnapshot(oid, {
|
|
89
|
+
// only get the snapshot up to the previous version (newer operations may have synced)
|
|
90
|
+
to: meta.time.now(migration.oldSchema.version),
|
|
91
|
+
});
|
|
92
|
+
return doc;
|
|
93
|
+
},
|
|
94
|
+
findAll: async (filter: CollectionFilter) => {
|
|
95
|
+
const oids = await findAllOids({
|
|
96
|
+
collection: collectionName,
|
|
97
|
+
index: filter,
|
|
98
|
+
context,
|
|
99
|
+
});
|
|
100
|
+
const docs = await Promise.all(
|
|
101
|
+
oids.map((oid) =>
|
|
102
|
+
meta.getDocumentSnapshot(oid, {
|
|
103
|
+
// only get the snapshot up to the previous version (newer operations may have synced)
|
|
104
|
+
to: meta.time.now(migration.oldSchema.version),
|
|
105
|
+
}),
|
|
106
|
+
),
|
|
107
|
+
);
|
|
108
|
+
return docs;
|
|
109
|
+
},
|
|
110
|
+
};
|
|
111
|
+
return acc;
|
|
112
|
+
}, {} as any);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export function getMigrationEngine({
|
|
116
|
+
meta,
|
|
117
|
+
migration,
|
|
118
|
+
context,
|
|
119
|
+
}: {
|
|
120
|
+
log?: (...args: any[]) => void;
|
|
121
|
+
migration: Migration;
|
|
122
|
+
meta: Metadata;
|
|
123
|
+
context: Context;
|
|
124
|
+
}): MigrationEngine {
|
|
125
|
+
function getMigrationNow() {
|
|
126
|
+
return meta.time.zero(migration.version);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const newOids = new Array<ObjectIdentifier>();
|
|
130
|
+
|
|
131
|
+
const queries = getMigrationQueries({
|
|
132
|
+
migration,
|
|
133
|
+
context,
|
|
134
|
+
meta,
|
|
135
|
+
});
|
|
136
|
+
const mutations = getMigrationMutations({
|
|
137
|
+
migration,
|
|
138
|
+
getMigrationNow,
|
|
139
|
+
newOids,
|
|
140
|
+
meta,
|
|
141
|
+
});
|
|
142
|
+
const deleteCollection = async (collection: string) => {
|
|
143
|
+
const allOids = await meta.getAllCollectionRelatedOids(collection);
|
|
144
|
+
return meta.insertLocalOperations(
|
|
145
|
+
allOids.map((oid) => ({
|
|
146
|
+
oid,
|
|
147
|
+
timestamp: getMigrationNow(),
|
|
148
|
+
data: { op: 'delete' },
|
|
149
|
+
})),
|
|
150
|
+
);
|
|
151
|
+
};
|
|
152
|
+
const awaitables = new Array<Promise<any>>();
|
|
153
|
+
const engine: MigrationEngine = {
|
|
154
|
+
log: context.log,
|
|
155
|
+
newOids,
|
|
156
|
+
deleteCollection,
|
|
157
|
+
migrate: async (collection, strategy) => {
|
|
158
|
+
const docs = await queries[collection].findAll();
|
|
159
|
+
|
|
160
|
+
await Promise.all(
|
|
161
|
+
docs.filter(Boolean).map(async (doc: any) => {
|
|
162
|
+
const rootOid = getOid(doc);
|
|
163
|
+
assert(
|
|
164
|
+
!!rootOid,
|
|
165
|
+
`Document is missing an OID: ${JSON.stringify(doc)}`,
|
|
166
|
+
);
|
|
167
|
+
const original = cloneDeep(doc);
|
|
168
|
+
// @ts-ignore - excessive type resolution
|
|
169
|
+
const newValue = await strategy(doc);
|
|
170
|
+
if (newValue) {
|
|
171
|
+
// the migration has altered the shape of our document. we need
|
|
172
|
+
// to create the operation from the diff and write it to meta as
|
|
173
|
+
// a migration patch
|
|
174
|
+
removeOidPropertiesFromAllSubObjects(original);
|
|
175
|
+
removeOidPropertiesFromAllSubObjects(newValue);
|
|
176
|
+
assignOidsToAllSubObjects(newValue);
|
|
177
|
+
const patches = diffToPatches(
|
|
178
|
+
original,
|
|
179
|
+
newValue,
|
|
180
|
+
getMigrationNow,
|
|
181
|
+
undefined,
|
|
182
|
+
[],
|
|
183
|
+
{
|
|
184
|
+
mergeUnknownObjects: true,
|
|
185
|
+
},
|
|
186
|
+
);
|
|
187
|
+
if (patches.length > 0) {
|
|
188
|
+
await meta.insertLocalOperations(patches);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}),
|
|
192
|
+
);
|
|
193
|
+
},
|
|
194
|
+
queries,
|
|
195
|
+
mutations,
|
|
196
|
+
awaitables,
|
|
197
|
+
};
|
|
198
|
+
return engine;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
export function getInitialMigrationEngine({
|
|
202
|
+
meta,
|
|
203
|
+
migration,
|
|
204
|
+
context,
|
|
205
|
+
}: {
|
|
206
|
+
context: OpenDocumentDbContext;
|
|
207
|
+
migration: Migration;
|
|
208
|
+
meta: Metadata;
|
|
209
|
+
}): MigrationEngine {
|
|
210
|
+
function getMigrationNow() {
|
|
211
|
+
return meta.time.zero(migration.version);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
const newOids = new Array<ObjectIdentifier>();
|
|
215
|
+
|
|
216
|
+
const queries = new Proxy({} as any, {
|
|
217
|
+
get() {
|
|
218
|
+
throw new Error(
|
|
219
|
+
'Queries are not available in initial migrations; there is no database yet!',
|
|
220
|
+
);
|
|
221
|
+
},
|
|
222
|
+
}) as any;
|
|
223
|
+
|
|
224
|
+
const mutations = getMigrationMutations({
|
|
225
|
+
migration,
|
|
226
|
+
getMigrationNow,
|
|
227
|
+
newOids,
|
|
228
|
+
meta,
|
|
229
|
+
});
|
|
230
|
+
const engine: MigrationEngine = {
|
|
231
|
+
log: context.log,
|
|
232
|
+
newOids,
|
|
233
|
+
deleteCollection: () => {
|
|
234
|
+
throw new Error(
|
|
235
|
+
'Calling deleteCollection() in initial migrations is not supported! Use initial migrations to seed initial data using mutations.',
|
|
236
|
+
);
|
|
237
|
+
},
|
|
238
|
+
migrate: () => {
|
|
239
|
+
throw new Error(
|
|
240
|
+
'Calling migrate() in initial migrations is not supported! Use initial migrations to seed initial data using mutations.',
|
|
241
|
+
);
|
|
242
|
+
},
|
|
243
|
+
queries,
|
|
244
|
+
mutations,
|
|
245
|
+
awaitables: [],
|
|
246
|
+
};
|
|
247
|
+
return engine;
|
|
248
|
+
}
|