@dxos/client-services 0.1.22 → 0.1.24-next.6b1f434
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-ZPZBLOQW.mjs +4086 -0
- package/dist/lib/browser/chunk-ZPZBLOQW.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +68 -3027
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/packlets/testing/index.mjs +198 -0
- package/dist/lib/browser/packlets/testing/index.mjs.map +7 -0
- package/dist/lib/node/index.cjs +1694 -667
- 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 +3806 -0
- package/dist/lib/node/packlets/testing/index.cjs.map +7 -0
- package/dist/types/src/packlets/deprecated/profile.d.ts +3 -1
- package/dist/types/src/packlets/deprecated/profile.d.ts.map +1 -1
- package/dist/types/src/packlets/deprecated/space.d.ts.map +1 -1
- package/dist/types/src/packlets/deprecated/system.d.ts +15 -9
- package/dist/types/src/packlets/deprecated/system.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/devtools/feeds.d.ts.map +1 -1
- package/dist/types/src/packlets/devtools/network.d.ts +3 -3
- package/dist/types/src/packlets/devtools/network.d.ts.map +1 -1
- package/dist/types/src/packlets/devtools/spaces.d.ts.map +1 -1
- package/dist/types/src/packlets/identity/authenticator.d.ts +9 -9
- package/dist/types/src/packlets/identity/authenticator.d.ts.map +1 -1
- package/dist/types/src/packlets/identity/identity-manager.d.ts +8 -2
- package/dist/types/src/packlets/identity/identity-manager.d.ts.map +1 -1
- package/dist/types/src/packlets/identity/identity.d.ts +5 -4
- package/dist/types/src/packlets/identity/identity.d.ts.map +1 -1
- package/dist/types/src/packlets/invitations/halo-invitations-handler.d.ts +11 -3
- package/dist/types/src/packlets/invitations/halo-invitations-handler.d.ts.map +1 -1
- package/dist/types/src/packlets/invitations/invitations.d.ts +1 -1
- package/dist/types/src/packlets/invitations/invitations.d.ts.map +1 -1
- package/dist/types/src/packlets/invitations/space-invitations-handler.d.ts +1 -1
- package/dist/types/src/packlets/invitations/space-invitations-handler.d.ts.map +1 -1
- package/dist/types/src/packlets/services/service-context.d.ts +3 -1
- package/dist/types/src/packlets/services/service-context.d.ts.map +1 -1
- package/dist/types/src/packlets/services/service-context.test.d.ts +2 -0
- package/dist/types/src/packlets/services/service-context.test.d.ts.map +1 -0
- package/dist/types/src/packlets/services/service-definitions.d.ts +1 -1
- package/dist/types/src/packlets/services/service-definitions.d.ts.map +1 -1
- package/dist/types/src/packlets/services/service-host.d.ts +9 -6
- package/dist/types/src/packlets/services/service-host.d.ts.map +1 -1
- package/dist/types/src/packlets/services/service-registry.d.ts +4 -3
- package/dist/types/src/packlets/services/service-registry.d.ts.map +1 -1
- package/dist/types/src/packlets/spaces/data-space-manager.d.ts +9 -2
- package/dist/types/src/packlets/spaces/data-space-manager.d.ts.map +1 -1
- package/dist/types/src/packlets/spaces/data-space-manager.test.d.ts +2 -0
- package/dist/types/src/packlets/spaces/data-space-manager.test.d.ts.map +1 -0
- package/dist/types/src/packlets/spaces/data-space.d.ts +20 -4
- package/dist/types/src/packlets/spaces/data-space.d.ts.map +1 -1
- package/dist/types/src/packlets/spaces/notarization-plugin.d.ts +50 -0
- package/dist/types/src/packlets/spaces/notarization-plugin.d.ts.map +1 -0
- package/dist/types/src/packlets/spaces/notarization-plugin.test.d.ts +2 -0
- package/dist/types/src/packlets/spaces/notarization-plugin.test.d.ts.map +1 -0
- package/dist/types/src/packlets/spaces/spaces-service.d.ts +3 -2
- package/dist/types/src/packlets/spaces/spaces-service.d.ts.map +1 -1
- package/dist/types/src/packlets/testing/credential-utils.d.ts +8 -0
- package/dist/types/src/packlets/testing/credential-utils.d.ts.map +1 -0
- package/dist/types/src/packlets/testing/index.d.ts +2 -0
- package/dist/types/src/packlets/testing/index.d.ts.map +1 -1
- package/dist/types/src/packlets/testing/invitation-utils.d.ts +3 -0
- package/dist/types/src/packlets/testing/invitation-utils.d.ts.map +1 -0
- package/dist/types/src/packlets/testing/test-builder.d.ts +31 -2
- package/dist/types/src/packlets/testing/test-builder.d.ts.map +1 -1
- package/dist/types/src/packlets/vault/iframe-host-runtime.d.ts +35 -0
- package/dist/types/src/packlets/vault/iframe-host-runtime.d.ts.map +1 -0
- package/dist/types/src/packlets/vault/{iframe-runtime.d.ts → iframe-proxy-runtime.d.ts} +9 -4
- package/dist/types/src/packlets/vault/iframe-proxy-runtime.d.ts.map +1 -0
- package/dist/types/src/packlets/vault/index.d.ts +6 -1
- package/dist/types/src/packlets/vault/index.d.ts.map +1 -1
- package/dist/types/src/packlets/vault/services.d.ts +9 -1
- package/dist/types/src/packlets/vault/services.d.ts.map +1 -1
- package/dist/types/src/packlets/vault/shell-controller.d.ts +24 -0
- package/dist/types/src/packlets/vault/shell-controller.d.ts.map +1 -0
- package/dist/types/src/packlets/vault/shell-runtime.d.ts +45 -0
- package/dist/types/src/packlets/vault/shell-runtime.d.ts.map +1 -0
- package/dist/types/src/packlets/vault/vault-resource-lock.d.ts +19 -0
- package/dist/types/src/packlets/vault/vault-resource-lock.d.ts.map +1 -0
- package/dist/types/src/packlets/vault/worker-runtime.d.ts +2 -1
- package/dist/types/src/packlets/vault/worker-runtime.d.ts.map +1 -1
- package/dist/types/src/packlets/vault/worker-session.d.ts +7 -4
- package/dist/types/src/packlets/vault/worker-session.d.ts.map +1 -1
- package/package.json +30 -29
- package/src/packlets/deprecated/profile.ts +20 -2
- package/src/packlets/deprecated/space.ts +41 -37
- package/src/packlets/deprecated/system.ts +26 -13
- package/src/packlets/devtools/devtools.ts +4 -4
- package/src/packlets/devtools/feeds.ts +11 -6
- package/src/packlets/devtools/network.ts +20 -9
- package/src/packlets/devtools/spaces.ts +8 -7
- package/src/packlets/identity/authenticator.test.ts +4 -4
- package/src/packlets/identity/authenticator.ts +27 -17
- package/src/packlets/identity/identity-manager.test.ts +25 -16
- package/src/packlets/identity/identity-manager.ts +25 -13
- package/src/packlets/identity/identity.test.ts +22 -23
- package/src/packlets/identity/identity.ts +15 -10
- package/src/packlets/invitations/halo-invitations-handler.test.ts +4 -45
- package/src/packlets/invitations/halo-invitations-handler.ts +33 -17
- package/src/packlets/invitations/invitations.ts +2 -1
- package/src/packlets/invitations/space-invitations-handler.test.ts +43 -26
- package/src/packlets/invitations/space-invitations-handler.ts +9 -12
- package/src/packlets/services/service-context.test.ts +56 -0
- package/src/packlets/services/service-context.ts +62 -10
- package/src/packlets/services/service-definitions.ts +1 -1
- package/src/packlets/services/service-host.test.ts +74 -5
- package/src/packlets/services/service-host.ts +86 -24
- package/src/packlets/services/service-registry.test.ts +2 -1
- package/src/packlets/services/service-registry.ts +5 -1
- package/src/packlets/spaces/data-space-manager.test.ts +131 -0
- package/src/packlets/spaces/data-space-manager.ts +61 -15
- package/src/packlets/spaces/data-space.ts +125 -14
- package/src/packlets/spaces/notarization-plugin.test.ts +81 -0
- package/src/packlets/spaces/notarization-plugin.ts +211 -0
- package/src/packlets/spaces/spaces-service.ts +10 -2
- package/src/packlets/testing/credential-utils.ts +28 -0
- package/src/packlets/testing/index.ts +2 -0
- package/src/packlets/testing/invitation-utils.ts +44 -0
- package/src/packlets/testing/test-builder.ts +96 -14
- package/src/packlets/vault/iframe-host-runtime.ts +131 -0
- package/src/packlets/vault/{iframe-runtime.ts → iframe-proxy-runtime.ts} +18 -3
- package/src/packlets/vault/index.ts +6 -1
- package/src/packlets/vault/services.ts +17 -1
- package/src/packlets/vault/shell-controller.ts +77 -0
- package/src/packlets/vault/shell-runtime.ts +133 -0
- package/src/packlets/vault/vault-resource-lock.ts +76 -0
- package/src/packlets/vault/worker-runtime.ts +16 -4
- package/src/packlets/vault/worker-session.ts +36 -26
- package/dist/types/src/packlets/vault/iframe-runtime.d.ts.map +0 -1
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2023 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { createCredential } from '@dxos/credentials';
|
|
6
|
+
import { Signer } from '@dxos/crypto';
|
|
7
|
+
import { PublicKey } from '@dxos/keys';
|
|
8
|
+
import { Credential } from '@dxos/protocols/proto/dxos/halo/credentials';
|
|
9
|
+
|
|
10
|
+
export const createMockCredential = async ({
|
|
11
|
+
signer,
|
|
12
|
+
issuer
|
|
13
|
+
}: {
|
|
14
|
+
signer: Signer;
|
|
15
|
+
issuer: PublicKey;
|
|
16
|
+
}): Promise<Credential> =>
|
|
17
|
+
createCredential({
|
|
18
|
+
signer,
|
|
19
|
+
issuer,
|
|
20
|
+
subject: new PublicKey(Buffer.from('test')),
|
|
21
|
+
assertion: {
|
|
22
|
+
'@type': 'example.testing.rpc.MessageWithAny',
|
|
23
|
+
payload: {
|
|
24
|
+
'@type': 'google.protobuf.Any',
|
|
25
|
+
value: Buffer.from('test')
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
});
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2023 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import assert from 'node:assert';
|
|
6
|
+
|
|
7
|
+
import { Trigger } from '@dxos/async';
|
|
8
|
+
import { raise } from '@dxos/debug';
|
|
9
|
+
import { Invitation } from '@dxos/protocols/proto/dxos/client/services';
|
|
10
|
+
|
|
11
|
+
import { InvitationsHandler } from '../invitations';
|
|
12
|
+
|
|
13
|
+
export const performInvitation = async <T>(host: InvitationsHandler<T>, guest: InvitationsHandler<T>, context: T) => {
|
|
14
|
+
const done1 = new Trigger<Invitation>(); // peer 1 connected.
|
|
15
|
+
const done2 = new Trigger<Invitation>(); // peer 2 connected.
|
|
16
|
+
const observable1 = await host.createInvitation(context, { type: Invitation.Type.INTERACTIVE_TESTING });
|
|
17
|
+
observable1.subscribe({
|
|
18
|
+
onConnecting: async (invitation1: Invitation) => {
|
|
19
|
+
const observable2 = await guest.acceptInvitation(invitation1, { type: Invitation.Type.INTERACTIVE_TESTING });
|
|
20
|
+
observable2.subscribe({
|
|
21
|
+
onConnecting: async (invitation2: Invitation) => {
|
|
22
|
+
assert(invitation1.swarmKey!.equals(invitation2.swarmKey!));
|
|
23
|
+
},
|
|
24
|
+
onConnected: async (invitation2: Invitation) => {},
|
|
25
|
+
onSuccess: (invitation) => {
|
|
26
|
+
done2.wake(invitation);
|
|
27
|
+
},
|
|
28
|
+
onCancelled: () => raise(new Error()),
|
|
29
|
+
onTimeout: (err: Error) => raise(err),
|
|
30
|
+
onError: (err: Error) => raise(err)
|
|
31
|
+
});
|
|
32
|
+
},
|
|
33
|
+
onConnected: async (invitation1: Invitation) => {},
|
|
34
|
+
onCancelled: () => raise(new Error('cancelled')),
|
|
35
|
+
onSuccess: (invitation) => {
|
|
36
|
+
done1.wake(invitation);
|
|
37
|
+
},
|
|
38
|
+
onTimeout: (err: Error) => raise(err),
|
|
39
|
+
onError: (err: Error) => raise(err)
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
await done1.wait();
|
|
43
|
+
await done2.wait();
|
|
44
|
+
};
|
|
@@ -2,10 +2,19 @@
|
|
|
2
2
|
// Copyright 2022 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import { expect } from 'chai';
|
|
6
|
-
|
|
7
5
|
import { Config } from '@dxos/config';
|
|
8
|
-
import {
|
|
6
|
+
import { createCredentialSignerWithChain, CredentialGenerator } from '@dxos/credentials';
|
|
7
|
+
import {
|
|
8
|
+
SnapshotStore,
|
|
9
|
+
DataPipelineControllerImpl,
|
|
10
|
+
MetadataStore,
|
|
11
|
+
SigningContext,
|
|
12
|
+
SpaceManager,
|
|
13
|
+
valueEncoding
|
|
14
|
+
} from '@dxos/echo-pipeline';
|
|
15
|
+
import { testLocalDatabase } from '@dxos/echo-pipeline/testing';
|
|
16
|
+
import { FeedFactory, FeedStore } from '@dxos/feed-store';
|
|
17
|
+
import { Keyring } from '@dxos/keyring';
|
|
9
18
|
import { MemorySignalManager, MemorySignalManagerContext } from '@dxos/messaging';
|
|
10
19
|
import { MemoryTransportFactory, NetworkManager } from '@dxos/network-manager';
|
|
11
20
|
import { createStorage, Storage, StorageType } from '@dxos/random-access-storage';
|
|
@@ -63,18 +72,91 @@ export const createIdentity = async (peer: ServiceContext) => {
|
|
|
63
72
|
|
|
64
73
|
// TODO(burdon): Remove @dxos/client-testing.
|
|
65
74
|
// TODO(burdon): Create builder and make configurable.
|
|
66
|
-
export const syncItems = async (
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
75
|
+
export const syncItems = async (db1: DataPipelineControllerImpl, db2: DataPipelineControllerImpl) => {
|
|
76
|
+
await testLocalDatabase(db1, db2);
|
|
77
|
+
await testLocalDatabase(db2, db1);
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
export class TestBuilder {
|
|
81
|
+
public readonly signalContext = new MemorySignalManagerContext();
|
|
82
|
+
|
|
83
|
+
createPeer(): TestPeer {
|
|
84
|
+
return new TestPeer(this.signalContext);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export type TestPeerProps = {
|
|
89
|
+
storage?: Storage;
|
|
90
|
+
feedStore?: FeedStore<any>;
|
|
91
|
+
metadataStore?: MetadataStore;
|
|
92
|
+
keyring?: Keyring;
|
|
93
|
+
networkManager?: NetworkManager;
|
|
94
|
+
spaceManager?: SpaceManager;
|
|
95
|
+
snapshotStore?: SnapshotStore;
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
export class TestPeer {
|
|
99
|
+
private _props: TestPeerProps = {};
|
|
100
|
+
|
|
101
|
+
constructor(private readonly signalContext: MemorySignalManagerContext) {}
|
|
102
|
+
|
|
103
|
+
get storage() {
|
|
104
|
+
return (this._props.storage ??= createStorage({ type: StorageType.RAM }));
|
|
72
105
|
}
|
|
73
106
|
|
|
74
|
-
{
|
|
75
|
-
|
|
76
|
-
const item1 = await space2.database!.createItem({ type: 'type-2' });
|
|
77
|
-
const item2 = await space1.database!.waitForItem({ type: 'type-2' });
|
|
78
|
-
expect(item1.id).to.eq(item2.id);
|
|
107
|
+
get keyring() {
|
|
108
|
+
return (this._props.keyring ??= new Keyring(this.storage.createDirectory('keyring')));
|
|
79
109
|
}
|
|
110
|
+
|
|
111
|
+
get feedStore() {
|
|
112
|
+
return (this._props.feedStore ??= new FeedStore({
|
|
113
|
+
factory: new FeedFactory({
|
|
114
|
+
root: this.storage.createDirectory('feeds'),
|
|
115
|
+
signer: this.keyring,
|
|
116
|
+
hypercore: {
|
|
117
|
+
valueEncoding
|
|
118
|
+
}
|
|
119
|
+
})
|
|
120
|
+
}));
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
get metadataStore() {
|
|
124
|
+
return (this._props.metadataStore ??= new MetadataStore(this.storage.createDirectory('metadata')));
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
get snapshotStore() {
|
|
128
|
+
return (this._props.snapshotStore ??= new SnapshotStore(this.storage.createDirectory('snapshots')));
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
get networkManager() {
|
|
132
|
+
return (this._props.networkManager ??= new NetworkManager({
|
|
133
|
+
signalManager: new MemorySignalManager(this.signalContext),
|
|
134
|
+
transportFactory: MemoryTransportFactory
|
|
135
|
+
}));
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
get spaceManager() {
|
|
139
|
+
return (this._props.spaceManager ??= new SpaceManager({
|
|
140
|
+
feedStore: this.feedStore,
|
|
141
|
+
networkManager: this.networkManager
|
|
142
|
+
}));
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export const createSigningContext = async (keyring: Keyring): Promise<SigningContext> => {
|
|
147
|
+
const identityKey = await keyring.createKey();
|
|
148
|
+
const deviceKey = await keyring.createKey();
|
|
149
|
+
|
|
150
|
+
return {
|
|
151
|
+
identityKey,
|
|
152
|
+
deviceKey,
|
|
153
|
+
credentialSigner: createCredentialSignerWithChain(
|
|
154
|
+
keyring,
|
|
155
|
+
{
|
|
156
|
+
credential: await new CredentialGenerator(keyring, identityKey, deviceKey).createDeviceAuthorization(deviceKey)
|
|
157
|
+
},
|
|
158
|
+
deviceKey
|
|
159
|
+
),
|
|
160
|
+
recordCredential: async () => {} // No-op.
|
|
161
|
+
};
|
|
80
162
|
};
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2023 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { Trigger } from '@dxos/async';
|
|
6
|
+
import { Config } from '@dxos/config';
|
|
7
|
+
import { log, logInfo } from '@dxos/log';
|
|
8
|
+
import { MemorySignalManager, MemorySignalManagerContext, WebsocketSignalManager } from '@dxos/messaging';
|
|
9
|
+
import { createWebRTCTransportFactory, NetworkManager } from '@dxos/network-manager';
|
|
10
|
+
import { createProtoRpcPeer, ProtoRpcPeer, RpcPort } from '@dxos/rpc';
|
|
11
|
+
import { getAsyncValue, Provider } from '@dxos/util';
|
|
12
|
+
|
|
13
|
+
import { clientServiceBundle, ClientServices, ClientServicesHost } from '../services';
|
|
14
|
+
import { ShellRuntime, ShellRuntimeImpl } from './shell-runtime';
|
|
15
|
+
|
|
16
|
+
const LOCK_KEY = 'DXOS_RESOURCE_LOCK';
|
|
17
|
+
|
|
18
|
+
export type IFrameHostRuntimeParams = {
|
|
19
|
+
configProvider: Config | Provider<Promise<Config>>;
|
|
20
|
+
appPort: RpcPort;
|
|
21
|
+
shellPort?: RpcPort;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Runs the client services in the main thread.
|
|
26
|
+
*
|
|
27
|
+
* Holds a lock over the client services such that only one instance can run at a time.
|
|
28
|
+
* This should only be used when SharedWorker is not available.
|
|
29
|
+
*/
|
|
30
|
+
export class IFrameHostRuntime {
|
|
31
|
+
private readonly _configProvider: Config | Provider<Promise<Config>>;
|
|
32
|
+
private readonly _transportFactory = createWebRTCTransportFactory();
|
|
33
|
+
private readonly _ready = new Trigger<Error | undefined>();
|
|
34
|
+
private readonly _getService: <Service>(
|
|
35
|
+
find: (services: Partial<ClientServices>) => Service | undefined
|
|
36
|
+
) => Promise<Service>;
|
|
37
|
+
|
|
38
|
+
private readonly _appPort: RpcPort;
|
|
39
|
+
private readonly _shellPort?: RpcPort;
|
|
40
|
+
private _config!: Config;
|
|
41
|
+
private _clientServices!: ClientServicesHost;
|
|
42
|
+
private _clientRpc!: ProtoRpcPeer<ClientServices>;
|
|
43
|
+
private _shellRuntime?: ShellRuntimeImpl;
|
|
44
|
+
|
|
45
|
+
@logInfo
|
|
46
|
+
public origin?: string;
|
|
47
|
+
|
|
48
|
+
constructor({ configProvider, appPort, shellPort }: IFrameHostRuntimeParams) {
|
|
49
|
+
this._configProvider = configProvider;
|
|
50
|
+
this._appPort = appPort;
|
|
51
|
+
this._shellPort = shellPort;
|
|
52
|
+
|
|
53
|
+
this._getService = async (find) => {
|
|
54
|
+
const error = await this._ready.wait();
|
|
55
|
+
if (error) {
|
|
56
|
+
throw error;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const service = await find(this._clientServices.services);
|
|
60
|
+
if (!service) {
|
|
61
|
+
throw new Error('Service not found');
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return service;
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
if (this._shellPort) {
|
|
68
|
+
this._shellRuntime = new ShellRuntimeImpl(this._shellPort);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
get services() {
|
|
73
|
+
return this._clientServices;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
get shell(): ShellRuntime | undefined {
|
|
77
|
+
return this._shellRuntime;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
async start() {
|
|
81
|
+
log('starting...');
|
|
82
|
+
try {
|
|
83
|
+
this._config = await getAsyncValue(this._configProvider);
|
|
84
|
+
const signalServer = this._config.get('runtime.services.signal.server');
|
|
85
|
+
this._clientServices = new ClientServicesHost({
|
|
86
|
+
lockKey: LOCK_KEY,
|
|
87
|
+
config: this._config,
|
|
88
|
+
networkManager: new NetworkManager({
|
|
89
|
+
log: true,
|
|
90
|
+
signalManager: signalServer
|
|
91
|
+
? new WebsocketSignalManager([signalServer])
|
|
92
|
+
: new MemorySignalManager(new MemorySignalManagerContext()), // TODO(dmaretskyi): Inject this context.
|
|
93
|
+
transportFactory: this._transportFactory
|
|
94
|
+
})
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
this._clientRpc = createProtoRpcPeer({
|
|
98
|
+
exposed: clientServiceBundle,
|
|
99
|
+
handlers: {
|
|
100
|
+
DataService: async () => await this._getService((services) => services.DataService),
|
|
101
|
+
DevicesService: async () => await this._getService((services) => services.DevicesService),
|
|
102
|
+
DevtoolsHost: async () => await this._getService((services) => services.DevtoolsHost),
|
|
103
|
+
HaloInvitationsService: async () => await this._getService((services) => services.HaloInvitationsService),
|
|
104
|
+
NetworkService: async () => await this._getService((services) => services.NetworkService),
|
|
105
|
+
ProfileService: async () => await this._getService((services) => services.ProfileService),
|
|
106
|
+
SpaceInvitationsService: async () => await this._getService((services) => services.SpaceInvitationsService),
|
|
107
|
+
SpaceService: async () => await this._getService((services) => services.SpaceService),
|
|
108
|
+
SpacesService: async () => await this._getService((services) => services.SpacesService),
|
|
109
|
+
SystemService: async () => await this._getService((services) => services.SystemService),
|
|
110
|
+
TracingService: async () => await this._getService((services) => services.TracingService)
|
|
111
|
+
},
|
|
112
|
+
port: this._appPort
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
await Promise.all([this._clientServices.open(), this._clientRpc.open(), this._shellRuntime?.open()]);
|
|
116
|
+
this._ready.wake(undefined);
|
|
117
|
+
log('started');
|
|
118
|
+
} catch (err: any) {
|
|
119
|
+
this._ready.wake(err);
|
|
120
|
+
log.catch(err);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
async stop() {
|
|
125
|
+
log('stopping...');
|
|
126
|
+
await this._clientRpc.close();
|
|
127
|
+
await this._clientServices.close();
|
|
128
|
+
await this._shellRuntime?.close();
|
|
129
|
+
log('stopped');
|
|
130
|
+
}
|
|
131
|
+
}
|
|
@@ -7,28 +7,33 @@ import { BridgeService } from '@dxos/protocols/proto/dxos/mesh/bridge';
|
|
|
7
7
|
import { createProtoRpcPeer, ProtoRpcPeer, RpcPort } from '@dxos/rpc';
|
|
8
8
|
|
|
9
9
|
import { iframeServiceBundle, WorkerServiceBundle, workerServiceBundle } from './services';
|
|
10
|
+
import { ShellRuntime, ShellRuntimeImpl } from './shell-runtime';
|
|
10
11
|
|
|
11
12
|
// NOTE: Keep as RpcPorts to avoid dependency on @dxos/rpc-tunnel so we don't depend on browser-specific apis.
|
|
12
|
-
export type
|
|
13
|
+
export type IFrameProxyRuntimeParams = {
|
|
13
14
|
systemPort: RpcPort;
|
|
14
15
|
workerAppPort: RpcPort;
|
|
15
16
|
windowAppPort: RpcPort;
|
|
17
|
+
shellPort?: RpcPort;
|
|
16
18
|
};
|
|
17
19
|
|
|
18
20
|
/**
|
|
19
21
|
* Manages the client connection to the shared worker.
|
|
20
22
|
*/
|
|
21
|
-
export class
|
|
23
|
+
export class IFrameProxyRuntime {
|
|
22
24
|
private readonly _systemPort: RpcPort;
|
|
23
25
|
private readonly _windowAppPort: RpcPort;
|
|
24
26
|
private readonly _workerAppPort: RpcPort;
|
|
27
|
+
private readonly _shellPort?: RpcPort;
|
|
25
28
|
private readonly _systemRpc: ProtoRpcPeer<WorkerServiceBundle>;
|
|
29
|
+
private readonly _shellRuntime?: ShellRuntimeImpl;
|
|
26
30
|
private readonly _transportService = new WebRTCTransportService();
|
|
27
31
|
|
|
28
|
-
constructor({ systemPort, workerAppPort, windowAppPort }:
|
|
32
|
+
constructor({ systemPort, workerAppPort, windowAppPort, shellPort }: IFrameProxyRuntimeParams) {
|
|
29
33
|
this._systemPort = systemPort;
|
|
30
34
|
this._windowAppPort = windowAppPort;
|
|
31
35
|
this._workerAppPort = workerAppPort;
|
|
36
|
+
this._shellPort = shellPort;
|
|
32
37
|
|
|
33
38
|
this._systemRpc = createProtoRpcPeer({
|
|
34
39
|
requested: workerServiceBundle,
|
|
@@ -47,14 +52,24 @@ export class IFrameRuntime {
|
|
|
47
52
|
|
|
48
53
|
this._workerAppPort.subscribe((msg) => this._windowAppPort.send(msg));
|
|
49
54
|
this._windowAppPort.subscribe((msg) => this._workerAppPort.send(msg));
|
|
55
|
+
|
|
56
|
+
if (this._shellPort) {
|
|
57
|
+
this._shellRuntime = new ShellRuntimeImpl(this._shellPort);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
get shell(): ShellRuntime | undefined {
|
|
62
|
+
return this._shellRuntime;
|
|
50
63
|
}
|
|
51
64
|
|
|
52
65
|
async open(origin: string) {
|
|
53
66
|
await this._systemRpc.open();
|
|
54
67
|
await this._systemRpc.rpc.WorkerService.start({ origin });
|
|
68
|
+
await this._shellRuntime?.open();
|
|
55
69
|
}
|
|
56
70
|
|
|
57
71
|
async close() {
|
|
72
|
+
await this._shellRuntime?.close();
|
|
58
73
|
await this._systemRpc.rpc.WorkerService.stop();
|
|
59
74
|
await this._systemRpc.close();
|
|
60
75
|
}
|
|
@@ -2,6 +2,11 @@
|
|
|
2
2
|
// Copyright 2022 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
export * from './iframe-runtime';
|
|
5
|
+
export * from './iframe-host-runtime';
|
|
6
|
+
export * from './iframe-proxy-runtime';
|
|
7
|
+
export * from './services';
|
|
8
|
+
export * from './shell-controller';
|
|
9
|
+
export * from './shell-runtime';
|
|
10
|
+
export * from './vault-resource-lock';
|
|
6
11
|
export * from './worker-runtime';
|
|
7
12
|
export * from './worker-session';
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
//
|
|
4
4
|
|
|
5
5
|
import { schema } from '@dxos/protocols';
|
|
6
|
-
import type { IframeService, WorkerService } from '@dxos/protocols/proto/dxos/iframe';
|
|
6
|
+
import type { IframeService, AppService, ShellService, WorkerService } from '@dxos/protocols/proto/dxos/iframe';
|
|
7
7
|
import type { BridgeService } from '@dxos/protocols/proto/dxos/mesh/bridge';
|
|
8
8
|
import { ServiceBundle } from '@dxos/rpc';
|
|
9
9
|
|
|
@@ -24,3 +24,19 @@ export type WorkerServiceBundle = {
|
|
|
24
24
|
export const workerServiceBundle: ServiceBundle<WorkerServiceBundle> = {
|
|
25
25
|
WorkerService: schema.getService('dxos.iframe.WorkerService')
|
|
26
26
|
};
|
|
27
|
+
|
|
28
|
+
export type AppServiceBundle = {
|
|
29
|
+
AppService: AppService;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export const appServiceBundle: ServiceBundle<AppServiceBundle> = {
|
|
33
|
+
AppService: schema.getService('dxos.iframe.AppService')
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export type ShellServiceBundle = {
|
|
37
|
+
ShellService: ShellService;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export const shellServiceBundle: ServiceBundle<ShellServiceBundle> = {
|
|
41
|
+
ShellService: schema.getService('dxos.iframe.ShellService')
|
|
42
|
+
};
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2023 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { Event } from '@dxos/async';
|
|
6
|
+
import { PublicKey } from '@dxos/keys';
|
|
7
|
+
import { log } from '@dxos/log';
|
|
8
|
+
import { AppContextRequest, LayoutRequest, ShellDisplay, ShellLayout } from '@dxos/protocols/proto/dxos/iframe';
|
|
9
|
+
import { createProtoRpcPeer, ProtoRpcPeer, RpcPort } from '@dxos/rpc';
|
|
10
|
+
|
|
11
|
+
import { appServiceBundle, shellServiceBundle, ShellServiceBundle } from './services';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Provide access to the shell via RPC connection.
|
|
15
|
+
*/
|
|
16
|
+
export class ShellController {
|
|
17
|
+
readonly contextUpdate = new Event<AppContextRequest>();
|
|
18
|
+
private _shellRpc?: ProtoRpcPeer<ShellServiceBundle>;
|
|
19
|
+
private _display = ShellDisplay.NONE;
|
|
20
|
+
private _spaceKey?: PublicKey;
|
|
21
|
+
private _invitationCode?: string;
|
|
22
|
+
|
|
23
|
+
constructor(private readonly _port: RpcPort) {}
|
|
24
|
+
|
|
25
|
+
get display() {
|
|
26
|
+
return this._display;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
get spaceKey() {
|
|
30
|
+
return this._spaceKey;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
setSpace(key?: PublicKey) {
|
|
34
|
+
this._spaceKey = key;
|
|
35
|
+
this._emitUpdate();
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async setLayout(layout: ShellLayout, options: Omit<LayoutRequest, 'layout'> = {}) {
|
|
39
|
+
this._display = ShellDisplay.FULLSCREEN;
|
|
40
|
+
this._emitUpdate();
|
|
41
|
+
await this._shellRpc?.rpc.ShellService.setLayout({ layout, ...options });
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
async open() {
|
|
45
|
+
this._shellRpc = createProtoRpcPeer({
|
|
46
|
+
requested: shellServiceBundle,
|
|
47
|
+
exposed: appServiceBundle,
|
|
48
|
+
handlers: {
|
|
49
|
+
AppService: {
|
|
50
|
+
setContext: async (request) => {
|
|
51
|
+
log('set context', request);
|
|
52
|
+
this._display = request.display;
|
|
53
|
+
this._spaceKey = request.spaceKey;
|
|
54
|
+
this._invitationCode = request.invitationCode;
|
|
55
|
+
this.contextUpdate.emit(request);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
port: this._port
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
await this._shellRpc.open();
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
async close() {
|
|
66
|
+
await this._shellRpc?.close();
|
|
67
|
+
this._shellRpc = undefined;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
private _emitUpdate() {
|
|
71
|
+
this.contextUpdate.emit({
|
|
72
|
+
display: this._display,
|
|
73
|
+
spaceKey: this._spaceKey,
|
|
74
|
+
invitationCode: this._invitationCode
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2023 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import assert from 'node:assert';
|
|
6
|
+
|
|
7
|
+
import { Event } from '@dxos/async';
|
|
8
|
+
import { PublicKey } from '@dxos/keys';
|
|
9
|
+
import { AppContextRequest, LayoutRequest, ShellLayout } from '@dxos/protocols/proto/dxos/iframe';
|
|
10
|
+
import { createProtoRpcPeer, ProtoRpcPeer, RpcPort } from '@dxos/rpc';
|
|
11
|
+
|
|
12
|
+
import { AppServiceBundle, appServiceBundle, shellServiceBundle } from './services';
|
|
13
|
+
|
|
14
|
+
export interface ShellRuntime {
|
|
15
|
+
layoutUpdate: Event<LayoutRequest>;
|
|
16
|
+
layout: ShellLayout;
|
|
17
|
+
invitationCode?: string;
|
|
18
|
+
spaceKey?: PublicKey;
|
|
19
|
+
setLayout: (layout: ShellLayout, options?: Omit<LayoutRequest, 'layout'>) => void;
|
|
20
|
+
setAppContext: (context: AppContextRequest) => Promise<void>;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Endpoint that handles shell services.
|
|
25
|
+
*/
|
|
26
|
+
export class ShellRuntimeImpl implements ShellRuntime {
|
|
27
|
+
readonly layoutUpdate = new Event<LayoutRequest>();
|
|
28
|
+
private _appRpc?: ProtoRpcPeer<AppServiceBundle>;
|
|
29
|
+
private _layout = ShellLayout.DEFAULT;
|
|
30
|
+
private _invitationCode?: string;
|
|
31
|
+
private _spaceKey?: PublicKey;
|
|
32
|
+
|
|
33
|
+
constructor(private readonly _port: RpcPort) {}
|
|
34
|
+
|
|
35
|
+
get layout() {
|
|
36
|
+
return this._layout;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
get invitationCode() {
|
|
40
|
+
return this._invitationCode;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
get spaceKey() {
|
|
44
|
+
return this._spaceKey;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
setLayout(layout: ShellLayout, options: Omit<LayoutRequest, 'layout'> = {}) {
|
|
48
|
+
this._layout = layout;
|
|
49
|
+
this._invitationCode = options.invitationCode;
|
|
50
|
+
this._spaceKey = options.spaceKey;
|
|
51
|
+
this.layoutUpdate.emit({ layout, ...options });
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
async setAppContext(context: AppContextRequest) {
|
|
55
|
+
assert(this._appRpc, 'runtime not open');
|
|
56
|
+
|
|
57
|
+
if (context.spaceKey) {
|
|
58
|
+
this._spaceKey = context.spaceKey;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (context.invitationCode) {
|
|
62
|
+
this._invitationCode = context.invitationCode;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
await this._appRpc.rpc.AppService.setContext({
|
|
66
|
+
...context,
|
|
67
|
+
spaceKey: this._spaceKey,
|
|
68
|
+
invitationCode: this._invitationCode
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
async open() {
|
|
73
|
+
this._appRpc = createProtoRpcPeer({
|
|
74
|
+
requested: appServiceBundle,
|
|
75
|
+
exposed: shellServiceBundle,
|
|
76
|
+
handlers: {
|
|
77
|
+
ShellService: {
|
|
78
|
+
setLayout: async (request) => {
|
|
79
|
+
this._layout = request.layout;
|
|
80
|
+
this._invitationCode = request.invitationCode;
|
|
81
|
+
this._spaceKey = request.spaceKey;
|
|
82
|
+
this.layoutUpdate.emit(request);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
port: this._port
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
await this._appRpc.open();
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
async close() {
|
|
93
|
+
await this._appRpc?.close();
|
|
94
|
+
this._appRpc = undefined;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export class MemoryShellRuntime implements ShellRuntime {
|
|
99
|
+
readonly layoutUpdate = new Event<LayoutRequest>();
|
|
100
|
+
readonly contextUpdate = new Event<AppContextRequest>();
|
|
101
|
+
private _layout: ShellLayout;
|
|
102
|
+
private _invitationCode?: string;
|
|
103
|
+
private _spaceKey?: PublicKey;
|
|
104
|
+
|
|
105
|
+
constructor({ layout, invitationCode, spaceKey }: Partial<LayoutRequest> & Partial<AppContextRequest> = {}) {
|
|
106
|
+
this._layout = layout ?? ShellLayout.DEFAULT;
|
|
107
|
+
this._invitationCode = invitationCode;
|
|
108
|
+
this._spaceKey = spaceKey;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
get layout() {
|
|
112
|
+
return this._layout;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
get invitationCode() {
|
|
116
|
+
return this._invitationCode;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
get spaceKey() {
|
|
120
|
+
return this._spaceKey;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
setLayout(layout: ShellLayout, options: Omit<LayoutRequest, 'layout'> = {}) {
|
|
124
|
+
this._layout = layout;
|
|
125
|
+
this._invitationCode = options.invitationCode;
|
|
126
|
+
this._spaceKey = options.spaceKey;
|
|
127
|
+
this.layoutUpdate.emit({ layout, ...options });
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
async setAppContext(context: AppContextRequest) {
|
|
131
|
+
this.contextUpdate.emit(context);
|
|
132
|
+
}
|
|
133
|
+
}
|