@dxos/client-services 0.1.30-next.7c60cd3 → 0.1.30
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-JLLUQQXD.mjs → chunk-DFO3FW2W.mjs} +949 -1150
- package/dist/lib/browser/chunk-DFO3FW2W.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +5 -41
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/packlets/testing/index.mjs +221 -50
- package/dist/lib/browser/packlets/testing/index.mjs.map +4 -4
- package/dist/lib/node/index.cjs +837 -1062
- package/dist/lib/node/index.cjs.map +4 -4
- package/dist/lib/node/meta.json +1 -1
- package/dist/lib/node/packlets/testing/index.cjs +2937 -2719
- package/dist/lib/node/packlets/testing/index.cjs.map +4 -4
- package/dist/types/src/packlets/invitations/device-invitations-handler.d.ts +1 -2
- package/dist/types/src/packlets/invitations/device-invitations-handler.d.ts.map +1 -1
- package/dist/types/src/packlets/invitations/device-invitations-service.d.ts +1 -1
- package/dist/types/src/packlets/invitations/device-invitations-service.d.ts.map +1 -1
- package/dist/types/src/packlets/invitations/index.d.ts +0 -6
- package/dist/types/src/packlets/invitations/index.d.ts.map +1 -1
- package/dist/types/src/packlets/invitations/invitations-service.d.ts +2 -3
- package/dist/types/src/packlets/invitations/invitations-service.d.ts.map +1 -1
- package/dist/types/src/packlets/invitations/space-invitations-handler.d.ts +2 -4
- package/dist/types/src/packlets/invitations/space-invitations-handler.d.ts.map +1 -1
- package/dist/types/src/packlets/invitations/space-invitations-service.d.ts +2 -3
- package/dist/types/src/packlets/invitations/space-invitations-service.d.ts.map +1 -1
- package/dist/types/src/packlets/services/client-rpc-server.d.ts +22 -0
- package/dist/types/src/packlets/services/client-rpc-server.d.ts.map +1 -0
- package/dist/types/src/packlets/services/index.d.ts +2 -3
- package/dist/types/src/packlets/services/index.d.ts.map +1 -1
- package/dist/types/src/packlets/services/local-client-services.d.ts +3 -2
- package/dist/types/src/packlets/services/local-client-services.d.ts.map +1 -1
- package/dist/types/src/packlets/services/service-context.d.ts +1 -1
- package/dist/types/src/packlets/services/service-context.d.ts.map +1 -1
- package/dist/types/src/packlets/services/service-host.d.ts +22 -8
- package/dist/types/src/packlets/services/service-host.d.ts.map +1 -1
- package/dist/types/src/packlets/services/service-registry.d.ts +3 -1
- package/dist/types/src/packlets/services/service-registry.d.ts.map +1 -1
- package/dist/types/src/packlets/services/utils.d.ts +7 -0
- package/dist/types/src/packlets/services/utils.d.ts.map +1 -0
- package/dist/types/src/packlets/spaces/data-space-manager.d.ts.map +1 -1
- package/dist/types/src/packlets/spaces/data-space.d.ts +8 -1
- package/dist/types/src/packlets/spaces/data-space.d.ts.map +1 -1
- package/dist/types/src/packlets/spaces/index.d.ts +2 -0
- package/dist/types/src/packlets/spaces/index.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 +4 -1
- package/dist/types/src/packlets/spaces/spaces-service.d.ts.map +1 -1
- package/dist/types/src/packlets/system/system-service.d.ts +2 -2
- package/dist/types/src/packlets/system/system-service.d.ts.map +1 -1
- package/dist/types/src/packlets/testing/host-test-builder.d.ts +43 -0
- package/dist/types/src/packlets/testing/host-test-builder.d.ts.map +1 -0
- package/dist/types/src/packlets/testing/index.d.ts +1 -0
- package/dist/types/src/packlets/testing/index.d.ts.map +1 -1
- package/dist/types/src/packlets/testing/invitation-utils.d.ts +1 -1
- package/dist/types/src/packlets/testing/invitation-utils.d.ts.map +1 -1
- package/dist/types/src/packlets/testing/test-builder.d.ts +32 -34
- package/dist/types/src/packlets/testing/test-builder.d.ts.map +1 -1
- package/dist/types/src/packlets/tests/client-services.test.d.ts +2 -0
- package/dist/types/src/packlets/tests/client-services.test.d.ts.map +1 -0
- package/dist/types/src/packlets/tests/client.test.d.ts +2 -0
- package/dist/types/src/packlets/tests/client.test.d.ts.map +1 -0
- package/dist/types/src/packlets/tests/halo-credentials.test.d.ts +2 -0
- package/dist/types/src/packlets/tests/halo-credentials.test.d.ts.map +1 -0
- package/dist/types/src/packlets/tests/halo-persistent.test.d.ts +2 -0
- package/dist/types/src/packlets/tests/halo-persistent.test.d.ts.map +1 -0
- package/dist/types/src/packlets/tests/halo-profile.test.d.ts +2 -0
- package/dist/types/src/packlets/tests/halo-profile.test.d.ts.map +1 -0
- package/dist/types/src/packlets/tests/shared-worker.test.d.ts +2 -0
- package/dist/types/src/packlets/tests/shared-worker.test.d.ts.map +1 -0
- package/dist/types/src/packlets/tests/spaces-invitations.test.d.ts +2 -0
- package/dist/types/src/packlets/tests/spaces-invitations.test.d.ts.map +1 -0
- package/dist/types/src/packlets/tests/spaces.test.d.ts +2 -0
- package/dist/types/src/packlets/tests/spaces.test.d.ts.map +1 -0
- package/dist/types/src/packlets/tests/text.test.d.ts +2 -0
- package/dist/types/src/packlets/tests/text.test.d.ts.map +1 -0
- package/dist/types/src/packlets/vault/iframe-host-runtime.d.ts +0 -1
- package/dist/types/src/packlets/vault/iframe-host-runtime.d.ts.map +1 -1
- package/dist/types/src/packlets/vault/iframe-proxy-runtime.d.ts.map +1 -1
- package/dist/types/src/packlets/vault/index.d.ts +0 -2
- package/dist/types/src/packlets/vault/index.d.ts.map +1 -1
- package/dist/types/src/packlets/vault/shell-runtime.d.ts.map +1 -1
- package/dist/types/src/packlets/vault/worker-runtime.d.ts.map +1 -1
- package/dist/types/src/packlets/vault/worker-session.d.ts +4 -4
- package/dist/types/src/packlets/vault/worker-session.d.ts.map +1 -1
- package/package.json +31 -30
- package/src/packlets/invitations/device-invitations-handler.ts +10 -9
- package/src/packlets/invitations/device-invitations-service.test.ts +1 -1
- package/src/packlets/invitations/device-invitations-service.ts +1 -1
- package/src/packlets/invitations/index.ts +0 -6
- package/src/packlets/invitations/invitations-service.ts +7 -2
- package/src/packlets/invitations/space-invitations-handler.test.ts +3 -3
- package/src/packlets/invitations/space-invitations-handler.ts +11 -11
- package/src/packlets/invitations/space-invitations-service.test.ts +1 -1
- package/src/packlets/invitations/space-invitations-service.ts +2 -3
- package/src/packlets/services/client-rpc-server.ts +91 -0
- package/src/packlets/services/index.ts +2 -3
- package/src/packlets/services/local-client-services.ts +5 -1
- package/src/packlets/services/service-context.test.ts +4 -4
- package/src/packlets/services/service-context.ts +1 -1
- package/src/packlets/services/service-host.ts +52 -19
- package/src/packlets/services/service-registry.test.ts +2 -2
- package/src/packlets/services/service-registry.ts +9 -1
- package/src/packlets/services/utils.ts +53 -0
- package/src/packlets/spaces/data-space-manager.test.ts +64 -4
- package/src/packlets/spaces/data-space-manager.ts +11 -7
- package/src/packlets/spaces/data-space.ts +13 -1
- package/src/packlets/spaces/index.ts +2 -0
- package/src/packlets/spaces/notarization-plugin.ts +9 -2
- package/src/packlets/spaces/spaces-service.ts +25 -0
- package/src/packlets/system/system-service.ts +3 -3
- package/src/packlets/testing/host-test-builder.ts +163 -0
- package/src/packlets/testing/index.ts +1 -0
- package/src/packlets/testing/invitation-utils.ts +1 -2
- package/src/packlets/testing/test-builder.ts +156 -138
- package/src/packlets/tests/client-services.test.ts +252 -0
- package/src/packlets/tests/client.test.ts +143 -0
- package/src/packlets/tests/halo-credentials.test.ts +94 -0
- package/src/packlets/tests/halo-persistent.test.ts +53 -0
- package/src/packlets/tests/halo-profile.test.ts +74 -0
- package/src/packlets/tests/shared-worker.test.ts +93 -0
- package/src/packlets/tests/spaces-invitations.test.ts +64 -0
- package/src/packlets/tests/spaces.test.ts +136 -0
- package/src/packlets/tests/text.test.ts +206 -0
- package/src/packlets/vault/iframe-host-runtime.ts +26 -34
- package/src/packlets/vault/iframe-proxy-runtime.ts +1 -1
- package/src/packlets/vault/index.ts +0 -2
- package/src/packlets/vault/shell-runtime.ts +1 -2
- package/src/packlets/vault/worker-runtime.ts +5 -15
- package/src/packlets/vault/worker-session.ts +39 -23
- package/dist/lib/browser/chunk-JLLUQQXD.mjs.map +0 -7
- package/dist/types/src/packlets/invitations/device-invitations-proxy.d.ts +0 -8
- package/dist/types/src/packlets/invitations/device-invitations-proxy.d.ts.map +0 -1
- package/dist/types/src/packlets/invitations/encoder.d.ts +0 -9
- package/dist/types/src/packlets/invitations/encoder.d.ts.map +0 -1
- package/dist/types/src/packlets/invitations/encoder.test.d.ts +0 -2
- package/dist/types/src/packlets/invitations/encoder.test.d.ts.map +0 -1
- package/dist/types/src/packlets/invitations/invitations-handler.d.ts +0 -56
- package/dist/types/src/packlets/invitations/invitations-handler.d.ts.map +0 -1
- package/dist/types/src/packlets/invitations/invitations-proxy.d.ts +0 -18
- package/dist/types/src/packlets/invitations/invitations-proxy.d.ts.map +0 -1
- package/dist/types/src/packlets/invitations/invitations.d.ts +0 -55
- package/dist/types/src/packlets/invitations/invitations.d.ts.map +0 -1
- package/dist/types/src/packlets/invitations/space-invitations-proxy.d.ts +0 -11
- package/dist/types/src/packlets/invitations/space-invitations-proxy.d.ts.map +0 -1
- package/dist/types/src/packlets/services/service-definitions.d.ts +0 -30
- package/dist/types/src/packlets/services/service-definitions.d.ts.map +0 -1
- package/dist/types/src/packlets/services/service-proxy.d.ts +0 -17
- package/dist/types/src/packlets/services/service-proxy.d.ts.map +0 -1
- package/dist/types/src/packlets/vault/services.d.ts +0 -21
- package/dist/types/src/packlets/vault/services.d.ts.map +0 -1
- package/dist/types/src/packlets/vault/shell-controller.d.ts +0 -18
- package/dist/types/src/packlets/vault/shell-controller.d.ts.map +0 -1
- package/src/packlets/invitations/device-invitations-proxy.ts +0 -14
- package/src/packlets/invitations/encoder.test.ts +0 -23
- package/src/packlets/invitations/encoder.ts +0 -26
- package/src/packlets/invitations/invitations-handler.ts +0 -68
- package/src/packlets/invitations/invitations-proxy.ts +0 -159
- package/src/packlets/invitations/invitations.ts +0 -113
- package/src/packlets/invitations/space-invitations-proxy.ts +0 -16
- package/src/packlets/services/service-definitions.ts +0 -68
- package/src/packlets/services/service-proxy.ts +0 -54
- package/src/packlets/vault/services.ts +0 -42
- package/src/packlets/vault/shell-controller.ts +0 -56
|
@@ -2,9 +2,8 @@
|
|
|
2
2
|
// Copyright 2022 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
+
export * from './local-client-services';
|
|
5
6
|
export * from './service-context';
|
|
6
|
-
export * from './service-definitions';
|
|
7
7
|
export * from './service-host';
|
|
8
|
-
export * from './service-proxy';
|
|
9
8
|
export * from './service-registry';
|
|
10
|
-
export * from './
|
|
9
|
+
export * from './utils';
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
// Copyright 2023 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
+
import { ClientServices, ClientServicesProvider } from '@dxos/client';
|
|
5
6
|
import { ServiceBundle } from '@dxos/rpc';
|
|
6
7
|
|
|
7
|
-
import { ClientServices, ClientServicesProvider } from './service-definitions';
|
|
8
8
|
import { ClientServicesHost, ClientServicesHostParams } from './service-host';
|
|
9
9
|
|
|
10
10
|
/**
|
|
@@ -25,6 +25,10 @@ export class LocalClientServices implements ClientServicesProvider {
|
|
|
25
25
|
return this._host.services;
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
+
get host(): ClientServicesHost {
|
|
29
|
+
return this._host;
|
|
30
|
+
}
|
|
31
|
+
|
|
28
32
|
async open(): Promise<void> {
|
|
29
33
|
await this._host.open();
|
|
30
34
|
}
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
import { MemorySignalManagerContext } from '@dxos/messaging';
|
|
6
6
|
import { describe, test } from '@dxos/test';
|
|
7
7
|
|
|
8
|
-
import { createServiceContext,
|
|
8
|
+
import { createServiceContext, syncItemsLocal } from '../testing';
|
|
9
9
|
import { performInvitation } from '../testing/invitation-utils';
|
|
10
10
|
|
|
11
11
|
describe('services/ServiceContext', () => {
|
|
@@ -19,7 +19,7 @@ describe('services/ServiceContext', () => {
|
|
|
19
19
|
await performInvitation(device1.deviceInvitations, device2.deviceInvitations, undefined);
|
|
20
20
|
|
|
21
21
|
const space2 = await device2.dataSpaceManager!.spaces.get(space1.key);
|
|
22
|
-
await
|
|
22
|
+
await syncItemsLocal(space1.dataPipelineController, space2!.dataPipelineController);
|
|
23
23
|
});
|
|
24
24
|
|
|
25
25
|
test('new space is synchronized on device invitations', async () => {
|
|
@@ -33,7 +33,7 @@ describe('services/ServiceContext', () => {
|
|
|
33
33
|
const space1 = await device1.dataSpaceManager!.createSpace();
|
|
34
34
|
await device2.dataSpaceManager?.updated.waitForCondition(() => !!device2.dataSpaceManager!.spaces.get(space1.key));
|
|
35
35
|
const space2 = await device2.dataSpaceManager!.spaces.get(space1.key);
|
|
36
|
-
await
|
|
36
|
+
await syncItemsLocal(space1.dataPipelineController, space2!.dataPipelineController);
|
|
37
37
|
});
|
|
38
38
|
|
|
39
39
|
test('joined space is synchronized on device invitations', async () => {
|
|
@@ -51,6 +51,6 @@ describe('services/ServiceContext', () => {
|
|
|
51
51
|
|
|
52
52
|
await device2.dataSpaceManager?.updated.waitForCondition(() => !!device2.dataSpaceManager!.spaces.get(space1.key));
|
|
53
53
|
const space2 = await device2.dataSpaceManager!.spaces.get(space1.key);
|
|
54
|
-
await
|
|
54
|
+
await syncItemsLocal(space1.dataPipelineController, space2!.dataPipelineController);
|
|
55
55
|
});
|
|
56
56
|
});
|
|
@@ -24,7 +24,7 @@ import { Storage } from '@dxos/random-access-storage';
|
|
|
24
24
|
|
|
25
25
|
import { CreateIdentityOptions, IdentityManager, JoinIdentityParams } from '../identity';
|
|
26
26
|
import { DeviceInvitationsHandler, SpaceInvitationsHandler } from '../invitations';
|
|
27
|
-
import { DataSpaceManager } from '../spaces
|
|
27
|
+
import { DataSpaceManager } from '../spaces';
|
|
28
28
|
|
|
29
29
|
/**
|
|
30
30
|
* Shared backend for all client services.
|
|
@@ -5,16 +5,15 @@
|
|
|
5
5
|
import assert from 'node:assert';
|
|
6
6
|
|
|
7
7
|
import { Event } from '@dxos/async';
|
|
8
|
+
import { clientServiceBundle, ClientServices, createDefaultModelFactory } from '@dxos/client';
|
|
8
9
|
import { Config } from '@dxos/config';
|
|
9
10
|
import { raise } from '@dxos/debug';
|
|
10
|
-
import { DocumentModel } from '@dxos/document-model';
|
|
11
11
|
import { DataServiceImpl } from '@dxos/echo-pipeline';
|
|
12
12
|
import { log } from '@dxos/log';
|
|
13
13
|
import { ModelFactory } from '@dxos/model-factory';
|
|
14
14
|
import { NetworkManager } from '@dxos/network-manager';
|
|
15
15
|
import { SystemStatus } from '@dxos/protocols/proto/dxos/client/services';
|
|
16
16
|
import { Storage } from '@dxos/random-access-storage';
|
|
17
|
-
import { TextModel } from '@dxos/text-model';
|
|
18
17
|
|
|
19
18
|
import { TracingServiceImpl } from '../deprecated';
|
|
20
19
|
import { DevicesServiceImpl } from '../devices';
|
|
@@ -27,23 +26,24 @@ import { createStorageObjects } from '../storage';
|
|
|
27
26
|
import { SystemServiceImpl } from '../system';
|
|
28
27
|
import { VaultResourceLock } from '../vault';
|
|
29
28
|
import { ServiceContext } from './service-context';
|
|
30
|
-
import { ClientServices, clientServiceBundle } from './service-definitions';
|
|
31
29
|
import { ServiceRegistry } from './service-registry';
|
|
32
30
|
|
|
33
|
-
// TODO(burdon): Factor out to spaces.
|
|
34
|
-
// TODO(burdon): Defaults (with TextModel).
|
|
35
|
-
export const createDefaultModelFactory = () => {
|
|
36
|
-
return new ModelFactory().registerModel(DocumentModel).registerModel(TextModel);
|
|
37
|
-
};
|
|
38
|
-
|
|
39
31
|
export type ClientServicesHostParams = {
|
|
40
|
-
|
|
32
|
+
/**
|
|
33
|
+
* Can be omitted if `initialize` is later called.
|
|
34
|
+
*/
|
|
35
|
+
config?: Config;
|
|
41
36
|
modelFactory?: ModelFactory;
|
|
42
|
-
networkManager
|
|
37
|
+
networkManager?: NetworkManager;
|
|
43
38
|
storage?: Storage;
|
|
44
39
|
lockKey?: string;
|
|
45
40
|
};
|
|
46
41
|
|
|
42
|
+
export type InitializeOptions = {
|
|
43
|
+
config?: Config;
|
|
44
|
+
networkManager?: NetworkManager;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
47
|
/**
|
|
48
48
|
* Remote service implementation.
|
|
49
49
|
*/
|
|
@@ -52,11 +52,11 @@ export class ClientServicesHost {
|
|
|
52
52
|
private readonly _serviceRegistry: ServiceRegistry<ClientServices>;
|
|
53
53
|
private readonly _systemService: SystemServiceImpl;
|
|
54
54
|
|
|
55
|
-
private
|
|
55
|
+
private _config?: Config;
|
|
56
56
|
private readonly _statusUpdate = new Event<void>();
|
|
57
57
|
private readonly _modelFactory: ModelFactory;
|
|
58
|
-
private
|
|
59
|
-
private
|
|
58
|
+
private _networkManager?: NetworkManager;
|
|
59
|
+
private _storage?: Storage;
|
|
60
60
|
|
|
61
61
|
/**
|
|
62
62
|
* @internal
|
|
@@ -69,14 +69,16 @@ export class ClientServicesHost {
|
|
|
69
69
|
modelFactory = createDefaultModelFactory(),
|
|
70
70
|
// TODO(burdon): Create ApolloLink abstraction (see Client).
|
|
71
71
|
networkManager,
|
|
72
|
-
storage
|
|
72
|
+
storage,
|
|
73
73
|
// TODO(wittjosiah): Turn this on by default.
|
|
74
74
|
lockKey
|
|
75
|
-
}: ClientServicesHostParams) {
|
|
76
|
-
this._config = config;
|
|
77
|
-
this._modelFactory = modelFactory;
|
|
78
|
-
this._networkManager = networkManager;
|
|
75
|
+
}: ClientServicesHostParams = {}) {
|
|
79
76
|
this._storage = storage;
|
|
77
|
+
this._modelFactory = modelFactory;
|
|
78
|
+
|
|
79
|
+
if (config) {
|
|
80
|
+
this.initialize({ config, networkManager });
|
|
81
|
+
}
|
|
80
82
|
|
|
81
83
|
this._resourceLock = lockKey
|
|
82
84
|
? new VaultResourceLock({
|
|
@@ -117,6 +119,10 @@ export class ClientServicesHost {
|
|
|
117
119
|
return this._open;
|
|
118
120
|
}
|
|
119
121
|
|
|
122
|
+
get serviceRegistry() {
|
|
123
|
+
return this._serviceRegistry;
|
|
124
|
+
}
|
|
125
|
+
|
|
120
126
|
get descriptors() {
|
|
121
127
|
return this._serviceRegistry.descriptors;
|
|
122
128
|
}
|
|
@@ -125,11 +131,38 @@ export class ClientServicesHost {
|
|
|
125
131
|
return this._serviceRegistry.services;
|
|
126
132
|
}
|
|
127
133
|
|
|
134
|
+
/**
|
|
135
|
+
* Initialize the service host with the config.
|
|
136
|
+
* Config can also be provided in the constructor.
|
|
137
|
+
* Can only be called once.
|
|
138
|
+
*/
|
|
139
|
+
initialize({ config, networkManager }: InitializeOptions) {
|
|
140
|
+
assert(!this._open, 'service host is open');
|
|
141
|
+
|
|
142
|
+
if (config) {
|
|
143
|
+
assert(!this._config, 'config already set');
|
|
144
|
+
|
|
145
|
+
this._config = config;
|
|
146
|
+
if (!this._storage) {
|
|
147
|
+
this._storage = createStorageObjects(config.get('runtime.client.storage', {})!).storage;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if (networkManager) {
|
|
152
|
+
assert(!this._networkManager, 'network manager already set');
|
|
153
|
+
this._networkManager = networkManager;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
128
157
|
async open() {
|
|
129
158
|
if (this._open) {
|
|
130
159
|
return;
|
|
131
160
|
}
|
|
132
161
|
|
|
162
|
+
assert(this._config, 'config not set');
|
|
163
|
+
assert(this._storage, 'storage not set');
|
|
164
|
+
assert(this._networkManager, 'network manager not set');
|
|
165
|
+
|
|
133
166
|
log('opening...');
|
|
134
167
|
await this._resourceLock?.acquire();
|
|
135
168
|
|
|
@@ -5,15 +5,15 @@
|
|
|
5
5
|
import assert from 'node:assert';
|
|
6
6
|
|
|
7
7
|
import { Trigger } from '@dxos/async';
|
|
8
|
+
import { ClientServices, SpaceInvitationsProxy } from '@dxos/client';
|
|
8
9
|
import { log } from '@dxos/log';
|
|
9
10
|
import { schema } from '@dxos/protocols';
|
|
10
11
|
import { Invitation, SpaceInvitationsService } from '@dxos/protocols/proto/dxos/client/services';
|
|
11
12
|
import { createLinkedPorts, createProtoRpcPeer, createServiceBundle } from '@dxos/rpc';
|
|
12
13
|
import { describe, test } from '@dxos/test';
|
|
13
14
|
|
|
14
|
-
import {
|
|
15
|
+
import { SpaceInvitationsServiceImpl } from '../invitations';
|
|
15
16
|
import { createServiceContext } from '../testing';
|
|
16
|
-
import { ClientServices } from './service-definitions';
|
|
17
17
|
import { ServiceRegistry } from './service-registry';
|
|
18
18
|
|
|
19
19
|
// TODO(burdon): Create TestService (that doesn't require peers).
|
|
@@ -11,7 +11,7 @@ export class ServiceRegistry<Services> {
|
|
|
11
11
|
// prettier-ignore
|
|
12
12
|
constructor (
|
|
13
13
|
private readonly _serviceBundle: ServiceBundle<Services>,
|
|
14
|
-
private _handlers: Partial<Services>
|
|
14
|
+
private _handlers: Partial<Services> = {}
|
|
15
15
|
) {}
|
|
16
16
|
|
|
17
17
|
get descriptors() {
|
|
@@ -25,4 +25,12 @@ export class ServiceRegistry<Services> {
|
|
|
25
25
|
setServices(services: Partial<Services>) {
|
|
26
26
|
this._handlers = services;
|
|
27
27
|
}
|
|
28
|
+
|
|
29
|
+
addService(name: keyof Services, service: Services[keyof Services]) {
|
|
30
|
+
this._handlers[name] = service;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
removeService(name: keyof Services) {
|
|
34
|
+
delete this._handlers[name];
|
|
35
|
+
}
|
|
28
36
|
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2022 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { ClientServicesProvider } from '@dxos/client';
|
|
6
|
+
import { Config } from '@dxos/config';
|
|
7
|
+
import { log } from '@dxos/log';
|
|
8
|
+
import { MemorySignalManager, MemorySignalManagerContext, WebsocketSignalManager } from '@dxos/messaging';
|
|
9
|
+
import {
|
|
10
|
+
createWebRTCTransportFactory,
|
|
11
|
+
MemoryTransportFactory,
|
|
12
|
+
NetworkManager,
|
|
13
|
+
NetworkManagerOptions
|
|
14
|
+
} from '@dxos/network-manager';
|
|
15
|
+
|
|
16
|
+
import { LocalClientServices } from './local-client-services';
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Creates stand-alone services without rpc.
|
|
20
|
+
*/
|
|
21
|
+
export const fromHost = (config: Config = new Config()): ClientServicesProvider => {
|
|
22
|
+
return new LocalClientServices({
|
|
23
|
+
config,
|
|
24
|
+
networkManager: createNetworkManager(config)
|
|
25
|
+
});
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Creates a WebRTC network manager connected to the specified signal server.
|
|
30
|
+
*/
|
|
31
|
+
// TODO(burdon): Move to client-services and remove dependencies from here.
|
|
32
|
+
const createNetworkManager = (config: Config, options: Partial<NetworkManagerOptions> = {}): NetworkManager => {
|
|
33
|
+
const signalServer = config.get('runtime.services.signal.server');
|
|
34
|
+
if (signalServer) {
|
|
35
|
+
const {
|
|
36
|
+
log = true,
|
|
37
|
+
signalManager = new WebsocketSignalManager([signalServer]),
|
|
38
|
+
transportFactory = createWebRTCTransportFactory({
|
|
39
|
+
iceServers: config.get('runtime.services.ice')
|
|
40
|
+
})
|
|
41
|
+
} = options;
|
|
42
|
+
|
|
43
|
+
return new NetworkManager({ log, signalManager, transportFactory });
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// TODO(burdon): Should not provide a memory signal manager since no shared context.
|
|
47
|
+
// Use TestClientBuilder for shared memory tests.
|
|
48
|
+
log.warn('P2P network is not configured.');
|
|
49
|
+
return new NetworkManager({
|
|
50
|
+
signalManager: new MemorySignalManager(new MemorySignalManagerContext()),
|
|
51
|
+
transportFactory: MemoryTransportFactory
|
|
52
|
+
});
|
|
53
|
+
};
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
import { expect } from 'chai';
|
|
6
6
|
|
|
7
|
+
import { latch } from '@dxos/async';
|
|
7
8
|
import { createAdmissionCredentials } from '@dxos/credentials';
|
|
8
9
|
import { DocumentModel } from '@dxos/document-model';
|
|
9
10
|
import { AuthStatus, DataServiceSubscriptions } from '@dxos/echo-pipeline';
|
|
@@ -12,12 +13,12 @@ import { log } from '@dxos/log';
|
|
|
12
13
|
import { ModelFactory } from '@dxos/model-factory';
|
|
13
14
|
import { test, describe } from '@dxos/test';
|
|
14
15
|
|
|
15
|
-
import { createSigningContext,
|
|
16
|
+
import { createSigningContext, HostTestBuilder, syncItemsLocal } from '../testing';
|
|
16
17
|
import { DataSpaceManager } from './data-space-manager';
|
|
17
18
|
|
|
18
19
|
describe('DataSpaceManager', () => {
|
|
19
20
|
test('create space', async () => {
|
|
20
|
-
const builder = new
|
|
21
|
+
const builder = new HostTestBuilder();
|
|
21
22
|
|
|
22
23
|
const peer = builder.createPeer();
|
|
23
24
|
const identity = await createSigningContext(peer.keyring);
|
|
@@ -43,7 +44,7 @@ describe('DataSpaceManager', () => {
|
|
|
43
44
|
});
|
|
44
45
|
|
|
45
46
|
test('sync between peers', async () => {
|
|
46
|
-
const builder = new
|
|
47
|
+
const builder = new HostTestBuilder();
|
|
47
48
|
|
|
48
49
|
const peer1 = builder.createPeer();
|
|
49
50
|
const identity1 = await createSigningContext(peer1.keyring);
|
|
@@ -121,11 +122,70 @@ describe('DataSpaceManager', () => {
|
|
|
121
122
|
});
|
|
122
123
|
log.break();
|
|
123
124
|
|
|
124
|
-
await
|
|
125
|
+
await syncItemsLocal(space1.dataPipelineController, space2.dataPipelineController);
|
|
125
126
|
|
|
126
127
|
expect(space1.inner.protocol.sessions.get(identity2.deviceKey)).to.exist;
|
|
127
128
|
expect(space1.inner.protocol.sessions.get(identity2.deviceKey)?.authStatus).to.equal(AuthStatus.SUCCESS);
|
|
128
129
|
expect(space2.inner.protocol.sessions.get(identity1.deviceKey)).to.exist;
|
|
129
130
|
expect(space2.inner.protocol.sessions.get(identity1.deviceKey)?.authStatus).to.equal(AuthStatus.SUCCESS);
|
|
130
131
|
});
|
|
132
|
+
|
|
133
|
+
test('pub/sub API', async () => {
|
|
134
|
+
const builder = new HostTestBuilder();
|
|
135
|
+
|
|
136
|
+
const peer1 = builder.createPeer();
|
|
137
|
+
const identity1 = await createSigningContext(peer1.keyring);
|
|
138
|
+
const dataSpaceManager1 = new DataSpaceManager(
|
|
139
|
+
peer1.spaceManager,
|
|
140
|
+
peer1.metadataStore,
|
|
141
|
+
new DataServiceSubscriptions(),
|
|
142
|
+
peer1.keyring,
|
|
143
|
+
identity1,
|
|
144
|
+
new ModelFactory().registerModel(DocumentModel),
|
|
145
|
+
peer1.feedStore,
|
|
146
|
+
peer1.snapshotStore
|
|
147
|
+
);
|
|
148
|
+
|
|
149
|
+
const peer2 = builder.createPeer();
|
|
150
|
+
const identity2 = await createSigningContext(peer2.keyring);
|
|
151
|
+
const dataSpaceManager2 = new DataSpaceManager(
|
|
152
|
+
peer2.spaceManager,
|
|
153
|
+
peer2.metadataStore,
|
|
154
|
+
new DataServiceSubscriptions(),
|
|
155
|
+
peer2.keyring,
|
|
156
|
+
identity2,
|
|
157
|
+
new ModelFactory().registerModel(DocumentModel),
|
|
158
|
+
peer2.feedStore,
|
|
159
|
+
peer1.snapshotStore
|
|
160
|
+
);
|
|
161
|
+
|
|
162
|
+
const space1 = await dataSpaceManager1.createSpace();
|
|
163
|
+
await space1.inner.controlPipeline.state.waitUntilTimeframe(space1.inner.controlPipeline.state.endTimeframe);
|
|
164
|
+
|
|
165
|
+
// Admit peer2 to space1.
|
|
166
|
+
await writeMessages(
|
|
167
|
+
space1.inner.controlPipeline.writer,
|
|
168
|
+
await createAdmissionCredentials(
|
|
169
|
+
identity1.credentialSigner,
|
|
170
|
+
identity2.identityKey,
|
|
171
|
+
space1.key,
|
|
172
|
+
space1.inner.genesisFeedKey
|
|
173
|
+
)
|
|
174
|
+
);
|
|
175
|
+
|
|
176
|
+
// Accept must be called after admission so that the peer can authenticate for notarization.
|
|
177
|
+
const space2 = await dataSpaceManager2.acceptSpace({
|
|
178
|
+
spaceKey: space1.key,
|
|
179
|
+
genesisFeedKey: space1.inner.genesisFeedKey
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
const [receivedMessage, inc] = latch({ count: 1 });
|
|
183
|
+
space2.listen('test', (message) => {
|
|
184
|
+
expect(message.channelId).to.equal('test');
|
|
185
|
+
inc();
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
await space1.postMessage('test', { '@type': 'google.protobuf.Any', test: true });
|
|
189
|
+
await receivedMessage();
|
|
190
|
+
});
|
|
131
191
|
});
|
|
@@ -24,7 +24,7 @@ import { log } from '@dxos/log';
|
|
|
24
24
|
import { ModelFactory } from '@dxos/model-factory';
|
|
25
25
|
import { FeedMessage } from '@dxos/protocols/proto/dxos/echo/feed';
|
|
26
26
|
import { SpaceMetadata } from '@dxos/protocols/proto/dxos/echo/metadata';
|
|
27
|
-
import { Presence } from '@dxos/teleport-extension-
|
|
27
|
+
import { Gossip, Presence } from '@dxos/teleport-extension-gossip';
|
|
28
28
|
import { ComplexMap, deferFunction } from '@dxos/util';
|
|
29
29
|
|
|
30
30
|
import { createAuthProvider } from '../identity';
|
|
@@ -150,11 +150,14 @@ export class DataSpaceManager {
|
|
|
150
150
|
}
|
|
151
151
|
|
|
152
152
|
private async _constructSpace(metadata: SpaceMetadata) {
|
|
153
|
+
const gossip = new Gossip({
|
|
154
|
+
localPeerId: this._signingContext.deviceKey
|
|
155
|
+
});
|
|
153
156
|
const presence = new Presence({
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
157
|
+
announceInterval: 500,
|
|
158
|
+
offlineTimeout: 5_000, // TODO(burdon): Config.
|
|
159
|
+
identityKey: this._signingContext.identityKey,
|
|
160
|
+
gossip
|
|
158
161
|
});
|
|
159
162
|
const snapshotManager = new SnapshotManager(this._snapshotStore);
|
|
160
163
|
|
|
@@ -171,8 +174,8 @@ export class DataSpaceManager {
|
|
|
171
174
|
},
|
|
172
175
|
onNetworkConnection: (session) => {
|
|
173
176
|
session.addExtension(
|
|
174
|
-
'dxos.mesh.teleport.
|
|
175
|
-
|
|
177
|
+
'dxos.mesh.teleport.gossip',
|
|
178
|
+
gossip.createExtension({ remotePeerId: session.remotePeerId })
|
|
176
179
|
);
|
|
177
180
|
session.addExtension('dxos.mesh.teleport.objectsync', snapshotManager.objectSync.createExtension());
|
|
178
181
|
session.addExtension('dxos.mesh.teleport.notarization', dataSpace.notarizationPlugin.createExtension());
|
|
@@ -186,6 +189,7 @@ export class DataSpaceManager {
|
|
|
186
189
|
modelFactory: this._modelFactory,
|
|
187
190
|
metadataStore: this._metadataStore,
|
|
188
191
|
snapshotManager,
|
|
192
|
+
gossip,
|
|
189
193
|
presence,
|
|
190
194
|
memberKey: this._signingContext.identityKey,
|
|
191
195
|
keyring: this._keyring,
|
|
@@ -20,7 +20,8 @@ import { PublicKey } from '@dxos/keys';
|
|
|
20
20
|
import { ModelFactory } from '@dxos/model-factory';
|
|
21
21
|
import { FeedMessage } from '@dxos/protocols/proto/dxos/echo/feed';
|
|
22
22
|
import { AdmittedFeed, Credential } from '@dxos/protocols/proto/dxos/halo/credentials';
|
|
23
|
-
import {
|
|
23
|
+
import { GossipMessage } from '@dxos/protocols/proto/dxos/mesh/teleport/gossip';
|
|
24
|
+
import { Gossip, Presence } from '@dxos/teleport-extension-gossip';
|
|
24
25
|
import { ComplexSet } from '@dxos/util';
|
|
25
26
|
|
|
26
27
|
import { TrustedKeySetAuthVerifier } from '../identity';
|
|
@@ -33,6 +34,7 @@ export type DataSpaceParams = {
|
|
|
33
34
|
modelFactory: ModelFactory;
|
|
34
35
|
metadataStore: MetadataStore;
|
|
35
36
|
snapshotManager: SnapshotManager;
|
|
37
|
+
gossip: Gossip;
|
|
36
38
|
presence: Presence;
|
|
37
39
|
keyring: Keyring;
|
|
38
40
|
feedStore: FeedStore<FeedMessage>;
|
|
@@ -47,6 +49,7 @@ export class DataSpace {
|
|
|
47
49
|
private readonly _ctx = new Context();
|
|
48
50
|
private readonly _dataPipelineController: DataPipelineControllerImpl;
|
|
49
51
|
private readonly _inner: Space;
|
|
52
|
+
private readonly _gossip: Gossip;
|
|
50
53
|
private readonly _presence: Presence;
|
|
51
54
|
private readonly _keyring: Keyring;
|
|
52
55
|
private readonly _feedStore: FeedStore<FeedMessage>;
|
|
@@ -58,6 +61,7 @@ export class DataSpace {
|
|
|
58
61
|
|
|
59
62
|
constructor(params: DataSpaceParams) {
|
|
60
63
|
this._inner = params.inner;
|
|
64
|
+
this._gossip = params.gossip;
|
|
61
65
|
this._presence = params.presence;
|
|
62
66
|
this._keyring = params.keyring;
|
|
63
67
|
this._feedStore = params.feedStore;
|
|
@@ -129,6 +133,14 @@ export class DataSpace {
|
|
|
129
133
|
await this._presence.destroy();
|
|
130
134
|
}
|
|
131
135
|
|
|
136
|
+
async postMessage(channel: string, message: any) {
|
|
137
|
+
return this._gossip.postMessage(channel, message);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
listen(channel: string, callback: (message: GossipMessage) => void) {
|
|
141
|
+
return this._gossip.listen(channel, callback);
|
|
142
|
+
}
|
|
143
|
+
|
|
132
144
|
async initializeDataPipeline() {
|
|
133
145
|
await this._inner.controlPipeline.state.waitUntilReachedTargetTimeframe({
|
|
134
146
|
timeout: CONTROL_PIPELINE_READY_TIMEFRAME
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
import assert from 'node:assert';
|
|
6
6
|
|
|
7
|
-
import { DeferredTask, Event, scheduleTask, TimeoutError, Trigger } from '@dxos/async';
|
|
7
|
+
import { DeferredTask, Event, scheduleTask, sleep, TimeoutError, Trigger } from '@dxos/async';
|
|
8
8
|
import { Context } from '@dxos/context';
|
|
9
9
|
import { CredentialProcessor } from '@dxos/credentials';
|
|
10
10
|
import { FeedWriter } from '@dxos/feed-store';
|
|
@@ -16,7 +16,13 @@ import { NotarizationService, NotarizeRequest } from '@dxos/protocols/proto/dxos
|
|
|
16
16
|
import { ExtensionContext, RpcExtension } from '@dxos/teleport';
|
|
17
17
|
import { ComplexMap, ComplexSet, entry } from '@dxos/util';
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
// Timeout for retrying notarization.
|
|
20
|
+
const RETRY_TIMEOUT = 1_000;
|
|
21
|
+
|
|
22
|
+
// Minimum wait time after a peer confirms successful notarization before attempting with a new peer.
|
|
23
|
+
const SUCCESS_DELAY = 1_000;
|
|
24
|
+
|
|
25
|
+
// Timeout for the whole notarization process.
|
|
20
26
|
const NOTARIZE_TIMEOUT = 10_000;
|
|
21
27
|
|
|
22
28
|
/**
|
|
@@ -92,6 +98,7 @@ export class NotarizationPlugin implements CredentialProcessor {
|
|
|
92
98
|
credentials: credentials.filter((credential) => !this._processedCredentials.has(credential.id!))
|
|
93
99
|
});
|
|
94
100
|
log('success');
|
|
101
|
+
await sleep(SUCCESS_DELAY); // wait before trying with a new peer
|
|
95
102
|
} catch (err) {
|
|
96
103
|
log.warn('error notarizing (recoverable)', err);
|
|
97
104
|
notarizeTask.schedule(); // retry immediately with next peer
|
|
@@ -8,16 +8,19 @@ import { raise, todo } from '@dxos/debug';
|
|
|
8
8
|
import { DataServiceSubscriptions, SpaceManager, SpaceNotFoundError } from '@dxos/echo-pipeline';
|
|
9
9
|
import { log } from '@dxos/log';
|
|
10
10
|
import {
|
|
11
|
+
PostMessageRequest,
|
|
11
12
|
QueryCredentialsRequest,
|
|
12
13
|
QuerySpacesResponse,
|
|
13
14
|
Space,
|
|
14
15
|
SpaceMember,
|
|
15
16
|
SpacesService,
|
|
16
17
|
SpaceStatus,
|
|
18
|
+
SubscribeMessagesRequest,
|
|
17
19
|
UpdateSpaceRequest,
|
|
18
20
|
WriteCredentialsRequest
|
|
19
21
|
} from '@dxos/protocols/proto/dxos/client/services';
|
|
20
22
|
import { Credential } from '@dxos/protocols/proto/dxos/halo/credentials';
|
|
23
|
+
import { GossipMessage } from '@dxos/protocols/proto/dxos/mesh/teleport/gossip';
|
|
21
24
|
import { humanize, Provider } from '@dxos/util';
|
|
22
25
|
|
|
23
26
|
import { IdentityManager } from '../identity';
|
|
@@ -96,6 +99,25 @@ export class SpacesServiceImpl implements SpacesService {
|
|
|
96
99
|
});
|
|
97
100
|
}
|
|
98
101
|
|
|
102
|
+
async postMessage({ spaceKey, channel, message }: PostMessageRequest) {
|
|
103
|
+
const dataSpaceManager = await this._getDataSpaceManager();
|
|
104
|
+
const space = dataSpaceManager.spaces.get(spaceKey) ?? raise(new SpaceNotFoundError(spaceKey));
|
|
105
|
+
await space.postMessage(getChannelId(channel), message);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
subscribeMessages({ spaceKey, channel }: SubscribeMessagesRequest) {
|
|
109
|
+
return new Stream<GossipMessage>(({ ctx, next }) => {
|
|
110
|
+
scheduleTask(ctx, async () => {
|
|
111
|
+
const dataSpaceManager = await this._getDataSpaceManager();
|
|
112
|
+
const space = dataSpaceManager.spaces.get(spaceKey) ?? raise(new SpaceNotFoundError(spaceKey));
|
|
113
|
+
const handle = space.listen(getChannelId(channel), (message) => {
|
|
114
|
+
next(message);
|
|
115
|
+
});
|
|
116
|
+
ctx.onDispose(() => handle.unsubscribe());
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
|
|
99
121
|
queryCredentials({ spaceKey }: QueryCredentialsRequest): Stream<Credential> {
|
|
100
122
|
return new Stream(({ ctx, next }) => {
|
|
101
123
|
const space = this._spaceManager.spaces.get(spaceKey) ?? raise(new SpaceNotFoundError(spaceKey));
|
|
@@ -137,3 +159,6 @@ export class SpacesServiceImpl implements SpacesService {
|
|
|
137
159
|
};
|
|
138
160
|
}
|
|
139
161
|
}
|
|
162
|
+
|
|
163
|
+
// Add `user-channel` prefix to the channel name, so that it doesn't collide with the internal channels.
|
|
164
|
+
const getChannelId = (channel: string): string => `user-channel/${channel}`;
|
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
import { MaybePromise } from '@dxos/util';
|
|
15
15
|
|
|
16
16
|
export type SystemServiceOptions = {
|
|
17
|
-
config
|
|
17
|
+
config?: Config;
|
|
18
18
|
statusUpdate: Event<void>;
|
|
19
19
|
getCurrentStatus: () => SystemStatus;
|
|
20
20
|
onUpdateStatus: (status: SystemStatus) => MaybePromise<void>;
|
|
@@ -25,7 +25,7 @@ export type SystemServiceOptions = {
|
|
|
25
25
|
*
|
|
26
26
|
*/
|
|
27
27
|
export class SystemServiceImpl implements SystemService {
|
|
28
|
-
private readonly _config
|
|
28
|
+
private readonly _config?: SystemServiceOptions['config'];
|
|
29
29
|
private readonly _statusUpdate: SystemServiceOptions['statusUpdate'];
|
|
30
30
|
private readonly _getCurrentStatus: SystemServiceOptions['getCurrentStatus'];
|
|
31
31
|
private readonly _onUpdateStatus: SystemServiceOptions['onUpdateStatus'];
|
|
@@ -40,7 +40,7 @@ export class SystemServiceImpl implements SystemService {
|
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
async getConfig() {
|
|
43
|
-
return this._config
|
|
43
|
+
return this._config?.values ?? {};
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
async updateStatus({ status }: UpdateSystemStatusRequest) {
|