@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
|
@@ -0,0 +1,439 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2022 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { Event, Trigger, sleepWithContext, synchronized } from '@dxos/async';
|
|
6
|
+
import { Context, rejectOnDispose } from '@dxos/context';
|
|
7
|
+
import { failUndefined } from '@dxos/debug';
|
|
8
|
+
import { FeedSetIterator, type FeedWrapper, type FeedWriter } from '@dxos/feed-store';
|
|
9
|
+
import { invariant } from '@dxos/invariant';
|
|
10
|
+
import { PublicKey } from '@dxos/keys';
|
|
11
|
+
import { log } from '@dxos/log';
|
|
12
|
+
import { type FeedMessageBlock } from '@dxos/protocols';
|
|
13
|
+
import type { FeedMessage } from '@dxos/protocols/proto/dxos/echo/feed';
|
|
14
|
+
import { Timeframe } from '@dxos/timeframe';
|
|
15
|
+
import { ComplexMap } from '@dxos/util';
|
|
16
|
+
|
|
17
|
+
import { createMappedFeedWriter } from './feeds';
|
|
18
|
+
import { createMessageSelector } from './message-selector';
|
|
19
|
+
import { TimeframeClock, mapFeedIndexesToTimeframe, startAfter } from './timeframe-clock';
|
|
20
|
+
|
|
21
|
+
export type WaitUntilReachedTargetProps = {
|
|
22
|
+
/**
|
|
23
|
+
* For cancellation.
|
|
24
|
+
*/
|
|
25
|
+
ctx?: Context;
|
|
26
|
+
timeout?: number;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* @default true
|
|
30
|
+
*/
|
|
31
|
+
breakOnStall?: boolean;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* External state accessor.
|
|
36
|
+
*/
|
|
37
|
+
export class PipelineState {
|
|
38
|
+
/**
|
|
39
|
+
* @internal
|
|
40
|
+
*/
|
|
41
|
+
_ctx = new Context();
|
|
42
|
+
|
|
43
|
+
// TODO(dmaretskyi): Remove?. Avoid accessing `_timeframeClock` before constructor initialization.
|
|
44
|
+
public get timeframeUpdate() {
|
|
45
|
+
return this._timeframeClock.update;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
public readonly stalled = new Event();
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* @internal
|
|
52
|
+
*/
|
|
53
|
+
_startTimeframe: Timeframe = new Timeframe();
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Target timeframe we are waiting to reach.
|
|
57
|
+
*/
|
|
58
|
+
private _targetTimeframe: Timeframe | undefined;
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* @internal
|
|
62
|
+
*/
|
|
63
|
+
_reachedTargetPromise: Promise<void> | undefined;
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* @internal
|
|
67
|
+
*/
|
|
68
|
+
_reachedTarget: boolean = false;
|
|
69
|
+
|
|
70
|
+
constructor(
|
|
71
|
+
private _feeds: ComplexMap<PublicKey, FeedWrapper<FeedMessage>>,
|
|
72
|
+
private _timeframeClock: TimeframeClock,
|
|
73
|
+
) {}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Latest theoretical timeframe based on the last mutation in each feed.
|
|
77
|
+
* NOTE: This might never be reached if the mutation dependencies
|
|
78
|
+
*/
|
|
79
|
+
// TODO(dmaretskyi): Rename `totalTimeframe`? or `lastTimeframe`.
|
|
80
|
+
get endTimeframe() {
|
|
81
|
+
return mapFeedIndexesToTimeframe(
|
|
82
|
+
Array.from(this._feeds.values())
|
|
83
|
+
.filter((feed) => feed.length > 0)
|
|
84
|
+
.map((feed) => ({
|
|
85
|
+
feedKey: feed.key,
|
|
86
|
+
index: feed.length - 1,
|
|
87
|
+
})),
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
get startTimeframe() {
|
|
92
|
+
return this._startTimeframe;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
get timeframe() {
|
|
96
|
+
return this._timeframeClock.timeframe;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
get pendingTimeframe() {
|
|
100
|
+
return this._timeframeClock.pendingTimeframe;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
get targetTimeframe() {
|
|
104
|
+
return this._targetTimeframe ? this._targetTimeframe : new Timeframe();
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
get reachedTarget() {
|
|
108
|
+
return this._reachedTarget;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
get feeds() {
|
|
112
|
+
return Array.from(this._feeds.values());
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
async waitUntilTimeframe(target: Timeframe): Promise<void> {
|
|
116
|
+
await this._timeframeClock.waitUntilReached(target);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
setTargetTimeframe(target: Timeframe): void {
|
|
120
|
+
this._targetTimeframe = target;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Wait until the pipeline processes all messages in the feed and reaches the target timeframe if that is set.
|
|
125
|
+
*
|
|
126
|
+
* This function will resolve immediately if the pipeline is stalled.
|
|
127
|
+
*
|
|
128
|
+
* @param timeout Timeout in milliseconds to specify the maximum wait time.
|
|
129
|
+
*/
|
|
130
|
+
async waitUntilReachedTargetTimeframe({
|
|
131
|
+
ctx = new Context(),
|
|
132
|
+
timeout,
|
|
133
|
+
breakOnStall = true,
|
|
134
|
+
}: WaitUntilReachedTargetProps = {}): Promise<void> {
|
|
135
|
+
log('waitUntilReachedTargetTimeframe', {
|
|
136
|
+
timeout,
|
|
137
|
+
current: this.timeframe,
|
|
138
|
+
target: this.targetTimeframe,
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
this._reachedTargetPromise ??= Promise.race([
|
|
142
|
+
this._timeframeClock.update.waitForCondition(() => {
|
|
143
|
+
return Timeframe.dependencies(this.targetTimeframe, this.timeframe).isEmpty();
|
|
144
|
+
}),
|
|
145
|
+
...(breakOnStall ? [this.stalled.discardParameter().waitForCount(1)] : []),
|
|
146
|
+
]);
|
|
147
|
+
|
|
148
|
+
let done = false;
|
|
149
|
+
|
|
150
|
+
if (timeout) {
|
|
151
|
+
return Promise.race([
|
|
152
|
+
rejectOnDispose(ctx),
|
|
153
|
+
rejectOnDispose(this._ctx),
|
|
154
|
+
this._reachedTargetPromise.then(() => {
|
|
155
|
+
done = true;
|
|
156
|
+
this._reachedTarget = true;
|
|
157
|
+
}),
|
|
158
|
+
sleepWithContext(this._ctx, timeout).then(() => {
|
|
159
|
+
if (done) {
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
log.warn('waitUntilReachedTargetTimeframe timed out', {
|
|
164
|
+
timeout,
|
|
165
|
+
current: this.timeframe,
|
|
166
|
+
target: this.targetTimeframe,
|
|
167
|
+
dependencies: Timeframe.dependencies(this.targetTimeframe, this.timeframe),
|
|
168
|
+
});
|
|
169
|
+
}),
|
|
170
|
+
]);
|
|
171
|
+
} else {
|
|
172
|
+
return this._reachedTargetPromise;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// TODO(mykola): Extract to `@dxos/echo-protocol`
|
|
178
|
+
export interface PipelineAccessor {
|
|
179
|
+
state: PipelineState;
|
|
180
|
+
writer: FeedWriter<FeedMessage.Payload>;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* A multi-reader pipeline that operates on feeds.
|
|
185
|
+
* Might have a single writable feed.
|
|
186
|
+
* Has a timeframe clock to handle message ordering.
|
|
187
|
+
*
|
|
188
|
+
* NOTE:
|
|
189
|
+
* - Feeds passed in must have value encoding consistent with the type expected by the iterator/writer.
|
|
190
|
+
*
|
|
191
|
+
* # Usage examples
|
|
192
|
+
*
|
|
193
|
+
* ## Create a new space.
|
|
194
|
+
*
|
|
195
|
+
* 1. Generate space key, genesis feed key.
|
|
196
|
+
* 2. Create and open pipeline reading from {}.
|
|
197
|
+
* 3. Create and add the writable genesis feed.
|
|
198
|
+
* 4. Write the initial sequence of control and credential messages.
|
|
199
|
+
*
|
|
200
|
+
* ## Load an existing space from storage
|
|
201
|
+
*
|
|
202
|
+
* 1. Load space key, genesis feed key, get starting timeframe from saved snapshot.
|
|
203
|
+
* 2. Create and open pipeline reading from the initial timeframe.
|
|
204
|
+
* 3. Open and add the genesis feed.
|
|
205
|
+
* 4. Once the writable feed is added, the pipeline becomes writable.
|
|
206
|
+
*
|
|
207
|
+
* ## Join an existing space created by another agent/device.
|
|
208
|
+
*
|
|
209
|
+
* 1. Get the space key, genesis feed key from another agent.
|
|
210
|
+
* 2. (optionally) Download the snapshot from another agent.
|
|
211
|
+
* 3. Create and open pipeline.
|
|
212
|
+
* 4. Generate the writable feed key.
|
|
213
|
+
* 5. Wait for the writable feed to be added.
|
|
214
|
+
*/
|
|
215
|
+
export class Pipeline implements PipelineAccessor {
|
|
216
|
+
private readonly _timeframeClock = new TimeframeClock(new Timeframe());
|
|
217
|
+
private readonly _feeds = new ComplexMap<PublicKey, FeedWrapper<FeedMessage>>(PublicKey.hash);
|
|
218
|
+
|
|
219
|
+
// External state accessor.
|
|
220
|
+
private readonly _state: PipelineState = new PipelineState(this._feeds, this._timeframeClock);
|
|
221
|
+
|
|
222
|
+
// Waits for the message consumer to process the message and yield control back to the pipeline.
|
|
223
|
+
private readonly _processingTrigger = new Trigger().wake();
|
|
224
|
+
private readonly _pauseTrigger = new Trigger().wake();
|
|
225
|
+
|
|
226
|
+
// Pending downloads.
|
|
227
|
+
private readonly _downloads = new ComplexMap<FeedWrapper<FeedMessage>, any>((value) => PublicKey.hash(value.key));
|
|
228
|
+
|
|
229
|
+
// Inbound feed stream.
|
|
230
|
+
private _feedSetIterator?: FeedSetIterator<FeedMessage>;
|
|
231
|
+
|
|
232
|
+
// Outbound feed writer.
|
|
233
|
+
private _writer: FeedWriter<FeedMessage.Payload> | undefined;
|
|
234
|
+
|
|
235
|
+
private _isStopping = false;
|
|
236
|
+
private _isStarted = false;
|
|
237
|
+
private _isBeingConsumed = false;
|
|
238
|
+
private _isPaused = false;
|
|
239
|
+
|
|
240
|
+
get state() {
|
|
241
|
+
return this._state;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
get writer(): FeedWriter<FeedMessage.Payload> {
|
|
245
|
+
invariant(this._writer, 'Writer not set.');
|
|
246
|
+
return this._writer;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
hasFeed(feedKey: PublicKey): boolean {
|
|
250
|
+
return this._feeds.has(feedKey);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
getFeeds(): FeedWrapper<FeedMessage>[] {
|
|
254
|
+
return this._feedSetIterator!.feeds;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// NOTE: This cannot be synchronized with `stop` because stop waits for the mutation processing to complete,
|
|
258
|
+
// which might be opening feeds during the mutation processing, which w
|
|
259
|
+
async addFeed(feed: FeedWrapper<FeedMessage>): Promise<void> {
|
|
260
|
+
this._feeds.set(feed.key, feed);
|
|
261
|
+
|
|
262
|
+
if (this._feedSetIterator) {
|
|
263
|
+
await this._feedSetIterator.addFeed(feed);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
if (this._isStarted && !this._isPaused) {
|
|
267
|
+
this._setFeedDownloadState(feed);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
setWriteFeed(feed: FeedWrapper<FeedMessage>): void {
|
|
272
|
+
invariant(!this._writer, 'Writer already set.');
|
|
273
|
+
invariant(feed.properties.writable, 'Feed must be writable.');
|
|
274
|
+
|
|
275
|
+
this._writer = createMappedFeedWriter<FeedMessage.Payload, FeedMessage>(
|
|
276
|
+
(payload: FeedMessage.Payload) => ({
|
|
277
|
+
timeframe: this._timeframeClock.timeframe,
|
|
278
|
+
payload,
|
|
279
|
+
}),
|
|
280
|
+
feed.createFeedWriter(),
|
|
281
|
+
);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
@synchronized
|
|
285
|
+
async start(): Promise<void> {
|
|
286
|
+
invariant(!this._isStarted, 'Pipeline is already started.');
|
|
287
|
+
log('starting...');
|
|
288
|
+
await this._initIterator();
|
|
289
|
+
await this._feedSetIterator!.open();
|
|
290
|
+
this._isStarted = true;
|
|
291
|
+
log('started');
|
|
292
|
+
|
|
293
|
+
if (!this._isPaused) {
|
|
294
|
+
for (const feed of this._feeds.values()) {
|
|
295
|
+
this._setFeedDownloadState(feed);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
@synchronized
|
|
301
|
+
async stop(): Promise<void> {
|
|
302
|
+
log('stopping...');
|
|
303
|
+
this._isStopping = true;
|
|
304
|
+
for (const [feed, handle] of this._downloads.entries()) {
|
|
305
|
+
feed.undownload(handle);
|
|
306
|
+
}
|
|
307
|
+
this._downloads.clear();
|
|
308
|
+
await this._feedSetIterator?.close();
|
|
309
|
+
await this._processingTrigger.wait(); // Wait for the in-flight message to be processed.
|
|
310
|
+
await this._state._ctx.dispose();
|
|
311
|
+
this._state._ctx = new Context();
|
|
312
|
+
this._state._reachedTargetPromise = undefined;
|
|
313
|
+
this._state._reachedTarget = false;
|
|
314
|
+
this._isStarted = false;
|
|
315
|
+
log('stopped');
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* @param timeframe Timeframe of already processed messages.
|
|
320
|
+
* The pipeline will start processing messages AFTER this timeframe.
|
|
321
|
+
*/
|
|
322
|
+
@synchronized
|
|
323
|
+
async setCursor(timeframe: Timeframe): Promise<void> {
|
|
324
|
+
invariant(!this._isStarted || this._isPaused, 'Invalid state.');
|
|
325
|
+
|
|
326
|
+
this._state._startTimeframe = timeframe;
|
|
327
|
+
this._timeframeClock.setTimeframe(timeframe);
|
|
328
|
+
|
|
329
|
+
// Cancel downloads of mutations before the cursor.
|
|
330
|
+
if (this._feedSetIterator) {
|
|
331
|
+
await this._feedSetIterator.close();
|
|
332
|
+
await this._initIterator();
|
|
333
|
+
await this._feedSetIterator.open();
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
/**
|
|
338
|
+
* Calling pause while processing will cause a deadlock.
|
|
339
|
+
*/
|
|
340
|
+
@synchronized
|
|
341
|
+
async pause(): Promise<void> {
|
|
342
|
+
if (this._isPaused) {
|
|
343
|
+
return;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
this._pauseTrigger.reset();
|
|
347
|
+
await this._processingTrigger.wait();
|
|
348
|
+
this._isPaused = true;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
@synchronized
|
|
352
|
+
async unpause(): Promise<void> {
|
|
353
|
+
invariant(this._isPaused, 'Pipeline is not paused.');
|
|
354
|
+
|
|
355
|
+
this._pauseTrigger.wake();
|
|
356
|
+
this._isPaused = false;
|
|
357
|
+
|
|
358
|
+
for (const feed of this._feeds.values()) {
|
|
359
|
+
this._setFeedDownloadState(feed);
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
/**
|
|
364
|
+
* Starts to iterate over the ordered messages from the added feeds.
|
|
365
|
+
* Updates the timeframe clock after the message has bee processed.
|
|
366
|
+
*/
|
|
367
|
+
async *consume(): AsyncIterable<FeedMessageBlock> {
|
|
368
|
+
invariant(!this._isBeingConsumed, 'Pipeline is already being consumed.');
|
|
369
|
+
this._isBeingConsumed = true;
|
|
370
|
+
|
|
371
|
+
invariant(this._feedSetIterator, 'Iterator not initialized.');
|
|
372
|
+
let lastFeedSetIterator = this._feedSetIterator;
|
|
373
|
+
let iterable = lastFeedSetIterator[Symbol.asyncIterator]();
|
|
374
|
+
|
|
375
|
+
while (!this._isStopping) {
|
|
376
|
+
await this._pauseTrigger.wait();
|
|
377
|
+
|
|
378
|
+
// Iterator might have been changed while we were waiting for the processing to complete.
|
|
379
|
+
if (lastFeedSetIterator !== this._feedSetIterator) {
|
|
380
|
+
invariant(this._feedSetIterator, 'Iterator not initialized.');
|
|
381
|
+
lastFeedSetIterator = this._feedSetIterator;
|
|
382
|
+
iterable = lastFeedSetIterator[Symbol.asyncIterator]();
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
// Will be canceled when the iterator gets closed.
|
|
386
|
+
const { done, value } = await iterable.next();
|
|
387
|
+
if (!done) {
|
|
388
|
+
const block = value ?? failUndefined();
|
|
389
|
+
this._processingTrigger.reset();
|
|
390
|
+
this._timeframeClock.updatePendingTimeframe(PublicKey.from(block.feedKey), block.seq);
|
|
391
|
+
yield block;
|
|
392
|
+
this._processingTrigger.wake();
|
|
393
|
+
this._timeframeClock.updateTimeframe();
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
// TODO(burdon): Test re-entrant?
|
|
398
|
+
this._isBeingConsumed = false;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
private _setFeedDownloadState(feed: FeedWrapper<FeedMessage>): void {
|
|
402
|
+
let handle = this._downloads.get(feed); // TODO(burdon): Always undefined?
|
|
403
|
+
if (handle) {
|
|
404
|
+
feed.undownload(handle);
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
const timeframe = this._state._startTimeframe;
|
|
408
|
+
const seq = timeframe.get(feed.key) ?? -1;
|
|
409
|
+
log('download', { feed: feed.key.truncate(), seq, length: feed.length });
|
|
410
|
+
handle = feed.download({ start: seq + 1, linear: true }, (err: any, data: any) => {
|
|
411
|
+
if (err) {
|
|
412
|
+
// log.warn(err); // TODO(burdon): Feed is closed/Download was cancelled.
|
|
413
|
+
} else {
|
|
414
|
+
log('downloaded', { data }); // TODO(burdon): Never called.
|
|
415
|
+
}
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
this._downloads.set(feed, handle);
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
private async _initIterator(): Promise<void> {
|
|
422
|
+
this._feedSetIterator = new FeedSetIterator<FeedMessage>(createMessageSelector(this._timeframeClock), {
|
|
423
|
+
start: startAfter(this._timeframeClock.timeframe),
|
|
424
|
+
stallTimeout: 1000,
|
|
425
|
+
});
|
|
426
|
+
|
|
427
|
+
this._feedSetIterator.stalled.on((iterator) => {
|
|
428
|
+
log.warn(`Stalled after ${iterator.options.stallTimeout}ms with ${iterator.size} feeds.`, {
|
|
429
|
+
currentTimeframe: this._timeframeClock.timeframe,
|
|
430
|
+
targetTimeframe: this._state.targetTimeframe,
|
|
431
|
+
});
|
|
432
|
+
this._state.stalled.emit();
|
|
433
|
+
});
|
|
434
|
+
|
|
435
|
+
for (const feed of this._feeds.values()) {
|
|
436
|
+
await this._feedSetIterator.addFeed(feed);
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2022 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { TestBuilder } from '@dxos/feed-store/testing';
|
|
6
|
+
import type { FeedMessage } from '@dxos/protocols/proto/dxos/echo/feed';
|
|
7
|
+
|
|
8
|
+
import { valueEncoding } from '../codec';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Builder with default encoder and generator.
|
|
12
|
+
*/
|
|
13
|
+
export class TestFeedBuilder extends TestBuilder<FeedMessage> {
|
|
14
|
+
constructor() {
|
|
15
|
+
super({
|
|
16
|
+
valueEncoding,
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2020 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { Event } from '@dxos/async';
|
|
6
|
+
import { timed } from '@dxos/debug';
|
|
7
|
+
import { type FeedIndex } from '@dxos/feed-store';
|
|
8
|
+
import { type PublicKey } from '@dxos/keys';
|
|
9
|
+
import { log } from '@dxos/log';
|
|
10
|
+
import { Timeframe } from '@dxos/timeframe';
|
|
11
|
+
|
|
12
|
+
export const mapTimeframeToFeedIndexes = (timeframe: Timeframe): FeedIndex[] =>
|
|
13
|
+
timeframe.frames().map(([feedKey, index]) => ({ feedKey, index }));
|
|
14
|
+
|
|
15
|
+
export const mapFeedIndexesToTimeframe = (indexes: FeedIndex[]): Timeframe =>
|
|
16
|
+
new Timeframe(indexes.map(({ feedKey, index }) => [feedKey, index]));
|
|
17
|
+
|
|
18
|
+
export const startAfter = (timeframe: Timeframe): FeedIndex[] =>
|
|
19
|
+
timeframe.frames().map(([feedKey, index]) => ({ feedKey, index: index + 1 }));
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Keeps state of the last timeframe that was processed by ECHO.
|
|
23
|
+
*/
|
|
24
|
+
export class TimeframeClock {
|
|
25
|
+
readonly update = new Event<Timeframe>();
|
|
26
|
+
|
|
27
|
+
private _pendingTimeframe: Timeframe;
|
|
28
|
+
|
|
29
|
+
constructor(private _timeframe = new Timeframe()) {
|
|
30
|
+
this._pendingTimeframe = _timeframe;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Timeframe that was processed by ECHO.
|
|
35
|
+
*/
|
|
36
|
+
get timeframe() {
|
|
37
|
+
return this._timeframe;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Timeframe that is currently being processed by ECHO.
|
|
42
|
+
* Will be equal to `timeframe` after the processing is complete.
|
|
43
|
+
*/
|
|
44
|
+
get pendingTimeframe() {
|
|
45
|
+
return this._pendingTimeframe;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
setTimeframe(timeframe: Timeframe): void {
|
|
49
|
+
this._timeframe = timeframe;
|
|
50
|
+
this._pendingTimeframe = timeframe;
|
|
51
|
+
this.update.emit(this._timeframe);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
updatePendingTimeframe(key: PublicKey, seq: number): void {
|
|
55
|
+
this._pendingTimeframe = Timeframe.merge(this._pendingTimeframe, new Timeframe([[key, seq]]));
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
updateTimeframe(): void {
|
|
59
|
+
this._timeframe = this._pendingTimeframe;
|
|
60
|
+
this.update.emit(this._timeframe);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
hasGaps(timeframe: Timeframe): boolean {
|
|
64
|
+
const gaps = Timeframe.dependencies(timeframe, this._timeframe);
|
|
65
|
+
return !gaps.isEmpty();
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
@timed(5_000)
|
|
69
|
+
async waitUntilReached(target: Timeframe): Promise<void> {
|
|
70
|
+
log('waitUntilReached', { target, current: this._timeframe });
|
|
71
|
+
await this.update.waitForCondition(() => {
|
|
72
|
+
log('check if reached', {
|
|
73
|
+
target,
|
|
74
|
+
current: this._timeframe,
|
|
75
|
+
deps: Timeframe.dependencies(target, this._timeframe),
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
return Timeframe.dependencies(target, this._timeframe).isEmpty();
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
}
|