@dxos/client-services 0.8.4-main.fd6878d → 0.8.4-staging.60fe92afc8
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/LICENSE +102 -5
- package/README.md +1 -1
- package/dist/lib/browser/{chunk-6C7MTZLC.mjs → chunk-HPR4MJ4W.mjs} +2916 -3802
- package/dist/lib/browser/chunk-HPR4MJ4W.mjs.map +7 -0
- package/dist/lib/browser/chunk-QCWEHHJW.mjs +24 -0
- package/dist/lib/browser/chunk-QCWEHHJW.mjs.map +7 -0
- package/dist/lib/browser/chunk-XJRPB3GA.mjs +22 -0
- package/dist/lib/browser/chunk-XJRPB3GA.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +576 -139
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/packlets/diagnostics/browser-diagnostics-broadcast.mjs +88 -0
- package/dist/lib/browser/packlets/diagnostics/browser-diagnostics-broadcast.mjs.map +7 -0
- package/dist/lib/browser/packlets/diagnostics/diagnostics-broadcast.mjs +11 -0
- package/dist/lib/browser/packlets/diagnostics/diagnostics-broadcast.mjs.map +7 -0
- package/dist/lib/browser/packlets/locks/browser.mjs +86 -0
- package/dist/lib/browser/packlets/locks/browser.mjs.map +7 -0
- package/dist/lib/browser/packlets/locks/node.mjs +48 -0
- package/dist/lib/browser/packlets/locks/node.mjs.map +7 -0
- package/dist/lib/browser/testing/index.mjs +59 -55
- package/dist/lib/browser/testing/index.mjs.map +3 -3
- package/dist/lib/node-esm/chunk-2DT3MZRL.mjs +22 -0
- package/dist/lib/node-esm/chunk-2DT3MZRL.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-2SZHAWBN.mjs +24 -0
- package/dist/lib/node-esm/chunk-2SZHAWBN.mjs.map +7 -0
- package/dist/lib/node-esm/{chunk-YOHACH7G.mjs → chunk-JW6QHPRJ.mjs} +2859 -3614
- package/dist/lib/node-esm/chunk-JW6QHPRJ.mjs.map +7 -0
- package/dist/lib/node-esm/index.mjs +576 -139
- package/dist/lib/node-esm/index.mjs.map +4 -4
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/lib/node-esm/packlets/diagnostics/browser-diagnostics-broadcast.mjs +88 -0
- package/dist/lib/node-esm/packlets/diagnostics/browser-diagnostics-broadcast.mjs.map +7 -0
- package/dist/lib/node-esm/packlets/diagnostics/diagnostics-broadcast.mjs +11 -0
- package/dist/lib/node-esm/packlets/diagnostics/diagnostics-broadcast.mjs.map +7 -0
- package/dist/lib/node-esm/packlets/locks/browser.mjs +86 -0
- package/dist/lib/node-esm/packlets/locks/browser.mjs.map +7 -0
- package/dist/lib/node-esm/packlets/locks/node.mjs +48 -0
- package/dist/lib/node-esm/packlets/locks/node.mjs.map +7 -0
- package/dist/lib/node-esm/testing/index.mjs +59 -55
- package/dist/lib/node-esm/testing/index.mjs.map +3 -3
- package/dist/types/src/index.d.ts +1 -0
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/packlets/agents/edge-agent-manager.d.ts +3 -2
- 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 +2 -1
- package/dist/types/src/packlets/agents/edge-agent-service.d.ts.map +1 -1
- package/dist/types/src/packlets/devices/devices-service.d.ts.map +1 -1
- package/dist/types/src/packlets/devtools/devtools.d.ts +7 -3
- 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/keys.d.ts +2 -2
- package/dist/types/src/packlets/devtools/keys.d.ts.map +1 -1
- package/dist/types/src/packlets/devtools/metadata.d.ts.map +1 -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/browser-diagnostics-broadcast.d.ts.map +1 -1
- 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 +2 -3
- package/dist/types/src/packlets/diagnostics/diagnostics.d.ts.map +1 -1
- package/dist/types/src/packlets/diagnostics/index.d.ts +1 -1
- package/dist/types/src/packlets/diagnostics/index.d.ts.map +1 -1
- package/dist/types/src/packlets/identity/authenticator.d.ts +3 -3
- package/dist/types/src/packlets/identity/authenticator.d.ts.map +1 -1
- package/dist/types/src/packlets/identity/contacts-service.d.ts +1 -1
- package/dist/types/src/packlets/identity/contacts-service.d.ts.map +1 -1
- package/dist/types/src/packlets/identity/identity-manager.d.ts +10 -10
- package/dist/types/src/packlets/identity/identity-manager.d.ts.map +1 -1
- package/dist/types/src/packlets/identity/identity-recovery-manager.d.ts +14 -9
- package/dist/types/src/packlets/identity/identity-recovery-manager.d.ts.map +1 -1
- package/dist/types/src/packlets/identity/identity-service.d.ts +7 -11
- package/dist/types/src/packlets/identity/identity-service.d.ts.map +1 -1
- package/dist/types/src/packlets/identity/identity.d.ts +10 -13
- package/dist/types/src/packlets/identity/identity.d.ts.map +1 -1
- package/dist/types/src/packlets/invitations/device-invitation-protocol.d.ts +7 -6
- package/dist/types/src/packlets/invitations/device-invitation-protocol.d.ts.map +1 -1
- package/dist/types/src/packlets/invitations/edge-invitation-handler.d.ts +1 -1
- package/dist/types/src/packlets/invitations/edge-invitation-handler.d.ts.map +1 -1
- package/dist/types/src/packlets/invitations/invitation-guest-extenstion.d.ts.map +1 -1
- package/dist/types/src/packlets/invitations/invitation-host-extension.d.ts.map +1 -1
- package/dist/types/src/packlets/invitations/invitation-protocol.d.ts +7 -4
- package/dist/types/src/packlets/invitations/invitation-protocol.d.ts.map +1 -1
- package/dist/types/src/packlets/invitations/invitation-state.d.ts.map +1 -1
- package/dist/types/src/packlets/invitations/invitation-topology.d.ts.map +1 -1
- package/dist/types/src/packlets/invitations/invitations-handler.d.ts +4 -4
- package/dist/types/src/packlets/invitations/invitations-handler.d.ts.map +1 -1
- package/dist/types/src/packlets/invitations/invitations-manager.d.ts +5 -5
- package/dist/types/src/packlets/invitations/invitations-manager.d.ts.map +1 -1
- package/dist/types/src/packlets/invitations/invitations-service.d.ts +3 -3
- package/dist/types/src/packlets/invitations/invitations-service.d.ts.map +1 -1
- package/dist/types/src/packlets/invitations/space-invitation-protocol.d.ts +6 -5
- package/dist/types/src/packlets/invitations/space-invitation-protocol.d.ts.map +1 -1
- package/dist/types/src/packlets/invitations/utils.d.ts.map +1 -1
- package/dist/types/src/packlets/locks/browser.d.ts.map +1 -1
- package/dist/types/src/packlets/locks/index.d.ts +1 -1
- package/dist/types/src/packlets/locks/index.d.ts.map +1 -1
- package/dist/types/src/packlets/locks/node.d.ts.map +1 -1
- package/dist/types/src/packlets/logging/logging-service.d.ts +4 -0
- package/dist/types/src/packlets/logging/logging-service.d.ts.map +1 -1
- package/dist/types/src/packlets/network/network-service.d.ts +5 -4
- package/dist/types/src/packlets/network/network-service.d.ts.map +1 -1
- package/dist/types/src/packlets/services/client-rpc-server.d.ts +5 -5
- package/dist/types/src/packlets/services/client-rpc-server.d.ts.map +1 -1
- package/dist/types/src/packlets/services/feed-syncer.d.ts +75 -0
- package/dist/types/src/packlets/services/feed-syncer.d.ts.map +1 -0
- package/dist/types/src/packlets/services/feed-syncer.test.d.ts +2 -0
- package/dist/types/src/packlets/services/feed-syncer.test.d.ts.map +1 -0
- package/dist/types/src/packlets/services/index.d.ts +1 -0
- package/dist/types/src/packlets/services/index.d.ts.map +1 -1
- package/dist/types/src/packlets/services/platform.d.ts.map +1 -1
- package/dist/types/src/packlets/services/service-context.d.ts +22 -19
- package/dist/types/src/packlets/services/service-context.d.ts.map +1 -1
- package/dist/types/src/packlets/services/service-host.d.ts +20 -13
- package/dist/types/src/packlets/services/service-host.d.ts.map +1 -1
- package/dist/types/src/packlets/services/service-registry.d.ts.map +1 -1
- package/dist/types/src/packlets/services/sqlite-storage.d.ts +27 -0
- package/dist/types/src/packlets/services/sqlite-storage.d.ts.map +1 -0
- package/dist/types/src/packlets/services/util.d.ts.map +1 -1
- package/dist/types/src/packlets/space-export/archive-format.d.ts +9 -0
- package/dist/types/src/packlets/space-export/archive-format.d.ts.map +1 -0
- package/dist/types/src/packlets/space-export/index.d.ts +4 -1
- package/dist/types/src/packlets/space-export/index.d.ts.map +1 -1
- package/dist/types/src/packlets/space-export/serialized-space-reader.d.ts +23 -0
- package/dist/types/src/packlets/space-export/serialized-space-reader.d.ts.map +1 -0
- package/dist/types/src/packlets/space-export/serialized-space-writer.d.ts +36 -0
- package/dist/types/src/packlets/space-export/serialized-space-writer.d.ts.map +1 -0
- package/dist/types/src/packlets/space-export/space-archive-reader.d.ts +9 -1
- package/dist/types/src/packlets/space-export/space-archive-reader.d.ts.map +1 -1
- package/dist/types/src/packlets/space-export/space-archive-writer.d.ts +7 -1
- package/dist/types/src/packlets/space-export/space-archive-writer.d.ts.map +1 -1
- package/dist/types/src/packlets/space-export/space-archive.test.d.ts +2 -0
- package/dist/types/src/packlets/space-export/space-archive.test.d.ts.map +1 -0
- package/dist/types/src/packlets/spaces/automerge-space-state.d.ts.map +1 -1
- package/dist/types/src/packlets/spaces/data-space-manager.d.ts +49 -22
- package/dist/types/src/packlets/spaces/data-space-manager.d.ts.map +1 -1
- package/dist/types/src/packlets/spaces/data-space.d.ts +38 -13
- package/dist/types/src/packlets/spaces/data-space.d.ts.map +1 -1
- package/dist/types/src/packlets/spaces/edge-feed-replicator.d.ts +2 -2
- package/dist/types/src/packlets/spaces/edge-feed-replicator.d.ts.map +1 -1
- package/dist/types/src/packlets/spaces/epoch-migrations.d.ts +1 -1
- package/dist/types/src/packlets/spaces/epoch-migrations.d.ts.map +1 -1
- package/dist/types/src/packlets/spaces/genesis.d.ts +4 -3
- package/dist/types/src/packlets/spaces/genesis.d.ts.map +1 -1
- package/dist/types/src/packlets/spaces/notarization-plugin.d.ts +6 -9
- package/dist/types/src/packlets/spaces/notarization-plugin.d.ts.map +1 -1
- package/dist/types/src/packlets/spaces/spaces-service.d.ts +10 -7
- package/dist/types/src/packlets/spaces/spaces-service.d.ts.map +1 -1
- package/dist/types/src/packlets/storage/index.d.ts +1 -0
- package/dist/types/src/packlets/storage/index.d.ts.map +1 -1
- package/dist/types/src/packlets/storage/level.d.ts.map +1 -1
- package/dist/types/src/packlets/storage/profile-archive-sqlite.d.ts +24 -0
- package/dist/types/src/packlets/storage/profile-archive-sqlite.d.ts.map +1 -0
- package/dist/types/src/packlets/storage/profile-archive-sqlite.test.d.ts +2 -0
- package/dist/types/src/packlets/storage/profile-archive-sqlite.test.d.ts.map +1 -0
- package/dist/types/src/packlets/storage/profile-archive.d.ts.map +1 -1
- package/dist/types/src/packlets/storage/storage.d.ts.map +1 -1
- package/dist/types/src/packlets/storage/util.d.ts.map +1 -1
- package/dist/types/src/packlets/system/system-service.d.ts +1 -1
- package/dist/types/src/packlets/system/system-service.d.ts.map +1 -1
- package/dist/types/src/packlets/testing/credential-utils.d.ts.map +1 -1
- package/dist/types/src/packlets/testing/invitation-utils.d.ts +6 -3
- package/dist/types/src/packlets/testing/invitation-utils.d.ts.map +1 -1
- package/dist/types/src/packlets/testing/test-builder.d.ts +20 -22
- package/dist/types/src/packlets/testing/test-builder.d.ts.map +1 -1
- package/dist/types/src/packlets/worker/worker-runtime.d.ts +41 -4
- package/dist/types/src/packlets/worker/worker-runtime.d.ts.map +1 -1
- package/dist/types/src/packlets/worker/worker-session.d.ts +2 -4
- package/dist/types/src/packlets/worker/worker-session.d.ts.map +1 -1
- package/dist/types/src/testing/setup.d.ts.map +1 -1
- package/dist/types/src/version.d.ts +1 -1
- package/dist/types/src/version.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +71 -57
- package/src/index.ts +1 -0
- package/src/packlets/agents/edge-agent-manager.ts +10 -7
- package/src/packlets/agents/edge-agent-service.ts +15 -3
- package/src/packlets/devices/devices-service.test.ts +0 -1
- package/src/packlets/devices/devices-service.ts +1 -1
- package/src/packlets/devtools/devtools.ts +28 -7
- package/src/packlets/devtools/feeds.ts +1 -1
- package/src/packlets/devtools/keys.ts +2 -2
- package/src/packlets/devtools/spaces.ts +1 -1
- package/src/packlets/diagnostics/diagnostics.ts +1 -2
- package/src/packlets/diagnostics/index.ts +1 -1
- package/src/packlets/identity/authenticator.ts +3 -3
- package/src/packlets/identity/contacts-service.ts +1 -2
- package/src/packlets/identity/identity-manager.test.ts +6 -6
- package/src/packlets/identity/identity-manager.ts +29 -28
- package/src/packlets/identity/identity-recovery-manager.ts +31 -22
- package/src/packlets/identity/identity-service.test.ts +6 -27
- package/src/packlets/identity/identity-service.ts +17 -83
- package/src/packlets/identity/identity.test.ts +7 -7
- package/src/packlets/identity/identity.ts +12 -35
- package/src/packlets/invitations/device-invitation-protocol.ts +10 -9
- package/src/packlets/invitations/edge-invitation-handler.ts +9 -5
- package/src/packlets/invitations/invitation-guest-extenstion.ts +6 -4
- package/src/packlets/invitations/invitation-host-extension.ts +13 -14
- package/src/packlets/invitations/invitation-protocol.ts +7 -4
- package/src/packlets/invitations/invitation-state.ts +1 -15
- package/src/packlets/invitations/invitations-handler.test.ts +4 -5
- package/src/packlets/invitations/invitations-handler.ts +74 -22
- package/src/packlets/invitations/invitations-manager.ts +42 -17
- package/src/packlets/invitations/invitations-service.ts +9 -9
- package/src/packlets/invitations/space-invitation-protocol.test.ts +17 -16
- package/src/packlets/invitations/space-invitation-protocol.ts +13 -18
- package/src/packlets/locks/index.ts +1 -1
- package/src/packlets/logging/logging-service.ts +20 -16
- package/src/packlets/network/network-service.test.ts +0 -1
- package/src/packlets/network/network-service.ts +10 -8
- package/src/packlets/services/client-rpc-server.ts +19 -16
- package/src/packlets/services/feed-syncer.test.ts +376 -0
- package/src/packlets/services/feed-syncer.ts +536 -0
- package/src/packlets/services/index.ts +1 -0
- package/src/packlets/services/platform.ts +7 -1
- package/src/packlets/services/service-context.test.ts +3 -2
- package/src/packlets/services/service-context.ts +218 -80
- package/src/packlets/services/service-host.test.ts +8 -10
- package/src/packlets/services/service-host.ts +104 -65
- package/src/packlets/services/service-registry.test.ts +0 -1
- package/src/packlets/services/sqlite-storage.ts +390 -0
- package/src/packlets/space-export/archive-format.ts +42 -0
- package/src/packlets/space-export/index.ts +4 -1
- package/src/packlets/space-export/serialized-space-reader.ts +129 -0
- package/src/packlets/space-export/serialized-space-writer.ts +260 -0
- package/src/packlets/space-export/space-archive-reader.ts +65 -4
- package/src/packlets/space-export/space-archive-writer.ts +43 -5
- package/src/packlets/space-export/space-archive.test.ts +482 -0
- package/src/packlets/spaces/data-space-manager.test.ts +169 -14
- package/src/packlets/spaces/data-space-manager.ts +209 -128
- package/src/packlets/spaces/data-space.ts +89 -43
- package/src/packlets/spaces/edge-feed-replicator.test.ts +3 -3
- package/src/packlets/spaces/edge-feed-replicator.ts +12 -10
- package/src/packlets/spaces/epoch-migrations.ts +7 -6
- package/src/packlets/spaces/genesis.ts +9 -4
- package/src/packlets/spaces/notarization-plugin.test.ts +2 -2
- package/src/packlets/spaces/notarization-plugin.ts +10 -9
- package/src/packlets/spaces/spaces-service.test.ts +18 -11
- package/src/packlets/spaces/spaces-service.ts +131 -25
- package/src/packlets/storage/index.ts +1 -0
- package/src/packlets/storage/profile-archive-sqlite.test.ts +79 -0
- package/src/packlets/storage/profile-archive-sqlite.ts +100 -0
- package/src/packlets/storage/profile-archive.ts +3 -0
- package/src/packlets/storage/storage.ts +4 -4
- package/src/packlets/testing/invitation-utils.ts +10 -6
- package/src/packlets/testing/test-builder.ts +59 -40
- package/src/packlets/worker/worker-runtime.ts +173 -17
- package/src/packlets/worker/worker-session.ts +12 -18
- package/src/version.ts +1 -1
- package/dist/lib/browser/chunk-6C7MTZLC.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-YOHACH7G.mjs.map +0 -7
- package/dist/types/src/packlets/identity/default-space-state-machine.d.ts +0 -19
- package/dist/types/src/packlets/identity/default-space-state-machine.d.ts.map +0 -1
- package/src/packlets/identity/default-space-state-machine.ts +0 -44
|
@@ -0,0 +1,536 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2026 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import type * as SqlClient from '@effect/sql/SqlClient';
|
|
6
|
+
import { Encoder, decode as cborXdecode } from 'cbor-x';
|
|
7
|
+
import * as Effect from 'effect/Effect';
|
|
8
|
+
import * as Schema from 'effect/Schema';
|
|
9
|
+
|
|
10
|
+
import { AsyncTask, Mutex, scheduleTask } from '@dxos/async';
|
|
11
|
+
import { Context, Resource } from '@dxos/context';
|
|
12
|
+
import { type EdgeConnection, MessageSchema } from '@dxos/edge-client';
|
|
13
|
+
import { RuntimeProvider } from '@dxos/effect';
|
|
14
|
+
import { type FeedStore, SyncClient } from '@dxos/feed';
|
|
15
|
+
import { invariant } from '@dxos/invariant';
|
|
16
|
+
import { SpaceId } from '@dxos/keys';
|
|
17
|
+
import { log } from '@dxos/log';
|
|
18
|
+
import { FeedProtocol } from '@dxos/protocols';
|
|
19
|
+
import { EdgeService } from '@dxos/protocols';
|
|
20
|
+
import { createBuf } from '@dxos/protocols/buf';
|
|
21
|
+
import { type Message as RouterMessage } from '@dxos/protocols/buf/dxos/edge/messenger_pb';
|
|
22
|
+
import type { GetSyncStateRequest, GetSyncStateResponse } from '@dxos/protocols/proto/dxos/client/services';
|
|
23
|
+
import type { SqlTransaction } from '@dxos/sql-sqlite';
|
|
24
|
+
import { bufferToArray } from '@dxos/util';
|
|
25
|
+
|
|
26
|
+
const encoder = new Encoder({ tagUint8Array: false, useRecords: false });
|
|
27
|
+
|
|
28
|
+
const DEFAULT_MESSAGE_BLOCKS_LIMIT = 50;
|
|
29
|
+
const DEFAULT_SYNC_CONCURRENCY = 5;
|
|
30
|
+
const DEFAULT_POLLING_INTERVAL = 5_000;
|
|
31
|
+
const DEFAULT_POLL_REQUEST_THROTTLE_MS = 250;
|
|
32
|
+
const DEFAULT_PUSH_FAILURE_BACKOFF_MS = 250;
|
|
33
|
+
const MAX_PUSH_FAILURE_BACKOFF_MS = 30_000;
|
|
34
|
+
const MAX_BLOCKING_SYNC_ITERATIONS = 100;
|
|
35
|
+
|
|
36
|
+
export type FeedSyncerOptions = {
|
|
37
|
+
runtime: RuntimeProvider.RuntimeProvider<SqlClient.SqlClient | SqlTransaction.SqlTransaction>;
|
|
38
|
+
feedStore: FeedStore;
|
|
39
|
+
edgeClient: EdgeConnection;
|
|
40
|
+
peerId: string;
|
|
41
|
+
getSpaceIds: () => SpaceId[];
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Namespaces to sync.
|
|
45
|
+
*/
|
|
46
|
+
syncNamespaces: string[];
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Maximum number of blocks to sync in a single message.
|
|
50
|
+
* @default 50
|
|
51
|
+
*/
|
|
52
|
+
messageBlocksLimit?: number;
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Maximum number of spaces to sync concurrently.
|
|
56
|
+
* @default 5
|
|
57
|
+
*/
|
|
58
|
+
syncConcurrency?: number;
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Interval between full polls.
|
|
62
|
+
* @default 10 seconds
|
|
63
|
+
*/
|
|
64
|
+
pollingInterval?: number;
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Minimum delay between externally requested best-effort polls.
|
|
68
|
+
* @default 250 ms
|
|
69
|
+
*/
|
|
70
|
+
pollRequestThrottleMs?: number;
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* When false, only wires the edge message handler; poll/push background tasks and
|
|
74
|
+
* `feedStore.onNewBlocks` auto-push are disabled. Use for tests that call `syncBlocking` explicitly.
|
|
75
|
+
* @default true
|
|
76
|
+
*/
|
|
77
|
+
backgroundSync?: boolean;
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Max time to wait for a feed sync RPC response from edge, in milliseconds.
|
|
81
|
+
* @default 30000 (see `DEFAULT_SYNC_RPC_TIMEOUT_MS` in `@dxos/feed`).
|
|
82
|
+
*/
|
|
83
|
+
syncRpcTimeoutMs?: number;
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
export class FeedSyncer extends Resource {
|
|
87
|
+
readonly #syncNamespaces: string[];
|
|
88
|
+
readonly #messageBlocksLimit: number;
|
|
89
|
+
readonly #syncConcurrency: number;
|
|
90
|
+
readonly #pollingInterval: number;
|
|
91
|
+
readonly #pollRequestThrottleMs: number;
|
|
92
|
+
readonly #backgroundSync: boolean;
|
|
93
|
+
|
|
94
|
+
readonly #runtime: RuntimeProvider.RuntimeProvider<SqlClient.SqlClient | SqlTransaction.SqlTransaction>;
|
|
95
|
+
readonly #feedStore: FeedStore;
|
|
96
|
+
readonly #edgeClient: EdgeConnection;
|
|
97
|
+
readonly #syncClient: SyncClient;
|
|
98
|
+
readonly #getSpaceIds: () => SpaceId[];
|
|
99
|
+
|
|
100
|
+
#spacesToPoll = new Set<SpaceId>();
|
|
101
|
+
/** Last time full poll was completed. */
|
|
102
|
+
#lastFullPoll: number | null = null;
|
|
103
|
+
#throttledPollScheduled = false;
|
|
104
|
+
#lastRequestedPollAt: number | null = null;
|
|
105
|
+
readonly #feedStoreMutex = new Mutex();
|
|
106
|
+
#pushFailureBackoffMs = DEFAULT_PUSH_FAILURE_BACKOFF_MS;
|
|
107
|
+
|
|
108
|
+
constructor(options: FeedSyncerOptions) {
|
|
109
|
+
super();
|
|
110
|
+
this.#runtime = options.runtime;
|
|
111
|
+
this.#feedStore = options.feedStore;
|
|
112
|
+
this.#edgeClient = options.edgeClient;
|
|
113
|
+
this.#syncClient = new SyncClient({
|
|
114
|
+
peerId: options.peerId,
|
|
115
|
+
feedStore: options.feedStore,
|
|
116
|
+
sendMessage: this.#sendMessage.bind(this),
|
|
117
|
+
rpcTimeoutMs: options.syncRpcTimeoutMs,
|
|
118
|
+
});
|
|
119
|
+
this.#getSpaceIds = options.getSpaceIds;
|
|
120
|
+
this.#syncNamespaces = options.syncNamespaces;
|
|
121
|
+
this.#messageBlocksLimit = options.messageBlocksLimit ?? DEFAULT_MESSAGE_BLOCKS_LIMIT;
|
|
122
|
+
this.#syncConcurrency = options.syncConcurrency ?? DEFAULT_SYNC_CONCURRENCY;
|
|
123
|
+
this.#pollingInterval = options.pollingInterval ?? DEFAULT_POLLING_INTERVAL;
|
|
124
|
+
this.#pollRequestThrottleMs = options.pollRequestThrottleMs ?? DEFAULT_POLL_REQUEST_THROTTLE_MS;
|
|
125
|
+
this.#backgroundSync = options.backgroundSync ?? true;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
protected override async _open(): Promise<void> {
|
|
129
|
+
this._ctx.onDispose(
|
|
130
|
+
this.#edgeClient.onMessage((msg: RouterMessage) => {
|
|
131
|
+
if (!msg.serviceId) {
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
const service = msg.serviceId.split(':')[0];
|
|
135
|
+
if (service !== EdgeService.QUEUE_REPLICATOR) {
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
log('feed sync edge ingress', {
|
|
139
|
+
serviceId: msg.serviceId,
|
|
140
|
+
payloadByteLength: msg.payload?.value?.byteLength,
|
|
141
|
+
});
|
|
142
|
+
const handleMessageEffect = Effect.gen(this, function* () {
|
|
143
|
+
const decoded = yield* Effect.try({
|
|
144
|
+
try: () => cborXdecode(msg.payload!.value),
|
|
145
|
+
catch: (error) => new Error(`Failed to decode feed sync message: ${error}`),
|
|
146
|
+
});
|
|
147
|
+
const payload = yield* Schema.validate(FeedProtocol.ProtocolMessage)(decoded);
|
|
148
|
+
yield* this.#syncClient.handleMessage(payload);
|
|
149
|
+
}).pipe(
|
|
150
|
+
Effect.tapError((cause) =>
|
|
151
|
+
Effect.sync(() =>
|
|
152
|
+
log('feed sync edge message handling failed', {
|
|
153
|
+
serviceId: msg.serviceId,
|
|
154
|
+
payloadByteLength: msg.payload?.value?.byteLength,
|
|
155
|
+
cause: cause instanceof Error ? cause.message : String(cause),
|
|
156
|
+
}),
|
|
157
|
+
),
|
|
158
|
+
),
|
|
159
|
+
);
|
|
160
|
+
|
|
161
|
+
void this.#runSerialized(() => RuntimeProvider.runPromise(this.#runtime)(handleMessageEffect));
|
|
162
|
+
}),
|
|
163
|
+
);
|
|
164
|
+
|
|
165
|
+
if (this.#backgroundSync) {
|
|
166
|
+
// Tasks must be opened before registering listeners that call `schedule()`:
|
|
167
|
+
// * `onNewBlocks` can fire from any `feedStore.append` happening on a separate
|
|
168
|
+
// microtask while `_open()` is still awaiting.
|
|
169
|
+
// * The edge client invokes `onReconnected` as a microtask when already connected.
|
|
170
|
+
// `AsyncTask.schedule()` throws if the task is not yet open.
|
|
171
|
+
await this.#pollTask.open();
|
|
172
|
+
await this.#pushTask.open();
|
|
173
|
+
|
|
174
|
+
this.#feedStore.onNewBlocks.on(this._ctx, () => {
|
|
175
|
+
this.#pushTask.schedule();
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
this._ctx.onDispose(
|
|
180
|
+
// NOTE: Fires immediately (as a microtask) if the connection is already open, and again
|
|
181
|
+
// on every subsequent reconnect.
|
|
182
|
+
this.#edgeClient.onReconnected(async () => {
|
|
183
|
+
log('feed sync edge reconnected', {
|
|
184
|
+
peerKey: this.#edgeClient.peerKey,
|
|
185
|
+
identityKey: this.#edgeClient.identityKey,
|
|
186
|
+
});
|
|
187
|
+
if (this.#backgroundSync) {
|
|
188
|
+
this.#resetSpacesToPoll();
|
|
189
|
+
this.#pollTask.schedule();
|
|
190
|
+
this.#pushTask.schedule();
|
|
191
|
+
}
|
|
192
|
+
}),
|
|
193
|
+
);
|
|
194
|
+
|
|
195
|
+
if (this.#backgroundSync) {
|
|
196
|
+
this.#resetSpacesToPoll();
|
|
197
|
+
this.#pollTask.schedule();
|
|
198
|
+
// Flush blocks written before the syncer opened: `onNewBlocks` only fires on append,
|
|
199
|
+
// so existing unpositioned blocks would otherwise never be pushed.
|
|
200
|
+
this.#pushTask.schedule();
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
protected override async _close(): Promise<void> {
|
|
205
|
+
if (this.#backgroundSync) {
|
|
206
|
+
await this.#pollTask.close();
|
|
207
|
+
await this.#pushTask.close();
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Schedules a best-effort pull without blocking the caller.
|
|
213
|
+
*/
|
|
214
|
+
schedulePoll(): void {
|
|
215
|
+
if (!this.#backgroundSync) {
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
this.#resetSpacesToPoll();
|
|
219
|
+
if (this.#throttledPollScheduled) {
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
const now = Date.now();
|
|
224
|
+
const delay =
|
|
225
|
+
this.#lastRequestedPollAt == null
|
|
226
|
+
? 0
|
|
227
|
+
: Math.max(this.#pollRequestThrottleMs - (now - this.#lastRequestedPollAt), 0);
|
|
228
|
+
this.#throttledPollScheduled = true;
|
|
229
|
+
scheduleTask(
|
|
230
|
+
this._ctx,
|
|
231
|
+
() => {
|
|
232
|
+
this.#throttledPollScheduled = false;
|
|
233
|
+
this.#lastRequestedPollAt = Date.now();
|
|
234
|
+
this.#pollTask.schedule();
|
|
235
|
+
},
|
|
236
|
+
delay,
|
|
237
|
+
);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Returns per-namespace queue sync backlog for a space.
|
|
242
|
+
* `blocksToPull` and `blocksToPush` of 0 mean caught up for that namespace.
|
|
243
|
+
*/
|
|
244
|
+
async getSyncState(ctx: Context, request: GetSyncStateRequest): Promise<GetSyncStateResponse> {
|
|
245
|
+
const spaceId = request.spaceId as SpaceId;
|
|
246
|
+
invariant(SpaceId.isValid(spaceId));
|
|
247
|
+
const namespaces =
|
|
248
|
+
request.namespaces != null && request.namespaces.length > 0 ? request.namespaces : this.#syncNamespaces;
|
|
249
|
+
for (const feedNamespace of namespaces) {
|
|
250
|
+
invariant(FeedProtocol.isWellKnownNamespace(feedNamespace));
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
return this.#runSerialized(() =>
|
|
254
|
+
RuntimeProvider.runPromise(this.#runtime)(
|
|
255
|
+
Effect.gen(this, function* () {
|
|
256
|
+
const namespaceStates = yield* Effect.forEach(
|
|
257
|
+
namespaces,
|
|
258
|
+
(feedNamespace) =>
|
|
259
|
+
Effect.gen(this, function* () {
|
|
260
|
+
const blocksToPush = yield* this.#feedStore.countUnpositionedBlocks({
|
|
261
|
+
spaceId,
|
|
262
|
+
feedNamespace,
|
|
263
|
+
});
|
|
264
|
+
const totalBlocks = yield* this.#feedStore.countNamespaceBlocks({
|
|
265
|
+
spaceId,
|
|
266
|
+
feedNamespace,
|
|
267
|
+
});
|
|
268
|
+
const { blocksToPull } = yield* this.#syncClient
|
|
269
|
+
.peekPull(ctx, {
|
|
270
|
+
spaceId,
|
|
271
|
+
feedNamespace,
|
|
272
|
+
limit: this.#messageBlocksLimit,
|
|
273
|
+
})
|
|
274
|
+
.pipe(
|
|
275
|
+
Effect.catchAll((cause) =>
|
|
276
|
+
Effect.gen(this, function* () {
|
|
277
|
+
this.#logSyncFailure('peekPull', { spaceId, feedNamespace, cause });
|
|
278
|
+
return { blocksToPull: 0 };
|
|
279
|
+
}),
|
|
280
|
+
),
|
|
281
|
+
);
|
|
282
|
+
return {
|
|
283
|
+
namespace: feedNamespace,
|
|
284
|
+
blocksToPull: String(blocksToPull),
|
|
285
|
+
blocksToPush: String(blocksToPush),
|
|
286
|
+
totalBlocks: String(totalBlocks),
|
|
287
|
+
};
|
|
288
|
+
}),
|
|
289
|
+
{ concurrency: 'unbounded' },
|
|
290
|
+
);
|
|
291
|
+
return { namespaces: namespaceStates };
|
|
292
|
+
}),
|
|
293
|
+
),
|
|
294
|
+
);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* Performs queue sync and blocks until there are no pending sync batches.
|
|
299
|
+
*/
|
|
300
|
+
async syncBlocking(
|
|
301
|
+
ctx: Context,
|
|
302
|
+
{
|
|
303
|
+
spaceId,
|
|
304
|
+
subspaceTag,
|
|
305
|
+
shouldPush = true,
|
|
306
|
+
shouldPull = true,
|
|
307
|
+
}: {
|
|
308
|
+
spaceId: SpaceId;
|
|
309
|
+
subspaceTag: string;
|
|
310
|
+
shouldPush?: boolean;
|
|
311
|
+
shouldPull?: boolean;
|
|
312
|
+
},
|
|
313
|
+
): Promise<void> {
|
|
314
|
+
invariant(SpaceId.isValid(spaceId));
|
|
315
|
+
invariant(FeedProtocol.isWellKnownNamespace(subspaceTag));
|
|
316
|
+
if (!shouldPush && !shouldPull) {
|
|
317
|
+
return;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
await this.#runSerialized(() =>
|
|
321
|
+
RuntimeProvider.runPromise(this.#runtime)(
|
|
322
|
+
Effect.gen(this, function* () {
|
|
323
|
+
let done = false;
|
|
324
|
+
let iterations = 0;
|
|
325
|
+
while (!done) {
|
|
326
|
+
done = true;
|
|
327
|
+
if (shouldPull) {
|
|
328
|
+
const pullResult = yield* this.#syncClient.pull(ctx, {
|
|
329
|
+
spaceId,
|
|
330
|
+
feedNamespace: subspaceTag,
|
|
331
|
+
limit: this.#messageBlocksLimit,
|
|
332
|
+
});
|
|
333
|
+
done &&= pullResult.done;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
if (shouldPush) {
|
|
337
|
+
const pushResult = yield* this.#syncClient.push(ctx, {
|
|
338
|
+
spaceId,
|
|
339
|
+
feedNamespace: subspaceTag,
|
|
340
|
+
limit: this.#messageBlocksLimit,
|
|
341
|
+
});
|
|
342
|
+
done &&= pushResult.done;
|
|
343
|
+
}
|
|
344
|
+
iterations++;
|
|
345
|
+
if (iterations > MAX_BLOCKING_SYNC_ITERATIONS) {
|
|
346
|
+
throw new Error('Blocking sync exceeded max iterations.');
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
}),
|
|
350
|
+
),
|
|
351
|
+
);
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
async #runSerialized<A>(run: () => Promise<A>): Promise<A> {
|
|
355
|
+
using _guard = await this.#feedStoreMutex.acquire('feed-sync');
|
|
356
|
+
return run();
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
#schedulePushRetry({ hadFailure, needsMore }: { hadFailure: boolean; needsMore: boolean }): void {
|
|
360
|
+
if (!needsMore) {
|
|
361
|
+
this.#pushFailureBackoffMs = DEFAULT_PUSH_FAILURE_BACKOFF_MS;
|
|
362
|
+
return;
|
|
363
|
+
}
|
|
364
|
+
if (hadFailure) {
|
|
365
|
+
const delayMs = this.#pushFailureBackoffMs;
|
|
366
|
+
this.#pushFailureBackoffMs = Math.min(this.#pushFailureBackoffMs * 2, MAX_PUSH_FAILURE_BACKOFF_MS);
|
|
367
|
+
log.info('feed sync push retry scheduled with backoff', { delayMs });
|
|
368
|
+
scheduleTask(this._ctx, () => this.#pushTask.schedule(), delayMs);
|
|
369
|
+
return;
|
|
370
|
+
}
|
|
371
|
+
this.#pushFailureBackoffMs = DEFAULT_PUSH_FAILURE_BACKOFF_MS;
|
|
372
|
+
this.#pushTask.schedule();
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
#resetSpacesToPoll(): void {
|
|
376
|
+
this.#spacesToPoll.clear();
|
|
377
|
+
this.#getSpaceIds().forEach((spaceId) => {
|
|
378
|
+
this.#spacesToPoll.add(spaceId);
|
|
379
|
+
});
|
|
380
|
+
this.#lastFullPoll = Date.now();
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
#sendMessage(
|
|
384
|
+
ctx: Context,
|
|
385
|
+
message: FeedProtocol.QueryRequest | FeedProtocol.AppendRequest,
|
|
386
|
+
): Effect.Effect<void, unknown, never> {
|
|
387
|
+
return Effect.gen(this, function* () {
|
|
388
|
+
const encoded = encoder.encode(message);
|
|
389
|
+
const serviceId = this.#getTargetServiceId(message);
|
|
390
|
+
const rpcTag = 'blocks' in message ? 'AppendRequest' : 'QueryRequest';
|
|
391
|
+
log('feed sync edge rpc outgoing', {
|
|
392
|
+
tag: rpcTag,
|
|
393
|
+
serviceId,
|
|
394
|
+
payloadByteLength: encoded.byteLength,
|
|
395
|
+
spaceId: message.spaceId,
|
|
396
|
+
feedNamespace: message.feedNamespace,
|
|
397
|
+
requestId: message.requestId,
|
|
398
|
+
});
|
|
399
|
+
yield* Effect.tryPromise(async () =>
|
|
400
|
+
this.#edgeClient.send(
|
|
401
|
+
ctx,
|
|
402
|
+
createBuf(MessageSchema, {
|
|
403
|
+
source: {
|
|
404
|
+
identityKey: this.#edgeClient.identityKey,
|
|
405
|
+
peerKey: this.#edgeClient.peerKey,
|
|
406
|
+
},
|
|
407
|
+
serviceId,
|
|
408
|
+
payload: { value: bufferToArray(encoded) },
|
|
409
|
+
}),
|
|
410
|
+
),
|
|
411
|
+
).pipe(
|
|
412
|
+
Effect.tapError((cause) =>
|
|
413
|
+
Effect.sync(() =>
|
|
414
|
+
log('feed sync edge send failed', {
|
|
415
|
+
serviceId,
|
|
416
|
+
tag: rpcTag,
|
|
417
|
+
cause: cause instanceof Error ? cause.message : String(cause),
|
|
418
|
+
}),
|
|
419
|
+
),
|
|
420
|
+
),
|
|
421
|
+
);
|
|
422
|
+
});
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
#logSyncFailure(
|
|
426
|
+
operation: 'pull' | 'push' | 'peekPull',
|
|
427
|
+
{ spaceId, feedNamespace, cause }: { spaceId: SpaceId; feedNamespace: string; cause: unknown },
|
|
428
|
+
): void {
|
|
429
|
+
log('feed sync operation failed', {
|
|
430
|
+
operation,
|
|
431
|
+
spaceId,
|
|
432
|
+
feedNamespace,
|
|
433
|
+
cause: cause instanceof Error ? cause.message : String(cause),
|
|
434
|
+
errorTag: cause instanceof Error ? cause.name : undefined,
|
|
435
|
+
});
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
#getTargetServiceId(message: FeedProtocol.QueryRequest | FeedProtocol.AppendRequest): string {
|
|
439
|
+
// TODO(dmaretskyi): Perhaps in the future we will want to include the queue namespace here as well.
|
|
440
|
+
// This would require putting it at the top level of the message.
|
|
441
|
+
// For now, we let the edge router handle it.
|
|
442
|
+
return FeedProtocol.encodeServiceId(message.feedNamespace, message.spaceId as SpaceId);
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
readonly #pollTask = new AsyncTask(async () =>
|
|
446
|
+
Effect.gen(this, function* () {
|
|
447
|
+
yield* Effect.forEach(
|
|
448
|
+
this.#spacesToPoll,
|
|
449
|
+
(spaceId) =>
|
|
450
|
+
Effect.gen(this, function* () {
|
|
451
|
+
let doneForAllNamespaces = true;
|
|
452
|
+
for (const feedNamespace of this.#syncNamespaces) {
|
|
453
|
+
const { done } = yield* this.#syncClient
|
|
454
|
+
.pull(this._ctx, {
|
|
455
|
+
spaceId,
|
|
456
|
+
feedNamespace,
|
|
457
|
+
limit: this.#messageBlocksLimit,
|
|
458
|
+
})
|
|
459
|
+
.pipe(
|
|
460
|
+
Effect.catchAll((cause) =>
|
|
461
|
+
Effect.gen(this, function* () {
|
|
462
|
+
this.#logSyncFailure('pull', { spaceId, feedNamespace, cause });
|
|
463
|
+
return { done: false };
|
|
464
|
+
}),
|
|
465
|
+
),
|
|
466
|
+
);
|
|
467
|
+
if (!done) {
|
|
468
|
+
doneForAllNamespaces = false;
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
if (doneForAllNamespaces) {
|
|
472
|
+
this.#spacesToPoll.delete(spaceId);
|
|
473
|
+
}
|
|
474
|
+
}),
|
|
475
|
+
{ concurrency: this.#syncConcurrency },
|
|
476
|
+
);
|
|
477
|
+
|
|
478
|
+
// If its time to do a full poll, reset the spaces to poll and schedule the next poll immediately.
|
|
479
|
+
if (this.#lastFullPoll == null || Date.now() - this.#lastFullPoll > this.#pollingInterval) {
|
|
480
|
+
this.#resetSpacesToPoll();
|
|
481
|
+
this.#pollTask.schedule();
|
|
482
|
+
} else if (this.#spacesToPoll.size > 0) {
|
|
483
|
+
// If there are some spaces still syncing, poll them immediately.
|
|
484
|
+
this.#pollTask.schedule();
|
|
485
|
+
} else {
|
|
486
|
+
// All spaces sync, and there's time before the next full poll, schedule it later.
|
|
487
|
+
this.#resetSpacesToPoll();
|
|
488
|
+
scheduleTask(
|
|
489
|
+
this._ctx,
|
|
490
|
+
() => this.#pollTask.schedule(),
|
|
491
|
+
Math.max(this.#pollingInterval - (Date.now() - (this.#lastFullPoll ?? 0)), 0),
|
|
492
|
+
);
|
|
493
|
+
}
|
|
494
|
+
}).pipe((effect) => this.#runSerialized(() => RuntimeProvider.runPromise(this.#runtime)(effect))),
|
|
495
|
+
);
|
|
496
|
+
|
|
497
|
+
readonly #pushTask = new AsyncTask(async () =>
|
|
498
|
+
Effect.gen(this, function* () {
|
|
499
|
+
yield* Effect.forEach(
|
|
500
|
+
this.#getSpaceIds(),
|
|
501
|
+
(spaceId) =>
|
|
502
|
+
Effect.gen(this, function* () {
|
|
503
|
+
let needsMorePush = false;
|
|
504
|
+
let hadPushFailure = false;
|
|
505
|
+
for (const feedNamespace of this.#syncNamespaces) {
|
|
506
|
+
const { done } = yield* this.#syncClient
|
|
507
|
+
.push(this._ctx, {
|
|
508
|
+
spaceId,
|
|
509
|
+
feedNamespace,
|
|
510
|
+
limit: this.#messageBlocksLimit,
|
|
511
|
+
})
|
|
512
|
+
.pipe(
|
|
513
|
+
Effect.tap(() =>
|
|
514
|
+
Effect.sync(() => {
|
|
515
|
+
this.#pushFailureBackoffMs = DEFAULT_PUSH_FAILURE_BACKOFF_MS;
|
|
516
|
+
}),
|
|
517
|
+
),
|
|
518
|
+
Effect.catchAll((cause) =>
|
|
519
|
+
Effect.gen(this, function* () {
|
|
520
|
+
this.#logSyncFailure('push', { spaceId, feedNamespace, cause });
|
|
521
|
+
hadPushFailure = true;
|
|
522
|
+
return { done: false };
|
|
523
|
+
}),
|
|
524
|
+
),
|
|
525
|
+
);
|
|
526
|
+
if (!done) {
|
|
527
|
+
needsMorePush = true;
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
this.#schedulePushRetry({ hadFailure: hadPushFailure, needsMore: needsMorePush });
|
|
531
|
+
}),
|
|
532
|
+
{ concurrency: this.#syncConcurrency },
|
|
533
|
+
);
|
|
534
|
+
}).pipe((effect) => this.#runSerialized(() => RuntimeProvider.runPromise(this.#runtime)(effect))),
|
|
535
|
+
);
|
|
536
|
+
}
|
|
@@ -14,12 +14,18 @@ export const getPlatform = (): Platform => {
|
|
|
14
14
|
userAgent,
|
|
15
15
|
uptime: Math.floor((Date.now() - window.performance.timeOrigin) / 1_000),
|
|
16
16
|
};
|
|
17
|
-
} else {
|
|
17
|
+
} else if (typeof SharedWorkerGlobalScope !== 'undefined') {
|
|
18
18
|
// Shared worker.
|
|
19
19
|
return {
|
|
20
20
|
type: Platform.PLATFORM_TYPE.SHARED_WORKER,
|
|
21
21
|
uptime: Math.floor((Date.now() - performance.timeOrigin) / 1_000),
|
|
22
22
|
};
|
|
23
|
+
} else {
|
|
24
|
+
// Dedicated worker.
|
|
25
|
+
return {
|
|
26
|
+
type: Platform.PLATFORM_TYPE.DEDICATED_WORKER,
|
|
27
|
+
uptime: Math.floor((Date.now() - performance.timeOrigin) / 1_000),
|
|
28
|
+
};
|
|
23
29
|
}
|
|
24
30
|
} else {
|
|
25
31
|
// Node.
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
import { describe, test } from 'vitest';
|
|
6
6
|
|
|
7
|
+
import { Context } from '@dxos/context';
|
|
7
8
|
import { MemorySignalManager, MemorySignalManagerContext } from '@dxos/messaging';
|
|
8
9
|
import { Invitation } from '@dxos/protocols/proto/dxos/client/services';
|
|
9
10
|
import { openAndClose } from '@dxos/test-utils';
|
|
@@ -19,7 +20,7 @@ describe('services/ServiceContext', () => {
|
|
|
19
20
|
const device2 = await createOpenServiceContext(networkContext);
|
|
20
21
|
await Promise.all(performInvitation({ host: device1, guest: device2, options: { kind: Invitation.Kind.DEVICE } }));
|
|
21
22
|
|
|
22
|
-
const space1 = await device1.dataSpaceManager!.createSpace();
|
|
23
|
+
const space1 = await device1.dataSpaceManager!.createSpace(new Context());
|
|
23
24
|
await device2.dataSpaceManager!.waitUntilSpaceReady(space1!.key);
|
|
24
25
|
const space2 = await device2.dataSpaceManager!.spaces.get(space1.key);
|
|
25
26
|
await space2!.inner.controlPipeline.state.waitUntilTimeframe(space1.inner.controlPipeline.state.timeframe);
|
|
@@ -35,7 +36,7 @@ describe('services/ServiceContext', () => {
|
|
|
35
36
|
|
|
36
37
|
const identity2 = await createOpenServiceContext(networkContext);
|
|
37
38
|
await identity2.createIdentity();
|
|
38
|
-
const space1 = await identity2.dataSpaceManager!.createSpace();
|
|
39
|
+
const space1 = await identity2.dataSpaceManager!.createSpace(new Context());
|
|
39
40
|
await Promise.all(
|
|
40
41
|
performInvitation({
|
|
41
42
|
host: identity2,
|