@dxos/client-services 0.8.4-main.937b3ca → 0.8.4-main.9be5663bfe
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-UDCUM4WV.mjs → chunk-CK3KJB3B.mjs} +1737 -1199
- package/dist/lib/browser/chunk-CK3KJB3B.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +33 -16
- package/dist/lib/browser/index.mjs.map +3 -3
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/testing/index.mjs +14 -7
- package/dist/lib/browser/testing/index.mjs.map +3 -3
- package/dist/lib/node-esm/{chunk-Q56HAUWS.mjs → chunk-WHBWCIEN.mjs} +1737 -1199
- package/dist/lib/node-esm/chunk-WHBWCIEN.mjs.map +7 -0
- package/dist/lib/node-esm/index.mjs +33 -16
- package/dist/lib/node-esm/index.mjs.map +3 -3
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/lib/node-esm/testing/index.mjs +14 -7
- package/dist/lib/node-esm/testing/index.mjs.map +3 -3
- package/dist/types/src/index.d.ts +1 -0
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/packlets/agents/edge-agent-manager.d.ts +3 -2
- package/dist/types/src/packlets/agents/edge-agent-manager.d.ts.map +1 -1
- package/dist/types/src/packlets/agents/edge-agent-service.d.ts.map +1 -1
- package/dist/types/src/packlets/devtools/devtools.d.ts.map +1 -1
- package/dist/types/src/packlets/identity/contacts-service.d.ts.map +1 -1
- package/dist/types/src/packlets/identity/identity-manager.d.ts +3 -3
- package/dist/types/src/packlets/identity/identity-manager.d.ts.map +1 -1
- package/dist/types/src/packlets/identity/identity-recovery-manager.d.ts +5 -4
- package/dist/types/src/packlets/identity/identity-recovery-manager.d.ts.map +1 -1
- package/dist/types/src/packlets/identity/identity-service.d.ts +1 -6
- package/dist/types/src/packlets/identity/identity-service.d.ts.map +1 -1
- package/dist/types/src/packlets/identity/identity.d.ts +6 -9
- package/dist/types/src/packlets/identity/identity.d.ts.map +1 -1
- package/dist/types/src/packlets/invitations/device-invitation-protocol.d.ts.map +1 -1
- package/dist/types/src/packlets/invitations/edge-invitation-handler.d.ts.map +1 -1
- package/dist/types/src/packlets/invitations/invitations-manager.d.ts +3 -3
- package/dist/types/src/packlets/invitations/invitations-manager.d.ts.map +1 -1
- package/dist/types/src/packlets/invitations/space-invitation-protocol.d.ts.map +1 -1
- package/dist/types/src/packlets/logging/logging-service.d.ts +4 -0
- package/dist/types/src/packlets/logging/logging-service.d.ts.map +1 -1
- package/dist/types/src/packlets/network/network-service.d.ts.map +1 -1
- package/dist/types/src/packlets/services/feed-syncer.d.ts +59 -0
- package/dist/types/src/packlets/services/feed-syncer.d.ts.map +1 -0
- package/dist/types/src/packlets/services/feed-syncer.test.d.ts +2 -0
- package/dist/types/src/packlets/services/feed-syncer.test.d.ts.map +1 -0
- package/dist/types/src/packlets/services/platform.d.ts.map +1 -1
- package/dist/types/src/packlets/services/service-context.d.ts +3 -4
- 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/space-export/space-archive-reader.d.ts +9 -1
- package/dist/types/src/packlets/space-export/space-archive-reader.d.ts.map +1 -1
- package/dist/types/src/packlets/space-export/space-archive-writer.d.ts +6 -0
- package/dist/types/src/packlets/space-export/space-archive-writer.d.ts.map +1 -1
- package/dist/types/src/packlets/space-export/space-archive.test.d.ts +2 -0
- package/dist/types/src/packlets/space-export/space-archive.test.d.ts.map +1 -0
- package/dist/types/src/packlets/spaces/data-space-manager.d.ts +17 -10
- package/dist/types/src/packlets/spaces/data-space-manager.d.ts.map +1 -1
- package/dist/types/src/packlets/spaces/data-space.d.ts +22 -6
- package/dist/types/src/packlets/spaces/data-space.d.ts.map +1 -1
- package/dist/types/src/packlets/spaces/edge-feed-replicator.d.ts.map +1 -1
- package/dist/types/src/packlets/spaces/genesis.d.ts +2 -1
- package/dist/types/src/packlets/spaces/genesis.d.ts.map +1 -1
- package/dist/types/src/packlets/spaces/notarization-plugin.d.ts.map +1 -1
- 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/invitation-utils.d.ts.map +1 -1
- package/dist/types/src/packlets/worker/worker-runtime.d.ts +11 -3
- package/dist/types/src/packlets/worker/worker-runtime.d.ts.map +1 -1
- package/dist/types/src/version.d.ts +1 -1
- package/dist/types/src/version.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +43 -43
- package/src/index.ts +1 -0
- package/src/packlets/agents/edge-agent-manager.ts +8 -5
- package/src/packlets/agents/edge-agent-service.ts +2 -1
- package/src/packlets/devices/devices-service.test.ts +0 -1
- package/src/packlets/devtools/devtools.ts +0 -1
- package/src/packlets/identity/contacts-service.ts +0 -1
- package/src/packlets/identity/identity-manager.test.ts +5 -5
- package/src/packlets/identity/identity-manager.ts +16 -13
- package/src/packlets/identity/identity-recovery-manager.ts +20 -16
- package/src/packlets/identity/identity-service.test.ts +6 -27
- package/src/packlets/identity/identity-service.ts +5 -76
- package/src/packlets/identity/identity.test.ts +2 -2
- package/src/packlets/identity/identity.ts +7 -30
- package/src/packlets/invitations/device-invitation-protocol.ts +0 -1
- package/src/packlets/invitations/edge-invitation-handler.ts +4 -3
- package/src/packlets/invitations/invitations-handler.test.ts +4 -5
- package/src/packlets/invitations/invitations-handler.ts +3 -3
- package/src/packlets/invitations/invitations-manager.ts +37 -14
- package/src/packlets/invitations/invitations-service.ts +4 -4
- package/src/packlets/invitations/space-invitation-protocol.test.ts +17 -16
- package/src/packlets/invitations/space-invitation-protocol.ts +3 -2
- package/src/packlets/logging/logging-service.ts +4 -0
- package/src/packlets/network/network-service.test.ts +0 -1
- package/src/packlets/network/network-service.ts +5 -4
- package/src/packlets/services/feed-syncer.test.ts +340 -0
- package/src/packlets/services/feed-syncer.ts +337 -0
- package/src/packlets/services/platform.ts +7 -1
- package/src/packlets/services/service-context.test.ts +3 -2
- package/src/packlets/services/service-context.ts +105 -31
- package/src/packlets/services/service-host.test.ts +8 -8
- package/src/packlets/services/service-host.ts +10 -9
- package/src/packlets/services/service-registry.test.ts +0 -1
- package/src/packlets/space-export/space-archive-reader.ts +64 -3
- package/src/packlets/space-export/space-archive-writer.ts +36 -1
- package/src/packlets/space-export/space-archive.test.ts +287 -0
- package/src/packlets/spaces/data-space-manager.test.ts +79 -13
- package/src/packlets/spaces/data-space-manager.ts +71 -104
- package/src/packlets/spaces/data-space.ts +46 -24
- package/src/packlets/spaces/edge-feed-replicator.test.ts +1 -1
- package/src/packlets/spaces/edge-feed-replicator.ts +8 -7
- package/src/packlets/spaces/epoch-migrations.ts +3 -3
- package/src/packlets/spaces/genesis.ts +6 -1
- package/src/packlets/spaces/notarization-plugin.ts +2 -1
- package/src/packlets/spaces/spaces-service.test.ts +9 -7
- package/src/packlets/spaces/spaces-service.ts +30 -9
- package/src/packlets/testing/invitation-utils.ts +3 -2
- package/src/packlets/worker/worker-runtime.ts +14 -7
- package/src/packlets/worker/worker-session.ts +4 -4
- package/src/version.ts +1 -1
- package/dist/lib/browser/chunk-UDCUM4WV.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-Q56HAUWS.mjs.map +0 -7
- package/dist/types/src/packlets/identity/default-space-state-machine.d.ts +0 -19
- package/dist/types/src/packlets/identity/default-space-state-machine.d.ts.map +0 -1
- package/src/packlets/identity/default-space-state-machine.ts +0 -44
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
import { describe, test } from 'vitest';
|
|
6
6
|
|
|
7
|
+
import { Context } from '@dxos/context';
|
|
7
8
|
import { MemorySignalManager, MemorySignalManagerContext } from '@dxos/messaging';
|
|
8
9
|
import { Invitation } from '@dxos/protocols/proto/dxos/client/services';
|
|
9
10
|
import { openAndClose } from '@dxos/test-utils';
|
|
@@ -19,7 +20,7 @@ describe('services/ServiceContext', () => {
|
|
|
19
20
|
const device2 = await createOpenServiceContext(networkContext);
|
|
20
21
|
await Promise.all(performInvitation({ host: device1, guest: device2, options: { kind: Invitation.Kind.DEVICE } }));
|
|
21
22
|
|
|
22
|
-
const space1 = await device1.dataSpaceManager!.createSpace();
|
|
23
|
+
const space1 = await device1.dataSpaceManager!.createSpace(new Context());
|
|
23
24
|
await device2.dataSpaceManager!.waitUntilSpaceReady(space1!.key);
|
|
24
25
|
const space2 = await device2.dataSpaceManager!.spaces.get(space1.key);
|
|
25
26
|
await space2!.inner.controlPipeline.state.waitUntilTimeframe(space1.inner.controlPipeline.state.timeframe);
|
|
@@ -35,7 +36,7 @@ describe('services/ServiceContext', () => {
|
|
|
35
36
|
|
|
36
37
|
const identity2 = await createOpenServiceContext(networkContext);
|
|
37
38
|
await identity2.createIdentity();
|
|
38
|
-
const space1 = await identity2.dataSpaceManager!.createSpace();
|
|
39
|
+
const space1 = await identity2.dataSpaceManager!.createSpace(new Context());
|
|
39
40
|
await Promise.all(
|
|
40
41
|
performInvitation({
|
|
41
42
|
host: identity2,
|
|
@@ -22,12 +22,13 @@ import { type RuntimeProvider } from '@dxos/effect';
|
|
|
22
22
|
import { FeedFactory, FeedStore } from '@dxos/feed-store';
|
|
23
23
|
import { invariant } from '@dxos/invariant';
|
|
24
24
|
import { Keyring } from '@dxos/keyring';
|
|
25
|
-
import { PublicKey } from '@dxos/keys';
|
|
25
|
+
import { PublicKey, type SpaceId } from '@dxos/keys';
|
|
26
26
|
import { type LevelDB } from '@dxos/kv-store';
|
|
27
27
|
import { log } from '@dxos/log';
|
|
28
28
|
import { type SignalManager } from '@dxos/messaging';
|
|
29
29
|
import { type SwarmNetworkManager } from '@dxos/network-manager';
|
|
30
30
|
import { InvalidStorageVersionError, STORAGE_VERSION, trace } from '@dxos/protocols';
|
|
31
|
+
import { FeedProtocol } from '@dxos/protocols';
|
|
31
32
|
import { Invitation } from '@dxos/protocols/proto/dxos/client/services';
|
|
32
33
|
import { type Runtime } from '@dxos/protocols/proto/dxos/config';
|
|
33
34
|
import type { FeedMessage } from '@dxos/protocols/proto/dxos/echo/feed';
|
|
@@ -41,6 +42,7 @@ import { safeInstanceof } from '@dxos/util';
|
|
|
41
42
|
import { EdgeAgentManager } from '../agents';
|
|
42
43
|
import {
|
|
43
44
|
type CreateIdentityOptions,
|
|
45
|
+
type Identity,
|
|
44
46
|
IdentityManager,
|
|
45
47
|
type IdentityManagerProps,
|
|
46
48
|
type JoinIdentityProps,
|
|
@@ -55,6 +57,7 @@ import {
|
|
|
55
57
|
SpaceInvitationProtocol,
|
|
56
58
|
} from '../invitations';
|
|
57
59
|
import { DataSpaceManager, type DataSpaceManagerRuntimeProps, type SigningContext } from '../spaces';
|
|
60
|
+
import { FeedSyncer } from './feed-syncer';
|
|
58
61
|
|
|
59
62
|
export type ServiceContextRuntimeProps = Pick<
|
|
60
63
|
IdentityManagerProps,
|
|
@@ -64,8 +67,6 @@ export type ServiceContextRuntimeProps = Pick<
|
|
|
64
67
|
invitationConnectionDefaultProps?: InvitationConnectionProps;
|
|
65
68
|
disableP2pReplication?: boolean;
|
|
66
69
|
enableVectorIndexing?: boolean;
|
|
67
|
-
enableSqlite?: boolean;
|
|
68
|
-
enableLocalQueues?: boolean;
|
|
69
70
|
};
|
|
70
71
|
/**
|
|
71
72
|
* Shared backend for all client services.
|
|
@@ -90,6 +91,7 @@ export class ServiceContext extends Resource {
|
|
|
90
91
|
public readonly echoHost: EchoHost;
|
|
91
92
|
private readonly _meshReplicator?: MeshEchoReplicator = undefined;
|
|
92
93
|
private readonly _echoEdgeReplicator?: EchoEdgeReplicator = undefined;
|
|
94
|
+
private readonly _feedSyncer?: FeedSyncer = undefined;
|
|
93
95
|
|
|
94
96
|
// Initialized after identity is initialized.
|
|
95
97
|
public dataSpaceManager?: DataSpaceManager;
|
|
@@ -166,12 +168,15 @@ export class ServiceContext extends Resource {
|
|
|
166
168
|
kv: this.level,
|
|
167
169
|
peerIdProvider: () => this.identityManager.identity?.deviceKey?.toHex(),
|
|
168
170
|
getSpaceKeyByRootDocumentId: (documentId) => this.spaceManager.findSpaceByRootDocumentId(documentId)?.key,
|
|
169
|
-
indexing: {
|
|
170
|
-
vector: this._runtimeProps?.enableVectorIndexing,
|
|
171
|
-
sqlIndex: this._runtimeProps?.enableSqlite,
|
|
172
|
-
},
|
|
173
171
|
runtime: this._runtime,
|
|
174
|
-
|
|
172
|
+
syncQueue: async (ctx, request) => {
|
|
173
|
+
return this._feedSyncer?.syncBlocking(ctx, {
|
|
174
|
+
spaceId: request.spaceId as SpaceId,
|
|
175
|
+
subspaceTag: request.subspaceTag,
|
|
176
|
+
shouldPush: request.shouldPush,
|
|
177
|
+
shouldPull: request.shouldPull,
|
|
178
|
+
});
|
|
179
|
+
},
|
|
175
180
|
});
|
|
176
181
|
|
|
177
182
|
this.invitations = new InvitationsHandler(
|
|
@@ -206,6 +211,17 @@ export class ServiceContext extends Resource {
|
|
|
206
211
|
edgeHttpClient: this._edgeHttpClient,
|
|
207
212
|
});
|
|
208
213
|
}
|
|
214
|
+
|
|
215
|
+
if (this.echoHost.feedStore && this._edgeConnection) {
|
|
216
|
+
this._feedSyncer = new FeedSyncer({
|
|
217
|
+
runtime: this._runtime,
|
|
218
|
+
feedStore: this.echoHost.feedStore,
|
|
219
|
+
edgeClient: this._edgeConnection,
|
|
220
|
+
peerId: this.identityManager.identity?.deviceKey?.toHex() ?? '',
|
|
221
|
+
getSpaceIds: () => this.echoHost!.spaceIds,
|
|
222
|
+
syncNamespaces: [FeedProtocol.WellKnownNamespaces.data, FeedProtocol.WellKnownNamespaces.trace],
|
|
223
|
+
});
|
|
224
|
+
}
|
|
209
225
|
}
|
|
210
226
|
|
|
211
227
|
@Trace.span()
|
|
@@ -215,32 +231,67 @@ export class ServiceContext extends Resource {
|
|
|
215
231
|
log('opening...');
|
|
216
232
|
log.trace('dxos.sdk.service-context.open', trace.begin({ id: this._instanceId }));
|
|
217
233
|
|
|
234
|
+
log('opening identityManager...');
|
|
218
235
|
await this.identityManager.open(ctx);
|
|
236
|
+
log('identityManager opened', { hasIdentity: !!this.identityManager.identity });
|
|
219
237
|
|
|
220
|
-
|
|
238
|
+
log('setting network identity...');
|
|
239
|
+
await this._setNetworkIdentity({ identity: this.identityManager.identity });
|
|
240
|
+
log('network identity set');
|
|
221
241
|
|
|
242
|
+
log('opening edge connection...');
|
|
222
243
|
await this._edgeConnection?.open();
|
|
244
|
+
log('edge connection opened');
|
|
245
|
+
|
|
246
|
+
log('opening signal manager...');
|
|
223
247
|
await this.signalManager.open();
|
|
248
|
+
log('signal manager opened');
|
|
249
|
+
|
|
250
|
+
log('opening network manager...');
|
|
224
251
|
await this.networkManager.open();
|
|
252
|
+
log('network manager opened');
|
|
225
253
|
|
|
254
|
+
log('opening echo host...');
|
|
226
255
|
await this.echoHost.open(ctx);
|
|
256
|
+
log('echo host opened');
|
|
227
257
|
|
|
228
258
|
if (this._meshReplicator) {
|
|
229
|
-
|
|
259
|
+
log('adding mesh replicator...');
|
|
260
|
+
await this.echoHost.addReplicator(ctx, this._meshReplicator);
|
|
261
|
+
log('mesh replicator added');
|
|
230
262
|
}
|
|
231
263
|
if (this._echoEdgeReplicator) {
|
|
232
|
-
|
|
264
|
+
log('adding edge replicator...');
|
|
265
|
+
await this.echoHost.addReplicator(ctx, this._echoEdgeReplicator);
|
|
266
|
+
log('edge replicator added');
|
|
233
267
|
}
|
|
234
268
|
|
|
269
|
+
log('loading metadata store...');
|
|
235
270
|
await this.metadataStore.load();
|
|
271
|
+
log('metadata store loaded');
|
|
272
|
+
|
|
273
|
+
log('opening space manager...');
|
|
236
274
|
await this.spaceManager.open();
|
|
275
|
+
log('space manager opened');
|
|
237
276
|
|
|
238
277
|
if (this.identityManager.identity) {
|
|
239
|
-
|
|
278
|
+
log('joining network...');
|
|
279
|
+
await this.identityManager.identity.joinNetwork(ctx);
|
|
280
|
+
log('network joined');
|
|
281
|
+
|
|
282
|
+
log('initializing spaces...(calling _initialize)');
|
|
240
283
|
await this._initialize(ctx);
|
|
284
|
+
log('spaces initialized');
|
|
285
|
+
} else {
|
|
286
|
+
log('no identity, skipping network join and space initialization');
|
|
241
287
|
}
|
|
242
288
|
|
|
243
|
-
|
|
289
|
+
log('opening feed syncer...');
|
|
290
|
+
await this._feedSyncer?.open();
|
|
291
|
+
log('feed syncer opened');
|
|
292
|
+
|
|
293
|
+
log('loading persistent invitations...');
|
|
294
|
+
const loadedInvitations = await this.invitationsManager.loadPersistentInvitations(ctx);
|
|
244
295
|
log('loaded persistent invitations', { count: loadedInvitations.invitations?.length });
|
|
245
296
|
|
|
246
297
|
log.trace('dxos.sdk.service-context.open', trace.end({ id: this._instanceId }));
|
|
@@ -249,16 +300,19 @@ export class ServiceContext extends Resource {
|
|
|
249
300
|
|
|
250
301
|
protected override async _close(ctx: Context): Promise<void> {
|
|
251
302
|
log('closing...');
|
|
303
|
+
|
|
304
|
+
await this._feedSyncer?.close();
|
|
305
|
+
|
|
252
306
|
if (this._deviceSpaceSync && this.identityManager.identity) {
|
|
253
307
|
await this.identityManager.identity.space.spaceState.removeCredentialProcessor(this._deviceSpaceSync);
|
|
254
308
|
}
|
|
255
|
-
await this.dataSpaceManager?.close();
|
|
309
|
+
await this.dataSpaceManager?.close(ctx);
|
|
256
310
|
await this.edgeAgentManager?.close();
|
|
257
|
-
await this.identityManager.close();
|
|
311
|
+
await this.identityManager.close(ctx);
|
|
258
312
|
await this.spaceManager.close();
|
|
259
313
|
await this.echoHost.close(ctx);
|
|
260
314
|
|
|
261
|
-
await this.networkManager.close();
|
|
315
|
+
await this.networkManager.close(ctx);
|
|
262
316
|
await this.signalManager.close();
|
|
263
317
|
await this._edgeConnection?.close();
|
|
264
318
|
await this.feedStore.close();
|
|
@@ -268,10 +322,11 @@ export class ServiceContext extends Resource {
|
|
|
268
322
|
}
|
|
269
323
|
|
|
270
324
|
async createIdentity(params: CreateIdentityOptions = {}) {
|
|
271
|
-
const
|
|
272
|
-
await this.
|
|
273
|
-
await
|
|
274
|
-
await
|
|
325
|
+
const ctx = Context.default();
|
|
326
|
+
const identity = await this.identityManager.createIdentity(params, ctx);
|
|
327
|
+
await this._setNetworkIdentity({ identity });
|
|
328
|
+
await identity.joinNetwork(ctx);
|
|
329
|
+
await this._initialize(ctx);
|
|
275
330
|
return identity;
|
|
276
331
|
}
|
|
277
332
|
|
|
@@ -295,11 +350,11 @@ export class ServiceContext extends Resource {
|
|
|
295
350
|
}
|
|
296
351
|
|
|
297
352
|
private async _acceptIdentity(params: JoinIdentityProps) {
|
|
298
|
-
const { identity, identityRecord } = await this.identityManager.prepareIdentity(params);
|
|
299
|
-
await this._setNetworkIdentity({ deviceCredential: params.authorizedDeviceCredential
|
|
300
|
-
await identity.joinNetwork();
|
|
353
|
+
const { identity, identityRecord } = await this.identityManager.prepareIdentity(params, this._ctx);
|
|
354
|
+
await this._setNetworkIdentity({ deviceCredential: params.authorizedDeviceCredential!, identity });
|
|
355
|
+
await identity.joinNetwork(this._ctx);
|
|
301
356
|
await this.identityManager.acceptIdentity(identity, identityRecord, params.deviceProfile);
|
|
302
|
-
await this._initialize(
|
|
357
|
+
await this._initialize(this._ctx);
|
|
303
358
|
return identity;
|
|
304
359
|
}
|
|
305
360
|
|
|
@@ -314,7 +369,7 @@ export class ServiceContext extends Resource {
|
|
|
314
369
|
// Called when identity is created.
|
|
315
370
|
@Trace.span()
|
|
316
371
|
private async _initialize(ctx: Context): Promise<void> {
|
|
317
|
-
log('
|
|
372
|
+
log('_initialize: start');
|
|
318
373
|
const identity = this.identityManager.identity ?? failUndefined();
|
|
319
374
|
const signingContext: SigningContext = {
|
|
320
375
|
credentialSigner: identity.getIdentityCredentialSigner(),
|
|
@@ -326,6 +381,7 @@ export class ServiceContext extends Resource {
|
|
|
326
381
|
},
|
|
327
382
|
};
|
|
328
383
|
|
|
384
|
+
log('_initialize: creating DataSpaceManager');
|
|
329
385
|
this.dataSpaceManager = new DataSpaceManager({
|
|
330
386
|
spaceManager: this.spaceManager,
|
|
331
387
|
metadataStore: this.metadataStore,
|
|
@@ -341,7 +397,9 @@ export class ServiceContext extends Resource {
|
|
|
341
397
|
runtimeProps: this._runtimeProps as DataSpaceManagerRuntimeProps,
|
|
342
398
|
edgeFeatures: this._edgeFeatures,
|
|
343
399
|
});
|
|
400
|
+
log('_initialize: opening DataSpaceManager...');
|
|
344
401
|
await this.dataSpaceManager.open();
|
|
402
|
+
log('_initialize: DataSpaceManager opened');
|
|
345
403
|
|
|
346
404
|
this.edgeAgentManager = new EdgeAgentManager(
|
|
347
405
|
this._edgeFeatures,
|
|
@@ -349,13 +407,16 @@ export class ServiceContext extends Resource {
|
|
|
349
407
|
this.dataSpaceManager,
|
|
350
408
|
identity,
|
|
351
409
|
);
|
|
410
|
+
log('_initialize: opening EdgeAgentManager...');
|
|
352
411
|
await this.edgeAgentManager.open();
|
|
412
|
+
log('_initialize: EdgeAgentManager opened');
|
|
353
413
|
|
|
354
414
|
this._handlerFactories.set(Invitation.Kind.SPACE, (invitation) => {
|
|
355
415
|
invariant(this.dataSpaceManager, 'dataSpaceManager not initialized yet');
|
|
356
416
|
return new SpaceInvitationProtocol(this.dataSpaceManager, signingContext, this.keyring, invitation.spaceKey);
|
|
357
417
|
});
|
|
358
418
|
this.initialized.wake();
|
|
419
|
+
log('_initialize: initialized.wake() called');
|
|
359
420
|
|
|
360
421
|
this._deviceSpaceSync = {
|
|
361
422
|
processCredential: async (credential: Credential) => {
|
|
@@ -378,7 +439,7 @@ export class ServiceContext extends Resource {
|
|
|
378
439
|
|
|
379
440
|
try {
|
|
380
441
|
log('accepting space recorded in halo', { details: assertion });
|
|
381
|
-
await this.dataSpaceManager.acceptSpace({
|
|
442
|
+
await this.dataSpaceManager.acceptSpace(this._ctx, {
|
|
382
443
|
spaceKey: assertion.spaceKey,
|
|
383
444
|
genesisFeedKey: assertion.genesisFeedKey,
|
|
384
445
|
});
|
|
@@ -391,33 +452,42 @@ export class ServiceContext extends Resource {
|
|
|
391
452
|
await identity.space.spaceState.addCredentialProcessor(this._deviceSpaceSync);
|
|
392
453
|
}
|
|
393
454
|
|
|
394
|
-
private async _setNetworkIdentity(params?: { deviceCredential
|
|
455
|
+
private async _setNetworkIdentity(params?: { deviceCredential?: Credential; identity?: Identity }): Promise<void> {
|
|
456
|
+
log('_setNetworkIdentity: acquiring mutex...');
|
|
395
457
|
using _ = await this._edgeIdentityUpdateMutex.acquire();
|
|
458
|
+
log('_setNetworkIdentity: mutex acquired');
|
|
396
459
|
|
|
397
460
|
let edgeIdentity: EdgeIdentity;
|
|
398
|
-
const identity =
|
|
461
|
+
const identity = params?.identity;
|
|
399
462
|
if (identity) {
|
|
400
|
-
log('
|
|
463
|
+
log('_setNetworkIdentity: has identity', {
|
|
401
464
|
identity: identity.identityKey.toHex(),
|
|
402
|
-
|
|
465
|
+
hasDeviceCredential: !!params?.deviceCredential,
|
|
403
466
|
});
|
|
404
467
|
|
|
405
468
|
if (params?.deviceCredential) {
|
|
469
|
+
log('_setNetworkIdentity: creating chain edge identity with device credential...');
|
|
406
470
|
edgeIdentity = await createChainEdgeIdentity(
|
|
407
471
|
identity.signer,
|
|
408
472
|
identity.identityKey,
|
|
409
473
|
identity.deviceKey,
|
|
410
|
-
|
|
474
|
+
{ credential: params.deviceCredential },
|
|
411
475
|
[], // TODO(dmaretskyi): Service access credentials.
|
|
412
476
|
);
|
|
477
|
+
log('_setNetworkIdentity: chain edge identity created');
|
|
413
478
|
} else {
|
|
479
|
+
log('_setNetworkIdentity: waiting for identity.ready()...');
|
|
414
480
|
// TODO: throw here or from identity if device chain can't be loaded, to avoid indefinite hangup
|
|
415
481
|
await warnAfterTimeout(10_000, 'Waiting for identity to be ready for edge connection', async () => {
|
|
416
482
|
await identity.ready();
|
|
417
483
|
});
|
|
484
|
+
log('_setNetworkIdentity: identity.ready() resolved', {
|
|
485
|
+
hasDeviceCredentialChain: !!identity.deviceCredentialChain,
|
|
486
|
+
});
|
|
418
487
|
|
|
419
488
|
invariant(identity.deviceCredentialChain);
|
|
420
489
|
|
|
490
|
+
log('_setNetworkIdentity: creating chain edge identity...');
|
|
421
491
|
edgeIdentity = await createChainEdgeIdentity(
|
|
422
492
|
identity.signer,
|
|
423
493
|
identity.identityKey,
|
|
@@ -425,9 +495,12 @@ export class ServiceContext extends Resource {
|
|
|
425
495
|
identity.deviceCredentialChain,
|
|
426
496
|
[], // TODO(dmaretskyi): Service access credentials.
|
|
427
497
|
);
|
|
498
|
+
log('_setNetworkIdentity: chain edge identity created');
|
|
428
499
|
}
|
|
429
500
|
} else {
|
|
501
|
+
log('_setNetworkIdentity: no identity, creating ephemeral edge identity...');
|
|
430
502
|
edgeIdentity = await createEphemeralEdgeIdentity();
|
|
503
|
+
log('_setNetworkIdentity: ephemeral edge identity created');
|
|
431
504
|
}
|
|
432
505
|
|
|
433
506
|
this._edgeConnection?.setIdentity(edgeIdentity);
|
|
@@ -436,5 +509,6 @@ export class ServiceContext extends Resource {
|
|
|
436
509
|
identityKey: edgeIdentity.identityKey,
|
|
437
510
|
peerKey: edgeIdentity.peerKey,
|
|
438
511
|
});
|
|
512
|
+
log('_setNetworkIdentity: done');
|
|
439
513
|
}
|
|
440
514
|
}
|
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
//
|
|
4
4
|
|
|
5
5
|
import { rmSync } from 'node:fs';
|
|
6
|
-
|
|
7
6
|
import { afterEach, describe, expect, onTestFinished, test } from 'vitest';
|
|
8
7
|
|
|
9
8
|
import { Trigger, asyncTimeout, latch } from '@dxos/async';
|
|
@@ -13,6 +12,7 @@ import { verifyPresentation } from '@dxos/credentials';
|
|
|
13
12
|
import { type PublicKey } from '@dxos/keys';
|
|
14
13
|
import { MemorySignalManagerContext } from '@dxos/messaging';
|
|
15
14
|
import { type Identity } from '@dxos/protocols/proto/dxos/client/services';
|
|
15
|
+
import { MembershipPolicy } from '@dxos/protocols/proto/dxos/halo/credentials';
|
|
16
16
|
import { type Credential } from '@dxos/protocols/proto/dxos/halo/credentials';
|
|
17
17
|
import { isNode } from '@dxos/util';
|
|
18
18
|
|
|
@@ -29,16 +29,16 @@ describe('ClientServicesHost', () => {
|
|
|
29
29
|
test('open and close', async () => {
|
|
30
30
|
const host = createServiceHost(new Config(), new MemorySignalManagerContext());
|
|
31
31
|
await host.open(new Context());
|
|
32
|
-
await host.close();
|
|
32
|
+
await host.close(Context.default());
|
|
33
33
|
});
|
|
34
34
|
|
|
35
35
|
test('queryCredentials', async () => {
|
|
36
36
|
const host = createServiceHost(new Config(), new MemorySignalManagerContext());
|
|
37
37
|
await host.open(new Context());
|
|
38
|
-
onTestFinished(() => host.close());
|
|
38
|
+
onTestFinished(() => host.close(Context.default()));
|
|
39
39
|
|
|
40
40
|
await host.services.IdentityService!.createIdentity({});
|
|
41
|
-
const { spaceKey } = await host.services.SpacesService!.createSpace();
|
|
41
|
+
const { spaceKey } = await host.services.SpacesService!.createSpace({ membershipPolicy: MembershipPolicy.INVITE });
|
|
42
42
|
|
|
43
43
|
const stream = host.services.SpacesService!.queryCredentials({ spaceKey });
|
|
44
44
|
const [done, tick] = latch({ count: 3 });
|
|
@@ -54,7 +54,7 @@ describe('ClientServicesHost', () => {
|
|
|
54
54
|
test('write and query credentials', async () => {
|
|
55
55
|
const host = createServiceHost(new Config(), new MemorySignalManagerContext());
|
|
56
56
|
await host.open(new Context());
|
|
57
|
-
onTestFinished(() => host.close());
|
|
57
|
+
onTestFinished(() => host.close(Context.default()));
|
|
58
58
|
|
|
59
59
|
await host.services.IdentityService!.createIdentity({});
|
|
60
60
|
|
|
@@ -91,7 +91,7 @@ describe('ClientServicesHost', () => {
|
|
|
91
91
|
test('sign presentation', async () => {
|
|
92
92
|
const host = createServiceHost(new Config(), new MemorySignalManagerContext());
|
|
93
93
|
await host.open(new Context());
|
|
94
|
-
onTestFinished(() => host.close());
|
|
94
|
+
onTestFinished(() => host.close(Context.default()));
|
|
95
95
|
|
|
96
96
|
await host.services.IdentityService!.createIdentity({});
|
|
97
97
|
|
|
@@ -128,7 +128,7 @@ describe('ClientServicesHost', () => {
|
|
|
128
128
|
expect(host.context.storage.size).to.exist;
|
|
129
129
|
|
|
130
130
|
await asyncTimeout(host.reset(), 1000);
|
|
131
|
-
await host.close();
|
|
131
|
+
await host.close(Context.default());
|
|
132
132
|
}
|
|
133
133
|
|
|
134
134
|
{
|
|
@@ -146,7 +146,7 @@ describe('ClientServicesHost', () => {
|
|
|
146
146
|
});
|
|
147
147
|
await expect(asyncTimeout(trigger.wait(), 200)).rejects.toBeInstanceOf(Error);
|
|
148
148
|
await stream?.close();
|
|
149
|
-
await host.close();
|
|
149
|
+
await host.close(Context.default());
|
|
150
150
|
}
|
|
151
151
|
});
|
|
152
152
|
});
|
|
@@ -47,7 +47,6 @@ import { NetworkServiceImpl } from '../network';
|
|
|
47
47
|
import { SpacesServiceImpl } from '../spaces';
|
|
48
48
|
import { createLevel, createStorageObjects } from '../storage';
|
|
49
49
|
import { SystemServiceImpl } from '../system';
|
|
50
|
-
|
|
51
50
|
import { ServiceContext, type ServiceContextRuntimeProps } from './service-context';
|
|
52
51
|
import { ServiceRegistry } from './service-registry';
|
|
53
52
|
|
|
@@ -147,7 +146,7 @@ export class ClientServicesHost {
|
|
|
147
146
|
void this.open(new Context());
|
|
148
147
|
}
|
|
149
148
|
},
|
|
150
|
-
onRelease: () => this.close(),
|
|
149
|
+
onRelease: () => this.close(Context.default()),
|
|
151
150
|
});
|
|
152
151
|
}
|
|
153
152
|
|
|
@@ -244,9 +243,6 @@ export class ClientServicesHost {
|
|
|
244
243
|
if (this._runtimeProps.enableVectorIndexing === undefined) {
|
|
245
244
|
this._runtimeProps.enableVectorIndexing = config?.get('runtime.client.enableVectorIndexing', false);
|
|
246
245
|
}
|
|
247
|
-
if (this._runtimeProps.enableLocalQueues === undefined) {
|
|
248
|
-
this._runtimeProps.enableLocalQueues = config?.get('runtime.client.enableLocalQueues', false);
|
|
249
|
-
}
|
|
250
246
|
|
|
251
247
|
invariant(!this._config, 'config already set');
|
|
252
248
|
this._config = config;
|
|
@@ -262,8 +258,9 @@ export class ClientServicesHost {
|
|
|
262
258
|
|
|
263
259
|
const endpoint = config?.get('runtime.services.edge.url');
|
|
264
260
|
if (endpoint) {
|
|
265
|
-
|
|
266
|
-
this.
|
|
261
|
+
const clientTag = config?.get('runtime.app.env.DX_EDGE_CLIENT_TAG');
|
|
262
|
+
this._edgeConnection = new EdgeClient(createStubEdgeIdentity(), { socketEndpoint: endpoint, clientTag });
|
|
263
|
+
this._edgeHttpClient = new EdgeHttpClient(endpoint, { clientTag });
|
|
267
264
|
}
|
|
268
265
|
|
|
269
266
|
const {
|
|
@@ -348,7 +345,6 @@ export class ClientServicesHost {
|
|
|
348
345
|
this._serviceContext.identityManager,
|
|
349
346
|
this._serviceContext.recoveryManager,
|
|
350
347
|
this._serviceContext.keyring,
|
|
351
|
-
() => this._serviceContext.dataSpaceManager!,
|
|
352
348
|
(params) => this._createIdentity(params),
|
|
353
349
|
(profile) => this._serviceContext.broadcastProfileUpdate(profile),
|
|
354
350
|
);
|
|
@@ -395,8 +391,13 @@ export class ClientServicesHost {
|
|
|
395
391
|
EdgeAgentService: new EdgeAgentServiceImpl(agentManagerProvider, this._edgeConnection),
|
|
396
392
|
});
|
|
397
393
|
|
|
394
|
+
log('service-host: opening service context...');
|
|
398
395
|
await this._serviceContext.open(ctx);
|
|
396
|
+
log('service-host: service context opened');
|
|
397
|
+
|
|
398
|
+
log('service-host: opening identity service...');
|
|
399
399
|
await identityService.open();
|
|
400
|
+
log('service-host: identity service opened');
|
|
400
401
|
|
|
401
402
|
const devtoolsProxy = this._config?.get('runtime.client.devtoolsProxy');
|
|
402
403
|
if (devtoolsProxy) {
|
|
@@ -420,7 +421,7 @@ export class ClientServicesHost {
|
|
|
420
421
|
|
|
421
422
|
@synchronized
|
|
422
423
|
@Trace.span()
|
|
423
|
-
async close(): Promise<void> {
|
|
424
|
+
async close(ctx: Context): Promise<void> {
|
|
424
425
|
if (!this._open) {
|
|
425
426
|
return;
|
|
426
427
|
}
|
|
@@ -15,7 +15,6 @@ import { createLinkedPorts, createProtoRpcPeer, createServiceBundle } from '@dxo
|
|
|
15
15
|
|
|
16
16
|
import { SystemServiceImpl } from '../system';
|
|
17
17
|
import { createServiceContext } from '../testing';
|
|
18
|
-
|
|
19
18
|
import { ServiceRegistry } from './service-registry';
|
|
20
19
|
|
|
21
20
|
// TODO(burdon): Create TestService (that doesn't require peers).
|
|
@@ -6,12 +6,26 @@ import type { DocumentId } from '@automerge/automerge-repo';
|
|
|
6
6
|
|
|
7
7
|
import { assertArgument, failedInvariant, invariant } from '@dxos/invariant';
|
|
8
8
|
import { log } from '@dxos/log';
|
|
9
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
type FeedArchiveBlock,
|
|
11
|
+
type FeedArchiveMetadata,
|
|
12
|
+
SpaceArchiveFileStructure,
|
|
13
|
+
type SpaceArchiveMetadata,
|
|
14
|
+
} from '@dxos/protocols';
|
|
10
15
|
import type { SpaceArchive } from '@dxos/protocols/proto/dxos/client/services';
|
|
11
16
|
|
|
17
|
+
/**
|
|
18
|
+
* Extracted feed data from the archive.
|
|
19
|
+
*/
|
|
20
|
+
export type ExtractedFeed = {
|
|
21
|
+
metadata: FeedArchiveMetadata;
|
|
22
|
+
blocks: FeedArchiveBlock[];
|
|
23
|
+
};
|
|
24
|
+
|
|
12
25
|
export type ExtractedSpaceArchive = {
|
|
13
26
|
metadata: SpaceArchiveMetadata;
|
|
14
27
|
documents: Record<DocumentId, Uint8Array>;
|
|
28
|
+
feeds: Record<string, ExtractedFeed>;
|
|
15
29
|
};
|
|
16
30
|
|
|
17
31
|
export const extractSpaceArchive = async (archive: SpaceArchive): Promise<ExtractedSpaceArchive> => {
|
|
@@ -29,6 +43,53 @@ export const extractSpaceArchive = async (archive: SpaceArchive): Promise<Extrac
|
|
|
29
43
|
documents[documentId] = entry.content ?? failedInvariant();
|
|
30
44
|
}
|
|
31
45
|
|
|
32
|
-
|
|
33
|
-
|
|
46
|
+
const feeds: Record<string, ExtractedFeed> = {};
|
|
47
|
+
const feedsPrefix = `${SpaceArchiveFileStructure.feeds}/`;
|
|
48
|
+
const feedEntries = entries.filter((entry) => entry.fileName.startsWith(feedsPrefix));
|
|
49
|
+
|
|
50
|
+
const feedMetadataByFeedId = new Map<string, FeedArchiveMetadata>();
|
|
51
|
+
const feedBlocksByFeedId = new Map<string, Map<number, FeedArchiveBlock[]>>();
|
|
52
|
+
|
|
53
|
+
for (const entry of feedEntries) {
|
|
54
|
+
const relativePath = entry.fileName.slice(feedsPrefix.length);
|
|
55
|
+
const pathParts = relativePath.split('/');
|
|
56
|
+
if (pathParts.length !== 2) {
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const [feedId, fileName] = pathParts;
|
|
61
|
+
invariant(feedId, 'Feed ID is required');
|
|
62
|
+
invariant(fileName, 'File name is required');
|
|
63
|
+
|
|
64
|
+
if (fileName === SpaceArchiveFileStructure.feedMetadata) {
|
|
65
|
+
const feedMetadata = JSON.parse(entry.getContentAsText()) as FeedArchiveMetadata;
|
|
66
|
+
feedMetadataByFeedId.set(feedId, feedMetadata);
|
|
67
|
+
} else if (fileName.startsWith(SpaceArchiveFileStructure.feedBlocksPrefix) && fileName.endsWith('.json')) {
|
|
68
|
+
const chunkIndexStr = fileName.slice(SpaceArchiveFileStructure.feedBlocksPrefix.length).replace(/\.json$/, '');
|
|
69
|
+
const chunkIndex = parseInt(chunkIndexStr, 10);
|
|
70
|
+
invariant(!isNaN(chunkIndex), `Invalid chunk index: ${chunkIndexStr}`);
|
|
71
|
+
|
|
72
|
+
const blocks = JSON.parse(entry.getContentAsText()) as FeedArchiveBlock[];
|
|
73
|
+
if (!feedBlocksByFeedId.has(feedId)) {
|
|
74
|
+
feedBlocksByFeedId.set(feedId, new Map());
|
|
75
|
+
}
|
|
76
|
+
feedBlocksByFeedId.get(feedId)!.set(chunkIndex, blocks);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
for (const [feedId, feedMetadata] of feedMetadataByFeedId) {
|
|
81
|
+
const blockChunks = feedBlocksByFeedId.get(feedId) ?? new Map<number, FeedArchiveBlock[]>();
|
|
82
|
+
const sortedChunkIndices = Array.from(blockChunks.keys()).sort((a, b) => a - b);
|
|
83
|
+
const allBlocks: FeedArchiveBlock[] = [];
|
|
84
|
+
for (const chunkIndex of sortedChunkIndices) {
|
|
85
|
+
allBlocks.push(...blockChunks.get(chunkIndex)!);
|
|
86
|
+
}
|
|
87
|
+
feeds[feedId] = {
|
|
88
|
+
metadata: feedMetadata,
|
|
89
|
+
blocks: allBlocks,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
log('extracted space archive', { metadata, documents, feedCount: Object.keys(feeds).length });
|
|
94
|
+
return { metadata, documents, feeds };
|
|
34
95
|
};
|
|
@@ -7,7 +7,14 @@ import type * as tar from '@obsidize/tar-browserify';
|
|
|
7
7
|
import { type Context, Resource } from '@dxos/context';
|
|
8
8
|
import { assertArgument, assertState } from '@dxos/invariant';
|
|
9
9
|
import type { IdentityDid, SpaceId } from '@dxos/keys';
|
|
10
|
-
import {
|
|
10
|
+
import {
|
|
11
|
+
FEED_ARCHIVE_BLOCKS_PER_CHUNK,
|
|
12
|
+
type FeedArchiveBlock,
|
|
13
|
+
type FeedArchiveMetadata,
|
|
14
|
+
SpaceArchiveFileStructure,
|
|
15
|
+
type SpaceArchiveMetadata,
|
|
16
|
+
SpaceArchiveVersion,
|
|
17
|
+
} from '@dxos/protocols';
|
|
11
18
|
import type { SpaceArchive } from '@dxos/protocols/proto/dxos/client/services';
|
|
12
19
|
import { createFilename } from '@dxos/util';
|
|
13
20
|
|
|
@@ -57,6 +64,34 @@ export class SpaceArchiveWriter extends Resource {
|
|
|
57
64
|
this._archive.addBinaryFile(`${SpaceArchiveFileStructure.documents}/${documentId}.bin`, data);
|
|
58
65
|
}
|
|
59
66
|
|
|
67
|
+
/**
|
|
68
|
+
* Writes a feed with its metadata and blocks to the archive.
|
|
69
|
+
* Blocks are written in chunks of {@link FEED_ARCHIVE_BLOCKS_PER_CHUNK}.
|
|
70
|
+
*/
|
|
71
|
+
async writeFeed(feedId: string, namespace: string, blocks: FeedArchiveBlock[]): Promise<void> {
|
|
72
|
+
assertArgument(feedId, 'feedId', 'Feed ID is required');
|
|
73
|
+
assertArgument(namespace, 'namespace', 'Namespace is required');
|
|
74
|
+
assertState(this._archive, 'Not open');
|
|
75
|
+
|
|
76
|
+
const feedPath = `${SpaceArchiveFileStructure.feeds}/${feedId}`;
|
|
77
|
+
|
|
78
|
+
const metadata: FeedArchiveMetadata = {
|
|
79
|
+
id: feedId,
|
|
80
|
+
namespace,
|
|
81
|
+
};
|
|
82
|
+
this._archive.addTextFile(`${feedPath}/${SpaceArchiveFileStructure.feedMetadata}`, JSON.stringify(metadata));
|
|
83
|
+
|
|
84
|
+
for (let chunkIndex = 0; chunkIndex * FEED_ARCHIVE_BLOCKS_PER_CHUNK < blocks.length; chunkIndex++) {
|
|
85
|
+
const start = chunkIndex * FEED_ARCHIVE_BLOCKS_PER_CHUNK;
|
|
86
|
+
const end = Math.min(start + FEED_ARCHIVE_BLOCKS_PER_CHUNK, blocks.length);
|
|
87
|
+
const chunk = blocks.slice(start, end);
|
|
88
|
+
this._archive.addTextFile(
|
|
89
|
+
`${feedPath}/${SpaceArchiveFileStructure.feedBlocksPrefix}${chunkIndex}.json`,
|
|
90
|
+
JSON.stringify(chunk),
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
60
95
|
async finish(): Promise<SpaceArchive> {
|
|
61
96
|
assertState(this._archive, 'Not open');
|
|
62
97
|
assertState(this._meta, 'Not started');
|