@dxos/client-services 0.1.53 → 0.1.54-main.270ec56
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-6ITWCQIT.mjs → chunk-F4BHRFH4.mjs} +861 -702
- package/dist/lib/browser/chunk-F4BHRFH4.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +115 -173
- 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 +24 -7
- package/dist/lib/browser/packlets/testing/index.mjs.map +3 -3
- package/dist/lib/node/index.cjs +951 -853
- 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 +832 -708
- package/dist/lib/node/packlets/testing/index.cjs.map +4 -4
- package/dist/types/src/packlets/identity/authenticator.d.ts.map +1 -1
- package/dist/types/src/packlets/identity/identity.d.ts.map +1 -1
- package/dist/types/src/packlets/services/index.d.ts +1 -0
- package/dist/types/src/packlets/services/index.d.ts.map +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 +7 -1
- package/dist/types/src/packlets/services/service-host.d.ts.map +1 -1
- package/dist/types/src/packlets/spaces/data-space-manager.d.ts.map +1 -1
- package/dist/types/src/packlets/spaces/data-space.d.ts +11 -2
- package/dist/types/src/packlets/spaces/data-space.d.ts.map +1 -1
- package/dist/types/src/packlets/spaces/notarization-plugin.d.ts +2 -1
- package/dist/types/src/packlets/spaces/notarization-plugin.d.ts.map +1 -1
- package/dist/types/src/packlets/spaces/spaces-service.d.ts +1 -1
- package/dist/types/src/packlets/spaces/spaces-service.d.ts.map +1 -1
- package/dist/types/src/packlets/testing/test-builder.d.ts +7 -1
- package/dist/types/src/packlets/testing/test-builder.d.ts.map +1 -1
- package/dist/types/src/packlets/vault/iframe-host-runtime.d.ts +1 -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 +1 -1
- package/dist/types/src/packlets/vault/iframe-proxy-runtime.d.ts.map +1 -1
- package/dist/types/src/packlets/vault/shell-runtime.d.ts +1 -21
- package/dist/types/src/packlets/vault/shell-runtime.d.ts.map +1 -1
- package/dist/types/src/packlets/vault/worker-session.d.ts.map +1 -1
- package/package.json +33 -32
- package/src/packlets/identity/authenticator.ts +4 -2
- package/src/packlets/identity/identity-manager.test.ts +1 -1
- package/src/packlets/identity/identity-manager.ts +8 -8
- package/src/packlets/identity/identity-service.ts +2 -2
- package/src/packlets/identity/identity.test.ts +1 -1
- package/src/packlets/identity/identity.ts +22 -27
- package/src/packlets/invitations/device-invitation-protocol.ts +4 -4
- package/src/packlets/invitations/invitation-extension.ts +5 -5
- package/src/packlets/invitations/invitations-handler.ts +6 -6
- package/src/packlets/invitations/invitations-service.ts +3 -3
- package/src/packlets/invitations/space-invitation-protocol.ts +9 -9
- package/src/packlets/locks/node.ts +2 -2
- package/src/packlets/logging/logging-service.ts +4 -4
- package/src/packlets/services/index.ts +1 -0
- package/src/packlets/services/service-context.test.ts +1 -1
- package/src/packlets/services/service-context.ts +12 -10
- package/src/packlets/services/service-host.test.ts +50 -2
- package/src/packlets/services/service-host.ts +25 -9
- package/src/packlets/spaces/data-space-manager.test.ts +75 -119
- package/src/packlets/spaces/data-space-manager.ts +18 -9
- package/src/packlets/spaces/data-space.ts +66 -20
- package/src/packlets/spaces/notarization-plugin.test.ts +1 -1
- package/src/packlets/spaces/notarization-plugin.ts +9 -5
- package/src/packlets/spaces/spaces-service.test.ts +5 -3
- package/src/packlets/spaces/spaces-service.ts +26 -8
- package/src/packlets/testing/invitation-utils.ts +2 -2
- package/src/packlets/testing/test-builder.ts +36 -4
- package/src/packlets/vault/iframe-host-runtime.ts +2 -2
- package/src/packlets/vault/iframe-proxy-runtime.ts +2 -2
- package/src/packlets/vault/shell-runtime.ts +3 -49
- package/src/packlets/vault/worker-runtime.ts +1 -1
- package/src/packlets/vault/worker-session.ts +7 -6
- package/dist/lib/browser/chunk-6ITWCQIT.mjs.map +0 -7
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// Copyright 2022 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import
|
|
5
|
+
import invariant from 'tiny-invariant';
|
|
6
6
|
|
|
7
7
|
import { Event } from '@dxos/async';
|
|
8
8
|
import { AUTH_TIMEOUT, LOAD_CONTROL_FEEDS_TIMEOUT } from '@dxos/client-protocol';
|
|
@@ -12,7 +12,6 @@ import {
|
|
|
12
12
|
createCredentialSignerWithKey,
|
|
13
13
|
createCredentialSignerWithChain,
|
|
14
14
|
ProfileStateMachine,
|
|
15
|
-
CredentialConsumer,
|
|
16
15
|
} from '@dxos/credentials';
|
|
17
16
|
import { Signer } from '@dxos/crypto';
|
|
18
17
|
import { failUndefined } from '@dxos/debug';
|
|
@@ -40,8 +39,8 @@ export type IdentityParams = {
|
|
|
40
39
|
export class Identity {
|
|
41
40
|
public readonly space: Space;
|
|
42
41
|
private readonly _signer: Signer;
|
|
43
|
-
private readonly _deviceStateMachine:
|
|
44
|
-
private readonly _profileStateMachine:
|
|
42
|
+
private readonly _deviceStateMachine: DeviceStateMachine;
|
|
43
|
+
private readonly _profileStateMachine: ProfileStateMachine;
|
|
45
44
|
public readonly authVerifier: TrustedKeySetAuthVerifier;
|
|
46
45
|
|
|
47
46
|
public readonly identityKey: PublicKey;
|
|
@@ -56,19 +55,15 @@ export class Identity {
|
|
|
56
55
|
this.identityKey = identityKey;
|
|
57
56
|
this.deviceKey = deviceKey;
|
|
58
57
|
|
|
59
|
-
this._deviceStateMachine =
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
identityKey: this.identityKey,
|
|
69
|
-
onUpdate: () => this.stateUpdate.emit(),
|
|
70
|
-
}),
|
|
71
|
-
);
|
|
58
|
+
this._deviceStateMachine = new DeviceStateMachine({
|
|
59
|
+
identityKey: this.identityKey,
|
|
60
|
+
deviceKey: this.deviceKey,
|
|
61
|
+
onUpdate: () => this.stateUpdate.emit(),
|
|
62
|
+
});
|
|
63
|
+
this._profileStateMachine = new ProfileStateMachine({
|
|
64
|
+
identityKey: this.identityKey,
|
|
65
|
+
onUpdate: () => this.stateUpdate.emit(),
|
|
66
|
+
});
|
|
72
67
|
|
|
73
68
|
this.authVerifier = new TrustedKeySetAuthVerifier({
|
|
74
69
|
trustedKeysProvider: () => this.authorizedDeviceKeys,
|
|
@@ -79,30 +74,30 @@ export class Identity {
|
|
|
79
74
|
|
|
80
75
|
// TODO(burdon): Expose state object?
|
|
81
76
|
get authorizedDeviceKeys(): ComplexSet<PublicKey> {
|
|
82
|
-
return this._deviceStateMachine.
|
|
77
|
+
return this._deviceStateMachine.authorizedDeviceKeys;
|
|
83
78
|
}
|
|
84
79
|
|
|
85
80
|
async open() {
|
|
86
|
-
await this.
|
|
87
|
-
await this.
|
|
81
|
+
await this.space.spaceState.addCredentialProcessor(this._deviceStateMachine);
|
|
82
|
+
await this.space.spaceState.addCredentialProcessor(this._profileStateMachine);
|
|
88
83
|
await this.space.open();
|
|
89
84
|
}
|
|
90
85
|
|
|
91
86
|
async close() {
|
|
92
87
|
await this.authVerifier.close();
|
|
93
|
-
await this.
|
|
94
|
-
await this.
|
|
88
|
+
await this.space.spaceState.removeCredentialProcessor(this._profileStateMachine);
|
|
89
|
+
await this.space.spaceState.removeCredentialProcessor(this._deviceStateMachine);
|
|
95
90
|
await this.space.close();
|
|
96
91
|
}
|
|
97
92
|
|
|
98
93
|
async ready() {
|
|
99
|
-
await this._deviceStateMachine.
|
|
94
|
+
await this._deviceStateMachine.deviceChainReady.wait();
|
|
100
95
|
|
|
101
96
|
await this.controlPipeline.state.waitUntilReachedTargetTimeframe({ timeout: LOAD_CONTROL_FEEDS_TIMEOUT });
|
|
102
97
|
}
|
|
103
98
|
|
|
104
99
|
get profileDocument(): ProfileDocument | undefined {
|
|
105
|
-
return this._profileStateMachine.
|
|
100
|
+
return this._profileStateMachine.profile;
|
|
106
101
|
}
|
|
107
102
|
|
|
108
103
|
/**
|
|
@@ -121,7 +116,7 @@ export class Identity {
|
|
|
121
116
|
}
|
|
122
117
|
|
|
123
118
|
get deviceCredentialChain() {
|
|
124
|
-
return this._deviceStateMachine.
|
|
119
|
+
return this._deviceStateMachine.deviceCredentialChain;
|
|
125
120
|
}
|
|
126
121
|
|
|
127
122
|
getAdmissionCredentials(): DeviceAdmissionRequest {
|
|
@@ -137,10 +132,10 @@ export class Identity {
|
|
|
137
132
|
* Requires identity to be ready.
|
|
138
133
|
*/
|
|
139
134
|
getIdentityCredentialSigner(): CredentialSigner {
|
|
140
|
-
|
|
135
|
+
invariant(this._deviceStateMachine.deviceCredentialChain, 'Device credential chain is not ready.');
|
|
141
136
|
return createCredentialSignerWithChain(
|
|
142
137
|
this._signer,
|
|
143
|
-
this._deviceStateMachine.
|
|
138
|
+
this._deviceStateMachine.deviceCredentialChain,
|
|
144
139
|
this.deviceKey,
|
|
145
140
|
);
|
|
146
141
|
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// Copyright 2023 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import
|
|
5
|
+
import invariant from 'tiny-invariant';
|
|
6
6
|
|
|
7
7
|
import { Keyring } from '@dxos/keyring';
|
|
8
8
|
import { Invitation } from '@dxos/protocols/proto/dxos/client/services';
|
|
@@ -29,7 +29,7 @@ export class DeviceInvitationProtocol implements InvitationProtocol {
|
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
async admit(request: AdmissionRequest): Promise<AdmissionResponse> {
|
|
32
|
-
|
|
32
|
+
invariant(request.device);
|
|
33
33
|
const identity = this._getIdentity();
|
|
34
34
|
await identity.admitDevice(request.device);
|
|
35
35
|
|
|
@@ -62,10 +62,10 @@ export class DeviceInvitationProtocol implements InvitationProtocol {
|
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
async accept(response: AdmissionResponse, request: AdmissionRequest): Promise<Partial<Invitation>> {
|
|
65
|
-
|
|
65
|
+
invariant(response.device);
|
|
66
66
|
const { identityKey, haloSpaceKey, genesisFeedKey, controlTimeframe } = response.device;
|
|
67
67
|
|
|
68
|
-
|
|
68
|
+
invariant(request.device);
|
|
69
69
|
const { deviceKey, controlFeedKey, dataFeedKey } = request.device;
|
|
70
70
|
|
|
71
71
|
await this._acceptIdentity({
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// Copyright 2022 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import
|
|
5
|
+
import invariant from 'tiny-invariant';
|
|
6
6
|
|
|
7
7
|
import { Trigger } from '@dxos/async';
|
|
8
8
|
import { cancelWithContext, Context } from '@dxos/context';
|
|
@@ -86,7 +86,7 @@ export class InvitationHostExtension extends RpcExtension<
|
|
|
86
86
|
// Perhaps in the future we will have more complex logic here.
|
|
87
87
|
InvitationHostService: {
|
|
88
88
|
options: async (options) => {
|
|
89
|
-
|
|
89
|
+
invariant(!this._remoteOptions, 'Remote options already set.');
|
|
90
90
|
this._remoteOptions = options;
|
|
91
91
|
this._remoteOptionsTrigger.wake();
|
|
92
92
|
},
|
|
@@ -128,7 +128,7 @@ export class InvitationHostExtension extends RpcExtension<
|
|
|
128
128
|
log('received authentication request', { authCode: code });
|
|
129
129
|
let status = AuthenticationResponse.Status.OK;
|
|
130
130
|
|
|
131
|
-
|
|
131
|
+
invariant(this.invitation, 'Invitation is not set.');
|
|
132
132
|
switch (this.invitation.authMethod) {
|
|
133
133
|
case Invitation.AuthMethod.NONE: {
|
|
134
134
|
log('authentication not required');
|
|
@@ -164,7 +164,7 @@ export class InvitationHostExtension extends RpcExtension<
|
|
|
164
164
|
log.trace('dxos.sdk.invitation-handler.host.admit', trace.begin({ id: traceId }));
|
|
165
165
|
|
|
166
166
|
try {
|
|
167
|
-
|
|
167
|
+
invariant(this.invitation, 'Invitation is not set.');
|
|
168
168
|
// Check authenticated.
|
|
169
169
|
if (isAuthenticationRequired(this.invitation) && !this.authenticationPassed) {
|
|
170
170
|
throw new Error('Not authenticated');
|
|
@@ -239,7 +239,7 @@ export class InvitationGuestExtension extends RpcExtension<
|
|
|
239
239
|
return {
|
|
240
240
|
InvitationHostService: {
|
|
241
241
|
options: async (options) => {
|
|
242
|
-
|
|
242
|
+
invariant(!this._remoteOptions, 'Remote options already set.');
|
|
243
243
|
this._remoteOptions = options;
|
|
244
244
|
this._remoteOptionsTrigger.wake();
|
|
245
245
|
},
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// Copyright 2022 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import
|
|
5
|
+
import invariant from 'tiny-invariant';
|
|
6
6
|
|
|
7
7
|
import { PushStream, scheduleTask, TimeoutError, Trigger } from '@dxos/async';
|
|
8
8
|
import {
|
|
@@ -74,7 +74,7 @@ export class InvitationsHandler {
|
|
|
74
74
|
const authCode =
|
|
75
75
|
options?.authCode ??
|
|
76
76
|
(authMethod === Invitation.AuthMethod.SHARED_SECRET ? generatePasscode(AUTHENTICATION_CODE_LENGTH) : undefined);
|
|
77
|
-
|
|
77
|
+
invariant(protocol);
|
|
78
78
|
|
|
79
79
|
const invitation: Invitation = {
|
|
80
80
|
invitationId,
|
|
@@ -117,7 +117,7 @@ export class InvitationsHandler {
|
|
|
117
117
|
admit: async (admissionRequest) => {
|
|
118
118
|
try {
|
|
119
119
|
const deviceKey = admissionRequest.device?.deviceKey ?? admissionRequest.space?.deviceKey;
|
|
120
|
-
|
|
120
|
+
invariant(deviceKey);
|
|
121
121
|
const admissionResponse = await protocol.admit(admissionRequest, extension.guestProfile);
|
|
122
122
|
|
|
123
123
|
// Updating credentials complete.
|
|
@@ -208,7 +208,7 @@ export class InvitationsHandler {
|
|
|
208
208
|
|
|
209
209
|
acceptInvitation(protocol: InvitationProtocol, invitation: Invitation): AuthenticatingInvitationObservable {
|
|
210
210
|
const { timeout = INVITATION_TIMEOUT } = invitation;
|
|
211
|
-
|
|
211
|
+
invariant(protocol);
|
|
212
212
|
|
|
213
213
|
const authenticated = new Trigger<string>();
|
|
214
214
|
|
|
@@ -219,7 +219,7 @@ export class InvitationsHandler {
|
|
|
219
219
|
let currentState: Invitation.State;
|
|
220
220
|
const stream = new PushStream<Invitation>();
|
|
221
221
|
const setState = (newData: Partial<Invitation>) => {
|
|
222
|
-
|
|
222
|
+
invariant(newData.state !== undefined);
|
|
223
223
|
currentState = newData.state;
|
|
224
224
|
stream.next({ ...invitation, ...newData });
|
|
225
225
|
};
|
|
@@ -354,7 +354,7 @@ export class InvitationsHandler {
|
|
|
354
354
|
};
|
|
355
355
|
|
|
356
356
|
scheduleTask(ctx, async () => {
|
|
357
|
-
|
|
357
|
+
invariant(invitation.swarmKey);
|
|
358
358
|
const topic = invitation.swarmKey;
|
|
359
359
|
const swarmConnection = await this._networkManager.joinSwarm({
|
|
360
360
|
topic,
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// Copyright 2022 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import
|
|
5
|
+
import invariant from 'tiny-invariant';
|
|
6
6
|
|
|
7
7
|
import { Event } from '@dxos/async';
|
|
8
8
|
import { AuthenticatingInvitationObservable, CancellableInvitationObservable } from '@dxos/client-protocol';
|
|
@@ -101,7 +101,7 @@ export class InvitationsServiceImpl implements InvitationsService {
|
|
|
101
101
|
|
|
102
102
|
async authenticate({ invitationId, authCode }: AuthenticationRequest): Promise<void> {
|
|
103
103
|
log('authenticating...');
|
|
104
|
-
|
|
104
|
+
invariant(invitationId);
|
|
105
105
|
const observable = this._acceptInvitations.get(invitationId);
|
|
106
106
|
if (!observable) {
|
|
107
107
|
log.warn('invalid invitation', { invitationId });
|
|
@@ -112,7 +112,7 @@ export class InvitationsServiceImpl implements InvitationsService {
|
|
|
112
112
|
|
|
113
113
|
async cancelInvitation({ invitationId }: { invitationId: string }): Promise<void> {
|
|
114
114
|
log('deleting...');
|
|
115
|
-
|
|
115
|
+
invariant(invitationId);
|
|
116
116
|
const created = this._createInvitations.get(invitationId);
|
|
117
117
|
const accepted = this._acceptInvitations.get(invitationId);
|
|
118
118
|
if (created) {
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// Copyright 2023 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import
|
|
5
|
+
import invariant from 'tiny-invariant';
|
|
6
6
|
|
|
7
7
|
import { createAdmissionCredentials, getCredentialAssertion } from '@dxos/credentials';
|
|
8
8
|
import { writeMessages } from '@dxos/feed-store';
|
|
@@ -40,11 +40,11 @@ export class SpaceInvitationProtocol implements InvitationProtocol {
|
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
async admit(request: AdmissionRequest, guestProfile?: ProfileDocument | undefined): Promise<AdmissionResponse> {
|
|
43
|
-
|
|
43
|
+
invariant(this._spaceKey);
|
|
44
44
|
const space = await this._spaceManager.spaces.get(this._spaceKey);
|
|
45
|
-
|
|
45
|
+
invariant(space);
|
|
46
46
|
|
|
47
|
-
|
|
47
|
+
invariant(request.space);
|
|
48
48
|
const { identityKey, deviceKey } = request.space;
|
|
49
49
|
|
|
50
50
|
log('writing guest credentials', { host: this._signingContext.deviceKey, guest: deviceKey });
|
|
@@ -58,9 +58,9 @@ export class SpaceInvitationProtocol implements InvitationProtocol {
|
|
|
58
58
|
);
|
|
59
59
|
|
|
60
60
|
// TODO(dmaretskyi): Refactor.
|
|
61
|
-
|
|
61
|
+
invariant(credentials[0].credential);
|
|
62
62
|
const spaceMemberCredential = credentials[0].credential.credential;
|
|
63
|
-
|
|
63
|
+
invariant(getCredentialAssertion(spaceMemberCredential)['@type'] === 'dxos.halo.credentials.SpaceMember');
|
|
64
64
|
|
|
65
65
|
await writeMessages(space.inner.controlPipeline.writer, credentials);
|
|
66
66
|
|
|
@@ -95,11 +95,11 @@ export class SpaceInvitationProtocol implements InvitationProtocol {
|
|
|
95
95
|
}
|
|
96
96
|
|
|
97
97
|
async accept(response: AdmissionResponse): Promise<Partial<Invitation>> {
|
|
98
|
-
|
|
98
|
+
invariant(response.space);
|
|
99
99
|
const { credential, controlTimeframe, dataTimeframe } = response.space;
|
|
100
100
|
const assertion = getCredentialAssertion(credential);
|
|
101
|
-
|
|
102
|
-
|
|
101
|
+
invariant(assertion['@type'] === 'dxos.halo.credentials.SpaceMember', 'Invalid credential');
|
|
102
|
+
invariant(credential.subject.id.equals(this._signingContext.identityKey));
|
|
103
103
|
|
|
104
104
|
// Create local space.
|
|
105
105
|
await this._spaceManager.acceptSpace({
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
// Copyright 2023 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import assert from 'node:assert';
|
|
6
5
|
import { FileHandle } from 'node:fs/promises';
|
|
6
|
+
import invariant from 'tiny-invariant';
|
|
7
7
|
|
|
8
8
|
import { LockFile } from '@dxos/lock-file';
|
|
9
9
|
import { log, logInfo } from '@dxos/log';
|
|
@@ -39,7 +39,7 @@ export class Lock implements ResourceLock {
|
|
|
39
39
|
|
|
40
40
|
async release() {
|
|
41
41
|
await this._onRelease?.();
|
|
42
|
-
|
|
42
|
+
invariant(this._fileHandle, 'Lock is not acquired');
|
|
43
43
|
await LockFile.release(this._fileHandle);
|
|
44
44
|
}
|
|
45
45
|
}
|
|
@@ -33,7 +33,7 @@ export class LoggingServiceImpl implements LoggingService {
|
|
|
33
33
|
|
|
34
34
|
// Prevent logging feedback loop from logging service.
|
|
35
35
|
if (
|
|
36
|
-
entry.meta?.
|
|
36
|
+
entry.meta?.F.includes('logging-service') ||
|
|
37
37
|
(entry.context &&
|
|
38
38
|
Object.values(entry.context).some((value) => typeof value === 'string' && value.includes('LoggingService')))
|
|
39
39
|
) {
|
|
@@ -50,8 +50,8 @@ export class LoggingServiceImpl implements LoggingService {
|
|
|
50
50
|
timestamp: new Date(),
|
|
51
51
|
meta: {
|
|
52
52
|
// TODO(dmaretskyi): Fix proto.
|
|
53
|
-
file: entry.meta?.
|
|
54
|
-
line: entry.meta?.
|
|
53
|
+
file: entry.meta?.F ?? '',
|
|
54
|
+
line: entry.meta?.L ?? 0,
|
|
55
55
|
},
|
|
56
56
|
};
|
|
57
57
|
|
|
@@ -94,7 +94,7 @@ const shouldLog = (entry: NaturalLogEntry, request: QueryLogsRequest): boolean =
|
|
|
94
94
|
if (request.filters === undefined) {
|
|
95
95
|
return options === QueryLogsRequest.MatchingOptions.INCLUSIVE;
|
|
96
96
|
} else {
|
|
97
|
-
return request.filters.some((filter) => matchFilter(filter, entry.level, entry.meta?.
|
|
97
|
+
return request.filters.some((filter) => matchFilter(filter, entry.level, entry.meta?.F ?? '', options));
|
|
98
98
|
}
|
|
99
99
|
};
|
|
100
100
|
|
|
@@ -36,7 +36,7 @@ describe('services/ServiceContext', () => {
|
|
|
36
36
|
await device2.dataSpaceManager!.waitUntilSpaceReady(space1!.key);
|
|
37
37
|
const space2 = await device2.dataSpaceManager!.spaces.get(space1.key);
|
|
38
38
|
await syncItemsLocal(space1.dataPipeline, space2!.dataPipeline);
|
|
39
|
-
});
|
|
39
|
+
}).tag('flaky');
|
|
40
40
|
|
|
41
41
|
test('joined space is synchronized on device invitations', async () => {
|
|
42
42
|
const networkContext = new MemorySignalManagerContext();
|
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
// Copyright 2022 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import
|
|
5
|
+
import invariant from 'tiny-invariant';
|
|
6
6
|
|
|
7
7
|
import { Trigger } from '@dxos/async';
|
|
8
|
-
import {
|
|
8
|
+
import { CredentialProcessor, getCredentialAssertion } from '@dxos/credentials';
|
|
9
9
|
import { failUndefined } from '@dxos/debug';
|
|
10
10
|
import {
|
|
11
11
|
valueEncoding,
|
|
@@ -64,7 +64,7 @@ export class ServiceContext {
|
|
|
64
64
|
(invitation: Partial<Invitation>) => InvitationProtocol
|
|
65
65
|
>();
|
|
66
66
|
|
|
67
|
-
private _deviceSpaceSync?:
|
|
67
|
+
private _deviceSpaceSync?: CredentialProcessor;
|
|
68
68
|
|
|
69
69
|
private readonly _instanceId = PublicKey.random().toHex();
|
|
70
70
|
|
|
@@ -141,7 +141,9 @@ export class ServiceContext {
|
|
|
141
141
|
|
|
142
142
|
async close() {
|
|
143
143
|
log('closing...');
|
|
144
|
-
|
|
144
|
+
if (this._deviceSpaceSync && this.identityManager.identity) {
|
|
145
|
+
await this.identityManager.identity.space.spaceState.removeCredentialProcessor(this._deviceSpaceSync);
|
|
146
|
+
}
|
|
145
147
|
await this.dataSpaceManager?.close();
|
|
146
148
|
await this.identityManager.close();
|
|
147
149
|
await this.spaceManager.close();
|
|
@@ -161,7 +163,7 @@ export class ServiceContext {
|
|
|
161
163
|
|
|
162
164
|
getInvitationHandler(invitation: Partial<Invitation> & Pick<Invitation, 'kind'>): InvitationProtocol {
|
|
163
165
|
const factory = this._handlerFactories.get(invitation.kind);
|
|
164
|
-
|
|
166
|
+
invariant(factory, `Unknown invitation kind: ${invitation.kind}`);
|
|
165
167
|
return factory(invitation);
|
|
166
168
|
}
|
|
167
169
|
|
|
@@ -205,13 +207,13 @@ export class ServiceContext {
|
|
|
205
207
|
await this.dataSpaceManager.open();
|
|
206
208
|
|
|
207
209
|
this._handlerFactories.set(Invitation.Kind.SPACE, (invitation) => {
|
|
208
|
-
|
|
210
|
+
invariant(this.dataSpaceManager, 'dataSpaceManager not initialized yet');
|
|
209
211
|
return new SpaceInvitationProtocol(this.dataSpaceManager, signingContext, this.keyring, invitation.spaceKey);
|
|
210
212
|
});
|
|
211
213
|
this.initialized.wake();
|
|
212
214
|
|
|
213
|
-
this._deviceSpaceSync =
|
|
214
|
-
|
|
215
|
+
this._deviceSpaceSync = {
|
|
216
|
+
processCredential: async (credential: Credential) => {
|
|
215
217
|
const assertion = getCredentialAssertion(credential);
|
|
216
218
|
if (assertion['@type'] !== 'dxos.halo.credentials.SpaceMember') {
|
|
217
219
|
return;
|
|
@@ -239,7 +241,7 @@ export class ServiceContext {
|
|
|
239
241
|
log.catch(err);
|
|
240
242
|
}
|
|
241
243
|
},
|
|
242
|
-
}
|
|
243
|
-
await this._deviceSpaceSync
|
|
244
|
+
};
|
|
245
|
+
await identity.space.spaceState.addCredentialProcessor(this._deviceSpaceSync);
|
|
244
246
|
}
|
|
245
247
|
}
|
|
@@ -2,19 +2,32 @@
|
|
|
2
2
|
// Copyright 2023 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import { expect } from 'chai';
|
|
5
|
+
import chai, { expect } from 'chai';
|
|
6
|
+
import chaiAsPromised from 'chai-as-promised';
|
|
7
|
+
import { rmSync } from 'node:fs';
|
|
6
8
|
|
|
7
|
-
import { latch, Trigger } from '@dxos/async';
|
|
9
|
+
import { asyncTimeout, latch, Trigger } from '@dxos/async';
|
|
8
10
|
import { Config } from '@dxos/config';
|
|
9
11
|
import { verifyPresentation } from '@dxos/credentials';
|
|
10
12
|
import { PublicKey } from '@dxos/keys';
|
|
11
13
|
import { MemorySignalManagerContext } from '@dxos/messaging';
|
|
14
|
+
import { Identity } from '@dxos/protocols/proto/dxos/client/services';
|
|
12
15
|
import { Credential } from '@dxos/protocols/proto/dxos/halo/credentials';
|
|
13
16
|
import { afterTest, describe, test } from '@dxos/test';
|
|
17
|
+
import { isNode } from '@dxos/util';
|
|
14
18
|
|
|
15
19
|
import { createMockCredential, createServiceHost } from '../testing';
|
|
16
20
|
|
|
21
|
+
chai.use(chaiAsPromised);
|
|
22
|
+
|
|
17
23
|
describe('ClientServicesHost', () => {
|
|
24
|
+
const persistentStoragePath = '/tmp/dxos/client-services/service-host/storage';
|
|
25
|
+
|
|
26
|
+
afterEach(async () => {
|
|
27
|
+
// Clean up.
|
|
28
|
+
isNode() && rmSync(persistentStoragePath, { recursive: true, force: true });
|
|
29
|
+
});
|
|
30
|
+
|
|
18
31
|
test('queryCredentials', async () => {
|
|
19
32
|
const host = createServiceHost(new Config(), new MemorySignalManagerContext());
|
|
20
33
|
await host.open();
|
|
@@ -97,4 +110,39 @@ describe('ClientServicesHost', () => {
|
|
|
97
110
|
kind: 'pass',
|
|
98
111
|
});
|
|
99
112
|
});
|
|
113
|
+
|
|
114
|
+
test('storage reset', async () => {
|
|
115
|
+
const config = new Config({
|
|
116
|
+
runtime: { client: { storage: { persistent: true, path: persistentStoragePath } } },
|
|
117
|
+
});
|
|
118
|
+
{
|
|
119
|
+
const host = createServiceHost(config, new MemorySignalManagerContext());
|
|
120
|
+
await host.open();
|
|
121
|
+
|
|
122
|
+
await host.services.IdentityService?.createIdentity({});
|
|
123
|
+
|
|
124
|
+
expect(host._serviceContext.storage.size).to.exist;
|
|
125
|
+
|
|
126
|
+
await asyncTimeout(host.reset(), 1000);
|
|
127
|
+
await host.close();
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
{
|
|
131
|
+
const host = createServiceHost(config, new MemorySignalManagerContext());
|
|
132
|
+
await host.open();
|
|
133
|
+
const trigger = new Trigger<Identity>();
|
|
134
|
+
|
|
135
|
+
const stream = host.services.IdentityService?.queryIdentity();
|
|
136
|
+
await stream?.waitUntilReady();
|
|
137
|
+
|
|
138
|
+
stream?.subscribe((identity) => {
|
|
139
|
+
if (identity.identity) {
|
|
140
|
+
trigger.wake(identity.identity);
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
await expect(asyncTimeout(trigger.wait(), 200)).to.be.rejectedWith();
|
|
144
|
+
stream?.close();
|
|
145
|
+
await host.close();
|
|
146
|
+
}
|
|
147
|
+
}).onlyEnvironments('nodejs', 'chromium', 'firefox');
|
|
100
148
|
});
|
|
@@ -2,11 +2,12 @@
|
|
|
2
2
|
// Copyright 2021 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import
|
|
5
|
+
import invariant from 'tiny-invariant';
|
|
6
6
|
|
|
7
7
|
import { Event, synchronized } from '@dxos/async';
|
|
8
|
-
import { clientServiceBundle, ClientServices
|
|
8
|
+
import { clientServiceBundle, ClientServices } from '@dxos/client-protocol';
|
|
9
9
|
import { Config } from '@dxos/config';
|
|
10
|
+
import { DocumentModel } from '@dxos/document-model';
|
|
10
11
|
import { DataServiceImpl } from '@dxos/echo-pipeline';
|
|
11
12
|
import { PublicKey } from '@dxos/keys';
|
|
12
13
|
import { log } from '@dxos/log';
|
|
@@ -16,6 +17,7 @@ import { createWebRTCTransportFactory, NetworkManager, TransportFactory } from '
|
|
|
16
17
|
import { trace } from '@dxos/protocols';
|
|
17
18
|
import { SystemStatus } from '@dxos/protocols/proto/dxos/client/services';
|
|
18
19
|
import { Storage } from '@dxos/random-access-storage';
|
|
20
|
+
import { TextModel } from '@dxos/text-model';
|
|
19
21
|
|
|
20
22
|
import { DevicesServiceImpl } from '../devices';
|
|
21
23
|
import { DevtoolsServiceImpl, DevtoolsHostEvents } from '../devtools';
|
|
@@ -30,6 +32,11 @@ import { SystemServiceImpl } from '../system';
|
|
|
30
32
|
import { ServiceContext } from './service-context';
|
|
31
33
|
import { ServiceRegistry } from './service-registry';
|
|
32
34
|
|
|
35
|
+
// TODO(burdon): Factor out to spaces.
|
|
36
|
+
export const createDefaultModelFactory = () => {
|
|
37
|
+
return new ModelFactory().registerModel(DocumentModel).registerModel(TextModel);
|
|
38
|
+
};
|
|
39
|
+
|
|
33
40
|
export type ClientServicesHostParams = {
|
|
34
41
|
/**
|
|
35
42
|
* Can be omitted if `initialize` is later called.
|
|
@@ -41,6 +48,11 @@ export type ClientServicesHostParams = {
|
|
|
41
48
|
connectionLog?: boolean;
|
|
42
49
|
storage?: Storage;
|
|
43
50
|
lockKey?: string;
|
|
51
|
+
callbacks?: ClientServicesHostCallbacks;
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
export type ClientServicesHostCallbacks = {
|
|
55
|
+
onReset?: () => Promise<void>;
|
|
44
56
|
};
|
|
45
57
|
|
|
46
58
|
export type InitializeOptions = {
|
|
@@ -65,6 +77,7 @@ export class ClientServicesHost {
|
|
|
65
77
|
private _signalManager?: SignalManager;
|
|
66
78
|
private _networkManager?: NetworkManager;
|
|
67
79
|
private _storage?: Storage;
|
|
80
|
+
private _callbacks?: ClientServicesHostCallbacks;
|
|
68
81
|
|
|
69
82
|
_serviceContext!: ServiceContext;
|
|
70
83
|
private _opening = false;
|
|
@@ -80,9 +93,11 @@ export class ClientServicesHost {
|
|
|
80
93
|
storage,
|
|
81
94
|
// TODO(wittjosiah): Turn this on by default.
|
|
82
95
|
lockKey,
|
|
96
|
+
callbacks,
|
|
83
97
|
}: ClientServicesHostParams = {}) {
|
|
84
98
|
this._storage = storage;
|
|
85
99
|
this._modelFactory = modelFactory;
|
|
100
|
+
this._callbacks = callbacks;
|
|
86
101
|
|
|
87
102
|
if (config) {
|
|
88
103
|
this.initialize({ config, transportFactory, signalManager });
|
|
@@ -150,10 +165,10 @@ export class ClientServicesHost {
|
|
|
150
165
|
* Can only be called once.
|
|
151
166
|
*/
|
|
152
167
|
initialize({ config, ...options }: InitializeOptions) {
|
|
153
|
-
|
|
168
|
+
invariant(!this._open, 'service host is open');
|
|
154
169
|
|
|
155
170
|
if (config) {
|
|
156
|
-
|
|
171
|
+
invariant(!this._config, 'config already set');
|
|
157
172
|
|
|
158
173
|
this._config = config;
|
|
159
174
|
if (!this._storage) {
|
|
@@ -170,7 +185,7 @@ export class ClientServicesHost {
|
|
|
170
185
|
} = options;
|
|
171
186
|
this._signalManager = signalManager;
|
|
172
187
|
|
|
173
|
-
|
|
188
|
+
invariant(!this._networkManager, 'network manager already set');
|
|
174
189
|
this._networkManager = new NetworkManager({
|
|
175
190
|
log: connectionLog,
|
|
176
191
|
transportFactory,
|
|
@@ -187,10 +202,10 @@ export class ClientServicesHost {
|
|
|
187
202
|
const traceId = PublicKey.random().toHex();
|
|
188
203
|
log.trace('dxos.sdk.client-services-host.open', trace.begin({ id: traceId }));
|
|
189
204
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
205
|
+
invariant(this._config, 'config not set');
|
|
206
|
+
invariant(this._storage, 'storage not set');
|
|
207
|
+
invariant(this._signalManager, 'signal manager not set');
|
|
208
|
+
invariant(this._networkManager, 'network manager not set');
|
|
194
209
|
|
|
195
210
|
this._opening = true;
|
|
196
211
|
log('opening...', { lockKey: this._resourceLock?.lockKey });
|
|
@@ -277,5 +292,6 @@ export class ClientServicesHost {
|
|
|
277
292
|
await this._storage!.reset();
|
|
278
293
|
log('reset');
|
|
279
294
|
log.trace('dxos.sdk.client-services-host.reset', trace.end({ id: traceId }));
|
|
295
|
+
await this._callbacks?.onReset?.();
|
|
280
296
|
}
|
|
281
297
|
}
|