@dxos/client-services 0.5.9-main.ea1d25b → 0.5.9-main.eacfffa
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/{chunk-LKSDZ2AB.mjs → chunk-J4DDMC6A.mjs} +1214 -844
- package/dist/lib/browser/chunk-J4DDMC6A.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +5 -4
- package/dist/lib/browser/index.mjs.map +1 -1
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/packlets/testing/index.mjs +20 -13
- package/dist/lib/browser/packlets/testing/index.mjs.map +3 -3
- package/dist/lib/node/{chunk-FBXXJHAL.cjs → chunk-6HFQ2SQ5.cjs} +1493 -1130
- package/dist/lib/node/chunk-6HFQ2SQ5.cjs.map +7 -0
- package/dist/lib/node/index.cjs +45 -44
- package/dist/lib/node/index.cjs.map +1 -1
- package/dist/lib/node/meta.json +1 -1
- package/dist/lib/node/packlets/testing/index.cjs +26 -19
- package/dist/lib/node/packlets/testing/index.cjs.map +3 -3
- package/dist/types/src/packlets/identity/default-space-state-machine.d.ts +19 -0
- package/dist/types/src/packlets/identity/default-space-state-machine.d.ts.map +1 -0
- package/dist/types/src/packlets/identity/identity-service.d.ts +14 -7
- package/dist/types/src/packlets/identity/identity-service.d.ts.map +1 -1
- package/dist/types/src/packlets/identity/identity.d.ts +4 -1
- package/dist/types/src/packlets/identity/identity.d.ts.map +1 -1
- package/dist/types/src/packlets/invitations/invitations-handler.d.ts.map +1 -1
- package/dist/types/src/packlets/services/service-context.d.ts.map +1 -1
- package/dist/types/src/packlets/services/service-host.d.ts +1 -1
- package/dist/types/src/packlets/services/service-host.d.ts.map +1 -1
- package/dist/types/src/packlets/spaces/automerge-space-state.d.ts +4 -1
- package/dist/types/src/packlets/spaces/automerge-space-state.d.ts.map +1 -1
- package/dist/types/src/packlets/spaces/data-space-manager.d.ts +5 -3
- package/dist/types/src/packlets/spaces/data-space-manager.d.ts.map +1 -1
- package/dist/types/src/packlets/spaces/data-space.d.ts +9 -9
- package/dist/types/src/packlets/spaces/data-space.d.ts.map +1 -1
- package/dist/types/src/packlets/spaces/epoch-migrations.d.ts +23 -0
- package/dist/types/src/packlets/spaces/epoch-migrations.d.ts.map +1 -0
- package/dist/types/src/packlets/spaces/spaces-service.d.ts +2 -2
- package/dist/types/src/packlets/spaces/spaces-service.d.ts.map +1 -1
- package/dist/types/src/packlets/testing/test-builder.d.ts +8 -6
- package/dist/types/src/packlets/testing/test-builder.d.ts.map +1 -1
- package/dist/types/src/version.d.ts +1 -1
- package/package.json +36 -36
- package/src/packlets/identity/default-space-state-machine.ts +44 -0
- package/src/packlets/identity/identity-service.test.ts +35 -5
- package/src/packlets/identity/identity-service.ts +76 -8
- package/src/packlets/identity/identity.ts +25 -2
- package/src/packlets/invitations/invitations-handler.ts +13 -5
- package/src/packlets/services/service-context.ts +1 -4
- package/src/packlets/services/service-host.ts +13 -40
- package/src/packlets/spaces/automerge-space-state.ts +11 -2
- package/src/packlets/spaces/data-space-manager.test.ts +46 -1
- package/src/packlets/spaces/data-space-manager.ts +81 -31
- package/src/packlets/spaces/data-space.ts +85 -152
- package/src/packlets/spaces/epoch-migrations.ts +135 -0
- package/src/packlets/spaces/spaces-service.ts +16 -4
- package/src/packlets/testing/test-builder.ts +12 -10
- package/src/version.ts +1 -1
- package/dist/lib/browser/chunk-LKSDZ2AB.mjs.map +0 -7
- package/dist/lib/node/chunk-FBXXJHAL.cjs.map +0 -7
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2024 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { asyncTimeout } from '@dxos/async';
|
|
6
|
+
import { next as am } from '@dxos/automerge/automerge';
|
|
7
|
+
import type { Repo, AutomergeUrl } from '@dxos/automerge/automerge-repo';
|
|
8
|
+
import { cancelWithContext, type Context } from '@dxos/context';
|
|
9
|
+
import {
|
|
10
|
+
convertLegacyReferences,
|
|
11
|
+
convertLegacySpaceRootDoc,
|
|
12
|
+
findInlineObjectOfType,
|
|
13
|
+
migrateDocument,
|
|
14
|
+
} from '@dxos/echo-db';
|
|
15
|
+
import { AutomergeDocumentLoaderImpl } from '@dxos/echo-pipeline';
|
|
16
|
+
import type { SpaceDoc } from '@dxos/echo-protocol';
|
|
17
|
+
import { TYPE_PROPERTIES } from '@dxos/echo-schema';
|
|
18
|
+
import { invariant } from '@dxos/invariant';
|
|
19
|
+
import type { PublicKey, SpaceId } from '@dxos/keys';
|
|
20
|
+
import { log } from '@dxos/log';
|
|
21
|
+
import { CreateEpochRequest } from '@dxos/protocols/proto/dxos/client/services';
|
|
22
|
+
import { assignDeep } from '@dxos/util';
|
|
23
|
+
|
|
24
|
+
export type MigrationContext = {
|
|
25
|
+
repo: Repo;
|
|
26
|
+
spaceId: SpaceId;
|
|
27
|
+
/**
|
|
28
|
+
* @deprecated Remove.
|
|
29
|
+
*/
|
|
30
|
+
spaceKey: PublicKey;
|
|
31
|
+
migration: CreateEpochRequest.Migration;
|
|
32
|
+
currentRoot: string | null;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* For set automerge root migration type.
|
|
36
|
+
*/
|
|
37
|
+
newAutomergeRoot?: string;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export type MigrationResult = {
|
|
41
|
+
newRoot?: string;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
export const runEpochMigration = async (ctx: Context, context: MigrationContext): Promise<MigrationResult> => {
|
|
45
|
+
switch (context.migration) {
|
|
46
|
+
case CreateEpochRequest.Migration.INIT_AUTOMERGE: {
|
|
47
|
+
const document = context.repo.create();
|
|
48
|
+
await context.repo.flush();
|
|
49
|
+
return { newRoot: document.url };
|
|
50
|
+
}
|
|
51
|
+
case CreateEpochRequest.Migration.PRUNE_AUTOMERGE_ROOT_HISTORY: {
|
|
52
|
+
if (!context.currentRoot) {
|
|
53
|
+
throw new Error('Space does not have an automerge root');
|
|
54
|
+
}
|
|
55
|
+
const rootHandle = context.repo.find(context.currentRoot as AutomergeUrl);
|
|
56
|
+
await cancelWithContext(ctx, asyncTimeout(rootHandle.whenReady(), 10_000));
|
|
57
|
+
|
|
58
|
+
const newRoot = context.repo.create(rootHandle.docSync());
|
|
59
|
+
await context.repo.flush();
|
|
60
|
+
return { newRoot: newRoot.url };
|
|
61
|
+
}
|
|
62
|
+
case CreateEpochRequest.Migration.FRAGMENT_AUTOMERGE_ROOT: {
|
|
63
|
+
log.info('Fragmenting');
|
|
64
|
+
|
|
65
|
+
const currentRootUrl = context.currentRoot;
|
|
66
|
+
const rootHandle = context.repo.find<SpaceDoc>(currentRootUrl as any);
|
|
67
|
+
await cancelWithContext(ctx, asyncTimeout(rootHandle.whenReady(), 10_000));
|
|
68
|
+
|
|
69
|
+
// Find properties object.
|
|
70
|
+
const objects = Object.entries((rootHandle.docSync() as SpaceDoc).objects!);
|
|
71
|
+
const properties = findInlineObjectOfType(rootHandle.docSync() as SpaceDoc, TYPE_PROPERTIES);
|
|
72
|
+
const otherObjects = objects.filter(([key]) => key !== properties?.[0]);
|
|
73
|
+
invariant(properties, 'Properties not found');
|
|
74
|
+
|
|
75
|
+
// Create a new space doc with the properties object.
|
|
76
|
+
const newSpaceDoc: SpaceDoc = { ...rootHandle.docSync(), objects: Object.fromEntries([properties]) };
|
|
77
|
+
const newRoot = context.repo.create(newSpaceDoc);
|
|
78
|
+
invariant(typeof newRoot.url === 'string' && newRoot.url.length > 0);
|
|
79
|
+
|
|
80
|
+
// Create new automerge documents for all objects.
|
|
81
|
+
const docLoader = new AutomergeDocumentLoaderImpl(context.spaceId, context.repo, context.spaceKey);
|
|
82
|
+
await docLoader.loadSpaceRootDocHandle(ctx, { rootUrl: newRoot.url });
|
|
83
|
+
|
|
84
|
+
otherObjects.forEach(([key, value]) => {
|
|
85
|
+
const handle = docLoader.createDocumentForObject(key);
|
|
86
|
+
handle.change((doc: any) => {
|
|
87
|
+
assignDeep(doc, ['objects', key], value);
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
await context.repo.flush();
|
|
92
|
+
return {
|
|
93
|
+
newRoot: newRoot.url,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
case CreateEpochRequest.Migration.MIGRATE_REFERENCES_TO_DXN: {
|
|
97
|
+
const currentRootUrl = context.currentRoot;
|
|
98
|
+
const rootHandle = context.repo.find<SpaceDoc>(currentRootUrl as any);
|
|
99
|
+
await cancelWithContext(ctx, asyncTimeout(rootHandle.whenReady(), 10_000));
|
|
100
|
+
invariant(rootHandle.docSync(), 'Root doc not found');
|
|
101
|
+
|
|
102
|
+
const newRootContent = await convertLegacySpaceRootDoc(structuredClone(rootHandle.docSync()!));
|
|
103
|
+
|
|
104
|
+
for (const [id, url] of Object.entries(newRootContent.links ?? {})) {
|
|
105
|
+
const handle = context.repo.find(url as any);
|
|
106
|
+
await cancelWithContext(ctx, asyncTimeout(handle.whenReady(), 10_000));
|
|
107
|
+
invariant(handle.docSync(), 'Doc not found');
|
|
108
|
+
const newDoc = await convertLegacyReferences(structuredClone(handle.docSync()!));
|
|
109
|
+
const migratedDoc = migrateDocument(handle.docSync(), newDoc);
|
|
110
|
+
const newHandle = context.repo.import(am.save(migratedDoc));
|
|
111
|
+
newRootContent.links![id] = newHandle.url;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const migratedRoot = migrateDocument(rootHandle.docSync(), newRootContent);
|
|
115
|
+
const newRoot = context.repo.import(am.save(migratedRoot));
|
|
116
|
+
|
|
117
|
+
await context.repo.flush();
|
|
118
|
+
return {
|
|
119
|
+
newRoot: newRoot.url,
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
// TODO(dmaretskyi): This path doesn't seem to fit here. This is not a migration.
|
|
123
|
+
case CreateEpochRequest.Migration.REPLACE_AUTOMERGE_ROOT: {
|
|
124
|
+
invariant(context.newAutomergeRoot);
|
|
125
|
+
|
|
126
|
+
// Defensive programming - it should be the responsibility of the caller to flush the new root.
|
|
127
|
+
await context.repo.flush();
|
|
128
|
+
return {
|
|
129
|
+
newRoot: context.newAutomergeRoot,
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return {};
|
|
135
|
+
};
|
|
@@ -30,6 +30,7 @@ import {
|
|
|
30
30
|
type UpdateSpaceRequest,
|
|
31
31
|
type WriteCredentialsRequest,
|
|
32
32
|
type UpdateMemberRoleRequest,
|
|
33
|
+
type CreateEpochResponse,
|
|
33
34
|
} from '@dxos/protocols/proto/dxos/client/services';
|
|
34
35
|
import { type Credential } from '@dxos/protocols/proto/dxos/halo/credentials';
|
|
35
36
|
import { type GossipMessage } from '@dxos/protocols/proto/dxos/mesh/teleport/gossip';
|
|
@@ -125,8 +126,18 @@ export class SpacesServiceImpl implements SpacesService {
|
|
|
125
126
|
subscriptions.clear();
|
|
126
127
|
|
|
127
128
|
for (const space of dataSpaceManager.spaces.values()) {
|
|
128
|
-
|
|
129
|
-
subscriptions.add(
|
|
129
|
+
let lastState: SpaceState | undefined;
|
|
130
|
+
subscriptions.add(
|
|
131
|
+
space.stateUpdate.on(ctx, () => {
|
|
132
|
+
// Always send a separate update if the space state has changed.
|
|
133
|
+
if (space.state !== lastState) {
|
|
134
|
+
scheduler.forceTrigger();
|
|
135
|
+
} else {
|
|
136
|
+
scheduler.trigger();
|
|
137
|
+
}
|
|
138
|
+
lastState = space.state;
|
|
139
|
+
}),
|
|
140
|
+
);
|
|
130
141
|
|
|
131
142
|
subscriptions.add(space.presence.updated.on(ctx, () => scheduler.trigger()));
|
|
132
143
|
subscriptions.add(space.automergeSpaceState.onNewEpoch.on(ctx, () => scheduler.trigger()));
|
|
@@ -208,10 +219,11 @@ export class SpacesServiceImpl implements SpacesService {
|
|
|
208
219
|
}
|
|
209
220
|
}
|
|
210
221
|
|
|
211
|
-
async createEpoch({ spaceKey, migration, automergeRootUrl }: CreateEpochRequest) {
|
|
222
|
+
async createEpoch({ spaceKey, migration, automergeRootUrl }: CreateEpochRequest): Promise<CreateEpochResponse> {
|
|
212
223
|
const dataSpaceManager = await this._getDataSpaceManager();
|
|
213
224
|
const space = dataSpaceManager.spaces.get(spaceKey) ?? raise(new SpaceNotFoundError(spaceKey));
|
|
214
|
-
await space.createEpoch({ migration, newAutomergeRoot: automergeRootUrl });
|
|
225
|
+
const credential = await space.createEpoch({ migration, newAutomergeRoot: automergeRootUrl });
|
|
226
|
+
return { epochCredential: credential ?? undefined };
|
|
215
227
|
}
|
|
216
228
|
|
|
217
229
|
private _serializeSpace(space: DataSpace): Space {
|
|
@@ -19,8 +19,8 @@ import { createStorage, StorageType, type Storage } from '@dxos/random-access-st
|
|
|
19
19
|
import { BlobStore } from '@dxos/teleport-extension-object-sync';
|
|
20
20
|
|
|
21
21
|
import { InvitationsHandler, InvitationsManager, SpaceInvitationProtocol } from '../invitations';
|
|
22
|
-
import { ClientServicesHost, ServiceContext } from '../services';
|
|
23
|
-
import { DataSpaceManager, type SigningContext } from '../spaces';
|
|
22
|
+
import { ClientServicesHost, ServiceContext, type ServiceContextRuntimeParams } from '../services';
|
|
23
|
+
import { DataSpaceManager, type DataSpaceManagerRuntimeParams, type SigningContext } from '../spaces';
|
|
24
24
|
|
|
25
25
|
//
|
|
26
26
|
// TODO(burdon): Replace with test builder.
|
|
@@ -37,9 +37,11 @@ export const createServiceHost = (config: Config, signalManagerContext: MemorySi
|
|
|
37
37
|
export const createServiceContext = async ({
|
|
38
38
|
signalContext = new MemorySignalManagerContext(),
|
|
39
39
|
storage = createStorage({ type: StorageType.RAM }),
|
|
40
|
+
runtimeParams,
|
|
40
41
|
}: {
|
|
41
42
|
signalContext?: MemorySignalManagerContext;
|
|
42
43
|
storage?: Storage;
|
|
44
|
+
runtimeParams?: ServiceContextRuntimeParams;
|
|
43
45
|
} = {}) => {
|
|
44
46
|
const signalManager = new MemorySignalManager(signalContext);
|
|
45
47
|
const networkManager = new SwarmNetworkManager({
|
|
@@ -51,6 +53,7 @@ export const createServiceContext = async ({
|
|
|
51
53
|
|
|
52
54
|
return new ServiceContext(storage, level, networkManager, signalManager, {
|
|
53
55
|
invitationConnectionDefaultParams: { controlHeartbeatInterval: 200 },
|
|
56
|
+
...runtimeParams,
|
|
54
57
|
});
|
|
55
58
|
};
|
|
56
59
|
|
|
@@ -88,6 +91,7 @@ export class TestBuilder {
|
|
|
88
91
|
|
|
89
92
|
export type TestPeerOpts = {
|
|
90
93
|
dataStore?: StorageType;
|
|
94
|
+
dataSpaceParams?: DataSpaceManagerRuntimeParams;
|
|
91
95
|
};
|
|
92
96
|
|
|
93
97
|
export type TestPeerProps = {
|
|
@@ -110,8 +114,8 @@ export class TestPeer {
|
|
|
110
114
|
private _props: TestPeerProps = {};
|
|
111
115
|
|
|
112
116
|
constructor(
|
|
113
|
-
private readonly
|
|
114
|
-
private readonly
|
|
117
|
+
private readonly _signalContext: MemorySignalManagerContext,
|
|
118
|
+
private readonly _opts: TestPeerOpts = { dataStore: StorageType.RAM },
|
|
115
119
|
) {}
|
|
116
120
|
|
|
117
121
|
get props() {
|
|
@@ -119,7 +123,7 @@ export class TestPeer {
|
|
|
119
123
|
}
|
|
120
124
|
|
|
121
125
|
get storage() {
|
|
122
|
-
return (this._props.storage ??= createStorage({ type: this.
|
|
126
|
+
return (this._props.storage ??= createStorage({ type: this._opts.dataStore }));
|
|
123
127
|
}
|
|
124
128
|
|
|
125
129
|
get keyring() {
|
|
@@ -156,7 +160,7 @@ export class TestPeer {
|
|
|
156
160
|
|
|
157
161
|
get networkManager() {
|
|
158
162
|
return (this._props.networkManager ??= new SwarmNetworkManager({
|
|
159
|
-
signalManager: new MemorySignalManager(this.
|
|
163
|
+
signalManager: new MemorySignalManager(this._signalContext),
|
|
160
164
|
transportFactory: MemoryTransportFactory,
|
|
161
165
|
}));
|
|
162
166
|
}
|
|
@@ -176,10 +180,7 @@ export class TestPeer {
|
|
|
176
180
|
}
|
|
177
181
|
|
|
178
182
|
get echoHost() {
|
|
179
|
-
return (this._props.echoHost ??= new EchoHost({
|
|
180
|
-
kv: this.level,
|
|
181
|
-
storage: this.storage,
|
|
182
|
-
}));
|
|
183
|
+
return (this._props.echoHost ??= new EchoHost({ kv: this.level }));
|
|
183
184
|
}
|
|
184
185
|
|
|
185
186
|
get dataSpaceManager(): DataSpaceManager {
|
|
@@ -191,6 +192,7 @@ export class TestPeer {
|
|
|
191
192
|
this.feedStore,
|
|
192
193
|
this.echoHost,
|
|
193
194
|
this.invitationsManager,
|
|
195
|
+
this._opts.dataSpaceParams,
|
|
194
196
|
));
|
|
195
197
|
}
|
|
196
198
|
|
package/src/version.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const DXOS_VERSION = "0.5.9-main.
|
|
1
|
+
export const DXOS_VERSION = "0.5.9-main.eacfffa";
|