@dxos/client-services 0.1.56-main.e47dfd1 → 0.1.56-main.e79d64a
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-CSJSC47N.mjs → chunk-T32PP5QD.mjs} +1284 -625
- package/dist/lib/browser/chunk-T32PP5QD.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +70 -54
- package/dist/lib/browser/index.mjs.map +3 -3
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/packlets/testing/index.mjs +42 -48
- package/dist/lib/browser/packlets/testing/index.mjs.map +3 -3
- package/dist/lib/node/index.cjs +1439 -767
- 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 +1590 -936
- package/dist/lib/node/packlets/testing/index.cjs.map +4 -4
- package/dist/types/src/packlets/identity/identity-manager.d.ts +7 -1
- package/dist/types/src/packlets/identity/identity-manager.d.ts.map +1 -1
- package/dist/types/src/packlets/identity/identity-service.d.ts +7 -3
- package/dist/types/src/packlets/identity/identity-service.d.ts.map +1 -1
- package/dist/types/src/packlets/identity/identity.d.ts +3 -2
- package/dist/types/src/packlets/identity/identity.d.ts.map +1 -1
- package/dist/types/src/packlets/invitations/device-invitation-protocol.d.ts.map +1 -1
- package/dist/types/src/packlets/invitations/invitation-extension.d.ts.map +1 -1
- package/dist/types/src/packlets/invitations/invitations-handler.d.ts.map +1 -1
- package/dist/types/src/packlets/invitations/invitations-service.d.ts.map +1 -1
- package/dist/types/src/packlets/invitations/space-invitation-protocol.d.ts.map +1 -1
- package/dist/types/src/packlets/logging/logging-service.d.ts +6 -2
- package/dist/types/src/packlets/logging/logging-service.d.ts.map +1 -1
- package/dist/types/src/packlets/services/client-rpc-server.d.ts.map +1 -1
- package/dist/types/src/packlets/services/diagnostics.d.ts +46 -0
- package/dist/types/src/packlets/services/diagnostics.d.ts.map +1 -0
- 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/platform.d.ts +16 -0
- package/dist/types/src/packlets/services/platform.d.ts.map +1 -0
- package/dist/types/src/packlets/services/service-context.d.ts +2 -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 -3
- package/dist/types/src/packlets/services/service-host.d.ts.map +1 -1
- package/dist/types/src/packlets/services/util.d.ts +6 -0
- package/dist/types/src/packlets/services/util.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.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.map +1 -1
- package/dist/types/src/packlets/system/system-service.d.ts +12 -2
- package/dist/types/src/packlets/system/system-service.d.ts.map +1 -1
- package/dist/types/src/packlets/testing/invitation-utils.d.ts.map +1 -1
- package/dist/types/src/packlets/vault/iframe-host-runtime.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.map +1 -1
- package/dist/types/src/version.d.ts +2 -0
- package/dist/types/src/version.d.ts.map +1 -0
- package/package.json +34 -33
- package/src/packlets/devices/devices-service.test.ts +2 -1
- package/src/packlets/identity/authenticator.test.ts +2 -2
- package/src/packlets/identity/identity-manager.test.ts +15 -3
- package/src/packlets/identity/identity-manager.ts +28 -8
- package/src/packlets/identity/identity-service.test.ts +17 -2
- package/src/packlets/identity/identity-service.ts +24 -16
- package/src/packlets/identity/identity.test.ts +7 -6
- package/src/packlets/identity/identity.ts +9 -5
- package/src/packlets/invitations/device-invitation-protocol.test.ts +2 -1
- package/src/packlets/invitations/device-invitation-protocol.ts +1 -2
- package/src/packlets/invitations/invitation-extension.ts +1 -2
- package/src/packlets/invitations/invitations-handler.ts +3 -2
- package/src/packlets/invitations/invitations-service.ts +1 -2
- package/src/packlets/invitations/space-invitation-protocol.ts +1 -2
- package/src/packlets/locks/node.ts +1 -1
- package/src/packlets/logging/logging-service.ts +17 -9
- package/src/packlets/network/network-service.test.ts +2 -1
- package/src/packlets/services/client-rpc-server.ts +4 -4
- package/src/packlets/services/diagnostics.ts +212 -0
- package/src/packlets/services/index.ts +1 -0
- package/src/packlets/services/platform.ts +48 -0
- package/src/packlets/services/service-context.test.ts +59 -0
- package/src/packlets/services/service-context.ts +21 -15
- package/src/packlets/services/service-host.test.ts +12 -11
- package/src/packlets/services/service-host.ts +39 -20
- package/src/packlets/services/service-registry.test.ts +3 -1
- package/src/packlets/services/util.ts +33 -0
- package/src/packlets/spaces/data-space-manager.test.ts +80 -22
- package/src/packlets/spaces/data-space-manager.ts +7 -4
- package/src/packlets/spaces/data-space.ts +17 -5
- package/src/packlets/spaces/notarization-plugin.ts +1 -2
- package/src/packlets/spaces/spaces-service.test.ts +2 -1
- package/src/packlets/spaces/spaces-service.ts +19 -13
- package/src/packlets/system/system-service.test.ts +1 -0
- package/src/packlets/system/system-service.ts +34 -2
- package/src/packlets/testing/invitation-utils.ts +1 -2
- package/src/packlets/testing/test-builder.ts +1 -1
- package/src/packlets/vault/iframe-host-runtime.ts +2 -1
- package/src/packlets/vault/shell-runtime.ts +1 -2
- package/src/packlets/vault/worker-runtime.ts +3 -2
- package/src/packlets/vault/worker-session.ts +7 -3
- package/src/version.ts +1 -0
- package/dist/lib/browser/chunk-CSJSC47N.mjs.map +0 -7
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
import { expect } from 'chai';
|
|
6
6
|
|
|
7
7
|
import { Trigger } from '@dxos/async';
|
|
8
|
+
import { Context } from '@dxos/context';
|
|
8
9
|
import { NetworkService, ConnectionState } from '@dxos/protocols/proto/dxos/client/services';
|
|
9
10
|
import { afterEach, afterTest, beforeEach, describe, test } from '@dxos/test';
|
|
10
11
|
|
|
@@ -18,7 +19,7 @@ describe('NetworkService', () => {
|
|
|
18
19
|
|
|
19
20
|
beforeEach(async () => {
|
|
20
21
|
serviceContext = createServiceContext();
|
|
21
|
-
await serviceContext.open();
|
|
22
|
+
await serviceContext.open(new Context());
|
|
22
23
|
networkService = new NetworkServiceImpl(serviceContext.networkManager, serviceContext.signalManager);
|
|
23
24
|
});
|
|
24
25
|
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
import { ClientServices } from '@dxos/client-protocol';
|
|
6
6
|
import { Any, ServiceHandler, Stream } from '@dxos/codec-protobuf';
|
|
7
7
|
import { raise } from '@dxos/debug';
|
|
8
|
-
import { parseMethodName, RpcPeer, RpcPeerOptions } from '@dxos/rpc';
|
|
8
|
+
import { parseMethodName, RpcPeer, RpcPeerOptions, ServiceBundle } from '@dxos/rpc';
|
|
9
9
|
import { MaybePromise } from '@dxos/util';
|
|
10
10
|
|
|
11
11
|
import { ServiceRegistry } from './service-registry';
|
|
@@ -74,16 +74,16 @@ export class ClientRpcServer {
|
|
|
74
74
|
private _getServiceHandler(serviceName: string) {
|
|
75
75
|
if (!this._handlerCache.has(serviceName)) {
|
|
76
76
|
const [key, descriptor] =
|
|
77
|
-
Object.entries(this._serviceRegistry.descriptors).find(
|
|
77
|
+
Object.entries(this._serviceRegistry.descriptors as ServiceBundle<Record<string, any>>).find(
|
|
78
78
|
([key, descriptor]) => descriptor.name === serviceName,
|
|
79
79
|
) ?? raise(new Error(`Service not available: ${serviceName}`));
|
|
80
80
|
|
|
81
|
-
const service = this._serviceRegistry.services[key as keyof ClientServices];
|
|
81
|
+
const service = this._serviceRegistry.services[key as keyof ClientServices] as any;
|
|
82
82
|
if (!service) {
|
|
83
83
|
throw new Error(`Service not available: ${serviceName}`);
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
-
this._handlerCache.set(serviceName, descriptor.createServer(service
|
|
86
|
+
this._handlerCache.set(serviceName, descriptor.createServer(service));
|
|
87
87
|
}
|
|
88
88
|
|
|
89
89
|
return this._handlerCache.get(serviceName)!;
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2022 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { ClientServices } from '@dxos/client-protocol';
|
|
6
|
+
import { getFirstStreamValue } from '@dxos/codec-protobuf';
|
|
7
|
+
import { Config, ConfigProto } from '@dxos/config';
|
|
8
|
+
import { credentialTypeFilter } from '@dxos/credentials';
|
|
9
|
+
import { DocumentModel, DocumentModelState } from '@dxos/document-model';
|
|
10
|
+
import { TYPE_PROPERTIES } from '@dxos/echo-db';
|
|
11
|
+
import { invariant } from '@dxos/invariant';
|
|
12
|
+
import { PublicKey } from '@dxos/keys';
|
|
13
|
+
import { log } from '@dxos/log';
|
|
14
|
+
import { STORAGE_VERSION } from '@dxos/protocols';
|
|
15
|
+
import {
|
|
16
|
+
Device,
|
|
17
|
+
Identity,
|
|
18
|
+
Metrics,
|
|
19
|
+
Space as SpaceProto,
|
|
20
|
+
SpaceMember,
|
|
21
|
+
} from '@dxos/protocols/proto/dxos/client/services';
|
|
22
|
+
import { SubscribeToFeedsResponse } from '@dxos/protocols/proto/dxos/devtools/host';
|
|
23
|
+
import { Epoch } from '@dxos/protocols/proto/dxos/halo/credentials';
|
|
24
|
+
|
|
25
|
+
import { DXOS_VERSION } from '../../version';
|
|
26
|
+
import { DataSpace } from '../spaces';
|
|
27
|
+
import { getPlatform, Platform } from './platform';
|
|
28
|
+
import { ServiceContext } from './service-context';
|
|
29
|
+
|
|
30
|
+
const DEFAULT_TIMEOUT = 1_000;
|
|
31
|
+
|
|
32
|
+
export type Diagnostics = {
|
|
33
|
+
created: string;
|
|
34
|
+
platform: Platform;
|
|
35
|
+
config?: ConfigProto;
|
|
36
|
+
client: {
|
|
37
|
+
version: string;
|
|
38
|
+
storage: {
|
|
39
|
+
version: number;
|
|
40
|
+
};
|
|
41
|
+
};
|
|
42
|
+
identity?: Identity;
|
|
43
|
+
devices?: Device[];
|
|
44
|
+
spaces?: SpaceStats[];
|
|
45
|
+
feeds?: Partial<SubscribeToFeedsResponse.Feed>[];
|
|
46
|
+
metrics?: Metrics;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
// TODO(burdon): Normalize for ECHO/HALO.
|
|
50
|
+
export type SpaceStats = {
|
|
51
|
+
key: PublicKey;
|
|
52
|
+
properties?: {
|
|
53
|
+
name: string;
|
|
54
|
+
};
|
|
55
|
+
db?: {
|
|
56
|
+
objects: number;
|
|
57
|
+
};
|
|
58
|
+
metrics?: SpaceProto.Metrics & {
|
|
59
|
+
startupTime?: number;
|
|
60
|
+
};
|
|
61
|
+
epochs?: (Epoch & { id?: PublicKey })[];
|
|
62
|
+
members?: SpaceMember[];
|
|
63
|
+
pipeline?: SpaceProto.PipelineState;
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Create diagnostics to provide snapshot of current system state.
|
|
68
|
+
*/
|
|
69
|
+
export const createDiagnostics = async (
|
|
70
|
+
clientServices: Partial<ClientServices>,
|
|
71
|
+
serviceContext: ServiceContext,
|
|
72
|
+
config: Config,
|
|
73
|
+
): Promise<Diagnostics> => {
|
|
74
|
+
const diagnostics: Diagnostics = {
|
|
75
|
+
created: new Date().toISOString(),
|
|
76
|
+
platform: getPlatform(),
|
|
77
|
+
client: {
|
|
78
|
+
version: DXOS_VERSION,
|
|
79
|
+
storage: {
|
|
80
|
+
version: STORAGE_VERSION,
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
// Trace metrics.
|
|
86
|
+
// TODO(burdon): Move here from logging service?
|
|
87
|
+
{
|
|
88
|
+
invariant(clientServices.LoggingService, 'SystemService is not available.');
|
|
89
|
+
diagnostics.metrics = await getFirstStreamValue(clientServices.LoggingService.queryMetrics({}), {
|
|
90
|
+
timeout: DEFAULT_TIMEOUT,
|
|
91
|
+
}).catch(() => undefined);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const identity = serviceContext.identityManager.identity;
|
|
95
|
+
if (identity) {
|
|
96
|
+
// Identity.
|
|
97
|
+
diagnostics.identity = {
|
|
98
|
+
identityKey: identity.identityKey,
|
|
99
|
+
spaceKey: identity.space.key,
|
|
100
|
+
profile: identity.profileDocument,
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
// Devices.
|
|
104
|
+
const { devices } =
|
|
105
|
+
(await getFirstStreamValue(clientServices.DevicesService!.queryDevices(), {
|
|
106
|
+
timeout: DEFAULT_TIMEOUT,
|
|
107
|
+
}).catch(() => undefined)) ?? {};
|
|
108
|
+
diagnostics.devices = devices;
|
|
109
|
+
|
|
110
|
+
// TODO(dmaretskyi): Add metrics for halo space.
|
|
111
|
+
|
|
112
|
+
// Spaces.
|
|
113
|
+
if (serviceContext.dataSpaceManager) {
|
|
114
|
+
diagnostics.spaces = await Promise.all(
|
|
115
|
+
Array.from(serviceContext.dataSpaceManager.spaces.values()).map((space) => getSpaceStats(space)) ?? [],
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Feeds.
|
|
120
|
+
const { feeds = [] } =
|
|
121
|
+
(await getFirstStreamValue(clientServices.DevtoolsHost!.subscribeToFeeds({}), {
|
|
122
|
+
timeout: DEFAULT_TIMEOUT,
|
|
123
|
+
}).catch(() => undefined)) ?? {};
|
|
124
|
+
diagnostics.feeds = feeds.map(({ feedKey, bytes, length }) => ({ feedKey, bytes, length }));
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
diagnostics.config = config.values;
|
|
128
|
+
|
|
129
|
+
return diagnostics;
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
const getProperties = (space: DataSpace) => {
|
|
133
|
+
let properties: any = {};
|
|
134
|
+
try {
|
|
135
|
+
// Add properties to cache.
|
|
136
|
+
const propertiesItem = space.dataPipeline.itemManager.items.find(
|
|
137
|
+
(item) =>
|
|
138
|
+
item.modelMeta?.type === DocumentModel.meta.type &&
|
|
139
|
+
(item.state as DocumentModelState)?.type === TYPE_PROPERTIES,
|
|
140
|
+
);
|
|
141
|
+
|
|
142
|
+
const state = propertiesItem?.state as DocumentModelState;
|
|
143
|
+
properties = state?.data;
|
|
144
|
+
} catch (err: any) {
|
|
145
|
+
log.warn(err.message);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
return properties;
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
const getSpaceStats = async (space: DataSpace): Promise<SpaceStats> => {
|
|
152
|
+
const stats: SpaceStats = {
|
|
153
|
+
key: space.key,
|
|
154
|
+
metrics: space.metrics,
|
|
155
|
+
|
|
156
|
+
epochs: space.inner.spaceState.credentials
|
|
157
|
+
.filter(credentialTypeFilter('dxos.halo.credentials.Epoch'))
|
|
158
|
+
.map((credential) => ({
|
|
159
|
+
...credential.subject.assertion,
|
|
160
|
+
id: credential.id,
|
|
161
|
+
})),
|
|
162
|
+
|
|
163
|
+
members: Array.from(space.inner.spaceState.members.values()).map((member) => ({
|
|
164
|
+
identity: {
|
|
165
|
+
identityKey: member.key,
|
|
166
|
+
profile: {
|
|
167
|
+
displayName: member.assertion.profile?.displayName,
|
|
168
|
+
},
|
|
169
|
+
},
|
|
170
|
+
presence:
|
|
171
|
+
space.presence.getPeersOnline().filter(({ identityKey }) => identityKey.equals(member.key)).length > 0
|
|
172
|
+
? SpaceMember.PresenceState.ONLINE
|
|
173
|
+
: SpaceMember.PresenceState.OFFLINE,
|
|
174
|
+
})),
|
|
175
|
+
|
|
176
|
+
pipeline: {
|
|
177
|
+
// TODO(burdon): Pick properties from credentials if needed.
|
|
178
|
+
// currentEpoch: space.dataPipeline.currentEpoch,
|
|
179
|
+
// appliedEpoch: space.dataPipeline.appliedEpoch,
|
|
180
|
+
|
|
181
|
+
controlFeeds: space.inner.controlPipeline.state.feeds.map((feed) => feed.key),
|
|
182
|
+
currentControlTimeframe: space.inner.controlPipeline.state.timeframe,
|
|
183
|
+
targetControlTimeframe: space.inner.controlPipeline.state.targetTimeframe,
|
|
184
|
+
totalControlTimeframe: space.inner.controlPipeline.state.endTimeframe,
|
|
185
|
+
|
|
186
|
+
// TODO(burdon): Empty?
|
|
187
|
+
dataFeeds: space.dataPipeline.pipelineState?.feeds.map((feed) => feed.key) ?? [],
|
|
188
|
+
startDataTimeframe: space.dataPipeline.pipelineState?.startTimeframe,
|
|
189
|
+
currentDataTimeframe: space.dataPipeline.pipelineState?.timeframe,
|
|
190
|
+
targetDataTimeframe: space.dataPipeline.pipelineState?.targetTimeframe,
|
|
191
|
+
totalDataTimeframe: space.dataPipeline.pipelineState?.endTimeframe,
|
|
192
|
+
},
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
// TODO(burdon): May not be open?
|
|
196
|
+
if (space.dataPipeline.itemManager) {
|
|
197
|
+
Object.assign(stats, {
|
|
198
|
+
properties: getProperties(space),
|
|
199
|
+
db: {
|
|
200
|
+
objects: space.dataPipeline.itemManager.entities.size,
|
|
201
|
+
},
|
|
202
|
+
} as SpaceStats);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// TODO(burdon): Factor out.
|
|
206
|
+
if (stats.metrics) {
|
|
207
|
+
const { open, ready } = stats.metrics;
|
|
208
|
+
stats.metrics.startupTime = open && ready && ready.getTime() - open.getTime();
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
return stats;
|
|
212
|
+
};
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2022 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
export type Platform = {
|
|
6
|
+
type: 'browser' | 'shared-worker' | 'node';
|
|
7
|
+
userAgent?: string;
|
|
8
|
+
platform?: string;
|
|
9
|
+
runtime?: string;
|
|
10
|
+
uptime?: number;
|
|
11
|
+
memory?: {
|
|
12
|
+
rss: number;
|
|
13
|
+
heapTotal: number;
|
|
14
|
+
heapUsed: number;
|
|
15
|
+
external: number;
|
|
16
|
+
arrayBuffers: number;
|
|
17
|
+
};
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export const getPlatform = (): Platform => {
|
|
21
|
+
if ((process as any).browser) {
|
|
22
|
+
if (typeof window !== 'undefined') {
|
|
23
|
+
// Browser.
|
|
24
|
+
const { userAgent } = window.navigator;
|
|
25
|
+
return {
|
|
26
|
+
type: 'browser',
|
|
27
|
+
userAgent,
|
|
28
|
+
uptime: Math.floor((Date.now() - window.performance.timeOrigin) / 1_000),
|
|
29
|
+
};
|
|
30
|
+
} else {
|
|
31
|
+
// Shared worker.
|
|
32
|
+
return {
|
|
33
|
+
type: 'shared-worker',
|
|
34
|
+
uptime: Math.floor((Date.now() - performance.timeOrigin) / 1_000),
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
} else {
|
|
38
|
+
// Node.
|
|
39
|
+
const { platform, version, arch } = process;
|
|
40
|
+
return {
|
|
41
|
+
type: 'node',
|
|
42
|
+
platform: `${platform} ${version} ${arch}`,
|
|
43
|
+
runtime: process.version,
|
|
44
|
+
uptime: Math.floor(process.uptime()),
|
|
45
|
+
memory: process.memoryUsage(),
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
};
|
|
@@ -2,9 +2,18 @@
|
|
|
2
2
|
// Copyright 2023 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
+
import { expect } from 'chai';
|
|
6
|
+
|
|
7
|
+
import { DocumentModel, DocumentModelState, MutationBuilder } from '@dxos/document-model';
|
|
8
|
+
import { createModelMutation, encodeModelMutation, genesisMutation } from '@dxos/echo-db';
|
|
9
|
+
import { WriteReceipt } from '@dxos/feed-store';
|
|
10
|
+
import { PublicKey } from '@dxos/keys';
|
|
11
|
+
import { log } from '@dxos/log';
|
|
5
12
|
import { MemorySignalManagerContext } from '@dxos/messaging';
|
|
6
13
|
import { Invitation } from '@dxos/protocols/proto/dxos/client/services';
|
|
7
14
|
import { describe, test } from '@dxos/test';
|
|
15
|
+
import { Timeframe } from '@dxos/timeframe';
|
|
16
|
+
import { range } from '@dxos/util';
|
|
8
17
|
|
|
9
18
|
import { createServiceContext, syncItemsLocal } from '../testing';
|
|
10
19
|
import { performInvitation } from '../testing/invitation-utils';
|
|
@@ -16,11 +25,61 @@ describe('services/ServiceContext', () => {
|
|
|
16
25
|
await device1.createIdentity();
|
|
17
26
|
const space1 = await device1.dataSpaceManager!.createSpace();
|
|
18
27
|
|
|
28
|
+
const itemId = PublicKey.random().toHex();
|
|
29
|
+
await space1.dataPipeline.databaseHost!.getWriteStream()?.write({
|
|
30
|
+
batch: genesisMutation(itemId, DocumentModel.meta.type),
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
let counter = 0;
|
|
34
|
+
|
|
35
|
+
for (const _ in range(5)) {
|
|
36
|
+
const receipts: WriteReceipt[] = [];
|
|
37
|
+
for (const _ in range(50)) {
|
|
38
|
+
receipts.push(
|
|
39
|
+
await space1.dataPipeline.databaseHost!.getWriteStream()!.write({
|
|
40
|
+
batch: createModelMutation(
|
|
41
|
+
itemId,
|
|
42
|
+
encodeModelMutation(DocumentModel.meta, new MutationBuilder().set('counter', ++counter).build()),
|
|
43
|
+
),
|
|
44
|
+
}),
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
await space1.dataPipeline.pipelineState!.waitUntilTimeframe(
|
|
49
|
+
Timeframe.merge(...receipts.map((receipt) => new Timeframe([[receipt.feedKey, receipt.seq]]))),
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
await space1.createEpoch();
|
|
53
|
+
log.info('epoch', { number: space1.dataPipeline.currentEpoch?.subject.assertion.number });
|
|
54
|
+
}
|
|
55
|
+
|
|
19
56
|
const device2 = createServiceContext({ signalContext: networkContext });
|
|
20
57
|
await Promise.all(performInvitation({ host: device1, guest: device2, options: { kind: Invitation.Kind.DEVICE } }));
|
|
21
58
|
|
|
22
59
|
await device2.dataSpaceManager!.waitUntilSpaceReady(space1!.key);
|
|
23
60
|
const space2 = await device2.dataSpaceManager!.spaces.get(space1.key);
|
|
61
|
+
|
|
62
|
+
log.info('peer 2', {
|
|
63
|
+
currentEpoch: space2!.dataPipeline.currentEpoch?.subject.assertion.number,
|
|
64
|
+
appliedEpoch: space2!.dataPipeline.appliedEpoch?.subject.assertion.number,
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
await space2?.dataPipeline.onNewEpoch.waitForCondition(
|
|
68
|
+
() =>
|
|
69
|
+
space2!.dataPipeline.appliedEpoch?.subject.assertion.number ===
|
|
70
|
+
space1.dataPipeline.currentEpoch?.subject.assertion.number,
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
expect(space2!.dataPipeline.currentEpoch!.subject.assertion.number).to.equal(
|
|
74
|
+
space1.dataPipeline.currentEpoch!.subject.assertion.number,
|
|
75
|
+
);
|
|
76
|
+
expect(space2!.dataPipeline.appliedEpoch!.subject.assertion.number).to.equal(
|
|
77
|
+
space1.dataPipeline.appliedEpoch!.subject.assertion.number,
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
const item2 = space2!.dataPipeline.itemManager.entities.get(itemId);
|
|
81
|
+
expect((item2!.state as DocumentModelState).data.counter).to.equal(counter);
|
|
82
|
+
|
|
24
83
|
await syncItemsLocal(space1.dataPipeline, space2!.dataPipeline);
|
|
25
84
|
});
|
|
26
85
|
|
|
@@ -2,9 +2,8 @@
|
|
|
2
2
|
// Copyright 2022 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import invariant from 'tiny-invariant';
|
|
6
|
-
|
|
7
5
|
import { Trigger } from '@dxos/async';
|
|
6
|
+
import { Context } from '@dxos/context';
|
|
8
7
|
import { CredentialProcessor, getCredentialAssertion } from '@dxos/credentials';
|
|
9
8
|
import { failUndefined } from '@dxos/debug';
|
|
10
9
|
import {
|
|
@@ -15,6 +14,7 @@ import {
|
|
|
15
14
|
SnapshotStore,
|
|
16
15
|
} from '@dxos/echo-pipeline';
|
|
17
16
|
import { FeedFactory, FeedStore } from '@dxos/feed-store';
|
|
17
|
+
import { invariant } from '@dxos/invariant';
|
|
18
18
|
import { Keyring } from '@dxos/keyring';
|
|
19
19
|
import { PublicKey } from '@dxos/keys';
|
|
20
20
|
import { log } from '@dxos/log';
|
|
@@ -27,6 +27,8 @@ import type { FeedMessage } from '@dxos/protocols/proto/dxos/echo/feed';
|
|
|
27
27
|
import { Credential } from '@dxos/protocols/proto/dxos/halo/credentials';
|
|
28
28
|
import { Storage } from '@dxos/random-access-storage';
|
|
29
29
|
import { BlobStore } from '@dxos/teleport-extension-object-sync';
|
|
30
|
+
import { trace as Trace } from '@dxos/tracing';
|
|
31
|
+
import { safeInstanceof } from '@dxos/util';
|
|
30
32
|
|
|
31
33
|
import { CreateIdentityOptions, IdentityManager, JoinIdentityParams } from '../identity';
|
|
32
34
|
import {
|
|
@@ -41,6 +43,9 @@ import { DataSpaceManager, SigningContext } from '../spaces';
|
|
|
41
43
|
* Shared backend for all client services.
|
|
42
44
|
*/
|
|
43
45
|
// TODO(burdon): Rename/break-up into smaller components. And/or make members private.
|
|
46
|
+
// TODO(dmaretskyi): Gets duplicated in CJS build between normal and testing bundles.
|
|
47
|
+
@safeInstanceof('dxos.client-services.ServiceContext')
|
|
48
|
+
@Trace.resource()
|
|
44
49
|
export class ServiceContext {
|
|
45
50
|
public readonly initialized = new Trigger();
|
|
46
51
|
public readonly dataServiceSubscriptions = new DataServiceSubscriptions();
|
|
@@ -86,8 +91,9 @@ export class ServiceContext {
|
|
|
86
91
|
root: storage.createDirectory('feeds'),
|
|
87
92
|
signer: this.keyring,
|
|
88
93
|
hypercore: {
|
|
89
|
-
valueEncoding
|
|
90
|
-
|
|
94
|
+
valueEncoding,
|
|
95
|
+
stats: true,
|
|
96
|
+
},
|
|
91
97
|
})
|
|
92
98
|
});
|
|
93
99
|
|
|
@@ -122,21 +128,21 @@ export class ServiceContext {
|
|
|
122
128
|
);
|
|
123
129
|
}
|
|
124
130
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
131
|
+
@Trace.span()
|
|
132
|
+
async open(ctx: Context) {
|
|
128
133
|
await this._checkStorageVersion();
|
|
129
134
|
|
|
130
135
|
log('opening...');
|
|
136
|
+
log.trace('dxos.sdk.service-context.open', trace.begin({ id: this._instanceId }));
|
|
131
137
|
await this.signalManager.open();
|
|
132
138
|
await this.networkManager.open();
|
|
133
139
|
await this.spaceManager.open();
|
|
134
|
-
await this.identityManager.open();
|
|
140
|
+
await this.identityManager.open(ctx);
|
|
135
141
|
if (this.identityManager.identity) {
|
|
136
|
-
await this._initialize();
|
|
142
|
+
await this._initialize(ctx);
|
|
137
143
|
}
|
|
138
|
-
log('opened');
|
|
139
144
|
log.trace('dxos.sdk.service-context.open', trace.end({ id: this._instanceId }));
|
|
145
|
+
log('opened');
|
|
140
146
|
}
|
|
141
147
|
|
|
142
148
|
async close() {
|
|
@@ -156,8 +162,7 @@ export class ServiceContext {
|
|
|
156
162
|
|
|
157
163
|
async createIdentity(params: CreateIdentityOptions = {}) {
|
|
158
164
|
const identity = await this.identityManager.createIdentity(params);
|
|
159
|
-
|
|
160
|
-
await this._initialize();
|
|
165
|
+
await this._initialize(new Context());
|
|
161
166
|
return identity;
|
|
162
167
|
}
|
|
163
168
|
|
|
@@ -169,8 +174,7 @@ export class ServiceContext {
|
|
|
169
174
|
|
|
170
175
|
private async _acceptIdentity(params: JoinIdentityParams) {
|
|
171
176
|
const identity = await this.identityManager.acceptIdentity(params);
|
|
172
|
-
|
|
173
|
-
await this._initialize();
|
|
177
|
+
await this._initialize(new Context());
|
|
174
178
|
return identity;
|
|
175
179
|
}
|
|
176
180
|
|
|
@@ -183,7 +187,8 @@ export class ServiceContext {
|
|
|
183
187
|
}
|
|
184
188
|
|
|
185
189
|
// Called when identity is created.
|
|
186
|
-
|
|
190
|
+
@Trace.span()
|
|
191
|
+
private async _initialize(ctx: Context) {
|
|
187
192
|
log('initializing spaces...');
|
|
188
193
|
const identity = this.identityManager.identity ?? failUndefined();
|
|
189
194
|
const signingContext: SigningContext = {
|
|
@@ -242,6 +247,7 @@ export class ServiceContext {
|
|
|
242
247
|
}
|
|
243
248
|
},
|
|
244
249
|
};
|
|
250
|
+
|
|
245
251
|
await identity.space.spaceState.addCredentialProcessor(this._deviceSpaceSync);
|
|
246
252
|
}
|
|
247
253
|
}
|
|
@@ -8,6 +8,7 @@ import { rmSync } from 'node:fs';
|
|
|
8
8
|
|
|
9
9
|
import { asyncTimeout, latch, Trigger } from '@dxos/async';
|
|
10
10
|
import { Config } from '@dxos/config';
|
|
11
|
+
import { Context } from '@dxos/context';
|
|
11
12
|
import { verifyPresentation } from '@dxos/credentials';
|
|
12
13
|
import { PublicKey } from '@dxos/keys';
|
|
13
14
|
import { MemorySignalManagerContext } from '@dxos/messaging';
|
|
@@ -30,7 +31,7 @@ describe('ClientServicesHost', () => {
|
|
|
30
31
|
|
|
31
32
|
test('queryCredentials', async () => {
|
|
32
33
|
const host = createServiceHost(new Config(), new MemorySignalManagerContext());
|
|
33
|
-
await host.open();
|
|
34
|
+
await host.open(new Context());
|
|
34
35
|
afterTest(() => host.close());
|
|
35
36
|
|
|
36
37
|
await host.services.IdentityService!.createIdentity({});
|
|
@@ -49,14 +50,14 @@ describe('ClientServicesHost', () => {
|
|
|
49
50
|
|
|
50
51
|
test('write and query credentials', async () => {
|
|
51
52
|
const host = createServiceHost(new Config(), new MemorySignalManagerContext());
|
|
52
|
-
await host.open();
|
|
53
|
+
await host.open(new Context());
|
|
53
54
|
afterTest(() => host.close());
|
|
54
55
|
|
|
55
56
|
await host.services.IdentityService!.createIdentity({});
|
|
56
57
|
|
|
57
58
|
const testCredential = await createMockCredential({
|
|
58
|
-
signer: host.
|
|
59
|
-
issuer: host.
|
|
59
|
+
signer: host.context.keyring,
|
|
60
|
+
issuer: host.context.identityManager.identity!.deviceKey,
|
|
60
61
|
});
|
|
61
62
|
|
|
62
63
|
// Test if Identity exposes haloSpace key.
|
|
@@ -86,14 +87,14 @@ describe('ClientServicesHost', () => {
|
|
|
86
87
|
|
|
87
88
|
test('sign presentation', async () => {
|
|
88
89
|
const host = createServiceHost(new Config(), new MemorySignalManagerContext());
|
|
89
|
-
await host.open();
|
|
90
|
+
await host.open(new Context());
|
|
90
91
|
afterTest(() => host.close());
|
|
91
92
|
|
|
92
93
|
await host.services.IdentityService!.createIdentity({});
|
|
93
94
|
|
|
94
95
|
const testCredential = await createMockCredential({
|
|
95
|
-
signer: host.
|
|
96
|
-
issuer: host.
|
|
96
|
+
signer: host.context.keyring,
|
|
97
|
+
issuer: host.context.identityManager.identity!.deviceKey,
|
|
97
98
|
});
|
|
98
99
|
|
|
99
100
|
const nonce = new Uint8Array([0, 0, 0, 0]);
|
|
@@ -117,11 +118,11 @@ describe('ClientServicesHost', () => {
|
|
|
117
118
|
});
|
|
118
119
|
{
|
|
119
120
|
const host = createServiceHost(config, new MemorySignalManagerContext());
|
|
120
|
-
await host.open();
|
|
121
|
+
await host.open(new Context());
|
|
121
122
|
|
|
122
123
|
await host.services.IdentityService?.createIdentity({});
|
|
123
124
|
|
|
124
|
-
expect(host.
|
|
125
|
+
expect(host.context.storage.size).to.exist;
|
|
125
126
|
|
|
126
127
|
await asyncTimeout(host.reset(), 1000);
|
|
127
128
|
await host.close();
|
|
@@ -129,7 +130,7 @@ describe('ClientServicesHost', () => {
|
|
|
129
130
|
|
|
130
131
|
{
|
|
131
132
|
const host = createServiceHost(config, new MemorySignalManagerContext());
|
|
132
|
-
await host.open();
|
|
133
|
+
await host.open(new Context());
|
|
133
134
|
const trigger = new Trigger<Identity>();
|
|
134
135
|
|
|
135
136
|
const stream = host.services.IdentityService?.queryIdentity();
|
|
@@ -141,7 +142,7 @@ describe('ClientServicesHost', () => {
|
|
|
141
142
|
}
|
|
142
143
|
});
|
|
143
144
|
await expect(asyncTimeout(trigger.wait(), 200)).to.be.rejectedWith();
|
|
144
|
-
stream?.close();
|
|
145
|
+
await stream?.close();
|
|
145
146
|
await host.close();
|
|
146
147
|
}
|
|
147
148
|
}).onlyEnvironments('nodejs', 'chromium', 'firefox');
|