@dxos/migrations 0.8.4-main.d05539e30a → 0.8.4-main.d9fc60f731
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/lib/browser/index.mjs +56 -35
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/node-esm/index.mjs +56 -35
- package/dist/lib/node-esm/index.mjs.map +4 -4
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/types/src/annotations.d.ts +4 -0
- package/dist/types/src/annotations.d.ts.map +1 -0
- package/dist/types/src/index.d.ts +2 -1
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/migration-builder.d.ts +7 -7
- package/dist/types/src/migration-builder.d.ts.map +1 -1
- package/dist/types/src/migrations.d.ts +3 -0
- package/dist/types/src/migrations.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +12 -13
- package/src/annotations.ts +13 -0
- package/src/index.ts +2 -1
- package/src/migration-builder.ts +18 -14
- package/src/migrations.test.ts +19 -11
- package/src/migrations.ts +21 -14
|
@@ -1,17 +1,26 @@
|
|
|
1
|
+
// src/annotations.ts
|
|
2
|
+
import * as Schema from "effect/Schema";
|
|
3
|
+
import { Annotation } from "@dxos/echo";
|
|
4
|
+
var MigrationVersionAnnotation = Annotation.make({
|
|
5
|
+
id: "org.dxos.migrations.version",
|
|
6
|
+
schema: Schema.String
|
|
7
|
+
});
|
|
8
|
+
|
|
1
9
|
// src/migration-builder.ts
|
|
2
10
|
import { next as A } from "@automerge/automerge";
|
|
3
11
|
import { CreateEpochRequest } from "@dxos/client/halo";
|
|
4
12
|
import { ObjectCore, migrateDocument } from "@dxos/echo-db";
|
|
5
13
|
import { EncodedReference, SpaceDocVersion } from "@dxos/echo-protocol";
|
|
6
|
-
import {
|
|
14
|
+
import { getSchemaURI } from "@dxos/echo/internal";
|
|
15
|
+
import * as Type from "@dxos/echo/Type";
|
|
7
16
|
import { invariant } from "@dxos/invariant";
|
|
8
|
-
import {
|
|
17
|
+
import { EID, EntityId } from "@dxos/keys";
|
|
9
18
|
var __dxlog_file = "/__w/dxos/dxos/packages/sdk/migrations/src/migration-builder.ts";
|
|
10
19
|
var MigrationBuilder = class {
|
|
11
20
|
_space;
|
|
12
21
|
_repo;
|
|
13
22
|
_rootDoc;
|
|
14
|
-
//
|
|
23
|
+
// echoUri -> automergeUrl
|
|
15
24
|
_newLinks = {};
|
|
16
25
|
_flushIds = [];
|
|
17
26
|
_deleteObjects = [];
|
|
@@ -36,9 +45,10 @@ var MigrationBuilder = class {
|
|
|
36
45
|
if (!objectStructure) {
|
|
37
46
|
return;
|
|
38
47
|
}
|
|
39
|
-
const {
|
|
48
|
+
const { type, props } = await migrate(objectStructure);
|
|
49
|
+
const schema = Type.getSchema(type);
|
|
40
50
|
const oldHandle = await this._findObjectContainingHandle(id);
|
|
41
|
-
invariant(oldHandle, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file, L:
|
|
51
|
+
invariant(oldHandle, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file, L: 61, S: this, A: ["oldHandle", ""] });
|
|
42
52
|
const newState = {
|
|
43
53
|
version: SpaceDocVersion.CURRENT,
|
|
44
54
|
access: {
|
|
@@ -47,7 +57,7 @@ var MigrationBuilder = class {
|
|
|
47
57
|
objects: {
|
|
48
58
|
[id]: {
|
|
49
59
|
system: {
|
|
50
|
-
type: EncodedReference.
|
|
60
|
+
type: EncodedReference.fromURI(getSchemaURI(schema))
|
|
51
61
|
},
|
|
52
62
|
data: props,
|
|
53
63
|
meta: {
|
|
@@ -59,19 +69,23 @@ var MigrationBuilder = class {
|
|
|
59
69
|
const migratedDoc = migrateDocument(oldHandle.doc(), newState);
|
|
60
70
|
const newHandle = this._repo.import(A.save(migratedDoc));
|
|
61
71
|
await newHandle.whenReady();
|
|
62
|
-
invariant(newHandle.url, "Migrated document URL not available after whenReady", { "~LogMeta": "~LogMeta", F: __dxlog_file, L:
|
|
72
|
+
invariant(newHandle.url, "Migrated document URL not available after whenReady", { "~LogMeta": "~LogMeta", F: __dxlog_file, L: 82, S: this, A: ["newHandle.url", "'Migrated document URL not available after whenReady'"] });
|
|
63
73
|
this._newLinks[id] = newHandle.url;
|
|
64
74
|
this._addHandleToFlushList(newHandle.documentId);
|
|
65
75
|
}
|
|
66
|
-
async addObject(
|
|
76
|
+
async addObject(type, props) {
|
|
77
|
+
const resolved = Type.getSchema(type);
|
|
67
78
|
const core = await this._createObject({
|
|
68
|
-
schema,
|
|
79
|
+
schema: resolved,
|
|
69
80
|
props
|
|
70
81
|
});
|
|
71
82
|
return core.id;
|
|
72
83
|
}
|
|
73
84
|
createReference(id) {
|
|
74
|
-
|
|
85
|
+
invariant(EntityId.isValid(id), "Invalid EntityId.", { "~LogMeta": "~LogMeta", F: __dxlog_file, L: 95, S: this, A: ["EntityId.isValid(id)", "'Invalid EntityId.'"] });
|
|
86
|
+
return EncodedReference.fromURI(EID.make({
|
|
87
|
+
entityId: id
|
|
88
|
+
}));
|
|
75
89
|
}
|
|
76
90
|
deleteObject(id) {
|
|
77
91
|
this._deleteObjects.push(id);
|
|
@@ -80,7 +94,7 @@ var MigrationBuilder = class {
|
|
|
80
94
|
if (!this._newRoot) {
|
|
81
95
|
await this._buildNewRoot();
|
|
82
96
|
}
|
|
83
|
-
invariant(this._newRoot, "New root not created", { "~LogMeta": "~LogMeta", F: __dxlog_file, L:
|
|
97
|
+
invariant(this._newRoot, "New root not created", { "~LogMeta": "~LogMeta", F: __dxlog_file, L: 107, S: this, A: ["this._newRoot", "'New root not created'"] });
|
|
84
98
|
this._newRoot.change((doc) => {
|
|
85
99
|
const propertiesStructure = doc.objects?.[this._space.properties.id];
|
|
86
100
|
propertiesStructure && changeFn(propertiesStructure);
|
|
@@ -95,9 +109,9 @@ var MigrationBuilder = class {
|
|
|
95
109
|
if (!this._newRoot) {
|
|
96
110
|
await this._buildNewRoot();
|
|
97
111
|
}
|
|
98
|
-
invariant(this._newRoot, "New root not created", { "~LogMeta": "~LogMeta", F: __dxlog_file, L:
|
|
112
|
+
invariant(this._newRoot, "New root not created", { "~LogMeta": "~LogMeta", F: __dxlog_file, L: 121, S: this, A: ["this._newRoot", "'New root not created'"] });
|
|
99
113
|
await this._space.db.flush();
|
|
100
|
-
invariant(this._newRoot.url, "New root URL not available", { "~LogMeta": "~LogMeta", F: __dxlog_file, L:
|
|
114
|
+
invariant(this._newRoot.url, "New root URL not available", { "~LogMeta": "~LogMeta", F: __dxlog_file, L: 124, S: this, A: ["this._newRoot.url", "'New root URL not available'"] });
|
|
101
115
|
await this._space.internal.createEpoch({
|
|
102
116
|
migration: CreateEpochRequest.Migration.REPLACE_AUTOMERGE_ROOT,
|
|
103
117
|
automergeRootUrl: this._newRoot.url
|
|
@@ -139,7 +153,7 @@ var MigrationBuilder = class {
|
|
|
139
153
|
core.id = id;
|
|
140
154
|
}
|
|
141
155
|
core.initNewObject(props);
|
|
142
|
-
core.setType(EncodedReference.
|
|
156
|
+
core.setType(EncodedReference.fromURI(getSchemaURI(schema)));
|
|
143
157
|
const newHandle = this._repo.create({
|
|
144
158
|
version: SpaceDocVersion.CURRENT,
|
|
145
159
|
access: {
|
|
@@ -162,7 +176,9 @@ var MigrationBuilder = class {
|
|
|
162
176
|
// src/migrations.ts
|
|
163
177
|
import { Atom } from "@effect-atom/atom";
|
|
164
178
|
import * as Registry from "@effect-atom/atom/Registry";
|
|
179
|
+
import * as Option from "effect/Option";
|
|
165
180
|
import { SpaceState } from "@dxos/client/echo";
|
|
181
|
+
import { Annotation as Annotation2, Obj } from "@dxos/echo";
|
|
166
182
|
import { invariant as invariant2 } from "@dxos/invariant";
|
|
167
183
|
var __dxlog_file2 = "/__w/dxos/dxos/packages/sdk/migrations/src/migrations.ts";
|
|
168
184
|
var Migrations = class {
|
|
@@ -172,6 +188,9 @@ var Migrations = class {
|
|
|
172
188
|
static _stateAtom = Atom.make({
|
|
173
189
|
running: []
|
|
174
190
|
}).pipe(Atom.keepAlive);
|
|
191
|
+
/**
|
|
192
|
+
* @deprecated Use `MigrationVersionAnnotation` via `Annotation.get/set` on space properties.
|
|
193
|
+
*/
|
|
175
194
|
static get versionProperty() {
|
|
176
195
|
return this.namespace && `${this.namespace}.version`;
|
|
177
196
|
}
|
|
@@ -187,10 +206,9 @@ var Migrations = class {
|
|
|
187
206
|
this.migrations = migrations;
|
|
188
207
|
}
|
|
189
208
|
static async migrate(space, targetVersion) {
|
|
190
|
-
invariant2(!this.running(space), "Migration already running", { "~LogMeta": "~LogMeta", F: __dxlog_file2, L:
|
|
191
|
-
invariant2(
|
|
192
|
-
|
|
193
|
-
const currentVersion = space.properties[this.versionProperty];
|
|
209
|
+
invariant2(!this.running(space), "Migration already running", { "~LogMeta": "~LogMeta", F: __dxlog_file2, L: 36, S: this, A: ["!this.running(space)", "'Migration already running'"] });
|
|
210
|
+
invariant2(space.state.get() === SpaceState.SPACE_READY, "Space not ready", { "~LogMeta": "~LogMeta", F: __dxlog_file2, L: 37, S: this, A: ["space.state.get() === SpaceState.SPACE_READY", "'Space not ready'"] });
|
|
211
|
+
const currentVersion = Annotation2.get(space.properties, MigrationVersionAnnotation).pipe(Option.getOrUndefined);
|
|
194
212
|
const currentIndex = this.migrations.findIndex((m) => m.version === currentVersion) + 1;
|
|
195
213
|
const i = this.migrations.findIndex((m) => m.version === targetVersion);
|
|
196
214
|
const targetIndex = i === -1 ? this.migrations.length : i + 1;
|
|
@@ -205,30 +223,33 @@ var Migrations = class {
|
|
|
205
223
|
spaceKey
|
|
206
224
|
]
|
|
207
225
|
});
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
const
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
226
|
+
try {
|
|
227
|
+
if (targetIndex > currentIndex) {
|
|
228
|
+
const migrations = this.migrations.slice(currentIndex, targetIndex);
|
|
229
|
+
for (const migration of migrations) {
|
|
230
|
+
const builder = new MigrationBuilder(space);
|
|
231
|
+
await migration.next({
|
|
232
|
+
space,
|
|
233
|
+
builder
|
|
234
|
+
});
|
|
235
|
+
await builder._commit();
|
|
236
|
+
Obj.update(space.properties, (properties) => {
|
|
237
|
+
Annotation2.set(properties, MigrationVersionAnnotation, migration.version);
|
|
238
|
+
});
|
|
239
|
+
}
|
|
221
240
|
}
|
|
241
|
+
} finally {
|
|
242
|
+
const finalState = this._registry.get(this._stateAtom);
|
|
243
|
+
this._registry.set(this._stateAtom, {
|
|
244
|
+
running: finalState.running.filter((key) => key !== spaceKey)
|
|
245
|
+
});
|
|
222
246
|
}
|
|
223
|
-
const finalState = this._registry.get(this._stateAtom);
|
|
224
|
-
this._registry.set(this._stateAtom, {
|
|
225
|
-
running: finalState.running.filter((key) => key !== spaceKey)
|
|
226
|
-
});
|
|
227
247
|
return true;
|
|
228
248
|
}
|
|
229
249
|
};
|
|
230
250
|
export {
|
|
231
251
|
MigrationBuilder,
|
|
252
|
+
MigrationVersionAnnotation,
|
|
232
253
|
Migrations
|
|
233
254
|
};
|
|
234
255
|
//# sourceMappingURL=index.mjs.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
|
-
"sources": ["../../../src/migration-builder.ts", "../../../src/migrations.ts"],
|
|
4
|
-
"sourcesContent": ["//\n// Copyright 2024 DXOS.org\n//\n\nimport { next as A, type Doc } from '@automerge/automerge';\nimport { type AnyDocumentId, type DocumentId } from '@automerge/automerge-repo';\nimport type * as Schema from 'effect/Schema';\n\nimport { type Space } from '@dxos/client/echo';\nimport { CreateEpochRequest } from '@dxos/client/halo';\nimport { type DocHandleProxy, ObjectCore, type RepoProxy, migrateDocument } from '@dxos/echo-db';\nimport { type DatabaseDirectory, EncodedReference, type ObjectStructure, SpaceDocVersion } from '@dxos/echo-protocol';\nimport { getSchemaDXN } from '@dxos/echo/internal';\nimport { invariant } from '@dxos/invariant';\nimport { DXN } from '@dxos/keys';\nimport { type MaybePromise } from '@dxos/util';\n\n/*\n\nConsidering a better API for this:\n\n```ts\nconst migration = space.db.beginMigration(); // all actions are not visible to queries and are only applied once you call `apply`\n\nmigration.applyObjectMigration(defineMigration(From, To, { ... }));\n\nmigration.delete(id);\nmigration.add(obj);\n\nawait migration.apply(); // Will create new epoch.\n```\n\n*/\n\n// TODO(dmaretskyi): We no longer need to hook into ECHO internals, with the changes to echo APIs.\nexport class MigrationBuilder {\n private readonly _repo: RepoProxy;\n private readonly _rootDoc: Doc<DatabaseDirectory>;\n\n // echoId -> automergeUrl\n private readonly _newLinks: Record<string, string> = {};\n private readonly _flushIds: DocumentId[] = [];\n private readonly _deleteObjects: string[] = [];\n\n private _newRoot?: DocHandleProxy<DatabaseDirectory> = undefined;\n\n constructor(private readonly _space: Space) {\n this._repo = this._space.internal.db.coreDatabase._repo;\n // TODO(wittjosiah): Accessing private API.\n this._rootDoc = (this._space.internal.db.coreDatabase as any)._automergeDocLoader\n .getSpaceRootDocHandle()\n .doc() as Doc<DatabaseDirectory>;\n }\n\n async findObject(id: string): Promise<ObjectStructure | undefined> {\n const documentId = (this._rootDoc.links?.[id] || this._newLinks[id])?.toString() as AnyDocumentId | undefined;\n const docHandle = documentId && this._repo.find(documentId);\n if (!docHandle) {\n return undefined;\n }\n\n await docHandle.whenReady();\n const doc = docHandle.doc() as Doc<DatabaseDirectory>;\n return doc.objects?.[id];\n }\n\n async migrateObject(\n id: string,\n migrate: (objectStructure: ObjectStructure) => MaybePromise<{ schema: Schema.Schema.AnyNoContext; props: any }>,\n ): Promise<void> {\n const objectStructure = await this.findObject(id);\n if (!objectStructure) {\n return;\n }\n\n const { schema, props } = await migrate(objectStructure);\n\n const oldHandle = await this._findObjectContainingHandle(id);\n invariant(oldHandle);\n\n const newState: DatabaseDirectory = {\n version: SpaceDocVersion.CURRENT,\n access: {\n spaceKey: this._space.key.toHex(),\n },\n objects: {\n [id]: {\n system: {\n type: EncodedReference.fromDXN(getSchemaDXN(schema)!),\n },\n data: props,\n meta: {\n keys: [],\n },\n },\n },\n };\n const migratedDoc = migrateDocument(oldHandle.doc() as Doc<DatabaseDirectory>, newState);\n const newHandle = this._repo.import<DatabaseDirectory>(A.save(migratedDoc));\n await newHandle.whenReady();\n invariant(newHandle.url, 'Migrated document URL not available after whenReady');\n this._newLinks[id] = newHandle.url;\n this._addHandleToFlushList(newHandle.documentId!);\n }\n\n async addObject(schema: Schema.Schema.AnyNoContext, props: any): Promise<string> {\n const core = await this._createObject({ schema, props });\n return core.id;\n }\n\n createReference(id: string) {\n return EncodedReference.fromDXN(DXN.fromLocalObjectId(id));\n }\n\n deleteObject(id: string): void {\n this._deleteObjects.push(id);\n }\n\n async changeProperties(changeFn: (properties: ObjectStructure) => void): Promise<void> {\n if (!this._newRoot) {\n await this._buildNewRoot();\n }\n invariant(this._newRoot, 'New root not created');\n\n this._newRoot.change((doc: DatabaseDirectory) => {\n const propertiesStructure = doc.objects?.[this._space.properties.id];\n propertiesStructure && changeFn(propertiesStructure);\n });\n await this._newRoot.whenReady();\n this._addHandleToFlushList(this._newRoot.documentId!);\n }\n\n /**\n * @internal\n */\n async _commit(): Promise<void> {\n if (!this._newRoot) {\n await this._buildNewRoot();\n }\n invariant(this._newRoot, 'New root not created');\n\n await this._space.db.flush();\n\n // Create new epoch.\n invariant(this._newRoot.url, 'New root URL not available');\n await this._space.internal.createEpoch({\n migration: CreateEpochRequest.Migration.REPLACE_AUTOMERGE_ROOT,\n automergeRootUrl: this._newRoot.url,\n });\n }\n\n private async _findObjectContainingHandle(id: string): Promise<DocHandleProxy<DatabaseDirectory> | undefined> {\n const documentId = (this._rootDoc.links?.[id] || this._newLinks[id])?.toString() as AnyDocumentId | undefined;\n const docHandle = documentId && this._repo.find(documentId);\n if (!docHandle) {\n return undefined;\n }\n\n await docHandle.whenReady();\n return docHandle;\n }\n\n private async _buildNewRoot(): Promise<void> {\n const links = { ...(this._rootDoc.links ?? {}) };\n for (const id of this._deleteObjects) {\n delete links[id];\n }\n\n for (const [id, url] of Object.entries(this._newLinks)) {\n links[id] = new A.RawString(url);\n }\n\n this._newRoot = this._repo.create<DatabaseDirectory>({\n version: SpaceDocVersion.CURRENT,\n access: {\n spaceKey: this._space.key.toHex(),\n },\n objects: this._rootDoc.objects,\n links,\n });\n await this._newRoot.whenReady();\n this._addHandleToFlushList(this._newRoot.documentId!);\n }\n\n private async _createObject({\n id,\n schema,\n props,\n }: {\n id?: string;\n schema: Schema.Schema.AnyNoContext;\n props: any;\n }): Promise<ObjectCore> {\n const core = new ObjectCore();\n if (id) {\n core.id = id;\n }\n\n core.initNewObject(props);\n core.setType(EncodedReference.fromDXN(getSchemaDXN(schema)!));\n const newHandle = this._repo.create<DatabaseDirectory>({\n version: SpaceDocVersion.CURRENT,\n access: {\n spaceKey: this._space.key.toHex(),\n },\n objects: {\n [core.id]: core.getDoc() as ObjectStructure,\n },\n });\n await newHandle.whenReady();\n this._newLinks[core.id] = newHandle.url!;\n this._addHandleToFlushList(newHandle.documentId!);\n\n return core;\n }\n\n private _addHandleToFlushList(id: DocumentId): void {\n this._flushIds.push(id);\n }\n}\n", "//\n// Copyright 2023 DXOS.org\n//\n\nimport { Atom } from '@effect-atom/atom';\nimport * as Registry from '@effect-atom/atom/Registry';\n\nimport { type Space, SpaceState } from '@dxos/client/echo';\nimport { invariant } from '@dxos/invariant';\nimport { type MaybePromise } from '@dxos/util';\n\nimport { MigrationBuilder } from './migration-builder';\n\nexport type MigrationContext = {\n space: Space;\n builder: MigrationBuilder;\n};\n\nexport type Migration = {\n version: string;\n next: (context: MigrationContext) => MaybePromise<void>;\n};\n\nexport class Migrations {\n static namespace?: string;\n static migrations: Migration[] = [];\n private static _registry = Registry.make();\n private static _stateAtom = Atom.make<{ running: string[] }>({ running: [] }).pipe(Atom.keepAlive);\n\n static get versionProperty() {\n return this.namespace && `${this.namespace}.version`;\n }\n\n static get targetVersion() {\n return this.migrations[this.migrations.length - 1]?.version;\n }\n\n static running(space: Space): boolean {\n const state = this._registry.get(this._stateAtom);\n return state.running.includes(space.key.toHex());\n }\n\n static define(namespace: string, migrations: Migration[]): void {\n this.namespace = namespace;\n this.migrations = migrations;\n }\n\n static async migrate(space: Space, targetVersion?: string | number): Promise<boolean> {\n invariant(!this.running(space), 'Migration already running');\n invariant(this.versionProperty, 'Migrations namespace not set');\n invariant(space.state.get() === SpaceState.SPACE_READY, 'Space not ready');\n const currentVersion = space.properties[this.versionProperty];\n const currentIndex = this.migrations.findIndex((m) => m.version === currentVersion) + 1;\n const i = this.migrations.findIndex((m) => m.version === targetVersion);\n const targetIndex = i === -1 ? this.migrations.length : i + 1;\n if (currentIndex === targetIndex) {\n return false;\n }\n\n const spaceKey = space.key.toHex();\n const currentState = this._registry.get(this._stateAtom);\n this._registry.set(this._stateAtom, { running: [...currentState.running, spaceKey] });\n if (targetIndex > currentIndex) {\n const migrations = this.migrations.slice(currentIndex, targetIndex);\n for (const migration of migrations) {\n const builder = new MigrationBuilder(space);\n await migration.next({ space, builder });\n await builder.changeProperties((propertiesStructure) => {\n invariant(this.versionProperty, 'Migrations namespace not set');\n propertiesStructure.data[this.versionProperty] = migration.version;\n });\n await builder._commit();\n }\n }\n const finalState = this._registry.get(this._stateAtom);\n this._registry.set(this._stateAtom, { running: finalState.running.filter((key) => key !== spaceKey) });\n\n return true;\n }\n}\n"],
|
|
5
|
-
"mappings": ";AAIA,
|
|
6
|
-
"names": ["next", "A", "CreateEpochRequest", "ObjectCore", "migrateDocument", "EncodedReference", "SpaceDocVersion", "
|
|
3
|
+
"sources": ["../../../src/annotations.ts", "../../../src/migration-builder.ts", "../../../src/migrations.ts"],
|
|
4
|
+
"sourcesContent": ["//\n// Copyright 2026 DXOS.org\n//\n\nimport * as Schema from 'effect/Schema';\n\nimport { Annotation } from '@dxos/echo';\n\n/** Migration version stored on space properties meta. */\nexport const MigrationVersionAnnotation = Annotation.make({\n id: 'org.dxos.migrations.version',\n schema: Schema.String,\n});\n", "//\n// Copyright 2024 DXOS.org\n//\n\nimport { next as A, type Doc } from '@automerge/automerge';\nimport { type AnyDocumentId, type DocumentId } from '@automerge/automerge-repo';\nimport type * as Schema from 'effect/Schema';\n\nimport { type Space } from '@dxos/client/echo';\nimport { CreateEpochRequest } from '@dxos/client/halo';\nimport { type DocHandleProxy, ObjectCore, type RepoProxy, migrateDocument } from '@dxos/echo-db';\nimport { type DatabaseDirectory, EncodedReference, type EntityStructure, SpaceDocVersion } from '@dxos/echo-protocol';\nimport { getSchemaURI } from '@dxos/echo/internal';\nimport * as Type from '@dxos/echo/Type';\nimport { invariant } from '@dxos/invariant';\nimport { EID, EntityId } from '@dxos/keys';\nimport { type MaybePromise } from '@dxos/util';\n\n/*\n\nConsidering a better API for this:\n\n```ts\nconst migration = space.db.beginMigration(); // all actions are not visible to queries and are only applied once you call `apply`\n\nmigration.applyObjectMigration(defineMigration(From, To, { ... }));\n\nmigration.delete(id);\nmigration.add(obj);\n\nawait migration.apply(); // Will create new epoch.\n```\n\n*/\n\n// TODO(dmaretskyi): We no longer need to hook into ECHO internals, with the changes to echo APIs.\nexport class MigrationBuilder {\n private readonly _repo: RepoProxy;\n private readonly _rootDoc: Doc<DatabaseDirectory>;\n\n // echoUri -> automergeUrl\n private readonly _newLinks: Record<string, string> = {};\n private readonly _flushIds: DocumentId[] = [];\n private readonly _deleteObjects: string[] = [];\n\n private _newRoot?: DocHandleProxy<DatabaseDirectory> = undefined;\n\n constructor(private readonly _space: Space) {\n this._repo = this._space.internal.db.coreDatabase._repo;\n // TODO(wittjosiah): Accessing private API.\n this._rootDoc = (this._space.internal.db.coreDatabase as any)._automergeDocLoader\n .getSpaceRootDocHandle()\n .doc() as Doc<DatabaseDirectory>;\n }\n\n async findObject(id: string): Promise<EntityStructure | undefined> {\n const documentId = (this._rootDoc.links?.[id] || this._newLinks[id])?.toString() as AnyDocumentId | undefined;\n const docHandle = documentId && this._repo.find(documentId);\n if (!docHandle) {\n return undefined;\n }\n\n await docHandle.whenReady();\n const doc = docHandle.doc() as Doc<DatabaseDirectory>;\n return doc.objects?.[id];\n }\n\n async migrateObject(\n id: string,\n migrate: (objectStructure: EntityStructure) => MaybePromise<{ type: Type.AnyEntity; props: any }>,\n ): Promise<void> {\n const objectStructure = await this.findObject(id);\n if (!objectStructure) {\n return;\n }\n\n const { type, props } = await migrate(objectStructure);\n const schema = Type.getSchema(type);\n\n const oldHandle = await this._findObjectContainingHandle(id);\n invariant(oldHandle);\n\n const newState: DatabaseDirectory = {\n version: SpaceDocVersion.CURRENT,\n access: {\n spaceKey: this._space.key.toHex(),\n },\n objects: {\n [id]: {\n system: {\n type: EncodedReference.fromURI(getSchemaURI(schema)!),\n },\n data: props,\n meta: {\n keys: [],\n },\n },\n },\n };\n const migratedDoc = migrateDocument(oldHandle.doc() as Doc<DatabaseDirectory>, newState);\n const newHandle = this._repo.import<DatabaseDirectory>(A.save(migratedDoc));\n await newHandle.whenReady();\n invariant(newHandle.url, 'Migrated document URL not available after whenReady');\n this._newLinks[id] = newHandle.url;\n this._addHandleToFlushList(newHandle.documentId!);\n }\n\n async addObject(type: Type.AnyEntity, props: any): Promise<string> {\n const resolved = Type.getSchema(type);\n const core = await this._createObject({ schema: resolved, props });\n return core.id;\n }\n\n createReference(id: string) {\n invariant(EntityId.isValid(id), 'Invalid EntityId.');\n return EncodedReference.fromURI(EID.make({ entityId: id }));\n }\n\n deleteObject(id: string): void {\n this._deleteObjects.push(id);\n }\n\n async changeProperties(changeFn: (properties: EntityStructure) => void): Promise<void> {\n if (!this._newRoot) {\n await this._buildNewRoot();\n }\n invariant(this._newRoot, 'New root not created');\n\n this._newRoot.change((doc: DatabaseDirectory) => {\n const propertiesStructure = doc.objects?.[this._space.properties.id];\n propertiesStructure && changeFn(propertiesStructure);\n });\n await this._newRoot.whenReady();\n this._addHandleToFlushList(this._newRoot.documentId!);\n }\n\n /**\n * @internal\n */\n async _commit(): Promise<void> {\n if (!this._newRoot) {\n await this._buildNewRoot();\n }\n invariant(this._newRoot, 'New root not created');\n\n await this._space.db.flush();\n\n // Create new epoch.\n invariant(this._newRoot.url, 'New root URL not available');\n await this._space.internal.createEpoch({\n migration: CreateEpochRequest.Migration.REPLACE_AUTOMERGE_ROOT,\n automergeRootUrl: this._newRoot.url,\n });\n }\n\n private async _findObjectContainingHandle(id: string): Promise<DocHandleProxy<DatabaseDirectory> | undefined> {\n const documentId = (this._rootDoc.links?.[id] || this._newLinks[id])?.toString() as AnyDocumentId | undefined;\n const docHandle = documentId && this._repo.find(documentId);\n if (!docHandle) {\n return undefined;\n }\n\n await docHandle.whenReady();\n return docHandle;\n }\n\n private async _buildNewRoot(): Promise<void> {\n const links = { ...(this._rootDoc.links ?? {}) };\n for (const id of this._deleteObjects) {\n delete links[id];\n }\n\n for (const [id, url] of Object.entries(this._newLinks)) {\n links[id] = new A.RawString(url);\n }\n\n this._newRoot = this._repo.create<DatabaseDirectory>({\n version: SpaceDocVersion.CURRENT,\n access: {\n spaceKey: this._space.key.toHex(),\n },\n objects: this._rootDoc.objects,\n links,\n });\n await this._newRoot.whenReady();\n this._addHandleToFlushList(this._newRoot.documentId!);\n }\n\n private async _createObject({\n id,\n schema,\n props,\n }: {\n id?: string;\n schema: Schema.Schema.AnyNoContext;\n props: any;\n }): Promise<ObjectCore> {\n const core = new ObjectCore();\n if (id) {\n core.id = id;\n }\n\n core.initNewObject(props);\n core.setType(EncodedReference.fromURI(getSchemaURI(schema)!));\n const newHandle = this._repo.create<DatabaseDirectory>({\n version: SpaceDocVersion.CURRENT,\n access: {\n spaceKey: this._space.key.toHex(),\n },\n objects: {\n [core.id]: core.getDoc() as EntityStructure,\n },\n });\n await newHandle.whenReady();\n this._newLinks[core.id] = newHandle.url!;\n this._addHandleToFlushList(newHandle.documentId!);\n\n return core;\n }\n\n private _addHandleToFlushList(id: DocumentId): void {\n this._flushIds.push(id);\n }\n}\n", "//\n// Copyright 2023 DXOS.org\n//\n\nimport { Atom } from '@effect-atom/atom';\nimport * as Registry from '@effect-atom/atom/Registry';\nimport * as Option from 'effect/Option';\n\nimport { type Space, SpaceState } from '@dxos/client/echo';\nimport { Annotation, Obj } from '@dxos/echo';\nimport { invariant } from '@dxos/invariant';\nimport { type MaybePromise } from '@dxos/util';\n\nimport { MigrationVersionAnnotation } from './annotations';\nimport { MigrationBuilder } from './migration-builder';\n\nexport type MigrationContext = {\n space: Space;\n builder: MigrationBuilder;\n};\n\nexport type Migration = {\n version: string;\n next: (context: MigrationContext) => MaybePromise<void>;\n};\n\nexport class Migrations {\n static namespace?: string;\n static migrations: Migration[] = [];\n private static _registry = Registry.make();\n private static _stateAtom = Atom.make<{ running: string[] }>({ running: [] }).pipe(Atom.keepAlive);\n\n /**\n * @deprecated Use `MigrationVersionAnnotation` via `Annotation.get/set` on space properties.\n */\n static get versionProperty() {\n return this.namespace && `${this.namespace}.version`;\n }\n\n static get targetVersion() {\n return this.migrations[this.migrations.length - 1]?.version;\n }\n\n static running(space: Space): boolean {\n const state = this._registry.get(this._stateAtom);\n return state.running.includes(space.key.toHex());\n }\n\n static define(namespace: string, migrations: Migration[]): void {\n this.namespace = namespace;\n this.migrations = migrations;\n }\n\n static async migrate(space: Space, targetVersion?: string | number): Promise<boolean> {\n invariant(!this.running(space), 'Migration already running');\n invariant(space.state.get() === SpaceState.SPACE_READY, 'Space not ready');\n const currentVersion = Annotation.get(space.properties, MigrationVersionAnnotation).pipe(Option.getOrUndefined);\n const currentIndex = this.migrations.findIndex((m) => m.version === currentVersion) + 1;\n const i = this.migrations.findIndex((m) => m.version === targetVersion);\n const targetIndex = i === -1 ? this.migrations.length : i + 1;\n if (currentIndex === targetIndex) {\n return false;\n }\n\n const spaceKey = space.key.toHex();\n const currentState = this._registry.get(this._stateAtom);\n this._registry.set(this._stateAtom, { running: [...currentState.running, spaceKey] });\n try {\n if (targetIndex > currentIndex) {\n const migrations = this.migrations.slice(currentIndex, targetIndex);\n for (const migration of migrations) {\n const builder = new MigrationBuilder(space);\n await migration.next({ space, builder });\n await builder._commit();\n Obj.update(space.properties, (properties) => {\n Annotation.set(properties, MigrationVersionAnnotation, migration.version);\n });\n }\n }\n } finally {\n const finalState = this._registry.get(this._stateAtom);\n this._registry.set(this._stateAtom, { running: finalState.running.filter((key) => key !== spaceKey) });\n }\n\n return true;\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["Schema", "Annotation", "MigrationVersionAnnotation", "make", "id", "schema", "String", "next", "A", "CreateEpochRequest", "ObjectCore", "migrateDocument", "EncodedReference", "SpaceDocVersion", "getSchemaURI", "Type", "invariant", "EID", "EntityId", "_repo", "_rootDoc", "_newLinks", "_deleteObjects", "_newRoot", "undefined", "_space", "internal", "db", "coreDatabase", "findObject", "docHandle", "objects", "migrateObject", "migrate", "objectStructure", "oldHandle", "_findObjectContainingHandle", "newState", "version", "access", "spaceKey", "system", "data", "props", "meta", "keys", "newHandle", "whenReady", "id", "_addHandleToFlushList", "addObject", "core", "_createObject", "schema", "resolved", "createReference", "entityId", "deleteObject", "changeProperties", "changeFn", "_buildNewRoot", "propertiesStructure", "properties", "migration", "REPLACE_AUTOMERGE_ROOT", "automergeRootUrl", "documentId", "url", "create", "links", "initNewObject", "CURRENT", "Atom", "Registry", "Option", "SpaceState", "Annotation", "Obj", "invariant", "__dxlog_file", "Migrations", "migrations", "_registry", "_stateAtom", "Atom", "running", "keepAlive", "targetVersion", "space", "state", "define", "namespace", "invariant", "get", "SpaceState", "currentVersion", "Annotation", "MigrationVersionAnnotation", "pipe", "getOrUndefined", "currentIndex", "findIndex", "version", "i", "m", "targetIndex", "length", "spaceKey", "set", "currentState", "migration", "builder", "MigrationBuilder", "Obj", "properties", "finalState", "filter", "key"]
|
|
7
7
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"inputs":{"src/migration-builder.ts":{"bytes":
|
|
1
|
+
{"inputs":{"src/annotations.ts":{"bytes":1241,"imports":[{"path":"effect/Schema","kind":"import-statement","external":true},{"path":"@dxos/echo","kind":"import-statement","external":true}],"format":"esm"},"src/migration-builder.ts":{"bytes":24278,"imports":[{"path":"@automerge/automerge","kind":"import-statement","external":true},{"path":"@dxos/client/halo","kind":"import-statement","external":true},{"path":"@dxos/echo-db","kind":"import-statement","external":true},{"path":"@dxos/echo-protocol","kind":"import-statement","external":true},{"path":"@dxos/echo/internal","kind":"import-statement","external":true},{"path":"@dxos/echo/Type","kind":"import-statement","external":true},{"path":"@dxos/invariant","kind":"import-statement","external":true},{"path":"@dxos/keys","kind":"import-statement","external":true}],"format":"esm"},"src/migrations.ts":{"bytes":11492,"imports":[{"path":"@effect-atom/atom","kind":"import-statement","external":true},{"path":"@effect-atom/atom/Registry","kind":"import-statement","external":true},{"path":"effect/Option","kind":"import-statement","external":true},{"path":"@dxos/client/echo","kind":"import-statement","external":true},{"path":"@dxos/echo","kind":"import-statement","external":true},{"path":"@dxos/invariant","kind":"import-statement","external":true},{"path":"src/annotations.ts","kind":"import-statement","original":"./annotations"},{"path":"src/migration-builder.ts","kind":"import-statement","original":"./migration-builder"}],"format":"esm"},"src/index.ts":{"bytes":674,"imports":[{"path":"src/annotations.ts","kind":"import-statement","original":"./annotations"},{"path":"src/migration-builder.ts","kind":"import-statement","original":"./migration-builder"},{"path":"src/migrations.ts","kind":"import-statement","original":"./migrations"}],"format":"esm"}},"outputs":{"dist/lib/browser/index.mjs.map":{"imports":[],"exports":[],"inputs":{},"bytes":17813},"dist/lib/browser/index.mjs":{"imports":[{"path":"effect/Schema","kind":"import-statement","external":true},{"path":"@dxos/echo","kind":"import-statement","external":true},{"path":"@automerge/automerge","kind":"import-statement","external":true},{"path":"@dxos/client/halo","kind":"import-statement","external":true},{"path":"@dxos/echo-db","kind":"import-statement","external":true},{"path":"@dxos/echo-protocol","kind":"import-statement","external":true},{"path":"@dxos/echo/internal","kind":"import-statement","external":true},{"path":"@dxos/echo/Type","kind":"import-statement","external":true},{"path":"@dxos/invariant","kind":"import-statement","external":true},{"path":"@dxos/keys","kind":"import-statement","external":true},{"path":"@effect-atom/atom","kind":"import-statement","external":true},{"path":"@effect-atom/atom/Registry","kind":"import-statement","external":true},{"path":"effect/Option","kind":"import-statement","external":true},{"path":"@dxos/client/echo","kind":"import-statement","external":true},{"path":"@dxos/echo","kind":"import-statement","external":true},{"path":"@dxos/invariant","kind":"import-statement","external":true}],"exports":["MigrationBuilder","MigrationVersionAnnotation","Migrations"],"entryPoint":"src/index.ts","inputs":{"src/annotations.ts":{"bytesInOutput":198},"src/index.ts":{"bytesInOutput":0},"src/migration-builder.ts":{"bytesInOutput":5754},"src/migrations.ts":{"bytesInOutput":2983}},"bytes":9118}}}
|
|
@@ -1,19 +1,28 @@
|
|
|
1
1
|
import { createRequire } from 'node:module';const require = createRequire(import.meta.url);
|
|
2
2
|
|
|
3
|
+
// src/annotations.ts
|
|
4
|
+
import * as Schema from "effect/Schema";
|
|
5
|
+
import { Annotation } from "@dxos/echo";
|
|
6
|
+
var MigrationVersionAnnotation = Annotation.make({
|
|
7
|
+
id: "org.dxos.migrations.version",
|
|
8
|
+
schema: Schema.String
|
|
9
|
+
});
|
|
10
|
+
|
|
3
11
|
// src/migration-builder.ts
|
|
4
12
|
import { next as A } from "@automerge/automerge";
|
|
5
13
|
import { CreateEpochRequest } from "@dxos/client/halo";
|
|
6
14
|
import { ObjectCore, migrateDocument } from "@dxos/echo-db";
|
|
7
15
|
import { EncodedReference, SpaceDocVersion } from "@dxos/echo-protocol";
|
|
8
|
-
import {
|
|
16
|
+
import { getSchemaURI } from "@dxos/echo/internal";
|
|
17
|
+
import * as Type from "@dxos/echo/Type";
|
|
9
18
|
import { invariant } from "@dxos/invariant";
|
|
10
|
-
import {
|
|
19
|
+
import { EID, EntityId } from "@dxos/keys";
|
|
11
20
|
var __dxlog_file = "/__w/dxos/dxos/packages/sdk/migrations/src/migration-builder.ts";
|
|
12
21
|
var MigrationBuilder = class {
|
|
13
22
|
_space;
|
|
14
23
|
_repo;
|
|
15
24
|
_rootDoc;
|
|
16
|
-
//
|
|
25
|
+
// echoUri -> automergeUrl
|
|
17
26
|
_newLinks = {};
|
|
18
27
|
_flushIds = [];
|
|
19
28
|
_deleteObjects = [];
|
|
@@ -38,9 +47,10 @@ var MigrationBuilder = class {
|
|
|
38
47
|
if (!objectStructure) {
|
|
39
48
|
return;
|
|
40
49
|
}
|
|
41
|
-
const {
|
|
50
|
+
const { type, props } = await migrate(objectStructure);
|
|
51
|
+
const schema = Type.getSchema(type);
|
|
42
52
|
const oldHandle = await this._findObjectContainingHandle(id);
|
|
43
|
-
invariant(oldHandle, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file, L:
|
|
53
|
+
invariant(oldHandle, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file, L: 61, S: this, A: ["oldHandle", ""] });
|
|
44
54
|
const newState = {
|
|
45
55
|
version: SpaceDocVersion.CURRENT,
|
|
46
56
|
access: {
|
|
@@ -49,7 +59,7 @@ var MigrationBuilder = class {
|
|
|
49
59
|
objects: {
|
|
50
60
|
[id]: {
|
|
51
61
|
system: {
|
|
52
|
-
type: EncodedReference.
|
|
62
|
+
type: EncodedReference.fromURI(getSchemaURI(schema))
|
|
53
63
|
},
|
|
54
64
|
data: props,
|
|
55
65
|
meta: {
|
|
@@ -61,19 +71,23 @@ var MigrationBuilder = class {
|
|
|
61
71
|
const migratedDoc = migrateDocument(oldHandle.doc(), newState);
|
|
62
72
|
const newHandle = this._repo.import(A.save(migratedDoc));
|
|
63
73
|
await newHandle.whenReady();
|
|
64
|
-
invariant(newHandle.url, "Migrated document URL not available after whenReady", { "~LogMeta": "~LogMeta", F: __dxlog_file, L:
|
|
74
|
+
invariant(newHandle.url, "Migrated document URL not available after whenReady", { "~LogMeta": "~LogMeta", F: __dxlog_file, L: 82, S: this, A: ["newHandle.url", "'Migrated document URL not available after whenReady'"] });
|
|
65
75
|
this._newLinks[id] = newHandle.url;
|
|
66
76
|
this._addHandleToFlushList(newHandle.documentId);
|
|
67
77
|
}
|
|
68
|
-
async addObject(
|
|
78
|
+
async addObject(type, props) {
|
|
79
|
+
const resolved = Type.getSchema(type);
|
|
69
80
|
const core = await this._createObject({
|
|
70
|
-
schema,
|
|
81
|
+
schema: resolved,
|
|
71
82
|
props
|
|
72
83
|
});
|
|
73
84
|
return core.id;
|
|
74
85
|
}
|
|
75
86
|
createReference(id) {
|
|
76
|
-
|
|
87
|
+
invariant(EntityId.isValid(id), "Invalid EntityId.", { "~LogMeta": "~LogMeta", F: __dxlog_file, L: 95, S: this, A: ["EntityId.isValid(id)", "'Invalid EntityId.'"] });
|
|
88
|
+
return EncodedReference.fromURI(EID.make({
|
|
89
|
+
entityId: id
|
|
90
|
+
}));
|
|
77
91
|
}
|
|
78
92
|
deleteObject(id) {
|
|
79
93
|
this._deleteObjects.push(id);
|
|
@@ -82,7 +96,7 @@ var MigrationBuilder = class {
|
|
|
82
96
|
if (!this._newRoot) {
|
|
83
97
|
await this._buildNewRoot();
|
|
84
98
|
}
|
|
85
|
-
invariant(this._newRoot, "New root not created", { "~LogMeta": "~LogMeta", F: __dxlog_file, L:
|
|
99
|
+
invariant(this._newRoot, "New root not created", { "~LogMeta": "~LogMeta", F: __dxlog_file, L: 107, S: this, A: ["this._newRoot", "'New root not created'"] });
|
|
86
100
|
this._newRoot.change((doc) => {
|
|
87
101
|
const propertiesStructure = doc.objects?.[this._space.properties.id];
|
|
88
102
|
propertiesStructure && changeFn(propertiesStructure);
|
|
@@ -97,9 +111,9 @@ var MigrationBuilder = class {
|
|
|
97
111
|
if (!this._newRoot) {
|
|
98
112
|
await this._buildNewRoot();
|
|
99
113
|
}
|
|
100
|
-
invariant(this._newRoot, "New root not created", { "~LogMeta": "~LogMeta", F: __dxlog_file, L:
|
|
114
|
+
invariant(this._newRoot, "New root not created", { "~LogMeta": "~LogMeta", F: __dxlog_file, L: 121, S: this, A: ["this._newRoot", "'New root not created'"] });
|
|
101
115
|
await this._space.db.flush();
|
|
102
|
-
invariant(this._newRoot.url, "New root URL not available", { "~LogMeta": "~LogMeta", F: __dxlog_file, L:
|
|
116
|
+
invariant(this._newRoot.url, "New root URL not available", { "~LogMeta": "~LogMeta", F: __dxlog_file, L: 124, S: this, A: ["this._newRoot.url", "'New root URL not available'"] });
|
|
103
117
|
await this._space.internal.createEpoch({
|
|
104
118
|
migration: CreateEpochRequest.Migration.REPLACE_AUTOMERGE_ROOT,
|
|
105
119
|
automergeRootUrl: this._newRoot.url
|
|
@@ -141,7 +155,7 @@ var MigrationBuilder = class {
|
|
|
141
155
|
core.id = id;
|
|
142
156
|
}
|
|
143
157
|
core.initNewObject(props);
|
|
144
|
-
core.setType(EncodedReference.
|
|
158
|
+
core.setType(EncodedReference.fromURI(getSchemaURI(schema)));
|
|
145
159
|
const newHandle = this._repo.create({
|
|
146
160
|
version: SpaceDocVersion.CURRENT,
|
|
147
161
|
access: {
|
|
@@ -164,7 +178,9 @@ var MigrationBuilder = class {
|
|
|
164
178
|
// src/migrations.ts
|
|
165
179
|
import { Atom } from "@effect-atom/atom";
|
|
166
180
|
import * as Registry from "@effect-atom/atom/Registry";
|
|
181
|
+
import * as Option from "effect/Option";
|
|
167
182
|
import { SpaceState } from "@dxos/client/echo";
|
|
183
|
+
import { Annotation as Annotation2, Obj } from "@dxos/echo";
|
|
168
184
|
import { invariant as invariant2 } from "@dxos/invariant";
|
|
169
185
|
var __dxlog_file2 = "/__w/dxos/dxos/packages/sdk/migrations/src/migrations.ts";
|
|
170
186
|
var Migrations = class {
|
|
@@ -174,6 +190,9 @@ var Migrations = class {
|
|
|
174
190
|
static _stateAtom = Atom.make({
|
|
175
191
|
running: []
|
|
176
192
|
}).pipe(Atom.keepAlive);
|
|
193
|
+
/**
|
|
194
|
+
* @deprecated Use `MigrationVersionAnnotation` via `Annotation.get/set` on space properties.
|
|
195
|
+
*/
|
|
177
196
|
static get versionProperty() {
|
|
178
197
|
return this.namespace && `${this.namespace}.version`;
|
|
179
198
|
}
|
|
@@ -189,10 +208,9 @@ var Migrations = class {
|
|
|
189
208
|
this.migrations = migrations;
|
|
190
209
|
}
|
|
191
210
|
static async migrate(space, targetVersion) {
|
|
192
|
-
invariant2(!this.running(space), "Migration already running", { "~LogMeta": "~LogMeta", F: __dxlog_file2, L:
|
|
193
|
-
invariant2(
|
|
194
|
-
|
|
195
|
-
const currentVersion = space.properties[this.versionProperty];
|
|
211
|
+
invariant2(!this.running(space), "Migration already running", { "~LogMeta": "~LogMeta", F: __dxlog_file2, L: 36, S: this, A: ["!this.running(space)", "'Migration already running'"] });
|
|
212
|
+
invariant2(space.state.get() === SpaceState.SPACE_READY, "Space not ready", { "~LogMeta": "~LogMeta", F: __dxlog_file2, L: 37, S: this, A: ["space.state.get() === SpaceState.SPACE_READY", "'Space not ready'"] });
|
|
213
|
+
const currentVersion = Annotation2.get(space.properties, MigrationVersionAnnotation).pipe(Option.getOrUndefined);
|
|
196
214
|
const currentIndex = this.migrations.findIndex((m) => m.version === currentVersion) + 1;
|
|
197
215
|
const i = this.migrations.findIndex((m) => m.version === targetVersion);
|
|
198
216
|
const targetIndex = i === -1 ? this.migrations.length : i + 1;
|
|
@@ -207,30 +225,33 @@ var Migrations = class {
|
|
|
207
225
|
spaceKey
|
|
208
226
|
]
|
|
209
227
|
});
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
const
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
228
|
+
try {
|
|
229
|
+
if (targetIndex > currentIndex) {
|
|
230
|
+
const migrations = this.migrations.slice(currentIndex, targetIndex);
|
|
231
|
+
for (const migration of migrations) {
|
|
232
|
+
const builder = new MigrationBuilder(space);
|
|
233
|
+
await migration.next({
|
|
234
|
+
space,
|
|
235
|
+
builder
|
|
236
|
+
});
|
|
237
|
+
await builder._commit();
|
|
238
|
+
Obj.update(space.properties, (properties) => {
|
|
239
|
+
Annotation2.set(properties, MigrationVersionAnnotation, migration.version);
|
|
240
|
+
});
|
|
241
|
+
}
|
|
223
242
|
}
|
|
243
|
+
} finally {
|
|
244
|
+
const finalState = this._registry.get(this._stateAtom);
|
|
245
|
+
this._registry.set(this._stateAtom, {
|
|
246
|
+
running: finalState.running.filter((key) => key !== spaceKey)
|
|
247
|
+
});
|
|
224
248
|
}
|
|
225
|
-
const finalState = this._registry.get(this._stateAtom);
|
|
226
|
-
this._registry.set(this._stateAtom, {
|
|
227
|
-
running: finalState.running.filter((key) => key !== spaceKey)
|
|
228
|
-
});
|
|
229
249
|
return true;
|
|
230
250
|
}
|
|
231
251
|
};
|
|
232
252
|
export {
|
|
233
253
|
MigrationBuilder,
|
|
254
|
+
MigrationVersionAnnotation,
|
|
234
255
|
Migrations
|
|
235
256
|
};
|
|
236
257
|
//# sourceMappingURL=index.mjs.map
|