@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
|
@@ -3,10 +3,10 @@
|
|
|
3
3
|
//
|
|
4
4
|
|
|
5
5
|
import { type Doc } from '@automerge/automerge';
|
|
6
|
-
import { type AutomergeUrl, type
|
|
6
|
+
import { type AutomergeUrl, type DocumentId, interpretAsDocumentId } from '@automerge/automerge-repo';
|
|
7
7
|
|
|
8
8
|
import { Event, synchronized, trackLeaks } from '@dxos/async';
|
|
9
|
-
import { SpaceProperties } from '@dxos/client-protocol';
|
|
9
|
+
import { LegacySpaceProperties, SpaceProperties } from '@dxos/client-protocol';
|
|
10
10
|
import { Context, LifecycleState, Resource, cancelWithContext } from '@dxos/context';
|
|
11
11
|
import {
|
|
12
12
|
type CredentialSigner,
|
|
@@ -16,7 +16,6 @@ import {
|
|
|
16
16
|
getCredentialAssertion,
|
|
17
17
|
} from '@dxos/credentials';
|
|
18
18
|
import { Type } from '@dxos/echo';
|
|
19
|
-
import { getSchemaDXN } from '@dxos/echo/internal';
|
|
20
19
|
import {
|
|
21
20
|
AuthStatus,
|
|
22
21
|
CredentialServerExtension,
|
|
@@ -31,45 +30,40 @@ import {
|
|
|
31
30
|
type SpaceProtocolSession,
|
|
32
31
|
findInlineObjectOfType,
|
|
33
32
|
} from '@dxos/echo-pipeline';
|
|
34
|
-
import {
|
|
35
|
-
type DatabaseDirectory,
|
|
36
|
-
EncodedReference,
|
|
37
|
-
type ObjectStructure,
|
|
38
|
-
SpaceDocVersion,
|
|
39
|
-
createIdFromSpaceKey,
|
|
40
|
-
} from '@dxos/echo-protocol';
|
|
33
|
+
import { type DatabaseDirectory, createIdFromSpaceKey } from '@dxos/echo-protocol';
|
|
41
34
|
import type { EdgeConnection, EdgeHttpClient } from '@dxos/edge-client';
|
|
42
35
|
import { type FeedStore, writeMessages } from '@dxos/feed-store';
|
|
43
36
|
import { assertArgument, assertState, failedInvariant, invariant } from '@dxos/invariant';
|
|
44
37
|
import { type Keyring } from '@dxos/keyring';
|
|
45
|
-
import {
|
|
38
|
+
import { PublicKey, type SpaceId } from '@dxos/keys';
|
|
46
39
|
import { log } from '@dxos/log';
|
|
47
40
|
import { AlreadyJoinedError, trace as Trace } from '@dxos/protocols';
|
|
48
41
|
import { Invitation, SpaceState } from '@dxos/protocols/proto/dxos/client/services';
|
|
49
42
|
import { type Runtime } from '@dxos/protocols/proto/dxos/config';
|
|
50
43
|
import { type FeedMessage } from '@dxos/protocols/proto/dxos/echo/feed';
|
|
51
44
|
import { EdgeReplicationSetting, type SpaceMetadata } from '@dxos/protocols/proto/dxos/echo/metadata';
|
|
52
|
-
import {
|
|
45
|
+
import {
|
|
46
|
+
type Credential,
|
|
47
|
+
MembershipPolicy,
|
|
48
|
+
type ProfileDocument,
|
|
49
|
+
SpaceMember,
|
|
50
|
+
} from '@dxos/protocols/proto/dxos/halo/credentials';
|
|
53
51
|
import { type DelegateSpaceInvitation } from '@dxos/protocols/proto/dxos/halo/invitations';
|
|
54
52
|
import { type PeerState } from '@dxos/protocols/proto/dxos/mesh/presence';
|
|
55
53
|
import { type Teleport } from '@dxos/teleport';
|
|
56
54
|
import { Gossip, Presence } from '@dxos/teleport-extension-gossip';
|
|
57
55
|
import { type Timeframe } from '@dxos/timeframe';
|
|
58
56
|
import { trace } from '@dxos/tracing';
|
|
59
|
-
import { ComplexMap, deferFunction, forEachAsync
|
|
57
|
+
import { ComplexMap, deferFunction, forEachAsync } from '@dxos/util';
|
|
60
58
|
|
|
61
59
|
import { createAuthProvider } from '../identity';
|
|
62
60
|
import { type InvitationsManager } from '../invitations';
|
|
63
|
-
|
|
64
61
|
import { DataSpace } from './data-space';
|
|
65
62
|
import { spaceGenesis } from './genesis';
|
|
66
63
|
|
|
67
64
|
const PRESENCE_ANNOUNCE_INTERVAL = 10_000;
|
|
68
65
|
const PRESENCE_OFFLINE_TIMEOUT = 20_000;
|
|
69
66
|
|
|
70
|
-
// Space properties key for default metadata.
|
|
71
|
-
const DEFAULT_SPACE_KEY = '__DEFAULT__';
|
|
72
|
-
|
|
73
67
|
export interface SigningContext {
|
|
74
68
|
identityKey: PublicKey;
|
|
75
69
|
deviceKey: PublicKey;
|
|
@@ -94,6 +88,9 @@ export type AcceptSpaceOptions = {
|
|
|
94
88
|
* We will try to catch up to this timeframe before initializing the database.
|
|
95
89
|
*/
|
|
96
90
|
dataTimeframe?: Timeframe;
|
|
91
|
+
|
|
92
|
+
/** Tags assigned to the space member. */
|
|
93
|
+
tags?: string[];
|
|
97
94
|
};
|
|
98
95
|
|
|
99
96
|
export type AdmitMemberOptions = {
|
|
@@ -102,6 +99,7 @@ export type AdmitMemberOptions = {
|
|
|
102
99
|
role: SpaceMember.Role;
|
|
103
100
|
profile?: ProfileDocument;
|
|
104
101
|
delegationCredentialId?: PublicKey;
|
|
102
|
+
tags?: string[];
|
|
105
103
|
};
|
|
106
104
|
|
|
107
105
|
export type DataSpaceManagerProps = {
|
|
@@ -135,9 +133,12 @@ export type DataSpaceManagerRuntimeProps = {
|
|
|
135
133
|
export type CreateSpaceOptions = {
|
|
136
134
|
rootUrl?: AutomergeUrl;
|
|
137
135
|
documents?: Record<DocumentId, Uint8Array>;
|
|
136
|
+
tags?: string[];
|
|
137
|
+
membershipPolicy?: MembershipPolicy;
|
|
138
138
|
};
|
|
139
139
|
|
|
140
140
|
@trackLeaks('open', 'close')
|
|
141
|
+
@trace.resource()
|
|
141
142
|
export class DataSpaceManager extends Resource {
|
|
142
143
|
public readonly updated = new Event();
|
|
143
144
|
|
|
@@ -184,12 +185,15 @@ export class DataSpaceManager extends Resource {
|
|
|
184
185
|
Array.from(this._spaces.values()).map(async (space) => {
|
|
185
186
|
const rootUrl = space.automergeSpaceState.rootUrl;
|
|
186
187
|
const rootHandle = rootUrl
|
|
187
|
-
? await this._echoHost.loadDoc<Doc<DatabaseDirectory>>(
|
|
188
|
+
? await this._echoHost.loadDoc<Doc<DatabaseDirectory>>(this._ctx, rootUrl as AutomergeUrl)
|
|
188
189
|
: undefined;
|
|
189
190
|
await rootHandle?.whenReady();
|
|
190
191
|
const rootDoc = rootHandle?.doc();
|
|
191
192
|
|
|
192
|
-
const properties =
|
|
193
|
+
const properties =
|
|
194
|
+
rootDoc &&
|
|
195
|
+
(findInlineObjectOfType(rootDoc, Type.getTypename(SpaceProperties)) ??
|
|
196
|
+
findInlineObjectOfType(rootDoc, Type.getTypename(LegacySpaceProperties)));
|
|
193
197
|
|
|
194
198
|
return {
|
|
195
199
|
key: space.key.toHex(),
|
|
@@ -226,7 +230,7 @@ export class DataSpaceManager extends Resource {
|
|
|
226
230
|
await forEachAsync(this._metadataStore.spaces, async (spaceMetadata) => {
|
|
227
231
|
try {
|
|
228
232
|
log('load space', { spaceMetadata });
|
|
229
|
-
const space = await this._constructSpace(spaceMetadata);
|
|
233
|
+
const space = await this._constructSpace(this._ctx, spaceMetadata);
|
|
230
234
|
// Track spaces that were previously active for auto-activation (used in dedicated worker mode).
|
|
231
235
|
if (this._runtimeProps?.autoActivateSpaces && spaceMetadata.state === SpaceState.SPACE_ACTIVE) {
|
|
232
236
|
spacesToActivate.push(space);
|
|
@@ -239,7 +243,7 @@ export class DataSpaceManager extends Resource {
|
|
|
239
243
|
// Auto-activate spaces that were previously active (used in dedicated worker mode after leader changeover).
|
|
240
244
|
for (const space of spacesToActivate) {
|
|
241
245
|
log('auto-activating space', { spaceKey: space.key });
|
|
242
|
-
space.activate().catch((err) => {
|
|
246
|
+
space.activate(this._ctx).catch((err) => {
|
|
243
247
|
log.error('Error auto-activating space', { spaceKey: space.key, err });
|
|
244
248
|
});
|
|
245
249
|
}
|
|
@@ -250,10 +254,10 @@ export class DataSpaceManager extends Resource {
|
|
|
250
254
|
}
|
|
251
255
|
|
|
252
256
|
@synchronized
|
|
253
|
-
protected override async _close(): Promise<void> {
|
|
257
|
+
protected override async _close(ctx: Context): Promise<void> {
|
|
254
258
|
log('close');
|
|
255
259
|
for (const space of this._spaces.values()) {
|
|
256
|
-
await space.close();
|
|
260
|
+
await space.close(ctx);
|
|
257
261
|
}
|
|
258
262
|
this._spaces.clear();
|
|
259
263
|
}
|
|
@@ -262,7 +266,8 @@ export class DataSpaceManager extends Resource {
|
|
|
262
266
|
* Creates a new space writing the genesis credentials to the control feed.
|
|
263
267
|
*/
|
|
264
268
|
@synchronized
|
|
265
|
-
|
|
269
|
+
@trace.span({ showInBrowserTimeline: true })
|
|
270
|
+
async createSpace(ctx: Context, options: CreateSpaceOptions = {}): Promise<DataSpace> {
|
|
266
271
|
assertArgument(
|
|
267
272
|
!!options.rootUrl === !!options.documents,
|
|
268
273
|
'options',
|
|
@@ -270,6 +275,8 @@ export class DataSpaceManager extends Resource {
|
|
|
270
275
|
);
|
|
271
276
|
|
|
272
277
|
assertState(this._lifecycleState === LifecycleState.OPEN, 'Not open.');
|
|
278
|
+
|
|
279
|
+
const tags = options.tags ? Array.from(options.tags) : [];
|
|
273
280
|
const spaceKey = await this._keyring.createKey();
|
|
274
281
|
const controlFeedKey = await this._keyring.createKey();
|
|
275
282
|
const dataFeedKey = await this._keyring.createKey();
|
|
@@ -282,6 +289,7 @@ export class DataSpaceManager extends Resource {
|
|
|
282
289
|
controlFeedKey,
|
|
283
290
|
dataFeedKey,
|
|
284
291
|
state: SpaceState.SPACE_ACTIVE,
|
|
292
|
+
tags,
|
|
285
293
|
};
|
|
286
294
|
|
|
287
295
|
log('creating space...', { spaceId, spaceKey });
|
|
@@ -319,30 +327,37 @@ export class DataSpaceManager extends Resource {
|
|
|
319
327
|
let root: DatabaseRoot;
|
|
320
328
|
if (options.rootUrl) {
|
|
321
329
|
const newRootDocId = documentIdMapping[interpretAsDocumentId(options.rootUrl)] ?? failedInvariant();
|
|
322
|
-
const rootDocHandle = await this._echoHost.loadDoc<DatabaseDirectory>(
|
|
330
|
+
const rootDocHandle = await this._echoHost.loadDoc<DatabaseDirectory>(ctx, newRootDocId);
|
|
323
331
|
DatabaseRoot.mapLinks(rootDocHandle, documentIdMapping);
|
|
324
332
|
|
|
325
|
-
root = await this._echoHost.openSpaceRoot(spaceId, `automerge:${newRootDocId}` as AutomergeUrl);
|
|
333
|
+
root = await this._echoHost.openSpaceRoot(ctx, spaceId, `automerge:${newRootDocId}` as AutomergeUrl);
|
|
326
334
|
} else {
|
|
327
|
-
root = await this._echoHost.createSpaceRoot(spaceKey);
|
|
335
|
+
root = await this._echoHost.createSpaceRoot(ctx, spaceKey);
|
|
328
336
|
}
|
|
329
|
-
await this._echoHost.flush();
|
|
337
|
+
await this._echoHost.flush(ctx);
|
|
330
338
|
|
|
331
339
|
log('constructing space...', { spaceKey });
|
|
332
340
|
|
|
333
|
-
const space = await this._constructSpace(metadata);
|
|
334
|
-
await space.open();
|
|
341
|
+
const space = await this._constructSpace(ctx, metadata);
|
|
342
|
+
await space.open(ctx);
|
|
335
343
|
|
|
336
344
|
log('adding space...', { spaceKey });
|
|
337
345
|
|
|
338
|
-
const credentials = await spaceGenesis(
|
|
346
|
+
const credentials = await spaceGenesis(
|
|
347
|
+
this._keyring,
|
|
348
|
+
this._signingContext,
|
|
349
|
+
space.inner,
|
|
350
|
+
root.url,
|
|
351
|
+
tags,
|
|
352
|
+
options.membershipPolicy,
|
|
353
|
+
);
|
|
339
354
|
await this._metadataStore.addSpace(metadata);
|
|
340
355
|
|
|
341
356
|
const memberCredential = credentials[1];
|
|
342
357
|
invariant(getCredentialAssertion(memberCredential)['@type'] === 'dxos.halo.credentials.SpaceMember');
|
|
343
358
|
await this._signingContext.recordCredential(memberCredential);
|
|
344
359
|
|
|
345
|
-
await space.initializeDataPipeline();
|
|
360
|
+
await space.initializeDataPipeline(ctx);
|
|
346
361
|
|
|
347
362
|
log('space ready.', { spaceId, spaceKey });
|
|
348
363
|
|
|
@@ -350,84 +365,30 @@ export class DataSpaceManager extends Resource {
|
|
|
350
365
|
return space;
|
|
351
366
|
}
|
|
352
367
|
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
case SpaceDocVersion.CURRENT: {
|
|
359
|
-
if (!space.databaseRoot.handle.isReady()) {
|
|
360
|
-
log.warn('waiting for space root to be ready', { spaceId: space.id });
|
|
361
|
-
await space.databaseRoot.handle.whenReady();
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
const [_, properties] =
|
|
365
|
-
findInlineObjectOfType(space.databaseRoot.doc()!, Type.getTypename(SpaceProperties)) ?? [];
|
|
366
|
-
return properties?.data?.[DEFAULT_SPACE_KEY] === this._signingContext.identityKey.toHex();
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
case SpaceDocVersion.LEGACY: {
|
|
370
|
-
throw new Error('Legacy space version is not supported');
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
default:
|
|
374
|
-
log.warn('unknown space version', { version: space.databaseRoot.getVersion(), spaceId: space.id });
|
|
375
|
-
return false;
|
|
376
|
-
}
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
async createDefaultSpace(): Promise<DataSpace> {
|
|
380
|
-
const space = await this.createSpace();
|
|
381
|
-
const document = await this._getSpaceRootDocument(space);
|
|
382
|
-
|
|
383
|
-
// TODO(dmaretskyi): Better API for low-level data access.
|
|
384
|
-
const properties: ObjectStructure = {
|
|
385
|
-
system: {
|
|
386
|
-
type: EncodedReference.fromDXN(getSchemaDXN(SpaceProperties)!),
|
|
387
|
-
},
|
|
388
|
-
data: {
|
|
389
|
-
[DEFAULT_SPACE_KEY]: this._signingContext.identityKey.toHex(),
|
|
390
|
-
},
|
|
391
|
-
meta: {
|
|
392
|
-
keys: [],
|
|
393
|
-
},
|
|
394
|
-
};
|
|
395
|
-
|
|
396
|
-
const propertiesId = ObjectId.random();
|
|
397
|
-
document.change((doc: DatabaseDirectory) => {
|
|
398
|
-
setDeep(doc, ['objects', propertiesId], properties);
|
|
399
|
-
});
|
|
400
|
-
|
|
401
|
-
await this._echoHost.flush();
|
|
402
|
-
return space;
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
private async _getSpaceRootDocument(space: DataSpace): Promise<DocHandle<DatabaseDirectory>> {
|
|
406
|
-
const automergeIndex = space.automergeSpaceState.rootUrl;
|
|
407
|
-
invariant(automergeIndex);
|
|
408
|
-
const document = await this._echoHost.loadDoc<DatabaseDirectory>(Context.default(), automergeIndex as any, {
|
|
409
|
-
fetchFromNetwork: true,
|
|
410
|
-
});
|
|
411
|
-
await document.whenReady();
|
|
412
|
-
return document;
|
|
413
|
-
}
|
|
414
|
-
|
|
368
|
+
/**
|
|
369
|
+
* Accepts an existing space by joining its swarm and initializing the data pipeline.
|
|
370
|
+
* @param ctx - Caller context for cancellation and tracing.
|
|
371
|
+
* @param opts - Space keys and optional timeframes for catch-up.
|
|
372
|
+
*/
|
|
415
373
|
// TODO(burdon): Rename join space.
|
|
416
374
|
@synchronized
|
|
417
|
-
|
|
375
|
+
@trace.span({ showInBrowserTimeline: true })
|
|
376
|
+
async acceptSpace(ctx: Context, opts: AcceptSpaceOptions): Promise<DataSpace> {
|
|
418
377
|
log('accept space', { opts });
|
|
419
378
|
invariant(this._lifecycleState === LifecycleState.OPEN, 'Not open.');
|
|
420
379
|
invariant(!this._spaces.has(opts.spaceKey), 'Space already exists.');
|
|
421
380
|
|
|
381
|
+
const tags = opts.tags ? Array.from(opts.tags) : [];
|
|
422
382
|
const metadata: SpaceMetadata = {
|
|
423
383
|
key: opts.spaceKey,
|
|
424
384
|
genesisFeedKey: opts.genesisFeedKey,
|
|
425
385
|
controlTimeframe: opts.controlTimeframe,
|
|
426
386
|
dataTimeframe: opts.dataTimeframe,
|
|
387
|
+
tags,
|
|
427
388
|
};
|
|
428
389
|
|
|
429
|
-
const space = await this._constructSpace(metadata);
|
|
430
|
-
await space.open();
|
|
390
|
+
const space = await this._constructSpace(ctx, metadata);
|
|
391
|
+
await space.open(ctx);
|
|
431
392
|
await this._metadataStore.addSpace(metadata);
|
|
432
393
|
space.initializeDataPipelineAsync();
|
|
433
394
|
|
|
@@ -453,6 +414,7 @@ export class DataSpaceManager extends Resource {
|
|
|
453
414
|
space.spaceState.membershipChainHeads,
|
|
454
415
|
options.profile,
|
|
455
416
|
options.delegationCredentialId,
|
|
417
|
+
space.spaceState.tags,
|
|
456
418
|
);
|
|
457
419
|
|
|
458
420
|
// TODO(dmaretskyi): Refactor.
|
|
@@ -479,8 +441,8 @@ export class DataSpaceManager extends Resource {
|
|
|
479
441
|
);
|
|
480
442
|
}
|
|
481
443
|
|
|
482
|
-
public async requestSpaceAdmissionCredential(spaceKey: PublicKey): Promise<Credential> {
|
|
483
|
-
return this._spaceManager.requestSpaceAdmissionCredential({
|
|
444
|
+
public async requestSpaceAdmissionCredential(ctx: Context, spaceKey: PublicKey): Promise<Credential> {
|
|
445
|
+
return this._spaceManager.requestSpaceAdmissionCredential(ctx, {
|
|
484
446
|
spaceKey,
|
|
485
447
|
identityKey: this._signingContext.identityKey,
|
|
486
448
|
timeout: 15_000,
|
|
@@ -493,7 +455,11 @@ export class DataSpaceManager extends Resource {
|
|
|
493
455
|
});
|
|
494
456
|
}
|
|
495
457
|
|
|
496
|
-
async setSpaceEdgeReplicationSetting(
|
|
458
|
+
async setSpaceEdgeReplicationSetting(
|
|
459
|
+
ctx: Context,
|
|
460
|
+
spaceKey: PublicKey,
|
|
461
|
+
setting: EdgeReplicationSetting,
|
|
462
|
+
): Promise<void> {
|
|
497
463
|
const space = this._spaces.get(spaceKey);
|
|
498
464
|
invariant(space, 'Space not found.');
|
|
499
465
|
|
|
@@ -505,7 +471,7 @@ export class DataSpaceManager extends Resource {
|
|
|
505
471
|
await this._echoEdgeReplicator?.disconnectFromSpace(space.id);
|
|
506
472
|
break;
|
|
507
473
|
case EdgeReplicationSetting.ENABLED:
|
|
508
|
-
await this._echoEdgeReplicator?.connectToSpace(space.id);
|
|
474
|
+
await this._echoEdgeReplicator?.connectToSpace(ctx, space.id);
|
|
509
475
|
break;
|
|
510
476
|
}
|
|
511
477
|
}
|
|
@@ -513,7 +479,7 @@ export class DataSpaceManager extends Resource {
|
|
|
513
479
|
space.stateUpdate.emit();
|
|
514
480
|
}
|
|
515
481
|
|
|
516
|
-
private async _constructSpace(metadata: SpaceMetadata): Promise<DataSpace> {
|
|
482
|
+
private async _constructSpace(ctx: Context, metadata: SpaceMetadata): Promise<DataSpace> {
|
|
517
483
|
log('construct space', { metadata });
|
|
518
484
|
const gossip = new Gossip({
|
|
519
485
|
localPeerId: this._signingContext.deviceKey,
|
|
@@ -603,6 +569,7 @@ export class DataSpaceManager extends Resource {
|
|
|
603
569
|
},
|
|
604
570
|
},
|
|
605
571
|
cache: metadata.cache,
|
|
572
|
+
tags: metadata.tags,
|
|
606
573
|
edgeConnection: this._edgeConnection,
|
|
607
574
|
edgeHttpClient: this._edgeHttpClient,
|
|
608
575
|
edgeFeatures: this._edgeFeatures,
|
|
@@ -611,7 +578,7 @@ export class DataSpaceManager extends Resource {
|
|
|
611
578
|
dataSpace.postOpen.append(async () => {
|
|
612
579
|
const setting = dataSpace.getEdgeReplicationSetting();
|
|
613
580
|
if (!setting || setting === EdgeReplicationSetting.ENABLED) {
|
|
614
|
-
await this._echoEdgeReplicator?.connectToSpace(dataSpace.id);
|
|
581
|
+
await this._echoEdgeReplicator?.connectToSpace(ctx, dataSpace.id);
|
|
615
582
|
} else if (this._echoEdgeReplicator) {
|
|
616
583
|
log('not connecting EchoEdgeReplicator because of EdgeReplicationSetting', { spaceId: dataSpace.id });
|
|
617
584
|
}
|
|
@@ -708,7 +675,7 @@ export class DataSpaceManager extends Resource {
|
|
|
708
675
|
invitations: Array<[PublicKey, DelegateSpaceInvitation]>,
|
|
709
676
|
): Promise<void> {
|
|
710
677
|
const tasks = invitations.map(([credentialId, invitation]) => {
|
|
711
|
-
return this._invitationsManager.createInvitation({
|
|
678
|
+
return this._invitationsManager.createInvitation(this._ctx, {
|
|
712
679
|
type: Invitation.Type.DELEGATED,
|
|
713
680
|
kind: Invitation.Kind.SPACE,
|
|
714
681
|
spaceKey: space.key,
|
|
@@ -24,7 +24,7 @@ import { failedInvariant, invariant } from '@dxos/invariant';
|
|
|
24
24
|
import { type Keyring } from '@dxos/keyring';
|
|
25
25
|
import { PublicKey } from '@dxos/keys';
|
|
26
26
|
import { log } from '@dxos/log';
|
|
27
|
-
import { CancelledError, SystemError } from '@dxos/protocols';
|
|
27
|
+
import { CancelledError, type FeedProtocol, SystemError } from '@dxos/protocols';
|
|
28
28
|
import {
|
|
29
29
|
type CreateEpochRequest,
|
|
30
30
|
type Space as SpaceProto,
|
|
@@ -37,6 +37,7 @@ import {
|
|
|
37
37
|
AdmittedFeed,
|
|
38
38
|
type Credential,
|
|
39
39
|
type Epoch,
|
|
40
|
+
MembershipPolicy,
|
|
40
41
|
type ProfileDocument,
|
|
41
42
|
SpaceMember,
|
|
42
43
|
} from '@dxos/protocols/proto/dxos/halo/credentials';
|
|
@@ -47,7 +48,6 @@ import { trace } from '@dxos/tracing';
|
|
|
47
48
|
import { type AsyncCallback, CallbackCollection, ComplexSet } from '@dxos/util';
|
|
48
49
|
|
|
49
50
|
import { TrustedKeySetAuthVerifier } from '../identity';
|
|
50
|
-
|
|
51
51
|
import { AutomergeSpaceState } from './automerge-space-state';
|
|
52
52
|
import { type SigningContext } from './data-space-manager';
|
|
53
53
|
import { EdgeFeedReplicator } from './edge-feed-replicator';
|
|
@@ -83,6 +83,7 @@ export type DataSpaceProps = {
|
|
|
83
83
|
signingContext: SigningContext;
|
|
84
84
|
callbacks?: DataSpaceCallbacks;
|
|
85
85
|
cache?: SpaceCache;
|
|
86
|
+
tags?: string[];
|
|
86
87
|
edgeConnection?: EdgeConnection;
|
|
87
88
|
edgeHttpClient?: EdgeHttpClient;
|
|
88
89
|
edgeFeatures?: Runtime.Client.EdgeFeatures;
|
|
@@ -120,6 +121,9 @@ export class DataSpace {
|
|
|
120
121
|
|
|
121
122
|
private _state = SpaceState.SPACE_CLOSED;
|
|
122
123
|
|
|
124
|
+
/** Immutable tags from space metadata, available immediately. */
|
|
125
|
+
readonly tags: string[];
|
|
126
|
+
|
|
123
127
|
private _databaseRoot: DatabaseRoot | null = null;
|
|
124
128
|
|
|
125
129
|
/**
|
|
@@ -167,6 +171,7 @@ export class DataSpace {
|
|
|
167
171
|
});
|
|
168
172
|
|
|
169
173
|
this._cache = params.cache;
|
|
174
|
+
this.tags = params.tags ?? [];
|
|
170
175
|
|
|
171
176
|
if (params.edgeConnection && params.edgeFeatures?.feedReplicator) {
|
|
172
177
|
this._edgeFeedReplicator = new EdgeFeedReplicator({ messenger: params.edgeConnection, spaceId: this.id });
|
|
@@ -212,6 +217,11 @@ export class DataSpace {
|
|
|
212
217
|
return this._cache;
|
|
213
218
|
}
|
|
214
219
|
|
|
220
|
+
/** Membership policy from the genesis credential, defaults to INVITE. */
|
|
221
|
+
get membershipPolicy(): MembershipPolicy {
|
|
222
|
+
return this._inner.spaceState.genesisCredential ? this._inner.spaceState.membershipPolicy : MembershipPolicy.INVITE;
|
|
223
|
+
}
|
|
224
|
+
|
|
215
225
|
get automergeSpaceState() {
|
|
216
226
|
return this._automergeSpaceState;
|
|
217
227
|
}
|
|
@@ -229,13 +239,14 @@ export class DataSpace {
|
|
|
229
239
|
}
|
|
230
240
|
|
|
231
241
|
@synchronized
|
|
232
|
-
|
|
242
|
+
@trace.span({ showInBrowserTimeline: true })
|
|
243
|
+
async open(ctx: Context): Promise<void> {
|
|
233
244
|
if (this._state === SpaceState.SPACE_CLOSED) {
|
|
234
|
-
await this._open();
|
|
245
|
+
await this._open(ctx);
|
|
235
246
|
}
|
|
236
247
|
}
|
|
237
248
|
|
|
238
|
-
private async _open(): Promise<void> {
|
|
249
|
+
private async _open(ctx: Context): Promise<void> {
|
|
239
250
|
await this._presence.open();
|
|
240
251
|
await this._gossip.open();
|
|
241
252
|
await this._notarizationPlugin.open();
|
|
@@ -247,8 +258,8 @@ export class DataSpace {
|
|
|
247
258
|
this.inner.protocol.feedAdded.append(this._onFeedAdded);
|
|
248
259
|
}
|
|
249
260
|
|
|
250
|
-
await this._inner.open(
|
|
251
|
-
await this._inner.startProtocol();
|
|
261
|
+
await this._inner.open(ctx);
|
|
262
|
+
await this._inner.startProtocol(ctx);
|
|
252
263
|
|
|
253
264
|
await this._edgeFeedReplicator?.open();
|
|
254
265
|
|
|
@@ -262,11 +273,12 @@ export class DataSpace {
|
|
|
262
273
|
}
|
|
263
274
|
|
|
264
275
|
@synchronized
|
|
265
|
-
|
|
266
|
-
|
|
276
|
+
@trace.span({ showInBrowserTimeline: true })
|
|
277
|
+
async close(ctx: Context): Promise<void> {
|
|
278
|
+
await this._close(ctx);
|
|
267
279
|
}
|
|
268
280
|
|
|
269
|
-
private async _close(): Promise<void> {
|
|
281
|
+
private async _close(ctx: Context): Promise<void> {
|
|
270
282
|
await this._callbacks.beforeClose?.();
|
|
271
283
|
|
|
272
284
|
await this.preClose.callSerial();
|
|
@@ -284,7 +296,7 @@ export class DataSpace {
|
|
|
284
296
|
|
|
285
297
|
await this.authVerifier.close();
|
|
286
298
|
|
|
287
|
-
await this._inner.close();
|
|
299
|
+
await this._inner.close(ctx);
|
|
288
300
|
await this._inner.spaceState.removeCredentialProcessor(this._automergeSpaceState);
|
|
289
301
|
await this._automergeSpaceState.close();
|
|
290
302
|
await this._inner.spaceState.removeCredentialProcessor(this._notarizationPlugin);
|
|
@@ -309,7 +321,7 @@ export class DataSpace {
|
|
|
309
321
|
scheduleTask(this._ctx, async () => {
|
|
310
322
|
try {
|
|
311
323
|
this.metrics.pipelineInitBegin = new Date();
|
|
312
|
-
await this.initializeDataPipeline();
|
|
324
|
+
await this.initializeDataPipeline(this._ctx);
|
|
313
325
|
} catch (err) {
|
|
314
326
|
if (err instanceof CancelledError || err instanceof ContextDisposedError) {
|
|
315
327
|
log('data pipeline initialization cancelled', err);
|
|
@@ -328,7 +340,7 @@ export class DataSpace {
|
|
|
328
340
|
}
|
|
329
341
|
|
|
330
342
|
@trace.span({ showInBrowserTimeline: true })
|
|
331
|
-
async initializeDataPipeline(): Promise<void> {
|
|
343
|
+
async initializeDataPipeline(ctx: Context): Promise<void> {
|
|
332
344
|
if (this._state !== SpaceState.SPACE_CONTROL_ONLY) {
|
|
333
345
|
throw new SystemError({ message: 'Invalid operation' });
|
|
334
346
|
}
|
|
@@ -337,7 +349,7 @@ export class DataSpace {
|
|
|
337
349
|
log('new state', { state: SpaceState[this._state] });
|
|
338
350
|
|
|
339
351
|
log('initializing control pipeline');
|
|
340
|
-
await this._initializeAndReadControlPipeline();
|
|
352
|
+
await this._initializeAndReadControlPipeline(ctx);
|
|
341
353
|
|
|
342
354
|
// Allow other tasks to run before loading the data pipeline.
|
|
343
355
|
await sleep(1);
|
|
@@ -360,11 +372,19 @@ export class DataSpace {
|
|
|
360
372
|
yield [this._databaseRoot.documentId, root];
|
|
361
373
|
|
|
362
374
|
for (const documentUrl of this._databaseRoot.getAllLinkedDocuments()) {
|
|
363
|
-
const data = await this._echoHost.exportDoc(
|
|
375
|
+
const data = await this._echoHost.exportDoc(documentUrl);
|
|
364
376
|
yield [documentUrl.replace(/^automerge:/, ''), data];
|
|
365
377
|
}
|
|
366
378
|
}
|
|
367
379
|
|
|
380
|
+
/**
|
|
381
|
+
* Get all feeds and their blocks for this space.
|
|
382
|
+
* Used for space archive export.
|
|
383
|
+
*/
|
|
384
|
+
async getAllFeeds(): Promise<Array<{ feedId: string; feedNamespace: string; blocks: FeedProtocol.Block[] }>> {
|
|
385
|
+
return this._echoHost.getAllFeedsForSpace(this.id);
|
|
386
|
+
}
|
|
387
|
+
|
|
368
388
|
private async _enterReadyState(): Promise<void> {
|
|
369
389
|
await this._callbacks.beforeReady?.();
|
|
370
390
|
|
|
@@ -376,9 +396,9 @@ export class DataSpace {
|
|
|
376
396
|
}
|
|
377
397
|
|
|
378
398
|
@trace.span({ showInBrowserTimeline: true })
|
|
379
|
-
private async _initializeAndReadControlPipeline(): Promise<void> {
|
|
399
|
+
private async _initializeAndReadControlPipeline(ctx: Context): Promise<void> {
|
|
380
400
|
await this._inner.controlPipeline.state.waitUntilReachedTargetTimeframe({
|
|
381
|
-
ctx
|
|
401
|
+
ctx,
|
|
382
402
|
timeout: 10_000,
|
|
383
403
|
breakOnStall: false,
|
|
384
404
|
});
|
|
@@ -450,6 +470,9 @@ export class DataSpace {
|
|
|
450
470
|
|
|
451
471
|
log('credentials notarized');
|
|
452
472
|
} catch (err) {
|
|
473
|
+
if (err instanceof ContextDisposedError) {
|
|
474
|
+
return;
|
|
475
|
+
}
|
|
453
476
|
log.error('error notarizing credentials for feed admission', err);
|
|
454
477
|
throw err;
|
|
455
478
|
}
|
|
@@ -470,7 +493,7 @@ export class DataSpace {
|
|
|
470
493
|
await warnAfterTimeout(5_000, 'Automerge root doc load timeout (DataSpace)', async () => {
|
|
471
494
|
handle = await cancelWithContext(
|
|
472
495
|
this._ctx,
|
|
473
|
-
this._echoHost.loadDoc<DatabaseDirectory>(
|
|
496
|
+
this._echoHost.loadDoc<DatabaseDirectory>(this._ctx, rootUrl as AutomergeUrl, {
|
|
474
497
|
fetchFromNetwork: true,
|
|
475
498
|
}),
|
|
476
499
|
);
|
|
@@ -493,7 +516,7 @@ export class DataSpace {
|
|
|
493
516
|
|
|
494
517
|
// TODO(dmaretskyi): Close roots.
|
|
495
518
|
// TODO(dmaretskyi): How do we handle changing to the next EPOCH?
|
|
496
|
-
const root = await this._echoHost.openSpaceRoot(this.id, handle.url);
|
|
519
|
+
const root = await this._echoHost.openSpaceRoot(this._ctx, this.id, handle.url);
|
|
497
520
|
|
|
498
521
|
// NOTE: Make sure this assignment happens synchronously together with the state change.
|
|
499
522
|
this._databaseRoot = root;
|
|
@@ -570,25 +593,24 @@ export class DataSpace {
|
|
|
570
593
|
}
|
|
571
594
|
|
|
572
595
|
@synchronized
|
|
573
|
-
async activate(): Promise<void> {
|
|
596
|
+
async activate(ctx: Context): Promise<void> {
|
|
574
597
|
if (![SpaceState.SPACE_CLOSED, SpaceState.SPACE_INACTIVE].includes(this._state)) {
|
|
575
598
|
return;
|
|
576
599
|
}
|
|
577
600
|
|
|
578
601
|
await this._metadataStore.setSpaceState(this.key, SpaceState.SPACE_ACTIVE);
|
|
579
|
-
await this._open();
|
|
602
|
+
await this._open(ctx);
|
|
580
603
|
this.initializeDataPipelineAsync();
|
|
581
604
|
}
|
|
582
605
|
|
|
583
606
|
@synchronized
|
|
584
|
-
async deactivate(): Promise<void> {
|
|
607
|
+
async deactivate(ctx: Context): Promise<void> {
|
|
585
608
|
if (this._state === SpaceState.SPACE_INACTIVE) {
|
|
586
609
|
return;
|
|
587
610
|
}
|
|
588
|
-
// Unregister from data service.
|
|
589
611
|
await this._metadataStore.setSpaceState(this.key, SpaceState.SPACE_INACTIVE);
|
|
590
612
|
if (this._state !== SpaceState.SPACE_CLOSED) {
|
|
591
|
-
await this._close();
|
|
613
|
+
await this._close(ctx);
|
|
592
614
|
}
|
|
593
615
|
this._state = SpaceState.SPACE_INACTIVE;
|
|
594
616
|
log('new state', { state: SpaceState[this._state] });
|
|
@@ -107,7 +107,7 @@ describe('EdgeFeedReplicator', () => {
|
|
|
107
107
|
const { feed } = await attachReplicator(messenger);
|
|
108
108
|
await appendMessage(feed);
|
|
109
109
|
|
|
110
|
-
sendSpy.mockImplementationOnce(async (request: any) => {
|
|
110
|
+
sendSpy.mockImplementationOnce(async (_ctx: any, request: any) => {
|
|
111
111
|
sendResponseMessage(request, encodeCbor({ type: 'metadata', feedKey: feed.key.toHex(), length: 0 }));
|
|
112
112
|
return Promise.resolve();
|
|
113
113
|
});
|