@dxos/client-services 0.8.4-main.bc674ce → 0.8.4-main.bcb3aa67d6
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-J33W6T4Q.mjs → chunk-5A3KX2RY.mjs} +1745 -1208
- package/dist/lib/browser/chunk-5A3KX2RY.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-34HKLADW.mjs → chunk-FNPO5UMU.mjs} +1745 -1208
- package/dist/lib/node-esm/chunk-FNPO5UMU.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/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/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 -2
- 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 -26
- 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 -29
- package/src/packlets/invitations/edge-invitation-handler.ts +4 -3
- package/src/packlets/invitations/invitations-handler.test.ts +4 -4
- 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 -1
- package/src/packlets/logging/logging-service.ts +4 -0
- 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 +106 -31
- package/src/packlets/services/service-host.test.ts +8 -7
- package/src/packlets/services/service-host.ts +9 -7
- 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 -103
- package/src/packlets/spaces/data-space.ts +46 -23
- 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 -6
- package/src/packlets/spaces/spaces-service.ts +30 -8
- package/src/packlets/testing/invitation-utils.ts +3 -2
- package/src/packlets/worker/worker-runtime.ts +14 -6
- package/src/packlets/worker/worker-session.ts +4 -4
- package/src/version.ts +1 -1
- package/dist/lib/browser/chunk-J33W6T4Q.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-34HKLADW.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,
|
|
@@ -56,6 +58,8 @@ import {
|
|
|
56
58
|
} from '../invitations';
|
|
57
59
|
import { DataSpaceManager, type DataSpaceManagerRuntimeProps, type SigningContext } from '../spaces';
|
|
58
60
|
|
|
61
|
+
import { FeedSyncer } from './feed-syncer';
|
|
62
|
+
|
|
59
63
|
export type ServiceContextRuntimeProps = Pick<
|
|
60
64
|
IdentityManagerProps,
|
|
61
65
|
'devicePresenceOfflineTimeout' | 'devicePresenceAnnounceInterval'
|
|
@@ -64,8 +68,6 @@ export type ServiceContextRuntimeProps = Pick<
|
|
|
64
68
|
invitationConnectionDefaultProps?: InvitationConnectionProps;
|
|
65
69
|
disableP2pReplication?: boolean;
|
|
66
70
|
enableVectorIndexing?: boolean;
|
|
67
|
-
enableSqlite?: boolean;
|
|
68
|
-
enableLocalQueues?: boolean;
|
|
69
71
|
};
|
|
70
72
|
/**
|
|
71
73
|
* Shared backend for all client services.
|
|
@@ -90,6 +92,7 @@ export class ServiceContext extends Resource {
|
|
|
90
92
|
public readonly echoHost: EchoHost;
|
|
91
93
|
private readonly _meshReplicator?: MeshEchoReplicator = undefined;
|
|
92
94
|
private readonly _echoEdgeReplicator?: EchoEdgeReplicator = undefined;
|
|
95
|
+
private readonly _feedSyncer?: FeedSyncer = undefined;
|
|
93
96
|
|
|
94
97
|
// Initialized after identity is initialized.
|
|
95
98
|
public dataSpaceManager?: DataSpaceManager;
|
|
@@ -166,12 +169,15 @@ export class ServiceContext extends Resource {
|
|
|
166
169
|
kv: this.level,
|
|
167
170
|
peerIdProvider: () => this.identityManager.identity?.deviceKey?.toHex(),
|
|
168
171
|
getSpaceKeyByRootDocumentId: (documentId) => this.spaceManager.findSpaceByRootDocumentId(documentId)?.key,
|
|
169
|
-
indexing: {
|
|
170
|
-
vector: this._runtimeProps?.enableVectorIndexing,
|
|
171
|
-
sqlIndex: this._runtimeProps?.enableSqlite,
|
|
172
|
-
},
|
|
173
172
|
runtime: this._runtime,
|
|
174
|
-
|
|
173
|
+
syncQueue: async (ctx, request) => {
|
|
174
|
+
return this._feedSyncer?.syncBlocking(ctx, {
|
|
175
|
+
spaceId: request.spaceId as SpaceId,
|
|
176
|
+
subspaceTag: request.subspaceTag,
|
|
177
|
+
shouldPush: request.shouldPush,
|
|
178
|
+
shouldPull: request.shouldPull,
|
|
179
|
+
});
|
|
180
|
+
},
|
|
175
181
|
});
|
|
176
182
|
|
|
177
183
|
this.invitations = new InvitationsHandler(
|
|
@@ -206,6 +212,17 @@ export class ServiceContext extends Resource {
|
|
|
206
212
|
edgeHttpClient: this._edgeHttpClient,
|
|
207
213
|
});
|
|
208
214
|
}
|
|
215
|
+
|
|
216
|
+
if (this.echoHost.feedStore && this._edgeConnection) {
|
|
217
|
+
this._feedSyncer = new FeedSyncer({
|
|
218
|
+
runtime: this._runtime,
|
|
219
|
+
feedStore: this.echoHost.feedStore,
|
|
220
|
+
edgeClient: this._edgeConnection,
|
|
221
|
+
peerId: this.identityManager.identity?.deviceKey?.toHex() ?? '',
|
|
222
|
+
getSpaceIds: () => this.echoHost!.spaceIds,
|
|
223
|
+
syncNamespaces: [FeedProtocol.WellKnownNamespaces.data, FeedProtocol.WellKnownNamespaces.trace],
|
|
224
|
+
});
|
|
225
|
+
}
|
|
209
226
|
}
|
|
210
227
|
|
|
211
228
|
@Trace.span()
|
|
@@ -215,32 +232,67 @@ export class ServiceContext extends Resource {
|
|
|
215
232
|
log('opening...');
|
|
216
233
|
log.trace('dxos.sdk.service-context.open', trace.begin({ id: this._instanceId }));
|
|
217
234
|
|
|
235
|
+
log('opening identityManager...');
|
|
218
236
|
await this.identityManager.open(ctx);
|
|
237
|
+
log('identityManager opened', { hasIdentity: !!this.identityManager.identity });
|
|
219
238
|
|
|
220
|
-
|
|
239
|
+
log('setting network identity...');
|
|
240
|
+
await this._setNetworkIdentity({ identity: this.identityManager.identity });
|
|
241
|
+
log('network identity set');
|
|
221
242
|
|
|
243
|
+
log('opening edge connection...');
|
|
222
244
|
await this._edgeConnection?.open();
|
|
245
|
+
log('edge connection opened');
|
|
246
|
+
|
|
247
|
+
log('opening signal manager...');
|
|
223
248
|
await this.signalManager.open();
|
|
249
|
+
log('signal manager opened');
|
|
250
|
+
|
|
251
|
+
log('opening network manager...');
|
|
224
252
|
await this.networkManager.open();
|
|
253
|
+
log('network manager opened');
|
|
225
254
|
|
|
255
|
+
log('opening echo host...');
|
|
226
256
|
await this.echoHost.open(ctx);
|
|
257
|
+
log('echo host opened');
|
|
227
258
|
|
|
228
259
|
if (this._meshReplicator) {
|
|
229
|
-
|
|
260
|
+
log('adding mesh replicator...');
|
|
261
|
+
await this.echoHost.addReplicator(ctx, this._meshReplicator);
|
|
262
|
+
log('mesh replicator added');
|
|
230
263
|
}
|
|
231
264
|
if (this._echoEdgeReplicator) {
|
|
232
|
-
|
|
265
|
+
log('adding edge replicator...');
|
|
266
|
+
await this.echoHost.addReplicator(ctx, this._echoEdgeReplicator);
|
|
267
|
+
log('edge replicator added');
|
|
233
268
|
}
|
|
234
269
|
|
|
270
|
+
log('loading metadata store...');
|
|
235
271
|
await this.metadataStore.load();
|
|
272
|
+
log('metadata store loaded');
|
|
273
|
+
|
|
274
|
+
log('opening space manager...');
|
|
236
275
|
await this.spaceManager.open();
|
|
276
|
+
log('space manager opened');
|
|
237
277
|
|
|
238
278
|
if (this.identityManager.identity) {
|
|
239
|
-
|
|
279
|
+
log('joining network...');
|
|
280
|
+
await this.identityManager.identity.joinNetwork(ctx);
|
|
281
|
+
log('network joined');
|
|
282
|
+
|
|
283
|
+
log('initializing spaces...(calling _initialize)');
|
|
240
284
|
await this._initialize(ctx);
|
|
285
|
+
log('spaces initialized');
|
|
286
|
+
} else {
|
|
287
|
+
log('no identity, skipping network join and space initialization');
|
|
241
288
|
}
|
|
242
289
|
|
|
243
|
-
|
|
290
|
+
log('opening feed syncer...');
|
|
291
|
+
await this._feedSyncer?.open();
|
|
292
|
+
log('feed syncer opened');
|
|
293
|
+
|
|
294
|
+
log('loading persistent invitations...');
|
|
295
|
+
const loadedInvitations = await this.invitationsManager.loadPersistentInvitations(ctx);
|
|
244
296
|
log('loaded persistent invitations', { count: loadedInvitations.invitations?.length });
|
|
245
297
|
|
|
246
298
|
log.trace('dxos.sdk.service-context.open', trace.end({ id: this._instanceId }));
|
|
@@ -249,16 +301,19 @@ export class ServiceContext extends Resource {
|
|
|
249
301
|
|
|
250
302
|
protected override async _close(ctx: Context): Promise<void> {
|
|
251
303
|
log('closing...');
|
|
304
|
+
|
|
305
|
+
await this._feedSyncer?.close();
|
|
306
|
+
|
|
252
307
|
if (this._deviceSpaceSync && this.identityManager.identity) {
|
|
253
308
|
await this.identityManager.identity.space.spaceState.removeCredentialProcessor(this._deviceSpaceSync);
|
|
254
309
|
}
|
|
255
|
-
await this.dataSpaceManager?.close();
|
|
310
|
+
await this.dataSpaceManager?.close(ctx);
|
|
256
311
|
await this.edgeAgentManager?.close();
|
|
257
|
-
await this.identityManager.close();
|
|
312
|
+
await this.identityManager.close(ctx);
|
|
258
313
|
await this.spaceManager.close();
|
|
259
314
|
await this.echoHost.close(ctx);
|
|
260
315
|
|
|
261
|
-
await this.networkManager.close();
|
|
316
|
+
await this.networkManager.close(ctx);
|
|
262
317
|
await this.signalManager.close();
|
|
263
318
|
await this._edgeConnection?.close();
|
|
264
319
|
await this.feedStore.close();
|
|
@@ -268,10 +323,11 @@ export class ServiceContext extends Resource {
|
|
|
268
323
|
}
|
|
269
324
|
|
|
270
325
|
async createIdentity(params: CreateIdentityOptions = {}) {
|
|
271
|
-
const
|
|
272
|
-
await this.
|
|
273
|
-
await
|
|
274
|
-
await
|
|
326
|
+
const ctx = Context.default();
|
|
327
|
+
const identity = await this.identityManager.createIdentity(params, ctx);
|
|
328
|
+
await this._setNetworkIdentity({ identity });
|
|
329
|
+
await identity.joinNetwork(ctx);
|
|
330
|
+
await this._initialize(ctx);
|
|
275
331
|
return identity;
|
|
276
332
|
}
|
|
277
333
|
|
|
@@ -295,11 +351,11 @@ export class ServiceContext extends Resource {
|
|
|
295
351
|
}
|
|
296
352
|
|
|
297
353
|
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();
|
|
354
|
+
const { identity, identityRecord } = await this.identityManager.prepareIdentity(params, this._ctx);
|
|
355
|
+
await this._setNetworkIdentity({ deviceCredential: params.authorizedDeviceCredential!, identity });
|
|
356
|
+
await identity.joinNetwork(this._ctx);
|
|
301
357
|
await this.identityManager.acceptIdentity(identity, identityRecord, params.deviceProfile);
|
|
302
|
-
await this._initialize(
|
|
358
|
+
await this._initialize(this._ctx);
|
|
303
359
|
return identity;
|
|
304
360
|
}
|
|
305
361
|
|
|
@@ -314,7 +370,7 @@ export class ServiceContext extends Resource {
|
|
|
314
370
|
// Called when identity is created.
|
|
315
371
|
@Trace.span()
|
|
316
372
|
private async _initialize(ctx: Context): Promise<void> {
|
|
317
|
-
log('
|
|
373
|
+
log('_initialize: start');
|
|
318
374
|
const identity = this.identityManager.identity ?? failUndefined();
|
|
319
375
|
const signingContext: SigningContext = {
|
|
320
376
|
credentialSigner: identity.getIdentityCredentialSigner(),
|
|
@@ -326,6 +382,7 @@ export class ServiceContext extends Resource {
|
|
|
326
382
|
},
|
|
327
383
|
};
|
|
328
384
|
|
|
385
|
+
log('_initialize: creating DataSpaceManager');
|
|
329
386
|
this.dataSpaceManager = new DataSpaceManager({
|
|
330
387
|
spaceManager: this.spaceManager,
|
|
331
388
|
metadataStore: this.metadataStore,
|
|
@@ -341,7 +398,9 @@ export class ServiceContext extends Resource {
|
|
|
341
398
|
runtimeProps: this._runtimeProps as DataSpaceManagerRuntimeProps,
|
|
342
399
|
edgeFeatures: this._edgeFeatures,
|
|
343
400
|
});
|
|
401
|
+
log('_initialize: opening DataSpaceManager...');
|
|
344
402
|
await this.dataSpaceManager.open();
|
|
403
|
+
log('_initialize: DataSpaceManager opened');
|
|
345
404
|
|
|
346
405
|
this.edgeAgentManager = new EdgeAgentManager(
|
|
347
406
|
this._edgeFeatures,
|
|
@@ -349,13 +408,16 @@ export class ServiceContext extends Resource {
|
|
|
349
408
|
this.dataSpaceManager,
|
|
350
409
|
identity,
|
|
351
410
|
);
|
|
411
|
+
log('_initialize: opening EdgeAgentManager...');
|
|
352
412
|
await this.edgeAgentManager.open();
|
|
413
|
+
log('_initialize: EdgeAgentManager opened');
|
|
353
414
|
|
|
354
415
|
this._handlerFactories.set(Invitation.Kind.SPACE, (invitation) => {
|
|
355
416
|
invariant(this.dataSpaceManager, 'dataSpaceManager not initialized yet');
|
|
356
417
|
return new SpaceInvitationProtocol(this.dataSpaceManager, signingContext, this.keyring, invitation.spaceKey);
|
|
357
418
|
});
|
|
358
419
|
this.initialized.wake();
|
|
420
|
+
log('_initialize: initialized.wake() called');
|
|
359
421
|
|
|
360
422
|
this._deviceSpaceSync = {
|
|
361
423
|
processCredential: async (credential: Credential) => {
|
|
@@ -378,7 +440,7 @@ export class ServiceContext extends Resource {
|
|
|
378
440
|
|
|
379
441
|
try {
|
|
380
442
|
log('accepting space recorded in halo', { details: assertion });
|
|
381
|
-
await this.dataSpaceManager.acceptSpace({
|
|
443
|
+
await this.dataSpaceManager.acceptSpace(this._ctx, {
|
|
382
444
|
spaceKey: assertion.spaceKey,
|
|
383
445
|
genesisFeedKey: assertion.genesisFeedKey,
|
|
384
446
|
});
|
|
@@ -391,33 +453,42 @@ export class ServiceContext extends Resource {
|
|
|
391
453
|
await identity.space.spaceState.addCredentialProcessor(this._deviceSpaceSync);
|
|
392
454
|
}
|
|
393
455
|
|
|
394
|
-
private async _setNetworkIdentity(params?: { deviceCredential
|
|
456
|
+
private async _setNetworkIdentity(params?: { deviceCredential?: Credential; identity?: Identity }): Promise<void> {
|
|
457
|
+
log('_setNetworkIdentity: acquiring mutex...');
|
|
395
458
|
using _ = await this._edgeIdentityUpdateMutex.acquire();
|
|
459
|
+
log('_setNetworkIdentity: mutex acquired');
|
|
396
460
|
|
|
397
461
|
let edgeIdentity: EdgeIdentity;
|
|
398
|
-
const identity =
|
|
462
|
+
const identity = params?.identity;
|
|
399
463
|
if (identity) {
|
|
400
|
-
log('
|
|
464
|
+
log('_setNetworkIdentity: has identity', {
|
|
401
465
|
identity: identity.identityKey.toHex(),
|
|
402
|
-
|
|
466
|
+
hasDeviceCredential: !!params?.deviceCredential,
|
|
403
467
|
});
|
|
404
468
|
|
|
405
469
|
if (params?.deviceCredential) {
|
|
470
|
+
log('_setNetworkIdentity: creating chain edge identity with device credential...');
|
|
406
471
|
edgeIdentity = await createChainEdgeIdentity(
|
|
407
472
|
identity.signer,
|
|
408
473
|
identity.identityKey,
|
|
409
474
|
identity.deviceKey,
|
|
410
|
-
|
|
475
|
+
{ credential: params.deviceCredential },
|
|
411
476
|
[], // TODO(dmaretskyi): Service access credentials.
|
|
412
477
|
);
|
|
478
|
+
log('_setNetworkIdentity: chain edge identity created');
|
|
413
479
|
} else {
|
|
480
|
+
log('_setNetworkIdentity: waiting for identity.ready()...');
|
|
414
481
|
// TODO: throw here or from identity if device chain can't be loaded, to avoid indefinite hangup
|
|
415
482
|
await warnAfterTimeout(10_000, 'Waiting for identity to be ready for edge connection', async () => {
|
|
416
483
|
await identity.ready();
|
|
417
484
|
});
|
|
485
|
+
log('_setNetworkIdentity: identity.ready() resolved', {
|
|
486
|
+
hasDeviceCredentialChain: !!identity.deviceCredentialChain,
|
|
487
|
+
});
|
|
418
488
|
|
|
419
489
|
invariant(identity.deviceCredentialChain);
|
|
420
490
|
|
|
491
|
+
log('_setNetworkIdentity: creating chain edge identity...');
|
|
421
492
|
edgeIdentity = await createChainEdgeIdentity(
|
|
422
493
|
identity.signer,
|
|
423
494
|
identity.identityKey,
|
|
@@ -425,9 +496,12 @@ export class ServiceContext extends Resource {
|
|
|
425
496
|
identity.deviceCredentialChain,
|
|
426
497
|
[], // TODO(dmaretskyi): Service access credentials.
|
|
427
498
|
);
|
|
499
|
+
log('_setNetworkIdentity: chain edge identity created');
|
|
428
500
|
}
|
|
429
501
|
} else {
|
|
502
|
+
log('_setNetworkIdentity: no identity, creating ephemeral edge identity...');
|
|
430
503
|
edgeIdentity = await createEphemeralEdgeIdentity();
|
|
504
|
+
log('_setNetworkIdentity: ephemeral edge identity created');
|
|
431
505
|
}
|
|
432
506
|
|
|
433
507
|
this._edgeConnection?.setIdentity(edgeIdentity);
|
|
@@ -436,5 +510,6 @@ export class ServiceContext extends Resource {
|
|
|
436
510
|
identityKey: edgeIdentity.identityKey,
|
|
437
511
|
peerKey: edgeIdentity.peerKey,
|
|
438
512
|
});
|
|
513
|
+
log('_setNetworkIdentity: done');
|
|
439
514
|
}
|
|
440
515
|
}
|
|
@@ -13,6 +13,7 @@ import { verifyPresentation } from '@dxos/credentials';
|
|
|
13
13
|
import { type PublicKey } from '@dxos/keys';
|
|
14
14
|
import { MemorySignalManagerContext } from '@dxos/messaging';
|
|
15
15
|
import { type Identity } from '@dxos/protocols/proto/dxos/client/services';
|
|
16
|
+
import { MembershipPolicy } from '@dxos/protocols/proto/dxos/halo/credentials';
|
|
16
17
|
import { type Credential } from '@dxos/protocols/proto/dxos/halo/credentials';
|
|
17
18
|
import { isNode } from '@dxos/util';
|
|
18
19
|
|
|
@@ -29,16 +30,16 @@ describe('ClientServicesHost', () => {
|
|
|
29
30
|
test('open and close', async () => {
|
|
30
31
|
const host = createServiceHost(new Config(), new MemorySignalManagerContext());
|
|
31
32
|
await host.open(new Context());
|
|
32
|
-
await host.close();
|
|
33
|
+
await host.close(Context.default());
|
|
33
34
|
});
|
|
34
35
|
|
|
35
36
|
test('queryCredentials', async () => {
|
|
36
37
|
const host = createServiceHost(new Config(), new MemorySignalManagerContext());
|
|
37
38
|
await host.open(new Context());
|
|
38
|
-
onTestFinished(() => host.close());
|
|
39
|
+
onTestFinished(() => host.close(Context.default()));
|
|
39
40
|
|
|
40
41
|
await host.services.IdentityService!.createIdentity({});
|
|
41
|
-
const { spaceKey } = await host.services.SpacesService!.createSpace();
|
|
42
|
+
const { spaceKey } = await host.services.SpacesService!.createSpace({ membershipPolicy: MembershipPolicy.INVITE });
|
|
42
43
|
|
|
43
44
|
const stream = host.services.SpacesService!.queryCredentials({ spaceKey });
|
|
44
45
|
const [done, tick] = latch({ count: 3 });
|
|
@@ -54,7 +55,7 @@ describe('ClientServicesHost', () => {
|
|
|
54
55
|
test('write and query credentials', async () => {
|
|
55
56
|
const host = createServiceHost(new Config(), new MemorySignalManagerContext());
|
|
56
57
|
await host.open(new Context());
|
|
57
|
-
onTestFinished(() => host.close());
|
|
58
|
+
onTestFinished(() => host.close(Context.default()));
|
|
58
59
|
|
|
59
60
|
await host.services.IdentityService!.createIdentity({});
|
|
60
61
|
|
|
@@ -91,7 +92,7 @@ describe('ClientServicesHost', () => {
|
|
|
91
92
|
test('sign presentation', async () => {
|
|
92
93
|
const host = createServiceHost(new Config(), new MemorySignalManagerContext());
|
|
93
94
|
await host.open(new Context());
|
|
94
|
-
onTestFinished(() => host.close());
|
|
95
|
+
onTestFinished(() => host.close(Context.default()));
|
|
95
96
|
|
|
96
97
|
await host.services.IdentityService!.createIdentity({});
|
|
97
98
|
|
|
@@ -128,7 +129,7 @@ describe('ClientServicesHost', () => {
|
|
|
128
129
|
expect(host.context.storage.size).to.exist;
|
|
129
130
|
|
|
130
131
|
await asyncTimeout(host.reset(), 1000);
|
|
131
|
-
await host.close();
|
|
132
|
+
await host.close(Context.default());
|
|
132
133
|
}
|
|
133
134
|
|
|
134
135
|
{
|
|
@@ -146,7 +147,7 @@ describe('ClientServicesHost', () => {
|
|
|
146
147
|
});
|
|
147
148
|
await expect(asyncTimeout(trigger.wait(), 200)).rejects.toBeInstanceOf(Error);
|
|
148
149
|
await stream?.close();
|
|
149
|
-
await host.close();
|
|
150
|
+
await host.close(Context.default());
|
|
150
151
|
}
|
|
151
152
|
});
|
|
152
153
|
});
|
|
@@ -147,7 +147,7 @@ export class ClientServicesHost {
|
|
|
147
147
|
void this.open(new Context());
|
|
148
148
|
}
|
|
149
149
|
},
|
|
150
|
-
onRelease: () => this.close(),
|
|
150
|
+
onRelease: () => this.close(Context.default()),
|
|
151
151
|
});
|
|
152
152
|
}
|
|
153
153
|
|
|
@@ -244,9 +244,6 @@ export class ClientServicesHost {
|
|
|
244
244
|
if (this._runtimeProps.enableVectorIndexing === undefined) {
|
|
245
245
|
this._runtimeProps.enableVectorIndexing = config?.get('runtime.client.enableVectorIndexing', false);
|
|
246
246
|
}
|
|
247
|
-
if (this._runtimeProps.enableLocalQueues === undefined) {
|
|
248
|
-
this._runtimeProps.enableLocalQueues = config?.get('runtime.client.enableLocalQueues', false);
|
|
249
|
-
}
|
|
250
247
|
|
|
251
248
|
invariant(!this._config, 'config already set');
|
|
252
249
|
this._config = config;
|
|
@@ -262,8 +259,9 @@ export class ClientServicesHost {
|
|
|
262
259
|
|
|
263
260
|
const endpoint = config?.get('runtime.services.edge.url');
|
|
264
261
|
if (endpoint) {
|
|
262
|
+
const clientTag = config?.get('runtime.app.env.DX_EDGE_CLIENT_TAG');
|
|
265
263
|
this._edgeConnection = new EdgeClient(createStubEdgeIdentity(), { socketEndpoint: endpoint });
|
|
266
|
-
this._edgeHttpClient = new EdgeHttpClient(endpoint);
|
|
264
|
+
this._edgeHttpClient = new EdgeHttpClient(endpoint, { clientTag });
|
|
267
265
|
}
|
|
268
266
|
|
|
269
267
|
const {
|
|
@@ -348,7 +346,6 @@ export class ClientServicesHost {
|
|
|
348
346
|
this._serviceContext.identityManager,
|
|
349
347
|
this._serviceContext.recoveryManager,
|
|
350
348
|
this._serviceContext.keyring,
|
|
351
|
-
() => this._serviceContext.dataSpaceManager!,
|
|
352
349
|
(params) => this._createIdentity(params),
|
|
353
350
|
(profile) => this._serviceContext.broadcastProfileUpdate(profile),
|
|
354
351
|
);
|
|
@@ -395,8 +392,13 @@ export class ClientServicesHost {
|
|
|
395
392
|
EdgeAgentService: new EdgeAgentServiceImpl(agentManagerProvider, this._edgeConnection),
|
|
396
393
|
});
|
|
397
394
|
|
|
395
|
+
log('service-host: opening service context...');
|
|
398
396
|
await this._serviceContext.open(ctx);
|
|
397
|
+
log('service-host: service context opened');
|
|
398
|
+
|
|
399
|
+
log('service-host: opening identity service...');
|
|
399
400
|
await identityService.open();
|
|
401
|
+
log('service-host: identity service opened');
|
|
400
402
|
|
|
401
403
|
const devtoolsProxy = this._config?.get('runtime.client.devtoolsProxy');
|
|
402
404
|
if (devtoolsProxy) {
|
|
@@ -420,7 +422,7 @@ export class ClientServicesHost {
|
|
|
420
422
|
|
|
421
423
|
@synchronized
|
|
422
424
|
@Trace.span()
|
|
423
|
-
async close(): Promise<void> {
|
|
425
|
+
async close(ctx: Context): Promise<void> {
|
|
424
426
|
if (!this._open) {
|
|
425
427
|
return;
|
|
426
428
|
}
|
|
@@ -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');
|