@dxos/client-services 0.3.11-main.3f845e1 → 0.3.11-main.463e5cd
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-PYCJUZ6O.mjs → chunk-HQLYQQH7.mjs} +245 -117
- package/dist/lib/browser/chunk-HQLYQQH7.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +42 -33
- 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 +4 -11
- package/dist/lib/browser/packlets/testing/index.mjs.map +3 -3
- package/dist/lib/node/{chunk-KSD7OWNS.cjs → chunk-QJFUVEBN.cjs} +384 -256
- package/dist/lib/node/chunk-QJFUVEBN.cjs.map +7 -0
- package/dist/lib/node/index.cjs +77 -68
- 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 +11 -18
- package/dist/lib/node/packlets/testing/index.cjs.map +3 -3
- package/dist/types/src/packlets/identity/identity-service.d.ts +3 -3
- package/dist/types/src/packlets/identity/identity-service.d.ts.map +1 -1
- package/dist/types/src/packlets/invitations/device-invitation-protocol.d.ts +2 -0
- package/dist/types/src/packlets/invitations/device-invitation-protocol.d.ts.map +1 -1
- package/dist/types/src/packlets/invitations/invitation-protocol.d.ts +28 -3
- package/dist/types/src/packlets/invitations/invitation-protocol.d.ts.map +1 -1
- package/dist/types/src/packlets/invitations/invitations-handler.d.ts.map +1 -1
- package/dist/types/src/packlets/invitations/space-invitation-protocol.d.ts +2 -0
- package/dist/types/src/packlets/invitations/space-invitation-protocol.d.ts.map +1 -1
- package/dist/types/src/packlets/network/network-service.d.ts.map +1 -1
- package/dist/types/src/packlets/services/diagnostics.d.ts +32 -16
- package/dist/types/src/packlets/services/diagnostics.d.ts.map +1 -1
- package/dist/types/src/packlets/services/platform.d.ts +1 -14
- package/dist/types/src/packlets/services/platform.d.ts.map +1 -1
- package/dist/types/src/packlets/services/service-host.d.ts.map +1 -1
- package/dist/types/src/packlets/spaces/automerge-space-state.d.ts +2 -0
- package/dist/types/src/packlets/spaces/automerge-space-state.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 +2 -0
- package/dist/types/src/packlets/spaces/data-space.d.ts.map +1 -1
- package/dist/types/src/packlets/system/system-service.d.ts +3 -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 +2 -0
- package/dist/types/src/packlets/vault/iframe-host-runtime.d.ts.map +1 -1
- package/dist/types/src/packlets/vault/index.d.ts +1 -1
- package/dist/types/src/packlets/vault/index.d.ts.map +1 -1
- package/dist/types/src/packlets/vault/{iframe-proxy-runtime.d.ts → shared-worker-connection.d.ts} +12 -5
- package/dist/types/src/packlets/vault/shared-worker-connection.d.ts.map +1 -0
- package/dist/types/src/packlets/vault/worker-runtime.d.ts +10 -3
- package/dist/types/src/packlets/vault/worker-runtime.d.ts.map +1 -1
- package/dist/types/src/packlets/vault/worker-session.d.ts +2 -2
- package/dist/types/src/packlets/vault/worker-session.d.ts.map +1 -1
- package/dist/types/src/version.d.ts +1 -1
- package/package.json +35 -35
- package/src/packlets/identity/identity-manager.ts +1 -1
- package/src/packlets/identity/identity-service.test.ts +1 -1
- package/src/packlets/identity/identity-service.ts +6 -3
- package/src/packlets/invitations/device-invitation-protocol.test.ts +14 -0
- package/src/packlets/invitations/device-invitation-protocol.ts +14 -0
- package/src/packlets/invitations/invitation-protocol.ts +44 -6
- package/src/packlets/invitations/invitations-handler.ts +20 -18
- package/src/packlets/invitations/space-invitation-protocol.test.ts +28 -0
- package/src/packlets/invitations/space-invitation-protocol.ts +11 -0
- package/src/packlets/network/network-service.ts +5 -1
- package/src/packlets/services/diagnostics.ts +52 -16
- package/src/packlets/services/platform.ts +7 -19
- package/src/packlets/services/service-host.ts +27 -12
- package/src/packlets/spaces/automerge-space-state.ts +4 -0
- package/src/packlets/spaces/data-space-manager.ts +5 -1
- package/src/packlets/spaces/data-space.ts +37 -4
- package/src/packlets/spaces/spaces-service.ts +3 -3
- package/src/packlets/system/system-service.ts +7 -1
- package/src/packlets/testing/invitation-utils.ts +2 -10
- package/src/packlets/vault/iframe-host-runtime.ts +2 -0
- package/src/packlets/vault/index.ts +1 -1
- package/src/packlets/vault/{iframe-proxy-runtime.ts → shared-worker-connection.ts} +18 -6
- package/src/packlets/vault/worker-runtime.ts +27 -9
- package/src/packlets/vault/worker-session.ts +11 -8
- package/src/version.ts +1 -1
- package/dist/lib/browser/chunk-PYCJUZ6O.mjs.map +0 -7
- package/dist/lib/node/chunk-KSD7OWNS.cjs.map +0 -7
- package/dist/types/src/packlets/vault/iframe-proxy-runtime.d.ts.map +0 -1
|
@@ -2,20 +2,7 @@
|
|
|
2
2
|
// Copyright 2022 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
|
|
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
|
-
};
|
|
5
|
+
import { Platform } from '@dxos/protocols/proto/dxos/client/services';
|
|
19
6
|
|
|
20
7
|
export const getPlatform = (): Platform => {
|
|
21
8
|
if ((process as any).browser) {
|
|
@@ -23,14 +10,14 @@ export const getPlatform = (): Platform => {
|
|
|
23
10
|
// Browser.
|
|
24
11
|
const { userAgent } = window.navigator;
|
|
25
12
|
return {
|
|
26
|
-
type:
|
|
13
|
+
type: Platform.PLATFORM_TYPE.BROWSER,
|
|
27
14
|
userAgent,
|
|
28
15
|
uptime: Math.floor((Date.now() - window.performance.timeOrigin) / 1_000),
|
|
29
16
|
};
|
|
30
17
|
} else {
|
|
31
18
|
// Shared worker.
|
|
32
19
|
return {
|
|
33
|
-
type:
|
|
20
|
+
type: Platform.PLATFORM_TYPE.SHARED_WORKER,
|
|
34
21
|
uptime: Math.floor((Date.now() - performance.timeOrigin) / 1_000),
|
|
35
22
|
};
|
|
36
23
|
}
|
|
@@ -38,9 +25,10 @@ export const getPlatform = (): Platform => {
|
|
|
38
25
|
// Node.
|
|
39
26
|
const { platform, version, arch } = process;
|
|
40
27
|
return {
|
|
41
|
-
type:
|
|
42
|
-
platform
|
|
43
|
-
|
|
28
|
+
type: Platform.PLATFORM_TYPE.NODE,
|
|
29
|
+
platform,
|
|
30
|
+
arch,
|
|
31
|
+
runtime: version,
|
|
44
32
|
uptime: Math.floor(process.uptime()),
|
|
45
33
|
memory: process.memoryUsage(),
|
|
46
34
|
};
|
|
@@ -8,7 +8,7 @@ import { type Config } from '@dxos/config';
|
|
|
8
8
|
import { Context } from '@dxos/context';
|
|
9
9
|
import { DocumentModel } from '@dxos/document-model';
|
|
10
10
|
import { DataServiceImpl } from '@dxos/echo-pipeline';
|
|
11
|
-
import { type TypedObject, base } from '@dxos/echo-schema';
|
|
11
|
+
import { type TypedObject, base, getRawDoc } from '@dxos/echo-schema';
|
|
12
12
|
import { invariant } from '@dxos/invariant';
|
|
13
13
|
import { PublicKey } from '@dxos/keys';
|
|
14
14
|
import { log } from '@dxos/log';
|
|
@@ -28,7 +28,7 @@ import { ServiceContext } from './service-context';
|
|
|
28
28
|
import { ServiceRegistry } from './service-registry';
|
|
29
29
|
import { DevicesServiceImpl } from '../devices';
|
|
30
30
|
import { DevtoolsServiceImpl, DevtoolsHostEvents } from '../devtools';
|
|
31
|
-
import { type CreateIdentityOptions
|
|
31
|
+
import { IdentityServiceImpl, type CreateIdentityOptions } from '../identity';
|
|
32
32
|
import { InvitationsServiceImpl } from '../invitations';
|
|
33
33
|
import { Lock, type ResourceLock } from '../locks';
|
|
34
34
|
import { LoggingServiceImpl } from '../logging';
|
|
@@ -260,7 +260,7 @@ export class ClientServicesHost {
|
|
|
260
260
|
SystemService: this._systemService,
|
|
261
261
|
|
|
262
262
|
IdentityService: new IdentityServiceImpl(
|
|
263
|
-
(params) => this._createIdentity(params),
|
|
263
|
+
(params, useAutomerge) => this._createIdentity(params, useAutomerge),
|
|
264
264
|
this._serviceContext.identityManager,
|
|
265
265
|
this._serviceContext.keyring,
|
|
266
266
|
(profile) => this._serviceContext.broadcastProfileUpdate(profile),
|
|
@@ -351,21 +351,36 @@ export class ClientServicesHost {
|
|
|
351
351
|
await this._callbacks?.onReset?.();
|
|
352
352
|
}
|
|
353
353
|
|
|
354
|
-
private async _createIdentity(params
|
|
354
|
+
private async _createIdentity(params: CreateIdentityOptions, useAutomerge: boolean) {
|
|
355
355
|
const identity = await this._serviceContext.createIdentity(params);
|
|
356
356
|
|
|
357
357
|
// Setup default space.
|
|
358
358
|
await this._serviceContext.initialized.wait();
|
|
359
359
|
const space = await this._serviceContext.dataSpaceManager!.createSpace();
|
|
360
|
-
|
|
360
|
+
|
|
361
|
+
const obj: TypedObject = new Properties(undefined, { automerge: useAutomerge });
|
|
361
362
|
obj[defaultKey] = identity.identityKey.toHex();
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
363
|
+
|
|
364
|
+
if (!useAutomerge) {
|
|
365
|
+
await this._serviceRegistry.services.DataService!.write({
|
|
366
|
+
spaceKey: space.key,
|
|
367
|
+
batch: {
|
|
368
|
+
objects: [createGenesisMutationFromTypedObject(obj)],
|
|
369
|
+
},
|
|
370
|
+
});
|
|
371
|
+
await this._serviceRegistry.services.DataService!.flush({ spaceKey: space.key });
|
|
372
|
+
} else {
|
|
373
|
+
// TODO(dmaretskyi): Refactor this.
|
|
374
|
+
const automergeIndex = space.automergeSpaceState.rootUrl;
|
|
375
|
+
invariant(automergeIndex);
|
|
376
|
+
const document = await this._serviceContext.automergeHost.repo.find(automergeIndex as any);
|
|
377
|
+
await document.whenReady();
|
|
378
|
+
|
|
379
|
+
document.change((doc: any) => {
|
|
380
|
+
doc.objects ??= {};
|
|
381
|
+
doc.objects[obj[base]._id] = getRawDoc(obj).handle.docSync();
|
|
382
|
+
});
|
|
383
|
+
}
|
|
369
384
|
|
|
370
385
|
return identity;
|
|
371
386
|
}
|
|
@@ -9,6 +9,8 @@ export class AutomergeSpaceState implements CredentialProcessor {
|
|
|
9
9
|
public rootUrl: string | undefined = undefined;
|
|
10
10
|
public lastEpoch: SpecificCredential<Epoch> | undefined = undefined;
|
|
11
11
|
|
|
12
|
+
constructor(private readonly _onNewRoot: (rootUrl: string) => void) {}
|
|
13
|
+
|
|
12
14
|
async processCredential(credential: Credential) {
|
|
13
15
|
if (!checkCredentialType(credential, 'dxos.halo.credentials.Epoch')) {
|
|
14
16
|
return;
|
|
@@ -17,6 +19,8 @@ export class AutomergeSpaceState implements CredentialProcessor {
|
|
|
17
19
|
this.lastEpoch = credential;
|
|
18
20
|
if (credential.subject.assertion.automergeRoot) {
|
|
19
21
|
this.rootUrl = credential.subject.assertion.automergeRoot;
|
|
22
|
+
|
|
23
|
+
this._onNewRoot(this.rootUrl);
|
|
20
24
|
}
|
|
21
25
|
}
|
|
22
26
|
}
|
|
@@ -143,6 +143,9 @@ export class DataSpaceManager {
|
|
|
143
143
|
const space = await this._constructSpace(metadata);
|
|
144
144
|
|
|
145
145
|
const automergeRoot = this._automergeHost.repo.create();
|
|
146
|
+
automergeRoot.change((doc: any) => {
|
|
147
|
+
doc.experimental_spaceKey = spaceKey.toHex();
|
|
148
|
+
});
|
|
146
149
|
|
|
147
150
|
const credentials = await spaceGenesis(this._keyring, this._signingContext, space.inner, automergeRoot.url);
|
|
148
151
|
await this._metadataStore.addSpace(metadata);
|
|
@@ -222,12 +225,13 @@ export class DataSpaceManager {
|
|
|
222
225
|
credentialProvider: createAuthProvider(this._signingContext.credentialSigner),
|
|
223
226
|
credentialAuthenticator: deferFunction(() => dataSpace.authVerifier.verifier),
|
|
224
227
|
},
|
|
225
|
-
|
|
228
|
+
onAuthorizedConnection: (session) => {
|
|
226
229
|
session.addExtension(
|
|
227
230
|
'dxos.mesh.teleport.gossip',
|
|
228
231
|
gossip.createExtension({ remotePeerId: session.remotePeerId }),
|
|
229
232
|
);
|
|
230
233
|
session.addExtension('dxos.mesh.teleport.notarization', dataSpace.notarizationPlugin.createExtension());
|
|
234
|
+
this._automergeHost.authorizeDevice(space.key, session.remotePeerId);
|
|
231
235
|
session.addExtension('dxos.mesh.teleport.automerge', this._automergeHost.createExtension());
|
|
232
236
|
},
|
|
233
237
|
onAuthFailure: () => {
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
// Copyright 2022 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import { Event, scheduleTask, sleep, synchronized, trackLeaks } from '@dxos/async';
|
|
5
|
+
import { Event, asyncTimeout, scheduleTask, sleep, synchronized, trackLeaks } from '@dxos/async';
|
|
6
6
|
import { AUTH_TIMEOUT } from '@dxos/client-protocol';
|
|
7
|
-
import { cancelWithContext, Context } from '@dxos/context';
|
|
7
|
+
import { cancelWithContext, Context, ContextDisposedError } from '@dxos/context';
|
|
8
8
|
import { timed } from '@dxos/debug';
|
|
9
9
|
import {
|
|
10
10
|
type MetadataStore,
|
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
type AutomergeHost,
|
|
16
16
|
} from '@dxos/echo-pipeline';
|
|
17
17
|
import { type FeedStore } from '@dxos/feed-store';
|
|
18
|
+
import { failedInvariant } from '@dxos/invariant';
|
|
18
19
|
import { type Keyring } from '@dxos/keyring';
|
|
19
20
|
import { PublicKey } from '@dxos/keys';
|
|
20
21
|
import { log } from '@dxos/log';
|
|
@@ -77,9 +78,12 @@ export type DataSpaceParams = {
|
|
|
77
78
|
const ENABLE_FEED_PURGE = false;
|
|
78
79
|
|
|
79
80
|
@trackLeaks('open', 'close')
|
|
81
|
+
@trace.resource()
|
|
80
82
|
export class DataSpace {
|
|
81
83
|
private _ctx = new Context();
|
|
84
|
+
@trace.info()
|
|
82
85
|
private readonly _inner: Space;
|
|
86
|
+
|
|
83
87
|
private readonly _gossip: Gossip;
|
|
84
88
|
private readonly _presence: Presence;
|
|
85
89
|
private readonly _keyring: Keyring;
|
|
@@ -92,7 +96,7 @@ export class DataSpace {
|
|
|
92
96
|
private readonly _automergeHost: AutomergeHost;
|
|
93
97
|
|
|
94
98
|
// TODO(dmaretskyi): Move into Space?
|
|
95
|
-
private readonly _automergeSpaceState = new AutomergeSpaceState();
|
|
99
|
+
private readonly _automergeSpaceState = new AutomergeSpaceState((rootUrl) => this._onNewAutomergeRoot(rootUrl));
|
|
96
100
|
|
|
97
101
|
private _state = SpaceState.CLOSED;
|
|
98
102
|
|
|
@@ -137,6 +141,7 @@ export class DataSpace {
|
|
|
137
141
|
log('new state', { state: SpaceState[this._state] });
|
|
138
142
|
}
|
|
139
143
|
|
|
144
|
+
@trace.info()
|
|
140
145
|
get key() {
|
|
141
146
|
return this._inner.key;
|
|
142
147
|
}
|
|
@@ -145,6 +150,7 @@ export class DataSpace {
|
|
|
145
150
|
return this._inner.isOpen;
|
|
146
151
|
}
|
|
147
152
|
|
|
153
|
+
@trace.info({ enum: SpaceState })
|
|
148
154
|
get state(): SpaceState {
|
|
149
155
|
return this._state;
|
|
150
156
|
}
|
|
@@ -174,6 +180,14 @@ export class DataSpace {
|
|
|
174
180
|
return this._automergeSpaceState;
|
|
175
181
|
}
|
|
176
182
|
|
|
183
|
+
@trace.info({ depth: null })
|
|
184
|
+
private get _automergeInfo() {
|
|
185
|
+
return {
|
|
186
|
+
rootUrl: this._automergeSpaceState.rootUrl,
|
|
187
|
+
lastEpoch: this._automergeSpaceState.lastEpoch,
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
|
|
177
191
|
@synchronized
|
|
178
192
|
async open() {
|
|
179
193
|
await this._open();
|
|
@@ -232,7 +246,7 @@ export class DataSpace {
|
|
|
232
246
|
this.metrics.pipelineInitBegin = new Date();
|
|
233
247
|
await this.initializeDataPipeline();
|
|
234
248
|
} catch (err) {
|
|
235
|
-
if (err instanceof CancelledError) {
|
|
249
|
+
if (err instanceof CancelledError || err instanceof ContextDisposedError) {
|
|
236
250
|
log('data pipeline initialization cancelled', err);
|
|
237
251
|
return;
|
|
238
252
|
}
|
|
@@ -363,6 +377,25 @@ export class DataSpace {
|
|
|
363
377
|
}
|
|
364
378
|
}
|
|
365
379
|
|
|
380
|
+
private _onNewAutomergeRoot(rootUrl: string) {
|
|
381
|
+
log('loading automerge root doc for space', { space: this.key, rootUrl });
|
|
382
|
+
const handle = this._automergeHost.repo.find(rootUrl as any);
|
|
383
|
+
|
|
384
|
+
queueMicrotask(async () => {
|
|
385
|
+
try {
|
|
386
|
+
await asyncTimeout(handle.whenReady(), 5_000);
|
|
387
|
+
const doc = handle.docSync() ?? failedInvariant();
|
|
388
|
+
if (!doc.experimental_spaceKey) {
|
|
389
|
+
handle.change((doc: any) => {
|
|
390
|
+
doc.experimental_spaceKey = this.key.toHex();
|
|
391
|
+
});
|
|
392
|
+
}
|
|
393
|
+
} catch (err) {
|
|
394
|
+
log.warn('error loading automerge root doc', { space: this.key, rootUrl, err });
|
|
395
|
+
}
|
|
396
|
+
});
|
|
397
|
+
}
|
|
398
|
+
|
|
366
399
|
// TODO(dmaretskyi): Use profile from signing context.
|
|
367
400
|
async updateOwnProfile(profile: ProfileDocument) {
|
|
368
401
|
const credential = await this._signingContext.credentialSigner.createCredential({
|
|
@@ -41,7 +41,7 @@ export class SpacesServiceImpl implements SpacesService {
|
|
|
41
41
|
|
|
42
42
|
async createSpace(): Promise<Space> {
|
|
43
43
|
if (!this._identityManager.identity) {
|
|
44
|
-
throw new Error('This device has no HALO identity available. See https://docs.dxos.org/guide/halo');
|
|
44
|
+
throw new Error('This device has no HALO identity available. See https://docs.dxos.org/guide/platform/halo');
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
const dataSpaceManager = await this._getDataSpaceManager();
|
|
@@ -222,8 +222,8 @@ export class SpacesServiceImpl implements SpacesService {
|
|
|
222
222
|
presence: member.removed
|
|
223
223
|
? SpaceMember.PresenceState.REMOVED
|
|
224
224
|
: isMe || peers.length > 0
|
|
225
|
-
|
|
226
|
-
|
|
225
|
+
? SpaceMember.PresenceState.ONLINE
|
|
226
|
+
: SpaceMember.PresenceState.OFFLINE,
|
|
227
227
|
peerStates: peers,
|
|
228
228
|
};
|
|
229
229
|
}),
|
|
@@ -12,16 +12,18 @@ import {
|
|
|
12
12
|
type UpdateStatusRequest,
|
|
13
13
|
type QueryStatusRequest,
|
|
14
14
|
type QueryStatusResponse,
|
|
15
|
+
type Platform,
|
|
15
16
|
} from '@dxos/protocols/proto/dxos/client/services';
|
|
16
17
|
import { jsonKeyReplacer, type MaybePromise } from '@dxos/util';
|
|
17
18
|
|
|
18
19
|
import { type Diagnostics } from '../services';
|
|
20
|
+
import { getPlatform } from '../services/platform';
|
|
19
21
|
|
|
20
22
|
export type SystemServiceOptions = {
|
|
21
23
|
config?: Config;
|
|
22
24
|
statusUpdate: Event<void>;
|
|
23
25
|
getCurrentStatus: () => SystemStatus;
|
|
24
|
-
getDiagnostics: () => Promise<Partial<Diagnostics>>;
|
|
26
|
+
getDiagnostics: () => Promise<Partial<Diagnostics['services']>>;
|
|
25
27
|
onUpdateStatus: (status: SystemStatus) => MaybePromise<void>;
|
|
26
28
|
onReset: () => MaybePromise<void>;
|
|
27
29
|
};
|
|
@@ -73,6 +75,10 @@ export class SystemServiceImpl implements SystemService {
|
|
|
73
75
|
};
|
|
74
76
|
}
|
|
75
77
|
|
|
78
|
+
async getPlatform(): Promise<Platform> {
|
|
79
|
+
return getPlatform();
|
|
80
|
+
}
|
|
81
|
+
|
|
76
82
|
async updateStatus({ status }: UpdateStatusRequest) {
|
|
77
83
|
await this._onUpdateStatus(status);
|
|
78
84
|
}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
//
|
|
4
4
|
|
|
5
5
|
import { Trigger } from '@dxos/async';
|
|
6
|
-
import { type AuthenticatingInvitation, type CancellableInvitation } from '@dxos/client-protocol';
|
|
6
|
+
import { InvitationEncoder, type AuthenticatingInvitation, type CancellableInvitation } from '@dxos/client-protocol';
|
|
7
7
|
import { invariant } from '@dxos/invariant';
|
|
8
8
|
import { Invitation } from '@dxos/protocols/proto/dxos/client/services';
|
|
9
9
|
|
|
@@ -13,15 +13,7 @@ import { ServiceContext } from '../services';
|
|
|
13
13
|
* Strip secrets from invitation before giving it to the peer.
|
|
14
14
|
*/
|
|
15
15
|
export const sanitizeInvitation = (invitation: Invitation): Invitation => {
|
|
16
|
-
return
|
|
17
|
-
invitationId: invitation.invitationId,
|
|
18
|
-
type: invitation.type,
|
|
19
|
-
kind: invitation.kind,
|
|
20
|
-
authMethod: invitation.authMethod,
|
|
21
|
-
swarmKey: invitation.swarmKey,
|
|
22
|
-
state: invitation.state,
|
|
23
|
-
timeout: invitation.timeout,
|
|
24
|
-
};
|
|
16
|
+
return InvitationEncoder.decode(InvitationEncoder.encode(invitation));
|
|
25
17
|
};
|
|
26
18
|
|
|
27
19
|
export type InvitationHost = {
|
|
@@ -30,6 +30,8 @@ export type IFrameHostRuntimeParams = {
|
|
|
30
30
|
*
|
|
31
31
|
* Holds a lock over the client services such that only one instance can run at a time.
|
|
32
32
|
* This should only be used when SharedWorker is not available.
|
|
33
|
+
*
|
|
34
|
+
* @deprecated
|
|
33
35
|
*/
|
|
34
36
|
export class IFrameHostRuntime {
|
|
35
37
|
private readonly _configProvider: IFrameHostRuntimeParams['config'];
|
|
@@ -20,18 +20,21 @@ import { getAsyncValue, type MaybePromise, type Provider } from '@dxos/util';
|
|
|
20
20
|
import { ShellRuntimeImpl } from './shell-runtime';
|
|
21
21
|
|
|
22
22
|
// NOTE: Keep as RpcPorts to avoid dependency on @dxos/rpc-tunnel so we don't depend on browser-specific apis.
|
|
23
|
-
export type
|
|
23
|
+
export type SharedWorkerConnectionOptions = {
|
|
24
24
|
config: Config | Provider<MaybePromise<Config>>;
|
|
25
25
|
systemPort: RpcPort;
|
|
26
|
+
/**
|
|
27
|
+
* @deprecated Only used with iframes.
|
|
28
|
+
*/
|
|
26
29
|
shellPort?: RpcPort;
|
|
27
30
|
};
|
|
28
31
|
|
|
29
32
|
/**
|
|
30
33
|
* Manages the client connection to the shared worker.
|
|
31
34
|
*/
|
|
32
|
-
export class
|
|
35
|
+
export class SharedWorkerConnection {
|
|
33
36
|
private readonly _id = String(Math.floor(Math.random() * 1000000));
|
|
34
|
-
private readonly _configProvider:
|
|
37
|
+
private readonly _configProvider: SharedWorkerConnectionOptions['config'];
|
|
35
38
|
private readonly _systemPort: RpcPort;
|
|
36
39
|
private readonly _shellPort?: RpcPort;
|
|
37
40
|
private _release = new Trigger();
|
|
@@ -40,7 +43,7 @@ export class IFrameProxyRuntime {
|
|
|
40
43
|
private _systemRpc!: ProtoRpcPeer<WorkerServiceBundle>;
|
|
41
44
|
private _shellRuntime?: ShellRuntimeImpl;
|
|
42
45
|
|
|
43
|
-
constructor({ config, systemPort, shellPort }:
|
|
46
|
+
constructor({ config, systemPort, shellPort }: SharedWorkerConnectionOptions) {
|
|
44
47
|
this._configProvider = config;
|
|
45
48
|
this._systemPort = systemPort;
|
|
46
49
|
this._shellPort = shellPort;
|
|
@@ -54,7 +57,12 @@ export class IFrameProxyRuntime {
|
|
|
54
57
|
return this._shellRuntime;
|
|
55
58
|
}
|
|
56
59
|
|
|
57
|
-
async open(
|
|
60
|
+
async open(
|
|
61
|
+
/**
|
|
62
|
+
* @deprecated Only used with iframes.
|
|
63
|
+
*/
|
|
64
|
+
origin: string,
|
|
65
|
+
) {
|
|
58
66
|
this._config = await getAsyncValue(this._configProvider);
|
|
59
67
|
|
|
60
68
|
this._transportService = new SimplePeerTransportService({
|
|
@@ -96,7 +104,11 @@ export class IFrameProxyRuntime {
|
|
|
96
104
|
async close() {
|
|
97
105
|
this._release.wake();
|
|
98
106
|
await this._shellRuntime?.close();
|
|
99
|
-
|
|
107
|
+
try {
|
|
108
|
+
await this._systemRpc.rpc.WorkerService.stop();
|
|
109
|
+
} catch {
|
|
110
|
+
// If this fails, the worker is probably already gone.
|
|
111
|
+
}
|
|
100
112
|
await this._systemRpc.close();
|
|
101
113
|
}
|
|
102
114
|
|
|
@@ -18,7 +18,13 @@ import { ClientServicesHost } from '../services';
|
|
|
18
18
|
export type CreateSessionParams = {
|
|
19
19
|
appPort: RpcPort;
|
|
20
20
|
systemPort: RpcPort;
|
|
21
|
-
shellPort
|
|
21
|
+
shellPort?: RpcPort;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export type WorkerRuntimeCallbacks = {
|
|
25
|
+
acquireLock: () => Promise<void>;
|
|
26
|
+
releaseLock: () => void;
|
|
27
|
+
onReset: () => Promise<void>;
|
|
22
28
|
};
|
|
23
29
|
|
|
24
30
|
/**
|
|
@@ -27,6 +33,8 @@ export type CreateSessionParams = {
|
|
|
27
33
|
* Tabs make requests to the `ClientServicesHost`, and provide a WebRTC gateway.
|
|
28
34
|
*/
|
|
29
35
|
export class WorkerRuntime {
|
|
36
|
+
private readonly _acquireLock: () => Promise<void>;
|
|
37
|
+
private readonly _releaseLock: () => void;
|
|
30
38
|
private readonly _transportFactory = new SimplePeerTransportProxyFactory();
|
|
31
39
|
private readonly _ready = new Trigger<Error | undefined>();
|
|
32
40
|
private readonly _sessions = new Set<WorkerSession>();
|
|
@@ -34,12 +42,15 @@ export class WorkerRuntime {
|
|
|
34
42
|
private _sessionForNetworking?: WorkerSession; // TODO(burdon): Expose to client QueryStatusResponse.
|
|
35
43
|
private _config!: Config;
|
|
36
44
|
|
|
37
|
-
constructor(
|
|
45
|
+
constructor(
|
|
46
|
+
private readonly _configProvider: () => MaybePromise<Config>,
|
|
47
|
+
{ acquireLock, releaseLock, onReset }: WorkerRuntimeCallbacks,
|
|
48
|
+
) {
|
|
49
|
+
this._acquireLock = acquireLock;
|
|
50
|
+
this._releaseLock = releaseLock;
|
|
38
51
|
this._clientServices = new ClientServicesHost({
|
|
39
52
|
callbacks: {
|
|
40
|
-
onReset: async () =>
|
|
41
|
-
self.close();
|
|
42
|
-
},
|
|
53
|
+
onReset: async () => onReset(),
|
|
43
54
|
},
|
|
44
55
|
});
|
|
45
56
|
}
|
|
@@ -51,6 +62,7 @@ export class WorkerRuntime {
|
|
|
51
62
|
async start() {
|
|
52
63
|
log('starting...');
|
|
53
64
|
try {
|
|
65
|
+
await this._acquireLock();
|
|
54
66
|
this._config = await this._configProvider();
|
|
55
67
|
const signals = this._config.get('runtime.services.signaling');
|
|
56
68
|
this._clientServices.initialize({
|
|
@@ -71,7 +83,8 @@ export class WorkerRuntime {
|
|
|
71
83
|
}
|
|
72
84
|
|
|
73
85
|
async stop() {
|
|
74
|
-
//
|
|
86
|
+
// Release the lock to notify remote clients that the worker is terminating.
|
|
87
|
+
this._releaseLock();
|
|
75
88
|
await this._clientServices.close();
|
|
76
89
|
}
|
|
77
90
|
|
|
@@ -87,10 +100,15 @@ export class WorkerRuntime {
|
|
|
87
100
|
readySignal: this._ready,
|
|
88
101
|
});
|
|
89
102
|
|
|
90
|
-
// When tab is closed.
|
|
103
|
+
// When tab is closed or client is destroyed.
|
|
91
104
|
session.onClose.set(async () => {
|
|
92
105
|
this._sessions.delete(session);
|
|
93
|
-
this.
|
|
106
|
+
if (this._sessions.size === 0) {
|
|
107
|
+
// Terminate the worker when all sessions are closed.
|
|
108
|
+
self.close();
|
|
109
|
+
} else {
|
|
110
|
+
this._reconnectWebrtc();
|
|
111
|
+
}
|
|
94
112
|
});
|
|
95
113
|
|
|
96
114
|
await session.open();
|
|
@@ -100,7 +118,7 @@ export class WorkerRuntime {
|
|
|
100
118
|
}
|
|
101
119
|
|
|
102
120
|
/**
|
|
103
|
-
* Selects one of the existing session
|
|
121
|
+
* Selects one of the existing session for WebRTC networking.
|
|
104
122
|
*/
|
|
105
123
|
private _reconnectWebrtc() {
|
|
106
124
|
log('reconnecting webrtc...');
|
|
@@ -21,7 +21,8 @@ export type WorkerSessionParams = {
|
|
|
21
21
|
serviceHost: ClientServicesHost;
|
|
22
22
|
systemPort: RpcPort;
|
|
23
23
|
appPort: RpcPort;
|
|
24
|
-
|
|
24
|
+
// TODO(wittjosiah): Remove shellPort.
|
|
25
|
+
shellPort?: RpcPort;
|
|
25
26
|
readySignal: Trigger<Error | undefined>;
|
|
26
27
|
};
|
|
27
28
|
|
|
@@ -30,7 +31,7 @@ export type WorkerSessionParams = {
|
|
|
30
31
|
*/
|
|
31
32
|
export class WorkerSession {
|
|
32
33
|
private readonly _clientRpc: ClientRpcServer;
|
|
33
|
-
private readonly _shellClientRpc
|
|
34
|
+
private readonly _shellClientRpc?: ClientRpcServer;
|
|
34
35
|
private readonly _iframeRpc: ProtoRpcPeer<IframeServiceBundle>;
|
|
35
36
|
private readonly _startTrigger = new Trigger();
|
|
36
37
|
private readonly _serviceHost: ClientServicesHost;
|
|
@@ -74,11 +75,13 @@ export class WorkerSession {
|
|
|
74
75
|
...middleware,
|
|
75
76
|
});
|
|
76
77
|
|
|
77
|
-
this._shellClientRpc =
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
78
|
+
this._shellClientRpc = shellPort
|
|
79
|
+
? new ClientRpcServer({
|
|
80
|
+
serviceRegistry: this._serviceHost.serviceRegistry,
|
|
81
|
+
port: shellPort,
|
|
82
|
+
...middleware,
|
|
83
|
+
})
|
|
84
|
+
: undefined;
|
|
82
85
|
|
|
83
86
|
this._iframeRpc = createProtoRpcPeer({
|
|
84
87
|
requested: iframeServiceBundle,
|
|
@@ -138,7 +141,7 @@ export class WorkerSession {
|
|
|
138
141
|
|
|
139
142
|
private async _maybeOpenShell() {
|
|
140
143
|
try {
|
|
141
|
-
await asyncTimeout(this._shellClientRpc.open(), 1_000);
|
|
144
|
+
this._shellClientRpc && (await asyncTimeout(this._shellClientRpc.open(), 1_000));
|
|
142
145
|
} catch {
|
|
143
146
|
log.info('No shell connected.');
|
|
144
147
|
}
|
package/src/version.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const DXOS_VERSION = "0.3.11-main.
|
|
1
|
+
export const DXOS_VERSION = "0.3.11-main.463e5cd";
|