@dxos/client-services 0.8.4-main.72ec0f3 → 0.8.4-main.74a063c4e0
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-HJH6BNTN.mjs → chunk-3LSLNVKQ.mjs} +2102 -1870
- package/dist/lib/browser/chunk-3LSLNVKQ.mjs.map +7 -0
- package/dist/lib/browser/chunk-NQSC7HOE.mjs +22 -0
- package/dist/lib/browser/chunk-NQSC7HOE.mjs.map +7 -0
- package/dist/lib/browser/chunk-QCWEHHJW.mjs +24 -0
- package/dist/lib/browser/chunk-QCWEHHJW.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +451 -67
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/packlets/diagnostics/browser-diagnostics-broadcast.mjs +93 -0
- package/dist/lib/browser/packlets/diagnostics/browser-diagnostics-broadcast.mjs.map +7 -0
- package/dist/lib/browser/packlets/diagnostics/diagnostics-broadcast.mjs +11 -0
- package/dist/lib/browser/packlets/diagnostics/diagnostics-broadcast.mjs.map +7 -0
- package/dist/lib/browser/packlets/locks/browser.mjs +126 -0
- package/dist/lib/browser/packlets/locks/browser.mjs.map +7 -0
- package/dist/lib/browser/packlets/locks/node.mjs +66 -0
- package/dist/lib/browser/packlets/locks/node.mjs.map +7 -0
- package/dist/lib/browser/testing/index.mjs +36 -17
- package/dist/lib/browser/testing/index.mjs.map +3 -3
- package/dist/lib/node-esm/chunk-2SZHAWBN.mjs +24 -0
- package/dist/lib/node-esm/chunk-2SZHAWBN.mjs.map +7 -0
- package/dist/lib/node-esm/{chunk-ONQM6RQH.mjs → chunk-5S7PIHLS.mjs} +1942 -1579
- package/dist/lib/node-esm/chunk-5S7PIHLS.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-PKEGMOQ4.mjs +22 -0
- package/dist/lib/node-esm/chunk-PKEGMOQ4.mjs.map +7 -0
- package/dist/lib/node-esm/index.mjs +451 -67
- package/dist/lib/node-esm/index.mjs.map +4 -4
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/lib/node-esm/packlets/diagnostics/browser-diagnostics-broadcast.mjs +93 -0
- package/dist/lib/node-esm/packlets/diagnostics/browser-diagnostics-broadcast.mjs.map +7 -0
- package/dist/lib/node-esm/packlets/diagnostics/diagnostics-broadcast.mjs +11 -0
- package/dist/lib/node-esm/packlets/diagnostics/diagnostics-broadcast.mjs.map +7 -0
- package/dist/lib/node-esm/packlets/locks/browser.mjs +126 -0
- package/dist/lib/node-esm/packlets/locks/browser.mjs.map +7 -0
- package/dist/lib/node-esm/packlets/locks/node.mjs +66 -0
- package/dist/lib/node-esm/packlets/locks/node.mjs.map +7 -0
- package/dist/lib/node-esm/testing/index.mjs +36 -17
- 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 +2 -2
- package/dist/types/src/packlets/devtools/devtools.d.ts.map +1 -1
- package/dist/types/src/packlets/diagnostics/index.d.ts +1 -1
- package/dist/types/src/packlets/diagnostics/index.d.ts.map +1 -1
- package/dist/types/src/packlets/identity/authenticator.d.ts +2 -2
- package/dist/types/src/packlets/identity/authenticator.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 +6 -6
- package/dist/types/src/packlets/identity/identity-manager.d.ts.map +1 -1
- package/dist/types/src/packlets/identity/identity-recovery-manager.d.ts +7 -6
- 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 +8 -11
- package/dist/types/src/packlets/identity/identity.d.ts.map +1 -1
- package/dist/types/src/packlets/invitations/device-invitation-protocol.d.ts +4 -4
- 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/invitation-guest-extenstion.d.ts.map +1 -1
- package/dist/types/src/packlets/invitations/invitation-host-extension.d.ts.map +1 -1
- package/dist/types/src/packlets/invitations/invitation-protocol.d.ts +2 -3
- package/dist/types/src/packlets/invitations/invitation-protocol.d.ts.map +1 -1
- package/dist/types/src/packlets/invitations/invitations-handler.d.ts +4 -4
- package/dist/types/src/packlets/invitations/invitations-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 +2 -2
- package/dist/types/src/packlets/invitations/space-invitation-protocol.d.ts.map +1 -1
- package/dist/types/src/packlets/locks/index.d.ts +1 -1
- package/dist/types/src/packlets/locks/index.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/client-rpc-server.d.ts +2 -2
- package/dist/types/src/packlets/services/client-rpc-server.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 +13 -8
- package/dist/types/src/packlets/services/service-context.d.ts.map +1 -1
- package/dist/types/src/packlets/services/service-host.d.ts +20 -6
- 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 +27 -15
- package/dist/types/src/packlets/spaces/data-space-manager.d.ts.map +1 -1
- package/dist/types/src/packlets/spaces/data-space.d.ts +24 -8
- package/dist/types/src/packlets/spaces/data-space.d.ts.map +1 -1
- package/dist/types/src/packlets/spaces/edge-feed-replicator.d.ts +2 -2
- 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 +6 -6
- 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 +6 -3
- package/dist/types/src/packlets/testing/invitation-utils.d.ts.map +1 -1
- package/dist/types/src/packlets/testing/test-builder.d.ts +6 -5
- package/dist/types/src/packlets/testing/test-builder.d.ts.map +1 -1
- package/dist/types/src/packlets/worker/worker-runtime.d.ts +31 -4
- package/dist/types/src/packlets/worker/worker-runtime.d.ts.map +1 -1
- package/dist/types/src/packlets/worker/worker-session.d.ts +2 -2
- package/dist/types/src/packlets/worker/worker-session.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 +70 -48
- 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 +2 -3
- package/src/packlets/diagnostics/index.ts +1 -1
- package/src/packlets/identity/authenticator.ts +2 -2
- 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 +21 -18
- package/src/packlets/identity/identity-recovery-manager.ts +22 -18
- 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 +9 -32
- package/src/packlets/invitations/device-invitation-protocol.ts +5 -6
- package/src/packlets/invitations/edge-invitation-handler.ts +4 -3
- package/src/packlets/invitations/invitation-guest-extenstion.ts +6 -4
- package/src/packlets/invitations/invitation-host-extension.ts +6 -4
- package/src/packlets/invitations/invitation-protocol.ts +2 -3
- package/src/packlets/invitations/invitations-handler.test.ts +4 -5
- package/src/packlets/invitations/invitations-handler.ts +10 -10
- 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 +10 -15
- package/src/packlets/locks/index.ts +1 -1
- 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/client-rpc-server.ts +4 -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 +129 -44
- package/src/packlets/services/service-host.test.ts +8 -8
- package/src/packlets/services/service-host.ts +63 -22
- 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 +39 -2
- 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 +97 -107
- package/src/packlets/spaces/data-space.ts +52 -29
- package/src/packlets/spaces/edge-feed-replicator.test.ts +1 -1
- package/src/packlets/spaces/edge-feed-replicator.ts +10 -9
- package/src/packlets/spaces/epoch-migrations.ts +5 -5
- package/src/packlets/spaces/genesis.ts +6 -1
- package/src/packlets/spaces/notarization-plugin.test.ts +2 -2
- package/src/packlets/spaces/notarization-plugin.ts +10 -9
- package/src/packlets/spaces/spaces-service.test.ts +9 -7
- package/src/packlets/spaces/spaces-service.ts +40 -16
- package/src/packlets/storage/storage.ts +4 -4
- package/src/packlets/testing/invitation-utils.ts +10 -6
- package/src/packlets/testing/test-builder.ts +36 -10
- package/src/packlets/worker/worker-runtime.ts +150 -13
- package/src/packlets/worker/worker-session.ts +8 -8
- package/src/version.ts +1 -1
- package/dist/lib/browser/chunk-HJH6BNTN.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-ONQM6RQH.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,7 +3,7 @@
|
|
|
3
3
|
//
|
|
4
4
|
|
|
5
5
|
import { save } from '@automerge/automerge';
|
|
6
|
-
import { type DocHandle } from '@automerge/automerge-repo';
|
|
6
|
+
import { type AutomergeUrl, type DocHandle } from '@automerge/automerge-repo';
|
|
7
7
|
|
|
8
8
|
import { Event, Mutex, scheduleTask, sleep, synchronized, trackLeaks } from '@dxos/async';
|
|
9
9
|
import { AUTH_TIMEOUT } from '@dxos/client-protocol';
|
|
@@ -13,7 +13,6 @@ import { timed, warnAfterTimeout } from '@dxos/debug';
|
|
|
13
13
|
import {
|
|
14
14
|
type DatabaseRoot,
|
|
15
15
|
type EchoHost,
|
|
16
|
-
FIND_PARAMS,
|
|
17
16
|
type MetadataStore,
|
|
18
17
|
type Space,
|
|
19
18
|
createMappedFeedWriter,
|
|
@@ -25,7 +24,7 @@ import { failedInvariant, invariant } from '@dxos/invariant';
|
|
|
25
24
|
import { type Keyring } from '@dxos/keyring';
|
|
26
25
|
import { PublicKey } from '@dxos/keys';
|
|
27
26
|
import { log } from '@dxos/log';
|
|
28
|
-
import { CancelledError, SystemError } from '@dxos/protocols';
|
|
27
|
+
import { CancelledError, type FeedProtocol, SystemError } from '@dxos/protocols';
|
|
29
28
|
import {
|
|
30
29
|
type CreateEpochRequest,
|
|
31
30
|
type Space as SpaceProto,
|
|
@@ -38,6 +37,7 @@ import {
|
|
|
38
37
|
AdmittedFeed,
|
|
39
38
|
type Credential,
|
|
40
39
|
type Epoch,
|
|
40
|
+
MembershipPolicy,
|
|
41
41
|
type ProfileDocument,
|
|
42
42
|
SpaceMember,
|
|
43
43
|
} from '@dxos/protocols/proto/dxos/halo/credentials';
|
|
@@ -48,7 +48,6 @@ import { trace } from '@dxos/tracing';
|
|
|
48
48
|
import { type AsyncCallback, CallbackCollection, ComplexSet } from '@dxos/util';
|
|
49
49
|
|
|
50
50
|
import { TrustedKeySetAuthVerifier } from '../identity';
|
|
51
|
-
|
|
52
51
|
import { AutomergeSpaceState } from './automerge-space-state';
|
|
53
52
|
import { type SigningContext } from './data-space-manager';
|
|
54
53
|
import { EdgeFeedReplicator } from './edge-feed-replicator';
|
|
@@ -72,7 +71,7 @@ export type DataSpaceCallbacks = {
|
|
|
72
71
|
beforeClose?: () => Promise<void>;
|
|
73
72
|
};
|
|
74
73
|
|
|
75
|
-
export type
|
|
74
|
+
export type DataSpaceProps = {
|
|
76
75
|
initialState: SpaceState;
|
|
77
76
|
inner: Space;
|
|
78
77
|
metadataStore: MetadataStore;
|
|
@@ -84,6 +83,7 @@ export type DataSpaceParams = {
|
|
|
84
83
|
signingContext: SigningContext;
|
|
85
84
|
callbacks?: DataSpaceCallbacks;
|
|
86
85
|
cache?: SpaceCache;
|
|
86
|
+
tags?: string[];
|
|
87
87
|
edgeConnection?: EdgeConnection;
|
|
88
88
|
edgeHttpClient?: EdgeHttpClient;
|
|
89
89
|
edgeFeatures?: Runtime.Client.EdgeFeatures;
|
|
@@ -121,6 +121,9 @@ export class DataSpace {
|
|
|
121
121
|
|
|
122
122
|
private _state = SpaceState.SPACE_CLOSED;
|
|
123
123
|
|
|
124
|
+
/** Immutable tags from space metadata, available immediately. */
|
|
125
|
+
readonly tags: string[];
|
|
126
|
+
|
|
124
127
|
private _databaseRoot: DatabaseRoot | null = null;
|
|
125
128
|
|
|
126
129
|
/**
|
|
@@ -136,7 +139,7 @@ export class DataSpace {
|
|
|
136
139
|
|
|
137
140
|
public metrics: SpaceProto.Metrics = {};
|
|
138
141
|
|
|
139
|
-
constructor(params:
|
|
142
|
+
constructor(params: DataSpaceProps) {
|
|
140
143
|
this._inner = params.inner;
|
|
141
144
|
this._inner.stateUpdate.on(this._ctx, () => this.stateUpdate.emit());
|
|
142
145
|
|
|
@@ -168,6 +171,7 @@ export class DataSpace {
|
|
|
168
171
|
});
|
|
169
172
|
|
|
170
173
|
this._cache = params.cache;
|
|
174
|
+
this.tags = params.tags ?? [];
|
|
171
175
|
|
|
172
176
|
if (params.edgeConnection && params.edgeFeatures?.feedReplicator) {
|
|
173
177
|
this._edgeFeedReplicator = new EdgeFeedReplicator({ messenger: params.edgeConnection, spaceId: this.id });
|
|
@@ -213,6 +217,11 @@ export class DataSpace {
|
|
|
213
217
|
return this._cache;
|
|
214
218
|
}
|
|
215
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
|
+
|
|
216
225
|
get automergeSpaceState() {
|
|
217
226
|
return this._automergeSpaceState;
|
|
218
227
|
}
|
|
@@ -230,13 +239,14 @@ export class DataSpace {
|
|
|
230
239
|
}
|
|
231
240
|
|
|
232
241
|
@synchronized
|
|
233
|
-
|
|
242
|
+
@trace.span({ showInBrowserTimeline: true })
|
|
243
|
+
async open(ctx: Context): Promise<void> {
|
|
234
244
|
if (this._state === SpaceState.SPACE_CLOSED) {
|
|
235
|
-
await this._open();
|
|
245
|
+
await this._open(ctx);
|
|
236
246
|
}
|
|
237
247
|
}
|
|
238
248
|
|
|
239
|
-
private async _open(): Promise<void> {
|
|
249
|
+
private async _open(ctx: Context): Promise<void> {
|
|
240
250
|
await this._presence.open();
|
|
241
251
|
await this._gossip.open();
|
|
242
252
|
await this._notarizationPlugin.open();
|
|
@@ -248,8 +258,8 @@ export class DataSpace {
|
|
|
248
258
|
this.inner.protocol.feedAdded.append(this._onFeedAdded);
|
|
249
259
|
}
|
|
250
260
|
|
|
251
|
-
await this._inner.open(
|
|
252
|
-
await this._inner.startProtocol();
|
|
261
|
+
await this._inner.open(ctx);
|
|
262
|
+
await this._inner.startProtocol(ctx);
|
|
253
263
|
|
|
254
264
|
await this._edgeFeedReplicator?.open();
|
|
255
265
|
|
|
@@ -263,11 +273,12 @@ export class DataSpace {
|
|
|
263
273
|
}
|
|
264
274
|
|
|
265
275
|
@synchronized
|
|
266
|
-
|
|
267
|
-
|
|
276
|
+
@trace.span({ showInBrowserTimeline: true })
|
|
277
|
+
async close(ctx: Context): Promise<void> {
|
|
278
|
+
await this._close(ctx);
|
|
268
279
|
}
|
|
269
280
|
|
|
270
|
-
private async _close(): Promise<void> {
|
|
281
|
+
private async _close(ctx: Context): Promise<void> {
|
|
271
282
|
await this._callbacks.beforeClose?.();
|
|
272
283
|
|
|
273
284
|
await this.preClose.callSerial();
|
|
@@ -285,7 +296,7 @@ export class DataSpace {
|
|
|
285
296
|
|
|
286
297
|
await this.authVerifier.close();
|
|
287
298
|
|
|
288
|
-
await this._inner.close();
|
|
299
|
+
await this._inner.close(ctx);
|
|
289
300
|
await this._inner.spaceState.removeCredentialProcessor(this._automergeSpaceState);
|
|
290
301
|
await this._automergeSpaceState.close();
|
|
291
302
|
await this._inner.spaceState.removeCredentialProcessor(this._notarizationPlugin);
|
|
@@ -310,7 +321,7 @@ export class DataSpace {
|
|
|
310
321
|
scheduleTask(this._ctx, async () => {
|
|
311
322
|
try {
|
|
312
323
|
this.metrics.pipelineInitBegin = new Date();
|
|
313
|
-
await this.initializeDataPipeline();
|
|
324
|
+
await this.initializeDataPipeline(this._ctx);
|
|
314
325
|
} catch (err) {
|
|
315
326
|
if (err instanceof CancelledError || err instanceof ContextDisposedError) {
|
|
316
327
|
log('data pipeline initialization cancelled', err);
|
|
@@ -329,16 +340,16 @@ export class DataSpace {
|
|
|
329
340
|
}
|
|
330
341
|
|
|
331
342
|
@trace.span({ showInBrowserTimeline: true })
|
|
332
|
-
async initializeDataPipeline(): Promise<void> {
|
|
343
|
+
async initializeDataPipeline(ctx: Context): Promise<void> {
|
|
333
344
|
if (this._state !== SpaceState.SPACE_CONTROL_ONLY) {
|
|
334
|
-
throw new SystemError('Invalid operation');
|
|
345
|
+
throw new SystemError({ message: 'Invalid operation' });
|
|
335
346
|
}
|
|
336
347
|
|
|
337
348
|
this._state = SpaceState.SPACE_INITIALIZING;
|
|
338
349
|
log('new state', { state: SpaceState[this._state] });
|
|
339
350
|
|
|
340
351
|
log('initializing control pipeline');
|
|
341
|
-
await this._initializeAndReadControlPipeline();
|
|
352
|
+
await this._initializeAndReadControlPipeline(ctx);
|
|
342
353
|
|
|
343
354
|
// Allow other tasks to run before loading the data pipeline.
|
|
344
355
|
await sleep(1);
|
|
@@ -361,11 +372,19 @@ export class DataSpace {
|
|
|
361
372
|
yield [this._databaseRoot.documentId, root];
|
|
362
373
|
|
|
363
374
|
for (const documentUrl of this._databaseRoot.getAllLinkedDocuments()) {
|
|
364
|
-
const data = await this._echoHost.exportDoc(
|
|
375
|
+
const data = await this._echoHost.exportDoc(documentUrl);
|
|
365
376
|
yield [documentUrl.replace(/^automerge:/, ''), data];
|
|
366
377
|
}
|
|
367
378
|
}
|
|
368
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
|
+
|
|
369
388
|
private async _enterReadyState(): Promise<void> {
|
|
370
389
|
await this._callbacks.beforeReady?.();
|
|
371
390
|
|
|
@@ -377,9 +396,9 @@ export class DataSpace {
|
|
|
377
396
|
}
|
|
378
397
|
|
|
379
398
|
@trace.span({ showInBrowserTimeline: true })
|
|
380
|
-
private async _initializeAndReadControlPipeline(): Promise<void> {
|
|
399
|
+
private async _initializeAndReadControlPipeline(ctx: Context): Promise<void> {
|
|
381
400
|
await this._inner.controlPipeline.state.waitUntilReachedTargetTimeframe({
|
|
382
|
-
ctx
|
|
401
|
+
ctx,
|
|
383
402
|
timeout: 10_000,
|
|
384
403
|
breakOnStall: false,
|
|
385
404
|
});
|
|
@@ -451,6 +470,9 @@ export class DataSpace {
|
|
|
451
470
|
|
|
452
471
|
log('credentials notarized');
|
|
453
472
|
} catch (err) {
|
|
473
|
+
if (err instanceof ContextDisposedError) {
|
|
474
|
+
return;
|
|
475
|
+
}
|
|
454
476
|
log.error('error notarizing credentials for feed admission', err);
|
|
455
477
|
throw err;
|
|
456
478
|
}
|
|
@@ -471,7 +493,9 @@ export class DataSpace {
|
|
|
471
493
|
await warnAfterTimeout(5_000, 'Automerge root doc load timeout (DataSpace)', async () => {
|
|
472
494
|
handle = await cancelWithContext(
|
|
473
495
|
this._ctx,
|
|
474
|
-
this._echoHost.
|
|
496
|
+
this._echoHost.loadDoc<DatabaseDirectory>(this._ctx, rootUrl as AutomergeUrl, {
|
|
497
|
+
fetchFromNetwork: true,
|
|
498
|
+
}),
|
|
475
499
|
);
|
|
476
500
|
await cancelWithContext(this._ctx, handle.whenReady());
|
|
477
501
|
});
|
|
@@ -492,7 +516,7 @@ export class DataSpace {
|
|
|
492
516
|
|
|
493
517
|
// TODO(dmaretskyi): Close roots.
|
|
494
518
|
// TODO(dmaretskyi): How do we handle changing to the next EPOCH?
|
|
495
|
-
const root = await this._echoHost.openSpaceRoot(this.id, handle.url);
|
|
519
|
+
const root = await this._echoHost.openSpaceRoot(this._ctx, this.id, handle.url);
|
|
496
520
|
|
|
497
521
|
// NOTE: Make sure this assignment happens synchronously together with the state change.
|
|
498
522
|
this._databaseRoot = root;
|
|
@@ -569,25 +593,24 @@ export class DataSpace {
|
|
|
569
593
|
}
|
|
570
594
|
|
|
571
595
|
@synchronized
|
|
572
|
-
async activate(): Promise<void> {
|
|
596
|
+
async activate(ctx: Context): Promise<void> {
|
|
573
597
|
if (![SpaceState.SPACE_CLOSED, SpaceState.SPACE_INACTIVE].includes(this._state)) {
|
|
574
598
|
return;
|
|
575
599
|
}
|
|
576
600
|
|
|
577
601
|
await this._metadataStore.setSpaceState(this.key, SpaceState.SPACE_ACTIVE);
|
|
578
|
-
await this._open();
|
|
602
|
+
await this._open(ctx);
|
|
579
603
|
this.initializeDataPipelineAsync();
|
|
580
604
|
}
|
|
581
605
|
|
|
582
606
|
@synchronized
|
|
583
|
-
async deactivate(): Promise<void> {
|
|
607
|
+
async deactivate(ctx: Context): Promise<void> {
|
|
584
608
|
if (this._state === SpaceState.SPACE_INACTIVE) {
|
|
585
609
|
return;
|
|
586
610
|
}
|
|
587
|
-
// Unregister from data service.
|
|
588
611
|
await this._metadataStore.setSpaceState(this.key, SpaceState.SPACE_INACTIVE);
|
|
589
612
|
if (this._state !== SpaceState.SPACE_CLOSED) {
|
|
590
|
-
await this._close();
|
|
613
|
+
await this._close(ctx);
|
|
591
614
|
}
|
|
592
615
|
this._state = SpaceState.SPACE_INACTIVE;
|
|
593
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
|
});
|
|
@@ -22,7 +22,7 @@ import type { FeedBlock, ProtocolMessage } from '@dxos/protocols/feed-replicatio
|
|
|
22
22
|
import { EdgeStatus } from '@dxos/protocols/proto/dxos/client/services';
|
|
23
23
|
import { ComplexMap, arrayToBuffer, bufferToArray, defaultMap, rangeFromTo } from '@dxos/util';
|
|
24
24
|
|
|
25
|
-
export type
|
|
25
|
+
export type EdgeFeedReplicatorProps = {
|
|
26
26
|
messenger: EdgeConnection;
|
|
27
27
|
spaceId: SpaceId;
|
|
28
28
|
};
|
|
@@ -47,7 +47,7 @@ export class EdgeFeedReplicator extends Resource {
|
|
|
47
47
|
*/
|
|
48
48
|
private _pushMutex = new ComplexMap<PublicKey, Mutex>(PublicKey.hash);
|
|
49
49
|
|
|
50
|
-
constructor({ messenger, spaceId }:
|
|
50
|
+
constructor({ messenger, spaceId }: EdgeFeedReplicatorProps) {
|
|
51
51
|
super();
|
|
52
52
|
this._messenger = messenger;
|
|
53
53
|
this._spaceId = spaceId;
|
|
@@ -132,7 +132,7 @@ export class EdgeFeedReplicator extends Resource {
|
|
|
132
132
|
|
|
133
133
|
private async _replicateFeed(ctx: Context, feed: FeedWrapper<any>): Promise<void> {
|
|
134
134
|
log('replicateFeed', { key: feed.key });
|
|
135
|
-
await this._sendMessage({
|
|
135
|
+
await this._sendMessage(ctx, {
|
|
136
136
|
type: 'get-metadata',
|
|
137
137
|
feedKey: feed.key.toHex(),
|
|
138
138
|
});
|
|
@@ -142,7 +142,7 @@ export class EdgeFeedReplicator extends Resource {
|
|
|
142
142
|
});
|
|
143
143
|
}
|
|
144
144
|
|
|
145
|
-
private async _sendMessage(message: ProtocolMessage): Promise<void> {
|
|
145
|
+
private async _sendMessage(ctx: Context, message: ProtocolMessage): Promise<void> {
|
|
146
146
|
if (!this._connectionCtx) {
|
|
147
147
|
log('message dropped because connection was disposed');
|
|
148
148
|
return;
|
|
@@ -160,6 +160,7 @@ export class EdgeFeedReplicator extends Resource {
|
|
|
160
160
|
|
|
161
161
|
log('send', { type: message.type });
|
|
162
162
|
await this._messenger.send(
|
|
163
|
+
ctx,
|
|
163
164
|
buf.create(RouterMessageSchema, {
|
|
164
165
|
source: {
|
|
165
166
|
identityKey: this._messenger.identityKey,
|
|
@@ -194,7 +195,7 @@ export class EdgeFeedReplicator extends Resource {
|
|
|
194
195
|
if (message.length > feed.length) {
|
|
195
196
|
log('requesting missing blocks', logMeta);
|
|
196
197
|
|
|
197
|
-
await this._sendMessage({
|
|
198
|
+
await this._sendMessage(this._connectionCtx!, {
|
|
198
199
|
type: 'request',
|
|
199
200
|
feedKey: feedKey.toHex(),
|
|
200
201
|
range: { from: feed.length, to: message.length },
|
|
@@ -202,7 +203,7 @@ export class EdgeFeedReplicator extends Resource {
|
|
|
202
203
|
} else if (message.length < feed.length) {
|
|
203
204
|
log('pushing blocks to remote', logMeta);
|
|
204
205
|
|
|
205
|
-
await this._pushBlocks(feed, message.length, feed.length);
|
|
206
|
+
await this._pushBlocks(this._connectionCtx!, feed, message.length, feed.length);
|
|
206
207
|
}
|
|
207
208
|
|
|
208
209
|
break;
|
|
@@ -229,7 +230,7 @@ export class EdgeFeedReplicator extends Resource {
|
|
|
229
230
|
});
|
|
230
231
|
}
|
|
231
232
|
|
|
232
|
-
private async _pushBlocks(feed: FeedWrapper<any>, from: number, to: number): Promise<void> {
|
|
233
|
+
private async _pushBlocks(ctx: Context, feed: FeedWrapper<any>, from: number, to: number): Promise<void> {
|
|
233
234
|
log('pushing blocks', { feed: feed.key.toHex(), from, to });
|
|
234
235
|
|
|
235
236
|
const blocks: FeedBlock[] = await Promise.all(
|
|
@@ -247,7 +248,7 @@ export class EdgeFeedReplicator extends Resource {
|
|
|
247
248
|
}),
|
|
248
249
|
);
|
|
249
250
|
|
|
250
|
-
await this._sendMessage({
|
|
251
|
+
await this._sendMessage(ctx, {
|
|
251
252
|
type: 'data',
|
|
252
253
|
feedKey: feed.key.toHex(),
|
|
253
254
|
blocks,
|
|
@@ -283,7 +284,7 @@ export class EdgeFeedReplicator extends Resource {
|
|
|
283
284
|
|
|
284
285
|
const remoteLength = this._remoteLength.get(feed.key)!;
|
|
285
286
|
if (remoteLength < feed.length) {
|
|
286
|
-
await this._pushBlocks(feed, remoteLength, feed.length);
|
|
287
|
+
await this._pushBlocks(this._connectionCtx!, feed, remoteLength, feed.length);
|
|
287
288
|
}
|
|
288
289
|
}
|
|
289
290
|
|
|
@@ -36,8 +36,8 @@ const LOAD_DOC_TIMEOUT = 10_000;
|
|
|
36
36
|
export const runEpochMigration = async (ctx: Context, context: MigrationContext): Promise<MigrationResult> => {
|
|
37
37
|
switch (context.migration) {
|
|
38
38
|
case CreateEpochRequest.Migration.INIT_AUTOMERGE: {
|
|
39
|
-
const document = context.echoHost.createDoc();
|
|
40
|
-
await context.echoHost.flush();
|
|
39
|
+
const document = await context.echoHost.createDoc();
|
|
40
|
+
await context.echoHost.flush(ctx);
|
|
41
41
|
return { newRoot: document.url };
|
|
42
42
|
}
|
|
43
43
|
case CreateEpochRequest.Migration.PRUNE_AUTOMERGE_ROOT_HISTORY: {
|
|
@@ -48,8 +48,8 @@ export const runEpochMigration = async (ctx: Context, context: MigrationContext)
|
|
|
48
48
|
timeout: LOAD_DOC_TIMEOUT,
|
|
49
49
|
});
|
|
50
50
|
|
|
51
|
-
const newRoot = context.echoHost.createDoc(rootHandle.doc());
|
|
52
|
-
await context.echoHost.flush();
|
|
51
|
+
const newRoot = await context.echoHost.createDoc(rootHandle.doc());
|
|
52
|
+
await context.echoHost.flush(ctx);
|
|
53
53
|
return { newRoot: newRoot.url };
|
|
54
54
|
}
|
|
55
55
|
case CreateEpochRequest.Migration.FRAGMENT_AUTOMERGE_ROOT: {
|
|
@@ -63,7 +63,7 @@ export const runEpochMigration = async (ctx: Context, context: MigrationContext)
|
|
|
63
63
|
invariant(context.newAutomergeRoot);
|
|
64
64
|
|
|
65
65
|
// Defensive programming - it should be the responsibility of the caller to flush the new root.
|
|
66
|
-
await context.echoHost.flush();
|
|
66
|
+
await context.echoHost.flush(ctx);
|
|
67
67
|
return {
|
|
68
68
|
newRoot: context.newAutomergeRoot,
|
|
69
69
|
};
|
|
@@ -6,7 +6,7 @@ import { createCredential } from '@dxos/credentials';
|
|
|
6
6
|
import { failUndefined } from '@dxos/debug';
|
|
7
7
|
import { type Space } from '@dxos/echo-pipeline';
|
|
8
8
|
import { type Keyring } from '@dxos/keyring';
|
|
9
|
-
import { AdmittedFeed, SpaceMember } from '@dxos/protocols/proto/dxos/halo/credentials';
|
|
9
|
+
import { AdmittedFeed, MembershipPolicy, SpaceMember } from '@dxos/protocols/proto/dxos/halo/credentials';
|
|
10
10
|
import { Timeframe } from '@dxos/timeframe';
|
|
11
11
|
|
|
12
12
|
import { type SigningContext } from './data-space-manager';
|
|
@@ -16,6 +16,8 @@ export const spaceGenesis = async (
|
|
|
16
16
|
signingContext: SigningContext,
|
|
17
17
|
space: Space,
|
|
18
18
|
automergeRoot?: string,
|
|
19
|
+
tags?: string[],
|
|
20
|
+
membershipPolicy?: MembershipPolicy,
|
|
19
21
|
) => {
|
|
20
22
|
// TODO(dmaretskyi): Find a way to reconcile with credential generator.
|
|
21
23
|
const credentials = [
|
|
@@ -26,6 +28,8 @@ export const spaceGenesis = async (
|
|
|
26
28
|
assertion: {
|
|
27
29
|
'@type': 'dxos.halo.credentials.SpaceGenesis',
|
|
28
30
|
spaceKey: space.key,
|
|
31
|
+
tags: tags ?? [],
|
|
32
|
+
membershipPolicy: membershipPolicy ?? MembershipPolicy.INVITE,
|
|
29
33
|
},
|
|
30
34
|
}),
|
|
31
35
|
|
|
@@ -39,6 +43,7 @@ export const spaceGenesis = async (
|
|
|
39
43
|
role: SpaceMember.Role.OWNER,
|
|
40
44
|
profile: signingContext.getProfile(),
|
|
41
45
|
genesisFeedKey: space.controlFeedKey ?? failUndefined(),
|
|
46
|
+
tags: tags ?? [],
|
|
42
47
|
},
|
|
43
48
|
}),
|
|
44
49
|
|
|
@@ -13,7 +13,7 @@ import { log } from '@dxos/log';
|
|
|
13
13
|
import { AdmittedFeed, type Credential } from '@dxos/protocols/proto/dxos/halo/credentials';
|
|
14
14
|
import { TestBuilder, type TestConnection, TestPeer } from '@dxos/teleport/testing';
|
|
15
15
|
|
|
16
|
-
import { NotarizationPlugin, type
|
|
16
|
+
import { NotarizationPlugin, type NotarizationPluginProps } from './notarization-plugin';
|
|
17
17
|
|
|
18
18
|
class TestAgent extends TestPeer {
|
|
19
19
|
private readonly _ctx = new Context();
|
|
@@ -21,7 +21,7 @@ class TestAgent extends TestPeer {
|
|
|
21
21
|
feed = new MockFeedWriter<Credential>();
|
|
22
22
|
notarizationPlugin: NotarizationPlugin;
|
|
23
23
|
|
|
24
|
-
constructor(params:
|
|
24
|
+
constructor(params: NotarizationPluginProps) {
|
|
25
25
|
super();
|
|
26
26
|
this.notarizationPlugin = new NotarizationPlugin(params);
|
|
27
27
|
this.feed.written.on(this._ctx, async ([credential]) => {
|
|
@@ -33,14 +33,14 @@ const WRITER_NOT_SET_ERROR_CODE = 'WRITER_NOT_SET';
|
|
|
33
33
|
|
|
34
34
|
const credentialCodec = schema.getCodecForType('dxos.halo.credentials.Credential');
|
|
35
35
|
|
|
36
|
-
export type
|
|
36
|
+
export type NotarizationPluginProps = {
|
|
37
37
|
spaceId: SpaceId;
|
|
38
38
|
edgeClient?: EdgeHttpClient;
|
|
39
39
|
edgeFeatures?: Runtime.Client.EdgeFeatures;
|
|
40
40
|
activeEdgePollingInterval?: number;
|
|
41
41
|
};
|
|
42
42
|
|
|
43
|
-
export type
|
|
43
|
+
export type NotarizeProps = {
|
|
44
44
|
/**
|
|
45
45
|
* For cancellation.
|
|
46
46
|
*/
|
|
@@ -97,7 +97,7 @@ export class NotarizationPlugin extends Resource implements CredentialProcessor
|
|
|
97
97
|
|
|
98
98
|
private readonly _edgeClient: EdgeHttpClient | undefined;
|
|
99
99
|
|
|
100
|
-
constructor(params:
|
|
100
|
+
constructor(params: NotarizationPluginProps) {
|
|
101
101
|
super();
|
|
102
102
|
this._spaceId = params.spaceId;
|
|
103
103
|
this._activeEdgePollingInterval = params.activeEdgePollingInterval ?? DEFAULT_ACTIVE_EDGE_POLLING_INTERVAL;
|
|
@@ -149,7 +149,7 @@ export class NotarizationPlugin extends Resource implements CredentialProcessor
|
|
|
149
149
|
retryTimeout = DEFAULT_RETRY_TIMEOUT,
|
|
150
150
|
successDelay = DEFAULT_SUCCESS_DELAY,
|
|
151
151
|
edgeRetryJitter,
|
|
152
|
-
}:
|
|
152
|
+
}: NotarizeProps): Promise<void> {
|
|
153
153
|
log('notarize', { credentials });
|
|
154
154
|
invariant(
|
|
155
155
|
credentials.every((credential) => credential.id),
|
|
@@ -246,6 +246,7 @@ export class NotarizationPlugin extends Resource implements CredentialProcessor
|
|
|
246
246
|
scheduleTask(ctx, async () => {
|
|
247
247
|
try {
|
|
248
248
|
await client.notarizeCredentials(
|
|
249
|
+
ctx,
|
|
249
250
|
this._spaceId,
|
|
250
251
|
{ credentials: encodedCredentials },
|
|
251
252
|
{ retry: { count: MAX_EDGE_RETRIES, timeout: timeouts.retryTimeout, jitter: timeouts.jitter } },
|
|
@@ -302,7 +303,7 @@ export class NotarizationPlugin extends Resource implements CredentialProcessor
|
|
|
302
303
|
private _notarizePendingEdgeCredentials(client: EdgeHttpClient, writer: FeedWriter<Credential>): void {
|
|
303
304
|
scheduleMicroTask(this._ctx, async () => {
|
|
304
305
|
try {
|
|
305
|
-
const response = await client.getCredentialsForNotarization(this._spaceId, {
|
|
306
|
+
const response = await client.getCredentialsForNotarization(this._ctx, this._spaceId, {
|
|
306
307
|
retry: { count: MAX_EDGE_RETRIES },
|
|
307
308
|
});
|
|
308
309
|
|
|
@@ -392,21 +393,21 @@ export class NotarizationPlugin extends Resource implements CredentialProcessor
|
|
|
392
393
|
}
|
|
393
394
|
|
|
394
395
|
const handleEdgeError = (error: any) => {
|
|
395
|
-
if (!(error instanceof EdgeCallFailedError) || error.
|
|
396
|
+
if (!(error instanceof EdgeCallFailedError) || error.data) {
|
|
396
397
|
log.catch(error);
|
|
397
398
|
} else {
|
|
398
|
-
log.info('Edge notarization failure', {
|
|
399
|
+
log.info('Edge notarization failure', { message: error.message });
|
|
399
400
|
}
|
|
400
401
|
};
|
|
401
402
|
|
|
402
|
-
export type
|
|
403
|
+
export type NotarizationTeleportExtensionProps = {
|
|
403
404
|
onOpen: () => Promise<void>;
|
|
404
405
|
onClose: () => Promise<void>;
|
|
405
406
|
onNotarize: (request: NotarizeRequest) => Promise<void>;
|
|
406
407
|
};
|
|
407
408
|
|
|
408
409
|
export class NotarizationTeleportExtension extends RpcExtension<Services, Services> {
|
|
409
|
-
constructor(private readonly _params:
|
|
410
|
+
constructor(private readonly _params: NotarizationTeleportExtensionProps) {
|
|
410
411
|
super({
|
|
411
412
|
requested: {
|
|
412
413
|
NotarizationService: schema.getService('dxos.mesh.teleport.notarization.NotarizationService'),
|
|
@@ -8,10 +8,10 @@ import { Trigger } from '@dxos/async';
|
|
|
8
8
|
import { Context } from '@dxos/context';
|
|
9
9
|
import { PublicKey } from '@dxos/keys';
|
|
10
10
|
import { type Space, type SpacesService } from '@dxos/protocols/proto/dxos/client/services';
|
|
11
|
+
import { MembershipPolicy } from '@dxos/protocols/proto/dxos/halo/credentials';
|
|
11
12
|
|
|
12
13
|
import { type ServiceContext } from '../services';
|
|
13
14
|
import { createServiceContext } from '../testing';
|
|
14
|
-
|
|
15
15
|
import { SpacesServiceImpl } from './spaces-service';
|
|
16
16
|
|
|
17
17
|
describe('SpacesService', () => {
|
|
@@ -33,12 +33,14 @@ describe('SpacesService', () => {
|
|
|
33
33
|
|
|
34
34
|
describe('createSpace', () => {
|
|
35
35
|
test('fails if no identity is available', async () => {
|
|
36
|
-
await expect(spacesService.createSpace()).rejects.toBeInstanceOf(
|
|
36
|
+
await expect(spacesService.createSpace({ membershipPolicy: MembershipPolicy.INVITE })).rejects.toBeInstanceOf(
|
|
37
|
+
Error,
|
|
38
|
+
);
|
|
37
39
|
});
|
|
38
40
|
|
|
39
41
|
test('creates a new space', async () => {
|
|
40
42
|
await serviceContext.createIdentity();
|
|
41
|
-
const space = await spacesService.createSpace();
|
|
43
|
+
const space = await spacesService.createSpace({ membershipPolicy: MembershipPolicy.INVITE });
|
|
42
44
|
expect(space).to.exist;
|
|
43
45
|
expect(space.spaceKey).to.be.instanceof(PublicKey);
|
|
44
46
|
});
|
|
@@ -60,9 +62,9 @@ describe('SpacesService', () => {
|
|
|
60
62
|
test('returns list of existing spaces', async () => {
|
|
61
63
|
await serviceContext.createIdentity();
|
|
62
64
|
const existingSpaces = [
|
|
63
|
-
await spacesService.createSpace(),
|
|
64
|
-
await spacesService.createSpace(),
|
|
65
|
-
await spacesService.createSpace(),
|
|
65
|
+
await spacesService.createSpace({ membershipPolicy: MembershipPolicy.INVITE }),
|
|
66
|
+
await spacesService.createSpace({ membershipPolicy: MembershipPolicy.INVITE }),
|
|
67
|
+
await spacesService.createSpace({ membershipPolicy: MembershipPolicy.INVITE }),
|
|
66
68
|
];
|
|
67
69
|
|
|
68
70
|
const query = spacesService.querySpaces();
|
|
@@ -88,7 +90,7 @@ describe('SpacesService', () => {
|
|
|
88
90
|
expect(await result.wait()).to.be.length(0);
|
|
89
91
|
|
|
90
92
|
result.reset();
|
|
91
|
-
const space = await spacesService.createSpace();
|
|
93
|
+
const space = await spacesService.createSpace({ membershipPolicy: MembershipPolicy.INVITE });
|
|
92
94
|
const spaces = await result.wait();
|
|
93
95
|
expect(spaces).to.be.length(1);
|
|
94
96
|
expect(spaces?.[0].spaceKey.equals(space.spaceKey)).to.be.true;
|