@dxos/client-services 0.10.0 → 0.11.1
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/chunk-client-services-layer.mjs +11425 -0
- package/dist/lib/chunk-client-services-layer.mjs.map +1 -0
- package/dist/lib/chunk-decorate.mjs +9 -0
- package/dist/lib/index.mjs +731 -0
- package/dist/lib/index.mjs.map +1 -0
- package/dist/lib/packlets/diagnostics/browser-diagnostics-broadcast.mjs +72 -0
- package/dist/lib/packlets/diagnostics/browser-diagnostics-broadcast.mjs.map +1 -0
- package/dist/lib/packlets/diagnostics/diagnostics-broadcast.mjs +14 -0
- package/dist/lib/packlets/diagnostics/diagnostics-broadcast.mjs.map +1 -0
- package/dist/lib/packlets/locks/browser.mjs +105 -0
- package/dist/lib/packlets/locks/browser.mjs.map +1 -0
- package/dist/lib/packlets/locks/node.mjs +53 -0
- package/dist/lib/packlets/locks/node.mjs.map +1 -0
- package/dist/lib/testing.mjs +570 -0
- package/dist/lib/testing.mjs.map +1 -0
- package/dist/types/src/index.d.ts +2 -0
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/packlets/agents/edge-agent-manager.d.ts +21 -4
- package/dist/types/src/packlets/agents/edge-agent-manager.d.ts.map +1 -1
- package/dist/types/src/packlets/agents/edge-agent-service.d.ts +8 -7
- package/dist/types/src/packlets/agents/edge-agent-service.d.ts.map +1 -1
- package/dist/types/src/packlets/devices/devices-service.d.ts +7 -5
- package/dist/types/src/packlets/devices/devices-service.d.ts.map +1 -1
- package/dist/types/src/packlets/devtools/devtools.d.ts +30 -29
- package/dist/types/src/packlets/devtools/devtools.d.ts.map +1 -1
- package/dist/types/src/packlets/devtools/feeds.d.ts +1 -1
- package/dist/types/src/packlets/devtools/feeds.d.ts.map +1 -1
- package/dist/types/src/packlets/devtools/network.d.ts +2 -1
- 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/diagnostics/diagnostics-broadcast.d.ts +2 -2
- package/dist/types/src/packlets/diagnostics/diagnostics-broadcast.d.ts.map +1 -1
- package/dist/types/src/packlets/diagnostics/diagnostics-collector.d.ts.map +1 -1
- package/dist/types/src/packlets/diagnostics/diagnostics.d.ts +1 -8
- package/dist/types/src/packlets/diagnostics/diagnostics.d.ts.map +1 -1
- package/dist/types/src/packlets/identity/authenticator.d.ts +1 -1
- package/dist/types/src/packlets/identity/authenticator.d.ts.map +1 -1
- package/dist/types/src/packlets/identity/contacts-service.d.ts +9 -6
- package/dist/types/src/packlets/identity/contacts-service.d.ts.map +1 -1
- package/dist/types/src/packlets/identity/identity-manager.d.ts +32 -3
- package/dist/types/src/packlets/identity/identity-manager.d.ts.map +1 -1
- package/dist/types/src/packlets/identity/identity-recovery-manager.d.ts +23 -4
- package/dist/types/src/packlets/identity/identity-recovery-manager.d.ts.map +1 -1
- package/dist/types/src/packlets/identity/identity-service.d.ts +15 -20
- package/dist/types/src/packlets/identity/identity-service.d.ts.map +1 -1
- package/dist/types/src/packlets/identity/identity.d.ts +2 -2
- package/dist/types/src/packlets/identity/identity.d.ts.map +1 -1
- package/dist/types/src/packlets/invitations/edge-invitation-handler.d.ts.map +1 -1
- package/dist/types/src/packlets/invitations/invitations-handler.d.ts +17 -0
- package/dist/types/src/packlets/invitations/invitations-handler.d.ts.map +1 -1
- package/dist/types/src/packlets/invitations/invitations-manager.d.ts +25 -4
- package/dist/types/src/packlets/invitations/invitations-manager.d.ts.map +1 -1
- package/dist/types/src/packlets/invitations/invitations-service.d.ts +11 -11
- package/dist/types/src/packlets/invitations/invitations-service.d.ts.map +1 -1
- package/dist/types/src/packlets/logging/logging-service.d.ts +14 -12
- package/dist/types/src/packlets/logging/logging-service.d.ts.map +1 -1
- package/dist/types/src/packlets/metadata/index.d.ts +3 -0
- package/dist/types/src/packlets/metadata/index.d.ts.map +1 -0
- package/dist/types/src/packlets/metadata/metadata-store.d.ts +107 -0
- package/dist/types/src/packlets/metadata/metadata-store.d.ts.map +1 -0
- package/dist/types/src/packlets/metadata/metadata-store.test.d.ts +2 -0
- package/dist/types/src/packlets/metadata/metadata-store.test.d.ts.map +1 -0
- package/dist/types/src/packlets/metadata/sqlite-metadata-store.d.ts +65 -0
- package/dist/types/src/packlets/metadata/sqlite-metadata-store.d.ts.map +1 -0
- package/dist/types/src/packlets/network/network-service.d.ts +15 -14
- package/dist/types/src/packlets/network/network-service.d.ts.map +1 -1
- package/dist/types/src/packlets/pipeline/codec.d.ts +11 -0
- package/dist/types/src/packlets/pipeline/codec.d.ts.map +1 -0
- package/dist/types/src/packlets/pipeline/feeds.d.ts +7 -0
- package/dist/types/src/packlets/pipeline/feeds.d.ts.map +1 -0
- package/dist/types/src/packlets/pipeline/index.d.ts +5 -0
- package/dist/types/src/packlets/pipeline/index.d.ts.map +1 -0
- package/dist/types/src/packlets/pipeline/message-selector.d.ts +11 -0
- package/dist/types/src/packlets/pipeline/message-selector.d.ts.map +1 -0
- package/dist/types/src/packlets/pipeline/pipeline-stress.test.d.ts +2 -0
- package/dist/types/src/packlets/pipeline/pipeline-stress.test.d.ts.map +1 -0
- package/dist/types/src/packlets/pipeline/pipeline.d.ts +131 -0
- package/dist/types/src/packlets/pipeline/pipeline.d.ts.map +1 -0
- package/dist/types/src/packlets/pipeline/pipeline.test.d.ts +2 -0
- package/dist/types/src/packlets/pipeline/pipeline.test.d.ts.map +1 -0
- package/dist/types/src/packlets/pipeline/testing/index.d.ts +2 -0
- package/dist/types/src/packlets/pipeline/testing/index.d.ts.map +1 -0
- package/dist/types/src/packlets/pipeline/testing/test-feed-builder.d.ts +9 -0
- package/dist/types/src/packlets/pipeline/testing/test-feed-builder.d.ts.map +1 -0
- package/dist/types/src/packlets/pipeline/timeframe-clock.d.ts +31 -0
- package/dist/types/src/packlets/pipeline/timeframe-clock.d.ts.map +1 -0
- package/dist/types/src/packlets/services/client-services-layer.d.ts +21 -0
- package/dist/types/src/packlets/services/client-services-layer.d.ts.map +1 -0
- package/dist/types/src/packlets/services/cross-device-space-synchronizer.d.ts +28 -0
- package/dist/types/src/packlets/services/cross-device-space-synchronizer.d.ts.map +1 -0
- package/dist/types/src/packlets/services/effect-rpc.test.d.ts +2 -0
- package/dist/types/src/packlets/services/effect-rpc.test.d.ts.map +1 -0
- package/dist/types/src/packlets/services/feed-syncer.d.ts +22 -2
- package/dist/types/src/packlets/services/feed-syncer.d.ts.map +1 -1
- package/dist/types/src/packlets/services/index.d.ts +2 -2
- package/dist/types/src/packlets/services/index.d.ts.map +1 -1
- package/dist/types/src/packlets/services/platform.d.ts.map +1 -1
- package/dist/types/src/packlets/services/service-context.d.ts +38 -57
- package/dist/types/src/packlets/services/service-context.d.ts.map +1 -1
- package/dist/types/src/packlets/services/service-host.d.ts +82 -31
- package/dist/types/src/packlets/services/service-host.d.ts.map +1 -1
- package/dist/types/src/packlets/services/sqlite-storage.d.ts +29 -0
- package/dist/types/src/packlets/services/sqlite-storage.d.ts.map +1 -1
- package/dist/types/src/packlets/services/sqlite-storage.test.d.ts +2 -0
- package/dist/types/src/packlets/services/sqlite-storage.test.d.ts.map +1 -0
- package/dist/types/src/packlets/space/admission-discovery-extension.d.ts +30 -0
- package/dist/types/src/packlets/space/admission-discovery-extension.d.ts.map +1 -0
- package/dist/types/src/packlets/space/auth.d.ts +24 -0
- package/dist/types/src/packlets/space/auth.d.ts.map +1 -0
- package/dist/types/src/packlets/space/control-pipeline.d.ts +46 -0
- package/dist/types/src/packlets/space/control-pipeline.d.ts.map +1 -0
- package/dist/types/src/packlets/space/control-pipeline.test.d.ts +2 -0
- package/dist/types/src/packlets/space/control-pipeline.test.d.ts.map +1 -0
- package/dist/types/src/packlets/space/index.d.ts +6 -0
- package/dist/types/src/packlets/space/index.d.ts.map +1 -0
- package/dist/types/src/packlets/space/replication.browser.test.d.ts +2 -0
- package/dist/types/src/packlets/space/replication.browser.test.d.ts.map +1 -0
- package/dist/types/src/packlets/space/space-manager.browser.test.d.ts +2 -0
- package/dist/types/src/packlets/space/space-manager.browser.test.d.ts.map +1 -0
- package/dist/types/src/packlets/space/space-manager.d.ts +72 -0
- package/dist/types/src/packlets/space/space-manager.d.ts.map +1 -0
- package/dist/types/src/packlets/space/space-protocol.browser.test.d.ts +2 -0
- package/dist/types/src/packlets/space/space-protocol.browser.test.d.ts.map +1 -0
- package/dist/types/src/packlets/space/space-protocol.d.ts +99 -0
- package/dist/types/src/packlets/space/space-protocol.d.ts.map +1 -0
- package/dist/types/src/packlets/space/space-protocol.test.d.ts +2 -0
- package/dist/types/src/packlets/space/space-protocol.test.d.ts.map +1 -0
- package/dist/types/src/packlets/space/space.d.ts +65 -0
- package/dist/types/src/packlets/space/space.d.ts.map +1 -0
- package/dist/types/src/packlets/space/space.test.d.ts +2 -0
- package/dist/types/src/packlets/space/space.test.d.ts.map +1 -0
- package/dist/types/src/packlets/space/testing/change-metadata.d.ts +8 -0
- package/dist/types/src/packlets/space/testing/change-metadata.d.ts.map +1 -0
- package/dist/types/src/packlets/space/testing/index.d.ts +4 -0
- package/dist/types/src/packlets/space/testing/index.d.ts.map +1 -0
- package/dist/types/src/packlets/space/testing/test-agent-builder.d.ts +62 -0
- package/dist/types/src/packlets/space/testing/test-agent-builder.d.ts.map +1 -0
- package/dist/types/src/packlets/space-export/serialized-space-reader.d.ts +1 -1
- package/dist/types/src/packlets/space-export/serialized-space-reader.d.ts.map +1 -1
- package/dist/types/src/packlets/space-export/serialized-space-writer.d.ts.map +1 -1
- package/dist/types/src/packlets/spaces/data-space-manager.d.ts +40 -7
- package/dist/types/src/packlets/spaces/data-space-manager.d.ts.map +1 -1
- package/dist/types/src/packlets/spaces/data-space.d.ts +3 -1
- package/dist/types/src/packlets/spaces/data-space.d.ts.map +1 -1
- package/dist/types/src/packlets/spaces/genesis.d.ts +1 -1
- package/dist/types/src/packlets/spaces/genesis.d.ts.map +1 -1
- package/dist/types/src/packlets/spaces/spaces-service.d.ts +25 -23
- package/dist/types/src/packlets/spaces/spaces-service.d.ts.map +1 -1
- package/dist/types/src/packlets/storage/level.d.ts.map +1 -1
- package/dist/types/src/packlets/system/system-service.d.ts +18 -18
- package/dist/types/src/packlets/system/system-service.d.ts.map +1 -1
- package/dist/types/src/packlets/testing/invitation-utils.d.ts +1 -1
- package/dist/types/src/packlets/testing/invitation-utils.d.ts.map +1 -1
- package/dist/types/src/packlets/testing/test-builder.d.ts +7 -6
- package/dist/types/src/packlets/testing/test-builder.d.ts.map +1 -1
- package/dist/types/src/packlets/worker/worker-runtime.d.ts +41 -58
- package/dist/types/src/packlets/worker/worker-runtime.d.ts.map +1 -1
- package/dist/types/src/packlets/worker/worker-session.d.ts +21 -8
- package/dist/types/src/packlets/worker/worker-session.d.ts.map +1 -1
- package/dist/types/src/testing/index.d.ts +1 -0
- package/dist/types/src/testing/index.d.ts.map +1 -1
- package/dist/types/src/version.d.ts +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +52 -48
- package/src/index.ts +2 -0
- package/src/packlets/agents/edge-agent-manager.ts +56 -15
- package/src/packlets/agents/edge-agent-service.ts +25 -15
- package/src/packlets/devices/devices-service.test.ts +20 -19
- package/src/packlets/devices/devices-service.ts +17 -17
- package/src/packlets/devtools/devtools.ts +133 -75
- package/src/packlets/devtools/feeds.ts +2 -1
- package/src/packlets/devtools/network.ts +42 -12
- package/src/packlets/devtools/spaces.ts +1 -1
- package/src/packlets/diagnostics/diagnostics-broadcast.ts +4 -2
- package/src/packlets/diagnostics/diagnostics-collector.ts +5 -18
- package/src/packlets/diagnostics/diagnostics.ts +0 -11
- package/src/packlets/identity/authenticator.ts +2 -1
- package/src/packlets/identity/contacts-service.ts +46 -37
- package/src/packlets/identity/identity-manager.test.ts +3 -1
- package/src/packlets/identity/identity-manager.ts +73 -5
- package/src/packlets/identity/identity-recovery-manager.ts +56 -7
- package/src/packlets/identity/identity-service.test.ts +27 -18
- package/src/packlets/identity/identity-service.ts +114 -71
- package/src/packlets/identity/identity.test.ts +4 -9
- package/src/packlets/identity/identity.ts +1 -2
- package/src/packlets/invitations/edge-invitation-handler.ts +0 -1
- package/src/packlets/invitations/invitations-handler.ts +39 -3
- package/src/packlets/invitations/invitations-manager.ts +52 -4
- package/src/packlets/invitations/invitations-service.ts +54 -28
- package/src/packlets/logging/logging-service.ts +41 -29
- package/src/packlets/logging/logging.test.ts +28 -15
- package/src/packlets/metadata/index.ts +6 -0
- package/src/packlets/metadata/metadata-store.test.ts +49 -0
- package/src/packlets/metadata/metadata-store.ts +446 -0
- package/src/packlets/metadata/sqlite-metadata-store.ts +409 -0
- package/src/packlets/network/network-service.test.ts +19 -12
- package/src/packlets/network/network-service.ts +96 -29
- package/src/packlets/pipeline/codec.ts +18 -0
- package/src/packlets/pipeline/feeds.ts +22 -0
- package/src/packlets/pipeline/index.ts +8 -0
- package/src/packlets/pipeline/message-selector.ts +35 -0
- package/src/packlets/pipeline/pipeline-stress.test.ts +230 -0
- package/src/packlets/pipeline/pipeline.test.ts +140 -0
- package/src/packlets/pipeline/pipeline.ts +439 -0
- package/src/packlets/pipeline/testing/index.ts +5 -0
- package/src/packlets/pipeline/testing/test-feed-builder.ts +19 -0
- package/src/packlets/pipeline/timeframe-clock.ts +81 -0
- package/src/packlets/services/client-services-layer.ts +186 -0
- package/src/packlets/services/cross-device-space-synchronizer.ts +132 -0
- package/src/packlets/services/effect-rpc.test.ts +314 -0
- package/src/packlets/services/feed-syncer.ts +57 -3
- package/src/packlets/services/index.ts +2 -2
- package/src/packlets/services/platform.ts +0 -6
- package/src/packlets/services/service-context.ts +219 -522
- package/src/packlets/services/service-host.test.ts +34 -11
- package/src/packlets/services/service-host.ts +693 -213
- package/src/packlets/services/spec.md +106 -0
- package/src/packlets/services/sqlite-storage.test.ts +48 -0
- package/src/packlets/services/sqlite-storage.ts +117 -20
- package/src/packlets/space/admission-discovery-extension.ts +91 -0
- package/src/packlets/space/auth.ts +92 -0
- package/src/packlets/space/control-pipeline.test.ts +165 -0
- package/src/packlets/space/control-pipeline.ts +229 -0
- package/src/packlets/space/index.ts +9 -0
- package/src/packlets/space/replication.browser.test.ts +58 -0
- package/src/packlets/space/space-manager.browser.test.ts +40 -0
- package/src/packlets/space/space-manager.ts +224 -0
- package/src/packlets/space/space-protocol.browser.test.ts +129 -0
- package/src/packlets/space/space-protocol.test.ts +158 -0
- package/src/packlets/space/space-protocol.ts +308 -0
- package/src/packlets/space/space.test.ts +181 -0
- package/src/packlets/space/space.ts +196 -0
- package/src/packlets/space/testing/change-metadata.ts +27 -0
- package/src/packlets/space/testing/index.ts +7 -0
- package/src/packlets/space/testing/test-agent-builder.ts +252 -0
- package/src/packlets/space-export/serialized-space-reader.ts +3 -1
- package/src/packlets/space-export/serialized-space-writer.ts +53 -30
- package/src/packlets/space-export/space-archive.test.ts +30 -0
- package/src/packlets/spaces/data-space-manager.test.ts +1 -1
- package/src/packlets/spaces/data-space-manager.ts +148 -33
- package/src/packlets/spaces/data-space.ts +29 -18
- package/src/packlets/spaces/edge-feed-replicator.test.ts +1 -1
- package/src/packlets/spaces/genesis.ts +1 -1
- package/src/packlets/spaces/spaces-service.test.ts +35 -22
- package/src/packlets/spaces/spaces-service.ts +259 -180
- package/src/packlets/storage/level.ts +3 -1
- package/src/packlets/system/system-service.test.ts +31 -13
- package/src/packlets/system/system-service.ts +65 -37
- package/src/packlets/testing/invitation-utils.ts +3 -3
- package/src/packlets/testing/test-builder.ts +35 -20
- package/src/packlets/worker/worker-runtime.ts +222 -260
- package/src/packlets/worker/worker-session.ts +108 -91
- package/src/testing/index.ts +1 -0
- package/src/version.ts +1 -1
- package/dist/lib/browser/chunk-QCWEHHJW.mjs +0 -24
- package/dist/lib/browser/chunk-QCWEHHJW.mjs.map +0 -7
- package/dist/lib/browser/chunk-XJRPB3GA.mjs +0 -22
- package/dist/lib/browser/chunk-XJRPB3GA.mjs.map +0 -7
- package/dist/lib/browser/chunk-XU5R2BMF.mjs +0 -8837
- package/dist/lib/browser/chunk-XU5R2BMF.mjs.map +0 -7
- package/dist/lib/browser/index.mjs +0 -876
- package/dist/lib/browser/index.mjs.map +0 -7
- package/dist/lib/browser/meta.json +0 -1
- package/dist/lib/browser/packlets/diagnostics/browser-diagnostics-broadcast.mjs +0 -88
- package/dist/lib/browser/packlets/diagnostics/browser-diagnostics-broadcast.mjs.map +0 -7
- package/dist/lib/browser/packlets/diagnostics/diagnostics-broadcast.mjs +0 -11
- package/dist/lib/browser/packlets/diagnostics/diagnostics-broadcast.mjs.map +0 -7
- package/dist/lib/browser/packlets/locks/browser.mjs +0 -86
- package/dist/lib/browser/packlets/locks/browser.mjs.map +0 -7
- package/dist/lib/browser/packlets/locks/node.mjs +0 -48
- package/dist/lib/browser/packlets/locks/node.mjs.map +0 -7
- package/dist/lib/browser/testing/index.mjs +0 -436
- package/dist/lib/browser/testing/index.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-2DT3MZRL.mjs +0 -22
- package/dist/lib/node-esm/chunk-2DT3MZRL.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-2SZHAWBN.mjs +0 -24
- package/dist/lib/node-esm/chunk-2SZHAWBN.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-H5YTUD35.mjs +0 -8837
- package/dist/lib/node-esm/chunk-H5YTUD35.mjs.map +0 -7
- package/dist/lib/node-esm/index.mjs +0 -876
- package/dist/lib/node-esm/index.mjs.map +0 -7
- package/dist/lib/node-esm/meta.json +0 -1
- package/dist/lib/node-esm/packlets/diagnostics/browser-diagnostics-broadcast.mjs +0 -88
- package/dist/lib/node-esm/packlets/diagnostics/browser-diagnostics-broadcast.mjs.map +0 -7
- package/dist/lib/node-esm/packlets/diagnostics/diagnostics-broadcast.mjs +0 -11
- package/dist/lib/node-esm/packlets/diagnostics/diagnostics-broadcast.mjs.map +0 -7
- package/dist/lib/node-esm/packlets/locks/browser.mjs +0 -86
- package/dist/lib/node-esm/packlets/locks/browser.mjs.map +0 -7
- package/dist/lib/node-esm/packlets/locks/node.mjs +0 -48
- package/dist/lib/node-esm/packlets/locks/node.mjs.map +0 -7
- package/dist/lib/node-esm/testing/index.mjs +0 -436
- package/dist/lib/node-esm/testing/index.mjs.map +0 -7
- package/dist/types/src/packlets/services/client-rpc-server.d.ts +0 -24
- package/dist/types/src/packlets/services/client-rpc-server.d.ts.map +0 -1
- package/dist/types/src/packlets/services/service-registry.d.ts +0 -15
- package/dist/types/src/packlets/services/service-registry.d.ts.map +0 -1
- package/dist/types/src/packlets/services/service-registry.test.d.ts +0 -2
- package/dist/types/src/packlets/services/service-registry.test.d.ts.map +0 -1
- package/dist/types/src/testing/setup.d.ts +0 -3
- package/dist/types/src/testing/setup.d.ts.map +0 -1
- package/src/packlets/services/client-rpc-server.ts +0 -111
- package/src/packlets/services/service-registry.test.ts +0 -78
- package/src/packlets/services/service-registry.ts +0 -36
- package/src/testing/setup.ts +0 -11
|
@@ -3,47 +3,135 @@
|
|
|
3
3
|
//
|
|
4
4
|
|
|
5
5
|
import * as SqlClient from '@effect/sql/SqlClient';
|
|
6
|
+
import type * as SqlError from '@effect/sql/SqlError';
|
|
7
|
+
import * as EffectContext from 'effect/Context';
|
|
6
8
|
import * as Effect from 'effect/Effect';
|
|
7
|
-
|
|
8
|
-
import
|
|
9
|
-
import
|
|
9
|
+
import * as Exit from 'effect/Exit';
|
|
10
|
+
import * as Layer from 'effect/Layer';
|
|
11
|
+
import * as ManagedRuntime from 'effect/ManagedRuntime';
|
|
12
|
+
import * as Option from 'effect/Option';
|
|
13
|
+
import * as Runtime from 'effect/Runtime';
|
|
14
|
+
import * as Scope from 'effect/Scope';
|
|
15
|
+
|
|
16
|
+
import { Event, Mutex, Trigger, synchronized } from '@dxos/async';
|
|
17
|
+
import {
|
|
18
|
+
type ClientServices,
|
|
19
|
+
type ClientServicesHandlers,
|
|
20
|
+
makeInProcessClientServicesRpc,
|
|
21
|
+
makeServicesFromRpc,
|
|
22
|
+
} from '@dxos/client-protocol';
|
|
10
23
|
import { type Config, resolveTelemetryTag } from '@dxos/config';
|
|
11
24
|
import { Context } from '@dxos/context';
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
25
|
+
import { failUndefined, warnAfterTimeout } from '@dxos/debug';
|
|
26
|
+
import {
|
|
27
|
+
type AutomergeReplicator,
|
|
28
|
+
type EchoHost,
|
|
29
|
+
EchoHostService,
|
|
30
|
+
type EdgeAutomergeReplicator,
|
|
31
|
+
EdgeAutomergeReplicatorService,
|
|
32
|
+
MeshEchoReplicatorService,
|
|
33
|
+
runSqliteHealthCheck,
|
|
34
|
+
} from '@dxos/echo-host';
|
|
35
|
+
import {
|
|
36
|
+
EdgeClient,
|
|
37
|
+
type EdgeConnection,
|
|
38
|
+
EdgeHttpClient,
|
|
39
|
+
type EdgeIdentity,
|
|
40
|
+
createChainEdgeIdentity,
|
|
41
|
+
createEphemeralEdgeIdentity,
|
|
42
|
+
createStubEdgeIdentity,
|
|
43
|
+
} from '@dxos/edge-client';
|
|
44
|
+
import { EffectEx, RuntimeProvider } from '@dxos/effect';
|
|
45
|
+
import { type FeedStore, FeedStoreService } from '@dxos/feed-store';
|
|
14
46
|
import { invariant } from '@dxos/invariant';
|
|
47
|
+
import { type KeyringApi, KeyringApiService } from '@dxos/keyring';
|
|
48
|
+
import { type SpaceId } from '@dxos/keys';
|
|
15
49
|
import { log } from '@dxos/log';
|
|
16
|
-
import {
|
|
50
|
+
import {
|
|
51
|
+
EdgeSignalManager,
|
|
52
|
+
MemorySignalManager,
|
|
53
|
+
MemorySignalManagerContext,
|
|
54
|
+
type SignalManager,
|
|
55
|
+
SignalManagerService,
|
|
56
|
+
} from '@dxos/messaging';
|
|
17
57
|
import {
|
|
18
58
|
SwarmNetworkManager,
|
|
59
|
+
SwarmNetworkManagerService,
|
|
19
60
|
type TransportFactory,
|
|
20
61
|
createIceProvider,
|
|
21
62
|
createRtcTransportFactory,
|
|
22
63
|
} from '@dxos/network-manager';
|
|
23
|
-
import {
|
|
64
|
+
import { InvalidStorageVersionError, STORAGE_VERSION } from '@dxos/protocols';
|
|
65
|
+
import { Invitation, SystemStatus } from '@dxos/protocols/proto/dxos/client/services';
|
|
66
|
+
import { type Credential, type ProfileDocument } from '@dxos/protocols/proto/dxos/halo/credentials';
|
|
67
|
+
import {
|
|
68
|
+
ContactsService,
|
|
69
|
+
DataService,
|
|
70
|
+
DevicesService,
|
|
71
|
+
EdgeAgentService,
|
|
72
|
+
FeedService,
|
|
73
|
+
IdentityService,
|
|
74
|
+
InvitationsService,
|
|
75
|
+
NetworkService,
|
|
76
|
+
QueryService,
|
|
77
|
+
SpacesService,
|
|
78
|
+
} from '@dxos/protocols/rpc';
|
|
24
79
|
import * as SqlExport from '@dxos/sql-sqlite/SqlExport';
|
|
25
80
|
import type * as SqlTransaction from '@dxos/sql-sqlite/SqlTransaction';
|
|
81
|
+
import { type BlobStoreApi, BlobStoreApiService } from '@dxos/teleport-extension-object-sync';
|
|
26
82
|
import { trace as Trace } from '@dxos/tracing';
|
|
27
83
|
import { WebsocketRpcClient } from '@dxos/websocket-rpc';
|
|
28
84
|
|
|
29
|
-
import {
|
|
30
|
-
import { DevicesServiceImpl } from '../devices';
|
|
85
|
+
import { type EdgeAgentManager, EdgeAgentManagerService } from '../agents';
|
|
31
86
|
import { DevtoolsHostEvents, DevtoolsServiceImpl } from '../devtools';
|
|
32
87
|
import {
|
|
33
88
|
type CollectDiagnosticsBroadcastHandler,
|
|
34
89
|
createCollectDiagnosticsBroadcastHandler,
|
|
35
90
|
createDiagnostics,
|
|
36
91
|
} from '../diagnostics';
|
|
37
|
-
import {
|
|
38
|
-
|
|
39
|
-
|
|
92
|
+
import {
|
|
93
|
+
type CreateIdentityOptions,
|
|
94
|
+
type Identity,
|
|
95
|
+
type IdentityManager,
|
|
96
|
+
IdentityManagerService,
|
|
97
|
+
type JoinIdentityProps,
|
|
98
|
+
} from '../identity';
|
|
99
|
+
import {
|
|
100
|
+
type EdgeIdentityRecoveryManager,
|
|
101
|
+
EdgeIdentityRecoveryManagerService,
|
|
102
|
+
} from '../identity/identity-recovery-manager';
|
|
103
|
+
import {
|
|
104
|
+
DeviceInvitationProtocol,
|
|
105
|
+
type InvitationProtocol,
|
|
106
|
+
type InvitationsHandler,
|
|
107
|
+
InvitationsHandlerService,
|
|
108
|
+
type InvitationsManager,
|
|
109
|
+
InvitationsManagerService,
|
|
110
|
+
SpaceInvitationProtocol,
|
|
111
|
+
} from '../invitations';
|
|
40
112
|
import { Lock, type ResourceLock } from '../locks';
|
|
41
113
|
import { LoggingServiceImpl } from '../logging';
|
|
42
|
-
import {
|
|
43
|
-
import {
|
|
114
|
+
import { type IMetadataStore, IMetadataStoreService } from '../metadata';
|
|
115
|
+
import { type SpaceManager, SpaceManagerService } from '../space';
|
|
116
|
+
import {
|
|
117
|
+
type DataSpaceManager,
|
|
118
|
+
DataSpaceManagerService,
|
|
119
|
+
type SigningContextProvider,
|
|
120
|
+
SigningContextProviderService,
|
|
121
|
+
} from '../spaces';
|
|
44
122
|
import { SystemServiceImpl } from '../system';
|
|
45
|
-
import {
|
|
46
|
-
import {
|
|
123
|
+
import { type ClientServicesRpcContext, ClientServicesRpcLayer } from './client-services-layer';
|
|
124
|
+
import {
|
|
125
|
+
type CrossDeviceSpaceSynchronizer,
|
|
126
|
+
CrossDeviceSpaceSynchronizerService,
|
|
127
|
+
} from './cross-device-space-synchronizer';
|
|
128
|
+
import { type FeedSyncer, FeedSyncerService } from './feed-syncer';
|
|
129
|
+
import {
|
|
130
|
+
ServiceContextLayer,
|
|
131
|
+
type ServiceContextRuntimeProps,
|
|
132
|
+
type ServiceContextStackContext,
|
|
133
|
+
StorageMigrationService,
|
|
134
|
+
} from './service-context';
|
|
47
135
|
|
|
48
136
|
export type ClientServicesHostProps = {
|
|
49
137
|
/**
|
|
@@ -70,40 +158,77 @@ export type InitializeOptions = {
|
|
|
70
158
|
connectionLog?: boolean;
|
|
71
159
|
};
|
|
72
160
|
|
|
161
|
+
// Alias for consumers (tests, devtools, diagnostics) that referred to the former `ServiceContext`
|
|
162
|
+
// orchestrator; its lifecycle and API now live on {@link ClientServicesHost} directly.
|
|
163
|
+
export type ServiceContext = ClientServicesHost;
|
|
164
|
+
|
|
73
165
|
/**
|
|
74
|
-
*
|
|
166
|
+
* Shared backend for all client services.
|
|
167
|
+
*
|
|
168
|
+
* Owns the full client stack and its lifecycle: it builds the layer-composed components (keyring,
|
|
169
|
+
* feed store, echo host, identity/space managers, …) plus the client RPC handlers, then drives the
|
|
170
|
+
* open sequence (migrate → identity → network → space initialization) and teardown. It reads or
|
|
171
|
+
* creates the identity, propagates it outward (`_setNetworkIdentity`), and opens the identity-bound
|
|
172
|
+
* services once an identity is available (`_initialize`). The host provides itself into the stack
|
|
173
|
+
* (via {@link ClientServicesHostService}) so the RPC handler layers can resolve the orchestration
|
|
174
|
+
* entry points they need without a separate `ServiceContext` service.
|
|
75
175
|
*/
|
|
76
|
-
@Trace.resource()
|
|
77
176
|
export class ClientServicesHost {
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
177
|
+
readonly #resourceLock?: ResourceLock;
|
|
178
|
+
// Effect-rpc handlers served over each connection, resolved from the Layer stack on open and reset
|
|
179
|
+
// to the host-local set on close. Held directly (no separate registry indirection).
|
|
180
|
+
#handlers: Partial<ClientServicesHandlers>;
|
|
181
|
+
readonly #systemService: SystemServiceImpl;
|
|
182
|
+
readonly #loggingService: LoggingServiceImpl;
|
|
183
|
+
readonly #statusUpdate = new Event<void>();
|
|
184
|
+
|
|
185
|
+
#config?: Config;
|
|
186
|
+
#signalManager?: SignalManager;
|
|
187
|
+
#networkManager?: SwarmNetworkManager;
|
|
188
|
+
#callbacks?: ClientServicesHostCallbacks;
|
|
189
|
+
#devtoolsProxy?: WebsocketRpcClient<{}, ClientServices>;
|
|
190
|
+
#edgeConnection?: EdgeConnection = undefined;
|
|
191
|
+
#edgeHttpClient?: EdgeHttpClient = undefined;
|
|
192
|
+
|
|
193
|
+
#stackRuntime?: ManagedRuntime.ManagedRuntime<
|
|
194
|
+
ClientServicesHostService | ClientServicesRpcContext | ServiceContextStackContext,
|
|
195
|
+
never
|
|
196
|
+
>;
|
|
197
|
+
readonly #runtime: RuntimeProvider.RuntimeProvider<
|
|
94
198
|
SqlClient.SqlClient | SqlExport.SqlExport | SqlTransaction.SqlTransaction
|
|
95
199
|
>;
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
200
|
+
readonly #runtimeProps: ServiceContextRuntimeProps;
|
|
201
|
+
#diagnosticsBroadcastHandler: CollectDiagnosticsBroadcastHandler;
|
|
202
|
+
|
|
203
|
+
#opening = false;
|
|
204
|
+
#open = false;
|
|
205
|
+
#resetting = false;
|
|
206
|
+
|
|
207
|
+
// Stack components, resolved from the layer runtime on open. Present after `open` starts.
|
|
208
|
+
#ctx?: Context;
|
|
209
|
+
#metadataStore?: IMetadataStore;
|
|
210
|
+
#blobStore?: BlobStoreApi;
|
|
211
|
+
#keyring?: KeyringApi;
|
|
212
|
+
#feedStore?: FeedStore<any>;
|
|
213
|
+
#spaceManager?: SpaceManager;
|
|
214
|
+
#identityManager?: IdentityManager;
|
|
215
|
+
#recoveryManager?: EdgeIdentityRecoveryManager;
|
|
216
|
+
#invitations?: InvitationsHandler;
|
|
217
|
+
#invitationsManager?: InvitationsManager;
|
|
218
|
+
#echoHost?: EchoHost;
|
|
219
|
+
#signingContextProvider?: SigningContextProvider;
|
|
220
|
+
#dataSpaceManager?: DataSpaceManager;
|
|
221
|
+
#edgeAgentManager?: EdgeAgentManager;
|
|
222
|
+
#deviceSpaceSync?: CrossDeviceSpaceSynchronizer;
|
|
223
|
+
#meshReplicator?: AutomergeReplicator;
|
|
224
|
+
#echoEdgeReplicator?: EdgeAutomergeReplicator;
|
|
225
|
+
#feedSyncer?: FeedSyncer;
|
|
226
|
+
#storageMigrate?: Effect.Effect<void, SqlError.SqlError, SqlClient.SqlClient | SqlTransaction.SqlTransaction>;
|
|
227
|
+
|
|
228
|
+
// Orchestration state (formerly on `ServiceContext`).
|
|
229
|
+
readonly #initialized = new Trigger();
|
|
230
|
+
readonly #edgeIdentityUpdateMutex = new Mutex();
|
|
231
|
+
readonly #handlerFactories = new Map<Invitation.Kind, (invitation: Partial<Invitation>) => InvitationProtocol>();
|
|
107
232
|
|
|
108
233
|
constructor({
|
|
109
234
|
config,
|
|
@@ -115,19 +240,19 @@ export class ClientServicesHost {
|
|
|
115
240
|
runtime,
|
|
116
241
|
runtimeProps,
|
|
117
242
|
}: ClientServicesHostProps) {
|
|
118
|
-
this
|
|
119
|
-
this
|
|
120
|
-
this
|
|
243
|
+
this.#callbacks = callbacks;
|
|
244
|
+
this.#runtime = runtime;
|
|
245
|
+
this.#runtimeProps = runtimeProps ?? {};
|
|
121
246
|
|
|
122
247
|
if (config) {
|
|
123
248
|
this.initialize({ config, transportFactory, signalManager });
|
|
124
249
|
}
|
|
125
250
|
|
|
126
251
|
if (lockKey) {
|
|
127
|
-
this
|
|
252
|
+
this.#resourceLock = new Lock({
|
|
128
253
|
lockKey,
|
|
129
254
|
onAcquire: () => {
|
|
130
|
-
if (!this
|
|
255
|
+
if (!this.#opening) {
|
|
131
256
|
void this.open(new Context());
|
|
132
257
|
}
|
|
133
258
|
},
|
|
@@ -136,18 +261,28 @@ export class ClientServicesHost {
|
|
|
136
261
|
}
|
|
137
262
|
|
|
138
263
|
// TODO(wittjosiah): If config is not defined here, system service will always have undefined config.
|
|
139
|
-
this
|
|
140
|
-
config: () => this
|
|
141
|
-
statusUpdate: this
|
|
142
|
-
getCurrentStatus: () => (this.isOpen && !this
|
|
143
|
-
getDiagnostics: () => {
|
|
144
|
-
|
|
264
|
+
this.#systemService = new SystemServiceImpl({
|
|
265
|
+
config: () => this.#config,
|
|
266
|
+
statusUpdate: this.#statusUpdate,
|
|
267
|
+
getCurrentStatus: () => (this.isOpen && !this.#resetting ? SystemStatus.ACTIVE : SystemStatus.INACTIVE),
|
|
268
|
+
getDiagnostics: async () => {
|
|
269
|
+
// Bridge the host Handlers to the proto services surface that diagnostics collection consumes.
|
|
270
|
+
const scope = Effect.runSync(Scope.make());
|
|
271
|
+
try {
|
|
272
|
+
const rpc = await EffectEx.runPromise(
|
|
273
|
+
makeInProcessClientServicesRpc(() => this.#handlers).pipe(Effect.provideService(Scope.Scope, scope)),
|
|
274
|
+
);
|
|
275
|
+
const services = makeServicesFromRpc(rpc, Runtime.defaultRuntime);
|
|
276
|
+
return await createDiagnostics(services, this, this.#config!);
|
|
277
|
+
} finally {
|
|
278
|
+
await EffectEx.runPromise(Scope.close(scope, Exit.void));
|
|
279
|
+
}
|
|
145
280
|
},
|
|
146
281
|
onUpdateStatus: async (status: SystemStatus) => {
|
|
147
282
|
if (!this.isOpen && status === SystemStatus.ACTIVE) {
|
|
148
|
-
await this
|
|
283
|
+
await this.#resourceLock?.acquire();
|
|
149
284
|
} else if (this.isOpen && status === SystemStatus.INACTIVE) {
|
|
150
|
-
await this
|
|
285
|
+
await this.#resourceLock?.release();
|
|
151
286
|
}
|
|
152
287
|
},
|
|
153
288
|
onReset: async () => {
|
|
@@ -155,43 +290,101 @@ export class ClientServicesHost {
|
|
|
155
290
|
},
|
|
156
291
|
});
|
|
157
292
|
|
|
158
|
-
this
|
|
159
|
-
this
|
|
293
|
+
this.#diagnosticsBroadcastHandler = createCollectDiagnosticsBroadcastHandler(this.#systemService);
|
|
294
|
+
this.#loggingService = new LoggingServiceImpl();
|
|
160
295
|
|
|
161
|
-
this
|
|
162
|
-
SystemService: this
|
|
163
|
-
}
|
|
296
|
+
this.#handlers = {
|
|
297
|
+
SystemService: this.#systemService,
|
|
298
|
+
};
|
|
164
299
|
}
|
|
165
300
|
|
|
166
301
|
get isOpen() {
|
|
167
|
-
return this
|
|
302
|
+
return this.#open;
|
|
168
303
|
}
|
|
169
304
|
|
|
170
305
|
get config() {
|
|
171
|
-
return this
|
|
306
|
+
return this.#config;
|
|
172
307
|
}
|
|
173
308
|
|
|
174
|
-
|
|
175
|
-
|
|
309
|
+
// Self-reference retained for the former `host.context` accessor.
|
|
310
|
+
get context(): ClientServicesHost {
|
|
311
|
+
return this;
|
|
176
312
|
}
|
|
177
313
|
|
|
178
|
-
get
|
|
179
|
-
return this
|
|
314
|
+
get services() {
|
|
315
|
+
return this.#handlers;
|
|
180
316
|
}
|
|
181
317
|
|
|
182
|
-
get
|
|
183
|
-
return this
|
|
318
|
+
get initialized() {
|
|
319
|
+
return this.#initialized;
|
|
184
320
|
}
|
|
185
321
|
|
|
186
|
-
get
|
|
187
|
-
return this
|
|
322
|
+
get identityManager(): IdentityManager {
|
|
323
|
+
return this.#identityManager ?? failUndefined();
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
get spaceManager(): SpaceManager {
|
|
327
|
+
return this.#spaceManager ?? failUndefined();
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
get metadataStore(): IMetadataStore {
|
|
331
|
+
return this.#metadataStore ?? failUndefined();
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
get blobStore(): BlobStoreApi {
|
|
335
|
+
return this.#blobStore ?? failUndefined();
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
get recoveryManager(): EdgeIdentityRecoveryManager {
|
|
339
|
+
return this.#recoveryManager ?? failUndefined();
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
get keyring(): KeyringApi {
|
|
343
|
+
return this.#keyring ?? failUndefined();
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
get feedStore(): FeedStore<any> {
|
|
347
|
+
return this.#feedStore ?? failUndefined();
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
get echoHost(): EchoHost {
|
|
351
|
+
return this.#echoHost ?? failUndefined();
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
get invitations(): InvitationsHandler {
|
|
355
|
+
return this.#invitations ?? failUndefined();
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
get invitationsManager(): InvitationsManager {
|
|
359
|
+
return this.#invitationsManager ?? failUndefined();
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
get networkManager(): SwarmNetworkManager {
|
|
363
|
+
return this.#networkManager ?? failUndefined();
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
get signalManager(): SignalManager {
|
|
367
|
+
return this.#signalManager ?? failUndefined();
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
// Present after the stack constructs it; usable once `initialized` wakes.
|
|
371
|
+
get dataSpaceManager(): DataSpaceManager | undefined {
|
|
372
|
+
return this.#dataSpaceManager;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
get edgeAgentManager(): EdgeAgentManager | undefined {
|
|
376
|
+
return this.#edgeAgentManager;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
get edgeConnection(): EdgeConnection | undefined {
|
|
380
|
+
return this.#edgeConnection;
|
|
188
381
|
}
|
|
189
382
|
|
|
190
383
|
/**
|
|
191
384
|
* Debugging util.
|
|
192
385
|
*/
|
|
193
386
|
async exportSqliteDatabase(): Promise<Uint8Array> {
|
|
194
|
-
return await RuntimeProvider.runPromise(this
|
|
387
|
+
return await RuntimeProvider.runPromise(this.#runtime)(
|
|
195
388
|
Effect.gen(function* () {
|
|
196
389
|
const sql = yield* SqlExport.SqlExport;
|
|
197
390
|
return yield* sql.export;
|
|
@@ -203,7 +396,7 @@ export class ClientServicesHost {
|
|
|
203
396
|
* Debugging util.
|
|
204
397
|
*/
|
|
205
398
|
async runSqliteQuery(query: string, params?: unknown[]): Promise<readonly Record<string, unknown>[]> {
|
|
206
|
-
return await RuntimeProvider.runPromise(this
|
|
399
|
+
return await RuntimeProvider.runPromise(this.#runtime)(
|
|
207
400
|
Effect.gen(function* () {
|
|
208
401
|
const sql = yield* SqlClient.SqlClient;
|
|
209
402
|
return yield* sql`${sql.unsafe(query, params)}`;
|
|
@@ -217,55 +410,52 @@ export class ClientServicesHost {
|
|
|
217
410
|
* Can only be called once.
|
|
218
411
|
*/
|
|
219
412
|
initialize({ config, ...options }: InitializeOptions): void {
|
|
220
|
-
invariant(!this
|
|
413
|
+
invariant(!this.#open, 'service host is open');
|
|
221
414
|
log('initializing...');
|
|
222
415
|
|
|
223
416
|
if (config) {
|
|
224
|
-
if (this.
|
|
225
|
-
this.
|
|
417
|
+
if (this.#runtimeProps.disableP2pReplication === undefined) {
|
|
418
|
+
this.#runtimeProps.disableP2pReplication = config?.get('runtime.client.disableP2pReplication', false);
|
|
226
419
|
}
|
|
227
|
-
if (this.
|
|
228
|
-
this.
|
|
420
|
+
if (this.#runtimeProps.enableVectorIndexing === undefined) {
|
|
421
|
+
this.#runtimeProps.enableVectorIndexing = config?.get('runtime.client.enableVectorIndexing', false);
|
|
229
422
|
}
|
|
230
423
|
|
|
231
|
-
invariant(!this
|
|
232
|
-
this
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
// TODO(wittjosiah): This is quite noisy during tests. Make configurable? Remove?
|
|
236
|
-
if (!options.signalManager) {
|
|
237
|
-
// log.warn('running signaling without telemetry metadata.');
|
|
424
|
+
invariant(!this.#config, 'config already set');
|
|
425
|
+
this.#config = config;
|
|
238
426
|
}
|
|
239
427
|
|
|
240
428
|
const endpoint = config?.get('runtime.services.edge.url');
|
|
241
429
|
if (endpoint) {
|
|
242
430
|
const clientTag = resolveTelemetryTag(config);
|
|
243
|
-
this
|
|
244
|
-
this
|
|
431
|
+
this.#edgeConnection = new EdgeClient(createStubEdgeIdentity(), { socketEndpoint: endpoint, clientTag });
|
|
432
|
+
this.#edgeHttpClient = new EdgeHttpClient(endpoint, { clientTag });
|
|
245
433
|
}
|
|
246
434
|
|
|
247
435
|
const {
|
|
248
436
|
connectionLog = true,
|
|
249
437
|
transportFactory = createRtcTransportFactory(
|
|
250
|
-
{ iceServers: this
|
|
251
|
-
this
|
|
252
|
-
createIceProvider(this
|
|
438
|
+
{ iceServers: this.#config?.get('runtime.services.ice') },
|
|
439
|
+
this.#config?.get('runtime.services.iceProviders') &&
|
|
440
|
+
createIceProvider(this.#config!.get('runtime.services.iceProviders')!),
|
|
253
441
|
),
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
442
|
+
// Edge is the only real signaling transport; without it fall back to an isolated in-memory
|
|
443
|
+
// manager (no cross-process signaling). The former KUBE `WebsocketSignalManager` is removed.
|
|
444
|
+
signalManager = this.#edgeConnection && this.#config?.get('runtime.client.edgeFeatures')?.signaling
|
|
445
|
+
? new EdgeSignalManager({ edgeConnection: this.#edgeConnection })
|
|
446
|
+
: new MemorySignalManager(new MemorySignalManagerContext()),
|
|
257
447
|
} = options;
|
|
258
|
-
this
|
|
448
|
+
this.#signalManager = signalManager;
|
|
259
449
|
|
|
260
|
-
invariant(!this
|
|
261
|
-
this
|
|
450
|
+
invariant(!this.#networkManager, 'network manager already set');
|
|
451
|
+
this.#networkManager = new SwarmNetworkManager({
|
|
262
452
|
enableDevtoolsLogging: connectionLog,
|
|
263
453
|
transportFactory,
|
|
264
454
|
signalManager,
|
|
265
|
-
peerInfo: this
|
|
455
|
+
peerInfo: this.#edgeConnection
|
|
266
456
|
? {
|
|
267
|
-
identityDid: this.
|
|
268
|
-
peerKey: this.
|
|
457
|
+
identityDid: this.#edgeConnection.identityDid,
|
|
458
|
+
peerKey: this.#edgeConnection.peerKey,
|
|
269
459
|
}
|
|
270
460
|
: undefined,
|
|
271
461
|
});
|
|
@@ -275,139 +465,190 @@ export class ClientServicesHost {
|
|
|
275
465
|
|
|
276
466
|
@synchronized
|
|
277
467
|
@Trace.span()
|
|
278
|
-
async open(ctx: Context): Promise<void> {
|
|
279
|
-
if (this
|
|
468
|
+
async open(ctx: Context = new Context()): Promise<void> {
|
|
469
|
+
if (this.#open) {
|
|
280
470
|
return;
|
|
281
471
|
}
|
|
282
472
|
|
|
283
473
|
log('opening service host');
|
|
284
474
|
|
|
285
|
-
invariant(this
|
|
286
|
-
invariant(this
|
|
287
|
-
invariant(this
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
this
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
475
|
+
invariant(this.#config, 'config not set');
|
|
476
|
+
invariant(this.#signalManager, 'signal manager not set');
|
|
477
|
+
invariant(this.#networkManager, 'network manager not set');
|
|
478
|
+
const config = this.#config;
|
|
479
|
+
const signalManager = this.#signalManager;
|
|
480
|
+
const networkManager = this.#networkManager;
|
|
481
|
+
|
|
482
|
+
this.#opening = true;
|
|
483
|
+
this.#ctx = ctx;
|
|
484
|
+
log('opening...', { lockKey: this.#resourceLock?.lockKey });
|
|
485
|
+
|
|
486
|
+
await this.#resourceLock?.acquire();
|
|
487
|
+
|
|
488
|
+
await this.#loggingService.open();
|
|
489
|
+
|
|
490
|
+
// Build a single runtime from the component layer stack plus the client RPC handlers, providing
|
|
491
|
+
// the host itself so the handler layers can resolve their orchestration entry points.
|
|
492
|
+
const stackLayer = ClientServicesRpcLayer.pipe(
|
|
493
|
+
Layer.provideMerge(Layer.succeed(ClientServicesHostService, this)),
|
|
494
|
+
Layer.provideMerge(
|
|
495
|
+
ServiceContextLayer({
|
|
496
|
+
...this.#runtimeProps,
|
|
497
|
+
edgeFeatures: config.get('runtime.client.edgeFeatures'),
|
|
498
|
+
edgeConnection: this.#edgeConnection,
|
|
499
|
+
edgeHttpClient: this.#edgeHttpClient,
|
|
500
|
+
}),
|
|
501
|
+
),
|
|
502
|
+
Layer.provideMerge(Layer.succeed(SwarmNetworkManagerService, networkManager)),
|
|
503
|
+
Layer.provideMerge(Layer.succeed(SignalManagerService, signalManager)),
|
|
504
|
+
Layer.provideMerge(RuntimeProvider.toLayer(this.#runtime)),
|
|
505
|
+
Layer.orDie,
|
|
304
506
|
);
|
|
305
|
-
|
|
306
|
-
const
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
507
|
+
this.#stackRuntime = ManagedRuntime.make(stackLayer);
|
|
508
|
+
const resolved = await this.#stackRuntime.runPromise(
|
|
509
|
+
Effect.all({
|
|
510
|
+
// Components.
|
|
511
|
+
metadataStore: IMetadataStoreService,
|
|
512
|
+
blobStore: BlobStoreApiService,
|
|
513
|
+
keyring: KeyringApiService,
|
|
514
|
+
feedStore: FeedStoreService,
|
|
515
|
+
spaceManager: SpaceManagerService,
|
|
516
|
+
identityManager: IdentityManagerService,
|
|
517
|
+
recoveryManager: EdgeIdentityRecoveryManagerService,
|
|
518
|
+
invitations: InvitationsHandlerService,
|
|
519
|
+
invitationsManager: InvitationsManagerService,
|
|
520
|
+
echoHost: EchoHostService,
|
|
521
|
+
signingContextProvider: SigningContextProviderService,
|
|
522
|
+
dataSpaceManager: DataSpaceManagerService,
|
|
523
|
+
edgeAgentManager: EdgeAgentManagerService,
|
|
524
|
+
deviceSpaceSync: CrossDeviceSpaceSynchronizerService,
|
|
525
|
+
storageMigrate: StorageMigrationService,
|
|
526
|
+
meshReplicator: Effect.serviceOption(MeshEchoReplicatorService),
|
|
527
|
+
echoEdgeReplicator: Effect.serviceOption(EdgeAutomergeReplicatorService),
|
|
528
|
+
feedSyncer: Effect.serviceOption(FeedSyncerService),
|
|
529
|
+
// Handlers.
|
|
530
|
+
identityService: IdentityService.Tag,
|
|
531
|
+
contactsService: ContactsService.Tag,
|
|
532
|
+
invitationsService: InvitationsService.Tag,
|
|
533
|
+
devicesService: DevicesService.Tag,
|
|
534
|
+
spacesService: SpacesService.Tag,
|
|
535
|
+
networkService: NetworkService.Tag,
|
|
536
|
+
edgeAgentService: EdgeAgentService.Tag,
|
|
537
|
+
dataService: DataService.Tag,
|
|
538
|
+
queryService: QueryService.Tag,
|
|
539
|
+
feedService: FeedService.Tag,
|
|
540
|
+
}),
|
|
322
541
|
);
|
|
323
542
|
|
|
324
|
-
this.
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
543
|
+
this.#metadataStore = resolved.metadataStore;
|
|
544
|
+
this.#blobStore = resolved.blobStore;
|
|
545
|
+
this.#keyring = resolved.keyring;
|
|
546
|
+
this.#feedStore = resolved.feedStore;
|
|
547
|
+
this.#spaceManager = resolved.spaceManager;
|
|
548
|
+
this.#identityManager = resolved.identityManager;
|
|
549
|
+
this.#recoveryManager = resolved.recoveryManager;
|
|
550
|
+
this.#invitations = resolved.invitations;
|
|
551
|
+
this.#invitationsManager = resolved.invitationsManager;
|
|
552
|
+
this.#echoHost = resolved.echoHost;
|
|
553
|
+
this.#signingContextProvider = resolved.signingContextProvider;
|
|
554
|
+
this.#dataSpaceManager = resolved.dataSpaceManager;
|
|
555
|
+
this.#edgeAgentManager = resolved.edgeAgentManager;
|
|
556
|
+
this.#deviceSpaceSync = resolved.deviceSpaceSync;
|
|
557
|
+
this.#storageMigrate = resolved.storageMigrate;
|
|
558
|
+
this.#meshReplicator = Option.getOrUndefined(resolved.meshReplicator);
|
|
559
|
+
this.#echoEdgeReplicator = Option.getOrUndefined(resolved.echoEdgeReplicator);
|
|
560
|
+
this.#feedSyncer = Option.getOrUndefined(resolved.feedSyncer);
|
|
561
|
+
|
|
562
|
+
// Wire the setters for components that point "up the stack".
|
|
563
|
+
this.#handlerFactories.set(
|
|
564
|
+
Invitation.Kind.DEVICE,
|
|
565
|
+
() =>
|
|
566
|
+
new DeviceInvitationProtocol(
|
|
567
|
+
this.#keyring!,
|
|
568
|
+
() => this.#identityManager!.identity ?? failUndefined(),
|
|
569
|
+
this._acceptIdentity.bind(this),
|
|
570
|
+
),
|
|
571
|
+
);
|
|
572
|
+
this.#recoveryManager.setAcceptRecoveredIdentity((params) => this._acceptIdentity(params));
|
|
573
|
+
this.#invitationsManager.setInvitationHandlerFactory((invitation) => this.getInvitationHandler(invitation));
|
|
574
|
+
this.#echoHost.setFeedSyncHandlers({
|
|
575
|
+
syncFeed: async (feedSyncCtx, request) =>
|
|
576
|
+
this.#feedSyncer?.syncBlocking(feedSyncCtx, {
|
|
577
|
+
spaceId: request.spaceId as SpaceId,
|
|
578
|
+
subspaceTag: request.subspaceTag,
|
|
579
|
+
shouldPush: request.shouldPush,
|
|
580
|
+
shouldPull: request.shouldPull,
|
|
581
|
+
}),
|
|
582
|
+
getSyncState: async (feedSyncCtx, request) => {
|
|
583
|
+
if (!this.#feedSyncer) {
|
|
584
|
+
return { namespaces: [] };
|
|
585
|
+
}
|
|
586
|
+
return this.#feedSyncer.getSyncState(feedSyncCtx, request);
|
|
587
|
+
},
|
|
588
|
+
});
|
|
355
589
|
|
|
590
|
+
this.#handlers = {
|
|
591
|
+
SystemService: this.#systemService,
|
|
592
|
+
IdentityService: resolved.identityService,
|
|
593
|
+
ContactsService: resolved.contactsService,
|
|
594
|
+
InvitationsService: resolved.invitationsService,
|
|
595
|
+
DevicesService: resolved.devicesService,
|
|
596
|
+
SpacesService: resolved.spacesService,
|
|
597
|
+
DataService: resolved.dataService,
|
|
598
|
+
QueryService: resolved.queryService,
|
|
599
|
+
FeedService: resolved.feedService,
|
|
600
|
+
NetworkService: resolved.networkService,
|
|
601
|
+
LoggingService: this.#loggingService,
|
|
356
602
|
// TODO(burdon): Move to new protobuf definitions.
|
|
357
603
|
DevtoolsHost: new DevtoolsServiceImpl({
|
|
358
604
|
events: new DevtoolsHostEvents(),
|
|
359
|
-
config: this
|
|
360
|
-
context: this
|
|
605
|
+
config: this.#config,
|
|
606
|
+
context: this,
|
|
361
607
|
exportSqliteDatabase: () => this.exportSqliteDatabase(),
|
|
362
608
|
runSqliteQuery: (query, params) => this.runSqliteQuery(query, params),
|
|
363
609
|
}),
|
|
610
|
+
EdgeAgentService: resolved.edgeAgentService,
|
|
611
|
+
};
|
|
364
612
|
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
await this._serviceContext.open(ctx);
|
|
370
|
-
log('service-host: service context opened');
|
|
371
|
-
|
|
372
|
-
log('service-host: opening identity service...');
|
|
373
|
-
await identityService.open();
|
|
374
|
-
log('service-host: identity service opened');
|
|
613
|
+
// Run the open lifecycle stages (formerly ServiceContext._open).
|
|
614
|
+
// The identity service impl's open/close lifecycle is owned by its layer scope in
|
|
615
|
+
// {@link ClientServicesRpcLayer} and runs when the stack runtime is disposed.
|
|
616
|
+
await this._openStack(ctx);
|
|
375
617
|
|
|
376
|
-
const devtoolsProxy = this
|
|
618
|
+
const devtoolsProxy = this.#config?.get('runtime.client.devtoolsProxy');
|
|
377
619
|
if (devtoolsProxy) {
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
handlers: this.services as ClientServices,
|
|
383
|
-
});
|
|
384
|
-
void this._devtoolsProxy.open();
|
|
620
|
+
// TODO(dxos): The devtools websocket proxy serves the protobuf service bundle, which is
|
|
621
|
+
// incompatible with the effect-rpc Handlers the host now provides. Re-enable once this legacy
|
|
622
|
+
// transport is migrated to effect-rpc (or bridged via makeInProcessClient + a proto adapter).
|
|
623
|
+
log.warn('devtoolsProxy is not supported with effect-rpc services; skipping', { devtoolsProxy });
|
|
385
624
|
}
|
|
386
|
-
this
|
|
625
|
+
this.#diagnosticsBroadcastHandler.start();
|
|
387
626
|
|
|
388
|
-
this
|
|
389
|
-
this
|
|
390
|
-
this.
|
|
391
|
-
const deviceKey = this
|
|
627
|
+
this.#opening = false;
|
|
628
|
+
this.#open = true;
|
|
629
|
+
this.#statusUpdate.emit();
|
|
630
|
+
const deviceKey = this.#identityManager?.identity?.deviceKey;
|
|
392
631
|
log('opened', { deviceKey });
|
|
393
632
|
}
|
|
394
633
|
|
|
395
634
|
@synchronized
|
|
396
635
|
@Trace.span()
|
|
397
|
-
async close(ctx: Context): Promise<void> {
|
|
398
|
-
if (!this
|
|
636
|
+
async close(ctx: Context = Context.default()): Promise<void> {
|
|
637
|
+
if (!this.#open) {
|
|
399
638
|
return;
|
|
400
639
|
}
|
|
401
640
|
|
|
402
|
-
const deviceKey = this
|
|
641
|
+
const deviceKey = this.#identityManager?.identity?.deviceKey;
|
|
403
642
|
log('closing...', { deviceKey });
|
|
404
|
-
this
|
|
405
|
-
await this
|
|
406
|
-
this
|
|
407
|
-
await this.
|
|
408
|
-
await this.
|
|
409
|
-
this
|
|
410
|
-
this
|
|
643
|
+
this.#diagnosticsBroadcastHandler.stop();
|
|
644
|
+
await this.#devtoolsProxy?.close();
|
|
645
|
+
this.#handlers = { SystemService: this.#systemService };
|
|
646
|
+
await this.#loggingService.close();
|
|
647
|
+
await this._closeStack(ctx);
|
|
648
|
+
await this.#stackRuntime?.dispose();
|
|
649
|
+
this.#stackRuntime = undefined;
|
|
650
|
+
this.#open = false;
|
|
651
|
+
this.#statusUpdate.emit();
|
|
411
652
|
log('closed', { deviceKey });
|
|
412
653
|
}
|
|
413
654
|
|
|
@@ -415,11 +656,13 @@ export class ClientServicesHost {
|
|
|
415
656
|
log.info('resetting...');
|
|
416
657
|
// Emit this status update immediately so app returns to fallback.
|
|
417
658
|
// This state is never cleared because the app reloads.
|
|
418
|
-
this
|
|
419
|
-
this.
|
|
420
|
-
|
|
659
|
+
this.#resetting = true;
|
|
660
|
+
this.#statusUpdate.emit();
|
|
661
|
+
if (this.#open) {
|
|
662
|
+
await this._closeStack(this.#ctx ?? Context.default());
|
|
663
|
+
}
|
|
421
664
|
// Wipe all SQLite tables so next open starts fresh.
|
|
422
|
-
await RuntimeProvider.runPromise(this
|
|
665
|
+
await RuntimeProvider.runPromise(this.#runtime)(
|
|
423
666
|
Effect.gen(function* () {
|
|
424
667
|
const sql = yield* SqlClient.SqlClient;
|
|
425
668
|
// Echo metadata + large space data.
|
|
@@ -449,12 +692,249 @@ export class ClientServicesHost {
|
|
|
449
692
|
}),
|
|
450
693
|
);
|
|
451
694
|
log.info('reset');
|
|
452
|
-
await this
|
|
695
|
+
await this.#callbacks?.onReset?.();
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
//
|
|
699
|
+
// Orchestration (formerly ServiceContext).
|
|
700
|
+
//
|
|
701
|
+
|
|
702
|
+
async createIdentity(params: CreateIdentityOptions = {}, ctx?: Context): Promise<Identity> {
|
|
703
|
+
ctx ??= this.#ctx ?? Context.default();
|
|
704
|
+
const identity = await this.identityManager.createIdentity(params, ctx);
|
|
705
|
+
await this._setNetworkIdentity({ identity });
|
|
706
|
+
await identity.joinNetwork(ctx);
|
|
707
|
+
await this._initialize(ctx);
|
|
708
|
+
return identity;
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
getInvitationHandler(invitation: Partial<Invitation> & Pick<Invitation, 'kind'>): InvitationProtocol {
|
|
712
|
+
if (this.identityManager.identity == null && invitation.kind === Invitation.Kind.SPACE) {
|
|
713
|
+
throw new Error('Identity must be created before joining a space.');
|
|
714
|
+
}
|
|
715
|
+
const factory = this.#handlerFactories.get(invitation.kind);
|
|
716
|
+
invariant(factory, `Unknown invitation kind: ${invitation.kind}`);
|
|
717
|
+
return factory(invitation);
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
async broadcastProfileUpdate(profile: ProfileDocument | undefined): Promise<void> {
|
|
721
|
+
if (!profile || !this.#dataSpaceManager) {
|
|
722
|
+
return;
|
|
723
|
+
}
|
|
724
|
+
for (const space of this.#dataSpaceManager.spaces.values()) {
|
|
725
|
+
await space.updateOwnProfile(profile);
|
|
726
|
+
}
|
|
453
727
|
}
|
|
454
728
|
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
729
|
+
/**
|
|
730
|
+
* Resolves the {@link DataSpaceManager} once identity-bound services have opened (`initialized`).
|
|
731
|
+
*/
|
|
732
|
+
async whenDataSpaceManagerReady(): Promise<DataSpaceManager> {
|
|
733
|
+
await this.#initialized.wait();
|
|
734
|
+
return this.#dataSpaceManager ?? failUndefined();
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
/**
|
|
738
|
+
* Resolves the {@link EdgeAgentManager} once identity-bound services have opened (`initialized`).
|
|
739
|
+
*/
|
|
740
|
+
async whenEdgeAgentManagerReady(): Promise<EdgeAgentManager> {
|
|
741
|
+
await this.#initialized.wait();
|
|
742
|
+
return this.#edgeAgentManager ?? failUndefined();
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
private async _acceptIdentity(params: JoinIdentityProps): Promise<Identity> {
|
|
746
|
+
const ctx = this.#ctx ?? Context.default();
|
|
747
|
+
const { identity, identityRecord } = await this.identityManager.prepareIdentity(params, ctx);
|
|
748
|
+
await this._setNetworkIdentity({ deviceCredential: params.authorizedDeviceCredential!, identity });
|
|
749
|
+
await identity.joinNetwork(ctx);
|
|
750
|
+
await this.identityManager.acceptIdentity(identity, identityRecord, params.deviceProfile);
|
|
751
|
+
await this._initialize(ctx);
|
|
458
752
|
return identity;
|
|
459
753
|
}
|
|
754
|
+
|
|
755
|
+
private async _checkStorageVersion(): Promise<void> {
|
|
756
|
+
await this.metadataStore.load();
|
|
757
|
+
if (this.metadataStore.version !== STORAGE_VERSION) {
|
|
758
|
+
throw new InvalidStorageVersionError(STORAGE_VERSION, this.metadataStore.version);
|
|
759
|
+
// TODO(mykola): Migrate storage to a new version if incompatibility is detected.
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
/**
|
|
764
|
+
* Opens the identity-bound services once an identity is available.
|
|
765
|
+
*/
|
|
766
|
+
@Trace.span()
|
|
767
|
+
private async _initialize(ctx: Context): Promise<void> {
|
|
768
|
+
log('_initialize: start');
|
|
769
|
+
const identity = this.identityManager.identity ?? failUndefined();
|
|
770
|
+
|
|
771
|
+
await this.#dataSpaceManager!.open(ctx);
|
|
772
|
+
log('_initialize: DataSpaceManager opened');
|
|
773
|
+
|
|
774
|
+
await this.#edgeAgentManager!.open(ctx);
|
|
775
|
+
log('_initialize: EdgeAgentManager opened');
|
|
776
|
+
|
|
777
|
+
this.#handlerFactories.set(
|
|
778
|
+
Invitation.Kind.SPACE,
|
|
779
|
+
(invitation) =>
|
|
780
|
+
new SpaceInvitationProtocol(
|
|
781
|
+
this.#dataSpaceManager!,
|
|
782
|
+
this.#signingContextProvider!(),
|
|
783
|
+
this.#keyring!,
|
|
784
|
+
invitation.spaceKey,
|
|
785
|
+
),
|
|
786
|
+
);
|
|
787
|
+
this.#initialized.wake();
|
|
788
|
+
|
|
789
|
+
this.#deviceSpaceSync!.setIdentity(identity);
|
|
790
|
+
await this.#deviceSpaceSync!.open?.(ctx);
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
private async _setNetworkIdentity(params?: { deviceCredential?: Credential; identity?: Identity }): Promise<void> {
|
|
794
|
+
log('_setNetworkIdentity: acquiring mutex...');
|
|
795
|
+
using _ = await this.#edgeIdentityUpdateMutex.acquire();
|
|
796
|
+
log('_setNetworkIdentity: mutex acquired');
|
|
797
|
+
|
|
798
|
+
let edgeIdentity: EdgeIdentity;
|
|
799
|
+
const identity = params?.identity;
|
|
800
|
+
if (identity) {
|
|
801
|
+
if (params?.deviceCredential) {
|
|
802
|
+
edgeIdentity = await createChainEdgeIdentity(
|
|
803
|
+
identity.signer,
|
|
804
|
+
identity.identityKey,
|
|
805
|
+
identity.deviceKey,
|
|
806
|
+
{ credential: params.deviceCredential },
|
|
807
|
+
[], // TODO(dmaretskyi): Service access credentials.
|
|
808
|
+
);
|
|
809
|
+
} else {
|
|
810
|
+
// TODO: throw here or from identity if device chain can't be loaded, to avoid indefinite hangup
|
|
811
|
+
await warnAfterTimeout(10_000, 'Waiting for identity to be ready for edge connection', async () => {
|
|
812
|
+
await identity.ready();
|
|
813
|
+
});
|
|
814
|
+
|
|
815
|
+
invariant(identity.deviceCredentialChain);
|
|
816
|
+
|
|
817
|
+
edgeIdentity = await createChainEdgeIdentity(
|
|
818
|
+
identity.signer,
|
|
819
|
+
identity.identityKey,
|
|
820
|
+
identity.deviceKey,
|
|
821
|
+
identity.deviceCredentialChain,
|
|
822
|
+
[], // TODO(dmaretskyi): Service access credentials.
|
|
823
|
+
);
|
|
824
|
+
}
|
|
825
|
+
} else {
|
|
826
|
+
edgeIdentity = await createEphemeralEdgeIdentity();
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
this.#edgeConnection?.setIdentity(edgeIdentity);
|
|
830
|
+
this.#edgeHttpClient?.setIdentity(edgeIdentity);
|
|
831
|
+
this.networkManager.setPeerInfo({
|
|
832
|
+
identityDid: edgeIdentity.identityDid,
|
|
833
|
+
peerKey: edgeIdentity.peerKey,
|
|
834
|
+
});
|
|
835
|
+
log('_setNetworkIdentity: done');
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
/**
|
|
839
|
+
* Open lifecycle stages over the resolved stack components (formerly ServiceContext._open).
|
|
840
|
+
*/
|
|
841
|
+
private async _openStack(ctx: Context): Promise<void> {
|
|
842
|
+
log('running storage migrations...');
|
|
843
|
+
await RuntimeProvider.runPromise(this.#runtime)(this.#storageMigrate!);
|
|
844
|
+
|
|
845
|
+
await this._checkStorageVersion();
|
|
846
|
+
|
|
847
|
+
log('running sqlite health check...');
|
|
848
|
+
await runSqliteHealthCheck(this.#runtime);
|
|
849
|
+
log('sqlite health check passed');
|
|
850
|
+
|
|
851
|
+
log('opening identityManager...');
|
|
852
|
+
await this.#identityManager!.open(ctx);
|
|
853
|
+
log('identityManager opened', { hasIdentity: !!this.#identityManager!.identity });
|
|
854
|
+
|
|
855
|
+
log('setting network identity...');
|
|
856
|
+
await this._setNetworkIdentity({ identity: this.#identityManager!.identity });
|
|
857
|
+
|
|
858
|
+
log('opening edge connection...');
|
|
859
|
+
await this.#edgeConnection?.open(ctx);
|
|
860
|
+
|
|
861
|
+
log('opening signal manager...');
|
|
862
|
+
await this.#signalManager!.open(ctx);
|
|
863
|
+
|
|
864
|
+
log('opening network manager...');
|
|
865
|
+
await this.#networkManager!.open();
|
|
866
|
+
|
|
867
|
+
// EchoHost open/close is owned by its layer scope; the host is already open here.
|
|
868
|
+
if (this.#meshReplicator) {
|
|
869
|
+
log('adding mesh replicator...');
|
|
870
|
+
await this.#echoHost!.addReplicator(ctx, this.#meshReplicator);
|
|
871
|
+
}
|
|
872
|
+
if (this.#echoEdgeReplicator) {
|
|
873
|
+
log('adding edge replicator...');
|
|
874
|
+
await this.#echoHost!.addReplicator(ctx, this.#echoEdgeReplicator);
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
log('loading metadata store...');
|
|
878
|
+
await this.#metadataStore!.load();
|
|
879
|
+
|
|
880
|
+
log('opening space manager...');
|
|
881
|
+
await this.#spaceManager!.open();
|
|
882
|
+
|
|
883
|
+
if (this.#identityManager!.identity) {
|
|
884
|
+
log('joining network...');
|
|
885
|
+
await this.#identityManager!.identity.joinNetwork(ctx);
|
|
886
|
+
|
|
887
|
+
log('initializing spaces...');
|
|
888
|
+
await this._initialize(ctx);
|
|
889
|
+
} else {
|
|
890
|
+
log('no identity, skipping network join and space initialization');
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
log('opening feed syncer...');
|
|
894
|
+
await this.#feedSyncer?.open(ctx);
|
|
895
|
+
|
|
896
|
+
log('loading persistent invitations...');
|
|
897
|
+
const loadedInvitations = await this.#invitationsManager!.loadPersistentInvitations(ctx);
|
|
898
|
+
log('loaded persistent invitations', { count: loadedInvitations.invitations?.length });
|
|
899
|
+
|
|
900
|
+
log('stack opened');
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
/**
|
|
904
|
+
* Close lifecycle stages (formerly ServiceContext._close).
|
|
905
|
+
*/
|
|
906
|
+
private async _closeStack(ctx: Context): Promise<void> {
|
|
907
|
+
log('closing stack...');
|
|
908
|
+
await this.#feedSyncer?.close();
|
|
909
|
+
await this.#deviceSpaceSync?.close?.();
|
|
910
|
+
await this.#dataSpaceManager?.close(ctx);
|
|
911
|
+
await this.#edgeAgentManager?.close();
|
|
912
|
+
await this.#identityManager?.close(ctx);
|
|
913
|
+
await this.#spaceManager?.close();
|
|
914
|
+
// EchoHost close is owned by its layer scope and runs when the runtime is disposed.
|
|
915
|
+
|
|
916
|
+
await this.#networkManager?.close(ctx);
|
|
917
|
+
await this.#signalManager?.close();
|
|
918
|
+
await this.#edgeConnection?.close();
|
|
919
|
+
await this.#feedStore?.close();
|
|
920
|
+
await this.#metadataStore?.close();
|
|
921
|
+
log('stack closed');
|
|
922
|
+
}
|
|
460
923
|
}
|
|
924
|
+
|
|
925
|
+
/**
|
|
926
|
+
* Context tag for the {@link ClientServicesHost}. The host provides itself under this tag when it
|
|
927
|
+
* builds its component stack, so client RPC handler layers can resolve the orchestration entry
|
|
928
|
+
* points (`createIdentity`, readiness gates, …) they need.
|
|
929
|
+
*/
|
|
930
|
+
export class ClientServicesHostService extends EffectContext.Tag('@dxos/client-services/ClientServicesHost')<
|
|
931
|
+
ClientServicesHostService,
|
|
932
|
+
ClientServicesHost
|
|
933
|
+
>() {}
|
|
934
|
+
|
|
935
|
+
/**
|
|
936
|
+
* Layer that constructs a {@link ClientServicesHost} from its props and exposes it under
|
|
937
|
+
* {@link ClientServicesHostService}. Host lifecycle (`open` / `close`) stays caller-driven.
|
|
938
|
+
*/
|
|
939
|
+
export const layerClientServicesHost = (props: ClientServicesHostProps): Layer.Layer<ClientServicesHostService> =>
|
|
940
|
+
Layer.sync(ClientServicesHostService, () => new ClientServicesHost(props));
|