@dxos/echo-pipeline 0.8.3 → 0.8.4-main.05e74ebcff
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/neutral/chunk-46DCRDA7.mjs +271 -0
- package/dist/lib/neutral/chunk-46DCRDA7.mjs.map +7 -0
- package/dist/lib/{browser/chunk-35I6ERLG.mjs → neutral/chunk-LR7UFVQX.mjs} +313 -709
- package/dist/lib/neutral/chunk-LR7UFVQX.mjs.map +7 -0
- package/dist/lib/{browser → neutral}/filter/index.mjs +3 -2
- package/dist/lib/neutral/index.mjs +7332 -0
- package/dist/lib/neutral/index.mjs.map +7 -0
- package/dist/lib/neutral/meta.json +1 -0
- package/dist/lib/{browser → neutral}/testing/index.mjs +90 -311
- package/dist/lib/neutral/testing/index.mjs.map +7 -0
- package/dist/types/src/automerge/automerge-host-subduction.test.d.ts +2 -0
- package/dist/types/src/automerge/automerge-host-subduction.test.d.ts.map +1 -0
- package/dist/types/src/automerge/automerge-host.d.ts +119 -54
- package/dist/types/src/automerge/automerge-host.d.ts.map +1 -1
- package/dist/types/src/automerge/automerge-repo-subduction.test.d.ts +2 -0
- package/dist/types/src/automerge/automerge-repo-subduction.test.d.ts.map +1 -0
- package/dist/types/src/automerge/automerge-repo.test.d.ts +1 -1
- package/dist/types/src/automerge/automerge-repo.test.d.ts.map +1 -1
- package/dist/types/src/automerge/automerge-subduction.test.d.ts +2 -0
- package/dist/types/src/automerge/automerge-subduction.test.d.ts.map +1 -0
- package/dist/types/src/automerge/collection-synchronizer.d.ts +25 -4
- package/dist/types/src/automerge/collection-synchronizer.d.ts.map +1 -1
- package/dist/types/src/automerge/echo-data-monitor.d.ts.map +1 -1
- package/dist/types/src/automerge/echo-network-adapter.d.ts +23 -10
- package/dist/types/src/automerge/echo-network-adapter.d.ts.map +1 -1
- package/dist/types/src/automerge/echo-replicator.d.ts +46 -15
- package/dist/types/src/automerge/echo-replicator.d.ts.map +1 -1
- package/dist/types/src/automerge/errors.d.ts +35 -0
- package/dist/types/src/automerge/errors.d.ts.map +1 -0
- package/dist/types/src/automerge/handle-state.d.ts +52 -0
- package/dist/types/src/automerge/handle-state.d.ts.map +1 -0
- package/dist/types/src/automerge/heads-store.d.ts +9 -2
- package/dist/types/src/automerge/heads-store.d.ts.map +1 -1
- package/dist/types/src/automerge/index.d.ts +3 -1
- package/dist/types/src/automerge/index.d.ts.map +1 -1
- package/dist/types/src/automerge/leveldb-storage-adapter.d.ts +11 -5
- package/dist/types/src/automerge/leveldb-storage-adapter.d.ts.map +1 -1
- package/dist/types/src/automerge/mesh-echo-replicator-connection.d.ts +9 -8
- package/dist/types/src/automerge/mesh-echo-replicator-connection.d.ts.map +1 -1
- package/dist/types/src/automerge/mesh-echo-replicator.d.ts +6 -5
- package/dist/types/src/automerge/mesh-echo-replicator.d.ts.map +1 -1
- package/dist/types/src/automerge/network-protocol.d.ts.map +1 -1
- package/dist/types/src/automerge/space-collection.d.ts.map +1 -1
- package/dist/types/src/common/codec.d.ts +1 -1
- package/dist/types/src/common/codec.d.ts.map +1 -1
- package/dist/types/src/common/feeds.d.ts.map +1 -1
- package/dist/types/src/common/space-id.d.ts.map +1 -1
- package/dist/types/src/db-host/automerge-data-source.d.ts +35 -0
- package/dist/types/src/db-host/automerge-data-source.d.ts.map +1 -0
- package/dist/types/src/db-host/automerge-data-source.test.d.ts +2 -0
- package/dist/types/src/db-host/automerge-data-source.test.d.ts.map +1 -0
- package/dist/types/src/db-host/automerge-metrics.d.ts.map +1 -1
- package/dist/types/src/db-host/data-service.d.ts +13 -7
- package/dist/types/src/db-host/data-service.d.ts.map +1 -1
- package/dist/types/src/db-host/database-root.d.ts +9 -3
- package/dist/types/src/db-host/database-root.d.ts.map +1 -1
- package/dist/types/src/db-host/documents-synchronizer.d.ts +30 -8
- package/dist/types/src/db-host/documents-synchronizer.d.ts.map +1 -1
- package/dist/types/src/db-host/echo-host.d.ts +74 -28
- package/dist/types/src/db-host/echo-host.d.ts.map +1 -1
- package/dist/types/src/db-host/index.d.ts +2 -0
- package/dist/types/src/db-host/index.d.ts.map +1 -1
- package/dist/types/src/db-host/invalidation-hint.d.ts +19 -0
- package/dist/types/src/db-host/invalidation-hint.d.ts.map +1 -0
- package/dist/types/src/db-host/local-queue-service.d.ts +19 -0
- package/dist/types/src/db-host/local-queue-service.d.ts.map +1 -0
- package/dist/types/src/db-host/query-invalidation.test.d.ts +2 -0
- package/dist/types/src/db-host/query-invalidation.test.d.ts.map +1 -0
- package/dist/types/src/db-host/query-service.d.ts +19 -11
- package/dist/types/src/db-host/query-service.d.ts.map +1 -1
- package/dist/types/src/db-host/queue-data-source.d.ts +38 -0
- package/dist/types/src/db-host/queue-data-source.d.ts.map +1 -0
- package/dist/types/src/db-host/queue-service.test.d.ts +2 -0
- package/dist/types/src/db-host/queue-service.test.d.ts.map +1 -0
- package/dist/types/src/db-host/space-state-manager.d.ts +4 -3
- package/dist/types/src/db-host/space-state-manager.d.ts.map +1 -1
- package/dist/types/src/db-host/stub.d.ts +11 -0
- package/dist/types/src/db-host/stub.d.ts.map +1 -0
- package/dist/types/src/edge/echo-edge-replicator.d.ts +10 -7
- package/dist/types/src/edge/echo-edge-replicator.d.ts.map +1 -1
- package/dist/types/src/edge/echo-edge-subduction-replicator.d.ts +39 -0
- package/dist/types/src/edge/echo-edge-subduction-replicator.d.ts.map +1 -0
- package/dist/types/src/edge/echo-edge-subduction-replicator.test.d.ts +2 -0
- package/dist/types/src/edge/echo-edge-subduction-replicator.test.d.ts.map +1 -0
- package/dist/types/src/edge/index.d.ts +1 -0
- package/dist/types/src/edge/index.d.ts.map +1 -1
- package/dist/types/src/edge/inflight-request-limiter.d.ts.map +1 -1
- package/dist/types/src/filter/filter-match.d.ts +4 -1
- package/dist/types/src/filter/filter-match.d.ts.map +1 -1
- package/dist/types/src/metadata/metadata-store.d.ts +1 -1
- package/dist/types/src/metadata/metadata-store.d.ts.map +1 -1
- package/dist/types/src/pipeline/message-selector.d.ts.map +1 -1
- package/dist/types/src/pipeline/pipeline.d.ts +3 -3
- package/dist/types/src/pipeline/pipeline.d.ts.map +1 -1
- package/dist/types/src/pipeline/timeframe-clock.d.ts.map +1 -1
- package/dist/types/src/query/errors.d.ts +22 -10
- package/dist/types/src/query/errors.d.ts.map +1 -1
- package/dist/types/src/query/plan.d.ts +66 -8
- package/dist/types/src/query/plan.d.ts.map +1 -1
- package/dist/types/src/query/query-executor.d.ts +66 -11
- package/dist/types/src/query/query-executor.d.ts.map +1 -1
- package/dist/types/src/query/query-planner.d.ts +12 -1
- package/dist/types/src/query/query-planner.d.ts.map +1 -1
- package/dist/types/src/space/admission-discovery-extension.d.ts.map +1 -1
- package/dist/types/src/space/auth.d.ts +3 -3
- package/dist/types/src/space/auth.d.ts.map +1 -1
- package/dist/types/src/space/control-pipeline.d.ts +5 -4
- package/dist/types/src/space/control-pipeline.d.ts.map +1 -1
- package/dist/types/src/space/space-manager.d.ts +8 -8
- package/dist/types/src/space/space-manager.d.ts.map +1 -1
- package/dist/types/src/space/space-protocol.d.ts +10 -9
- package/dist/types/src/space/space-protocol.d.ts.map +1 -1
- package/dist/types/src/space/space.d.ts +6 -6
- package/dist/types/src/space/space.d.ts.map +1 -1
- package/dist/types/src/testing/change-metadata.d.ts.map +1 -1
- package/dist/types/src/testing/index.d.ts +0 -2
- package/dist/types/src/testing/index.d.ts.map +1 -1
- package/dist/types/src/testing/test-agent-builder.d.ts +2 -2
- package/dist/types/src/testing/test-agent-builder.d.ts.map +1 -1
- package/dist/types/src/testing/test-feed-builder.d.ts.map +1 -1
- package/dist/types/src/testing/test-network-adapter.d.ts +1 -1
- package/dist/types/src/testing/test-network-adapter.d.ts.map +1 -1
- package/dist/types/src/testing/test-replicator.d.ts +14 -13
- package/dist/types/src/testing/test-replicator.d.ts.map +1 -1
- package/dist/types/src/util.d.ts +1 -1
- package/dist/types/src/util.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +59 -55
- package/src/automerge/automerge-host-subduction.test.ts +263 -0
- package/src/automerge/automerge-host.test.ts +162 -39
- package/src/automerge/automerge-host.ts +679 -201
- package/src/automerge/automerge-repo-subduction.test.ts +1054 -0
- package/src/automerge/automerge-repo.test.ts +349 -91
- package/src/automerge/automerge-subduction.test.ts +330 -0
- package/src/automerge/collection-synchronizer.test.ts +98 -17
- package/src/automerge/collection-synchronizer.ts +141 -37
- package/src/automerge/echo-data-monitor.ts +1 -1
- package/src/automerge/echo-network-adapter.test.ts +5 -4
- package/src/automerge/echo-network-adapter.ts +76 -31
- package/src/automerge/echo-replicator.ts +50 -15
- package/src/automerge/errors.ts +10 -0
- package/src/automerge/handle-state.ts +62 -0
- package/src/automerge/heads-store.ts +42 -3
- package/src/automerge/index.ts +3 -1
- package/src/automerge/leveldb-storage-adapter.ts +77 -11
- package/src/automerge/mesh-echo-replicator-connection.ts +12 -8
- package/src/automerge/mesh-echo-replicator.ts +15 -11
- package/src/automerge/storage-adapter.test.ts +1 -1
- package/src/common/space-id.ts +1 -1
- package/src/db-host/automerge-data-source.test.ts +299 -0
- package/src/db-host/automerge-data-source.ts +187 -0
- package/src/db-host/data-service.ts +35 -26
- package/src/db-host/database-root.ts +20 -8
- package/src/db-host/documents-synchronizer.test.ts +145 -14
- package/src/db-host/documents-synchronizer.ts +163 -51
- package/src/db-host/echo-host.ts +384 -99
- package/src/db-host/index.ts +2 -0
- package/src/db-host/invalidation-hint.ts +50 -0
- package/src/db-host/local-queue-service.ts +127 -0
- package/src/db-host/query-invalidation.test.ts +270 -0
- package/src/db-host/query-service.ts +111 -106
- package/src/db-host/queue-data-source.ts +160 -0
- package/src/db-host/queue-service.test.ts +145 -0
- package/src/db-host/space-state-manager.ts +12 -10
- package/src/db-host/stub.ts +33 -0
- package/src/edge/echo-edge-replicator.test.ts +12 -10
- package/src/edge/echo-edge-replicator.ts +144 -58
- package/src/edge/echo-edge-subduction-replicator.test.ts +158 -0
- package/src/edge/echo-edge-subduction-replicator.ts +377 -0
- package/src/edge/index.ts +1 -0
- package/src/filter/filter-match.test.ts +68 -32
- package/src/filter/filter-match.ts +230 -21
- package/src/metadata/metadata-store.ts +11 -8
- package/src/pipeline/pipeline-stress.test.ts +1 -1
- package/src/pipeline/pipeline.test.ts +2 -2
- package/src/pipeline/pipeline.ts +9 -7
- package/src/query/errors.ts +3 -1
- package/src/query/plan.ts +84 -9
- package/src/query/query-executor.ts +1197 -165
- package/src/query/query-planner.test.ts +993 -81
- package/src/query/query-planner.ts +503 -27
- package/src/space/admission-discovery-extension.ts +3 -3
- package/src/space/auth.ts +6 -6
- package/src/space/control-pipeline.test.ts +5 -4
- package/src/space/control-pipeline.ts +16 -13
- package/src/space/space-manager.browser.test.ts +1 -1
- package/src/space/space-manager.ts +16 -20
- package/src/space/space-protocol.browser.test.ts +15 -14
- package/src/space/space-protocol.test.ts +15 -14
- package/src/space/space-protocol.ts +22 -18
- package/src/space/space.test.ts +3 -3
- package/src/space/space.ts +11 -11
- package/src/testing/index.ts +0 -2
- package/src/testing/test-agent-builder.ts +3 -3
- package/src/testing/test-network-adapter.ts +24 -3
- package/src/testing/test-replicator.ts +19 -20
- package/src/util.ts +7 -3
- package/dist/lib/browser/chunk-35I6ERLG.mjs.map +0 -7
- package/dist/lib/browser/chunk-CGS2ULMK.mjs +0 -11
- package/dist/lib/browser/chunk-CGS2ULMK.mjs.map +0 -7
- package/dist/lib/browser/chunk-TQJTKNMS.mjs +0 -126
- package/dist/lib/browser/chunk-TQJTKNMS.mjs.map +0 -7
- package/dist/lib/browser/index.mjs +0 -4743
- package/dist/lib/browser/index.mjs.map +0 -7
- package/dist/lib/browser/meta.json +0 -1
- package/dist/lib/browser/testing/index.mjs.map +0 -7
- package/dist/lib/node/chunk-HOPOFWAL.cjs +0 -147
- package/dist/lib/node/chunk-HOPOFWAL.cjs.map +0 -7
- package/dist/lib/node/chunk-JXX6LF5U.cjs +0 -2084
- package/dist/lib/node/chunk-JXX6LF5U.cjs.map +0 -7
- package/dist/lib/node/chunk-Q7SFCCGT.cjs +0 -33
- package/dist/lib/node/chunk-Q7SFCCGT.cjs.map +0 -7
- package/dist/lib/node/filter/index.cjs +0 -32
- package/dist/lib/node/filter/index.cjs.map +0 -7
- package/dist/lib/node/index.cjs +0 -4699
- package/dist/lib/node/index.cjs.map +0 -7
- package/dist/lib/node/meta.json +0 -1
- package/dist/lib/node/testing/index.cjs +0 -753
- package/dist/lib/node/testing/index.cjs.map +0 -7
- package/dist/lib/node-esm/chunk-5BHLPT24.mjs +0 -2067
- package/dist/lib/node-esm/chunk-5BHLPT24.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-HSLMI22Q.mjs +0 -11
- package/dist/lib/node-esm/chunk-HSLMI22Q.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-RVK35BS7.mjs +0 -126
- package/dist/lib/node-esm/chunk-RVK35BS7.mjs.map +0 -7
- package/dist/lib/node-esm/filter/index.mjs +0 -11
- package/dist/lib/node-esm/filter/index.mjs.map +0 -7
- package/dist/lib/node-esm/index.mjs +0 -4743
- package/dist/lib/node-esm/index.mjs.map +0 -7
- package/dist/lib/node-esm/meta.json +0 -1
- package/dist/lib/node-esm/testing/index.mjs +0 -742
- package/dist/lib/node-esm/testing/index.mjs.map +0 -7
- package/dist/types/src/db-host/documents-iterator.d.ts +0 -7
- package/dist/types/src/db-host/documents-iterator.d.ts.map +0 -1
- package/dist/types/src/testing/test-data.d.ts +0 -18
- package/dist/types/src/testing/test-data.d.ts.map +0 -1
- package/dist/types/src/testing/test-schema.d.ts +0 -37
- package/dist/types/src/testing/test-schema.d.ts.map +0 -1
- package/src/db-host/documents-iterator.ts +0 -74
- package/src/testing/test-data.ts +0 -127
- package/src/testing/test-schema.ts +0 -53
- /package/dist/lib/{browser → neutral}/filter/index.mjs.map +0 -0
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
//
|
|
4
4
|
|
|
5
5
|
import {
|
|
6
|
+
next as A,
|
|
6
7
|
type Heads,
|
|
7
8
|
change,
|
|
8
9
|
clone,
|
|
@@ -10,7 +11,6 @@ import {
|
|
|
10
11
|
from,
|
|
11
12
|
getBackend,
|
|
12
13
|
getHeads,
|
|
13
|
-
next as A,
|
|
14
14
|
save,
|
|
15
15
|
saveSince,
|
|
16
16
|
} from '@automerge/automerge';
|
|
@@ -18,17 +18,20 @@ import {
|
|
|
18
18
|
type AutomergeUrl,
|
|
19
19
|
type DocHandle,
|
|
20
20
|
type DocumentId,
|
|
21
|
-
type
|
|
22
|
-
type
|
|
21
|
+
type DocumentProgress,
|
|
22
|
+
type Message,
|
|
23
23
|
type PeerId,
|
|
24
24
|
Repo,
|
|
25
25
|
type SharePolicy,
|
|
26
|
+
type StorageAdapterInterface,
|
|
26
27
|
generateAutomergeUrl,
|
|
28
|
+
initSubduction,
|
|
27
29
|
parseAutomergeUrl,
|
|
28
30
|
} from '@automerge/automerge-repo';
|
|
29
|
-
import {
|
|
31
|
+
import { beforeAll, describe, expect, onTestFinished, test } from 'vitest';
|
|
30
32
|
|
|
31
|
-
import { asyncTimeout, sleep } from '@dxos/async';
|
|
33
|
+
import { Trigger, asyncTimeout, sleep } from '@dxos/async';
|
|
34
|
+
import { Context } from '@dxos/context';
|
|
32
35
|
import { randomBytes } from '@dxos/crypto';
|
|
33
36
|
import { PublicKey } from '@dxos/keys';
|
|
34
37
|
import { createTestLevel } from '@dxos/kv-store/testing';
|
|
@@ -36,15 +39,69 @@ import { TestBuilder as TeleportBuilder, TestPeer as TeleportPeer } from '@dxos/
|
|
|
36
39
|
import { openAndClose } from '@dxos/test-utils';
|
|
37
40
|
import { isNonNullable, range } from '@dxos/util';
|
|
38
41
|
|
|
39
|
-
import {
|
|
42
|
+
import { TestAdapter, type TestConnectionStateProvider } from '../testing';
|
|
40
43
|
import { EchoNetworkAdapter } from './echo-network-adapter';
|
|
44
|
+
import { type HandleQueryState } from './handle-state';
|
|
41
45
|
import { LevelDBStorageAdapter } from './leveldb-storage-adapter';
|
|
42
46
|
import { MeshEchoReplicator } from './mesh-echo-replicator';
|
|
43
|
-
import { TestAdapter, type TestConnectionStateProvider } from '../testing';
|
|
44
47
|
|
|
45
48
|
const HOST_AND_CLIENT: [string, string] = ['host', 'client'];
|
|
46
49
|
|
|
50
|
+
/**
|
|
51
|
+
* Block until the {@link DocumentProgress} reports a state in `awaitStates`.
|
|
52
|
+
*/
|
|
53
|
+
const waitForQueryState = async (
|
|
54
|
+
progress: DocumentProgress<unknown>,
|
|
55
|
+
awaitStates: readonly HandleQueryState[],
|
|
56
|
+
{ timeout }: { timeout?: number } = {},
|
|
57
|
+
): Promise<void> => {
|
|
58
|
+
if (awaitStates.includes(progress.peek().state)) {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
const trigger = new Trigger();
|
|
62
|
+
const unsubscribe = progress.subscribe((state) => {
|
|
63
|
+
if (awaitStates.includes(state.state)) {
|
|
64
|
+
trigger.wake();
|
|
65
|
+
} else if (state.state === 'failed' && !awaitStates.includes('failed')) {
|
|
66
|
+
trigger.throw(state.error);
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
try {
|
|
70
|
+
await trigger.wait({ timeout });
|
|
71
|
+
} finally {
|
|
72
|
+
unsubscribe();
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Multi-state `Repo.find` replacement for the pre-subduction
|
|
78
|
+
* `Repo.find(url, { allowableStates })` ergonomics. */
|
|
79
|
+
const findInStates = async <T>(
|
|
80
|
+
repo: Repo,
|
|
81
|
+
url: AutomergeUrl,
|
|
82
|
+
awaitStates: readonly HandleQueryState[],
|
|
83
|
+
): Promise<DocHandle<T>> => {
|
|
84
|
+
const { documentId } = parseAutomergeUrl(url);
|
|
85
|
+
const progress = repo.findWithProgress<T>(url);
|
|
86
|
+
await waitForQueryState(progress, awaitStates);
|
|
87
|
+
return repo.handles[documentId] as DocHandle<T>;
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Default state set for `findInStates`. Equivalent to the pre-subduction
|
|
92
|
+
* `FIND_PARAMS = { allowableStates: ['ready', 'requesting'] }` — i.e. "give
|
|
93
|
+
* me the handle as soon as we have it OR are still trying to fetch it".
|
|
94
|
+
* `'loading'` is the {@link QueryState} equivalent of the old `'requesting'`.
|
|
95
|
+
*/
|
|
96
|
+
const FIND_STATES: readonly HandleQueryState[] = ['ready', 'loading'];
|
|
97
|
+
|
|
47
98
|
describe('AutomergeRepo', () => {
|
|
99
|
+
// Subduction-fork `Repo` constructs a `MemorySigner` internally; WASM must be
|
|
100
|
+
// initialized first or the constructor throws on `memorysigner_new`.
|
|
101
|
+
beforeAll(async () => {
|
|
102
|
+
await initSubduction();
|
|
103
|
+
});
|
|
104
|
+
|
|
48
105
|
test('change events', () => {
|
|
49
106
|
const repo = new Repo({ network: [] });
|
|
50
107
|
const handle = repo.create<{ field?: string }>();
|
|
@@ -111,13 +168,14 @@ describe('AutomergeRepo', () => {
|
|
|
111
168
|
expect(equals(a, c)).to.be.true;
|
|
112
169
|
});
|
|
113
170
|
|
|
114
|
-
test('documents missing from local storage go to
|
|
171
|
+
test('documents missing from local storage go to loading state', async () => {
|
|
115
172
|
const { repos, adapters } = await createHostClientRepoTopology();
|
|
116
173
|
const [host] = repos;
|
|
117
174
|
await connectAdapters(adapters);
|
|
118
175
|
const url = 'automerge:3JN8F3Z4dUWEEKKFN7WE9gEGvVUT';
|
|
119
|
-
|
|
120
|
-
|
|
176
|
+
// `'loading'` is the equivalent of the legacy `'requesting'` handle state.
|
|
177
|
+
const progress = host.findWithProgress(url as AutomergeUrl);
|
|
178
|
+
expect(progress.peek().state).to.equal('loading');
|
|
121
179
|
});
|
|
122
180
|
|
|
123
181
|
test('documents on disk go to ready state', async () => {
|
|
@@ -161,32 +219,6 @@ describe('AutomergeRepo', () => {
|
|
|
161
219
|
expect((await asyncTimeout(client.find<any>(handle.url), 1000))!.doc().text).to.equal(text);
|
|
162
220
|
});
|
|
163
221
|
|
|
164
|
-
test('share policy gets enabled afterwards', async () => {
|
|
165
|
-
let sharePolicy = false;
|
|
166
|
-
|
|
167
|
-
const { repos, adapters } = await createHostClientRepoTopology({
|
|
168
|
-
sharePolicy: async () => sharePolicy,
|
|
169
|
-
});
|
|
170
|
-
const [host, client] = repos;
|
|
171
|
-
await connectAdapters(adapters);
|
|
172
|
-
|
|
173
|
-
const handle = host.create();
|
|
174
|
-
const text = 'Hello world';
|
|
175
|
-
handle.change((doc: any) => {
|
|
176
|
-
doc.text = text;
|
|
177
|
-
});
|
|
178
|
-
|
|
179
|
-
{
|
|
180
|
-
await client.find(handle.url, { allowableStates: ['unavailable'] });
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
sharePolicy = true;
|
|
184
|
-
|
|
185
|
-
{
|
|
186
|
-
await client.find(handle.url, { allowableStates: ['unavailable'] });
|
|
187
|
-
}
|
|
188
|
-
});
|
|
189
|
-
|
|
190
222
|
test('two documents and share policy switching', async () => {
|
|
191
223
|
const allowedDocs: DocumentId[] = [];
|
|
192
224
|
|
|
@@ -236,15 +268,15 @@ describe('AutomergeRepo', () => {
|
|
|
236
268
|
await connectAdapters(adapters);
|
|
237
269
|
|
|
238
270
|
const handle = host.create();
|
|
239
|
-
const docOnClient = await
|
|
271
|
+
const docOnClient = await findInStates<any>(client, handle.url, FIND_STATES);
|
|
240
272
|
{
|
|
241
273
|
const sanityText = 'Hello world';
|
|
242
274
|
handle.change((doc: any) => {
|
|
243
275
|
doc.sanityText = sanityText;
|
|
244
276
|
});
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
expect(docOnClient.doc()
|
|
277
|
+
// Subduction-fork: `whenReady()` resolves on the handle's `ready` state
|
|
278
|
+
// (no longer waits for new data). Poll for the actual change to propagate.
|
|
279
|
+
await expect.poll(() => docOnClient.doc()?.sanityText, { timeout: 1000 }).toEqual(sanityText);
|
|
248
280
|
}
|
|
249
281
|
|
|
250
282
|
// Disrupt connection.
|
|
@@ -270,9 +302,7 @@ describe('AutomergeRepo', () => {
|
|
|
270
302
|
handle.change((doc: any) => {
|
|
271
303
|
doc.onlineText = onlineText;
|
|
272
304
|
});
|
|
273
|
-
await
|
|
274
|
-
await asyncTimeout(docOnClient.whenReady(), 1000);
|
|
275
|
-
expect(docOnClient.doc().onlineText).to.equal(onlineText);
|
|
305
|
+
await expect.poll(() => docOnClient.doc()?.onlineText, { timeout: 1000 }).toEqual(onlineText);
|
|
276
306
|
expect(docOnClient.doc().offlineText).to.equal(offlineText);
|
|
277
307
|
}
|
|
278
308
|
});
|
|
@@ -319,8 +349,8 @@ describe('AutomergeRepo', () => {
|
|
|
319
349
|
doc.text = 'Hello world';
|
|
320
350
|
});
|
|
321
351
|
|
|
322
|
-
const _ = repoB
|
|
323
|
-
const docC = await repoC
|
|
352
|
+
const _ = findInStates(repoB, docA.url, FIND_STATES);
|
|
353
|
+
const docC = await findInStates(repoC, docA.url, FIND_STATES);
|
|
324
354
|
await asyncTimeout(docC.whenReady(), 1000);
|
|
325
355
|
});
|
|
326
356
|
|
|
@@ -329,7 +359,6 @@ describe('AutomergeRepo', () => {
|
|
|
329
359
|
const [host, client] = repos;
|
|
330
360
|
const [[adapter1, adapter2]] = adapters;
|
|
331
361
|
|
|
332
|
-
const unavailable: HandleState = 'unavailable';
|
|
333
362
|
await connectAdapters(adapters, { noEmitPeerCandidate: true });
|
|
334
363
|
|
|
335
364
|
const docA = host.create();
|
|
@@ -338,21 +367,21 @@ describe('AutomergeRepo', () => {
|
|
|
338
367
|
doc.text = 'Hello world';
|
|
339
368
|
});
|
|
340
369
|
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
//
|
|
344
|
-
|
|
370
|
+
// Without peer candidates, the patched subduction source keeps the
|
|
371
|
+
// query pending (`'loading'`) instead of driving it to
|
|
372
|
+
// `'unavailable'` — it will retry on the next connection-state
|
|
373
|
+
// change.
|
|
374
|
+
const progress = client.findWithProgress(docA.url);
|
|
375
|
+
await expect.poll(() => progress.peek().state, { timeout: 500, interval: 50 }).toEqual('loading');
|
|
345
376
|
|
|
346
377
|
adapter1.peerCandidate(adapter2.peerId!);
|
|
347
378
|
await sleep(100);
|
|
348
379
|
adapter2.peerCandidate(adapter1.peerId!);
|
|
349
380
|
|
|
350
|
-
//
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
// Wait gets loaded after reconnect.
|
|
381
|
+
// Reconnecting cycles the peers and re-triggers sync; the doc
|
|
382
|
+
// becomes available.
|
|
354
383
|
await reconnectAdapters(adapters);
|
|
355
|
-
await
|
|
384
|
+
await waitForQueryState(progress, ['ready'], { timeout: 1_000 });
|
|
356
385
|
});
|
|
357
386
|
|
|
358
387
|
test('documents loaded from disk get replicated', async () => {
|
|
@@ -385,9 +414,13 @@ describe('AutomergeRepo', () => {
|
|
|
385
414
|
const repo = new Repo({ network: [] });
|
|
386
415
|
const serverHandle = repo.create<{ field?: string }>();
|
|
387
416
|
|
|
388
|
-
|
|
417
|
+
// The client must be bootstrapped via `A.load` on the first message
|
|
418
|
+
// (full save) — using `A.from({})` + `A.loadIncremental` panics
|
|
419
|
+
// automerge@3.2.5 with `PatchLogMismatch` when the donor history shares
|
|
420
|
+
// no lineage with the receiver's local actor.
|
|
421
|
+
let clientDoc: A.Doc<{ field?: string }> | undefined;
|
|
389
422
|
const receiveByClient = (blob: Uint8Array) => {
|
|
390
|
-
clientDoc = A.loadIncremental(clientDoc, blob);
|
|
423
|
+
clientDoc = clientDoc === undefined ? A.load(blob) : A.loadIncremental(clientDoc, blob);
|
|
391
424
|
};
|
|
392
425
|
|
|
393
426
|
// Sync handshake.
|
|
@@ -406,7 +439,7 @@ describe('AutomergeRepo', () => {
|
|
|
406
439
|
serverHandle.change((doc: any) => {
|
|
407
440
|
doc.field = value;
|
|
408
441
|
});
|
|
409
|
-
expect(clientDoc
|
|
442
|
+
expect(clientDoc?.field).to.deep.equal(value);
|
|
410
443
|
}
|
|
411
444
|
|
|
412
445
|
{
|
|
@@ -414,19 +447,13 @@ describe('AutomergeRepo', () => {
|
|
|
414
447
|
serverHandle.change((doc: any) => {
|
|
415
448
|
doc.field = value;
|
|
416
449
|
});
|
|
417
|
-
expect(clientDoc
|
|
450
|
+
expect(clientDoc?.field).to.deep.equal(value);
|
|
418
451
|
}
|
|
419
452
|
});
|
|
420
453
|
|
|
421
454
|
test('client creates doc and syncs with a Repo', async () => {
|
|
422
455
|
const repo = new Repo({ network: [] });
|
|
423
|
-
const receiveByServer =
|
|
424
|
-
const serverHandle = await repo.find(docId, FIND_PARAMS);
|
|
425
|
-
serverHandle.update((doc) => {
|
|
426
|
-
return A.loadIncremental(doc, blob);
|
|
427
|
-
});
|
|
428
|
-
};
|
|
429
|
-
|
|
456
|
+
const receiveByServer = (blob: Uint8Array, docId: DocumentId) => repo.import<any>(blob, { docId });
|
|
430
457
|
let clientDoc = A.from<{ field?: string }>({});
|
|
431
458
|
const { documentId } = parseAutomergeUrl(generateAutomergeUrl());
|
|
432
459
|
// Sync handshake.
|
|
@@ -434,7 +461,7 @@ describe('AutomergeRepo', () => {
|
|
|
434
461
|
|
|
435
462
|
// Sync protocol.
|
|
436
463
|
const sendDoc = async (doc: A.Doc<any>) => {
|
|
437
|
-
|
|
464
|
+
receiveByServer(saveSince(doc, sentHeads), documentId);
|
|
438
465
|
sentHeads = getHeads(doc);
|
|
439
466
|
};
|
|
440
467
|
|
|
@@ -456,10 +483,9 @@ describe('AutomergeRepo', () => {
|
|
|
456
483
|
|
|
457
484
|
const repo = new Repo({ network: [], storage });
|
|
458
485
|
const receiveByServer = async (blob: Uint8Array, docId: DocumentId) => {
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
});
|
|
486
|
+
repo.import<any>(blob, { docId });
|
|
487
|
+
// TODO(mykola): This should not be required. Document is not persisted without it.
|
|
488
|
+
await repo.flush([docId]);
|
|
463
489
|
};
|
|
464
490
|
|
|
465
491
|
let clientDoc = A.from<{ field?: string }>({ field: 'foo' });
|
|
@@ -516,6 +542,149 @@ describe('AutomergeRepo', () => {
|
|
|
516
542
|
await asyncTimeout(handleB.whenReady(), 1000);
|
|
517
543
|
expect(handleB.doc()!.text).to.equal(text);
|
|
518
544
|
});
|
|
545
|
+
|
|
546
|
+
test('retry share config', async () => {
|
|
547
|
+
let announce = false;
|
|
548
|
+
|
|
549
|
+
const { repos, adapters } = await createRepoTopology({
|
|
550
|
+
peers: ['A', 'B'],
|
|
551
|
+
connections: [['A', 'B']],
|
|
552
|
+
options: {
|
|
553
|
+
shareConfig: {
|
|
554
|
+
access: async () => true,
|
|
555
|
+
announce: async () => announce,
|
|
556
|
+
},
|
|
557
|
+
},
|
|
558
|
+
});
|
|
559
|
+
const [repoA, repoB] = repos;
|
|
560
|
+
await connectAdapters(adapters);
|
|
561
|
+
|
|
562
|
+
const docA = repoA.create({ text: 'Hello world' });
|
|
563
|
+
|
|
564
|
+
// TODO(mykola): The classical-network `DocSynchronizer` doesn't drive
|
|
565
|
+
// the query to `'unavailable'` while the dormant subduction source is
|
|
566
|
+
// still attached — the query stays in `'loading'` until something
|
|
567
|
+
// actually delivers data. Once the fork drives source state
|
|
568
|
+
// correctly, switch this to a positive `'unavailable'` assertion.
|
|
569
|
+
// For now we assert "stuck in loading" + empty doc body.
|
|
570
|
+
const progress = repoB.findWithProgress<{ text: string }>(docA.url);
|
|
571
|
+
await expect.poll(() => progress.peek().state, { timeout: 500, interval: 50 }).toEqual('loading');
|
|
572
|
+
const docB = repoB.handles[parseAutomergeUrl(docA.url).documentId] as DocHandle<{ text: string }>;
|
|
573
|
+
expect(docB.doc()).to.deep.equal({});
|
|
574
|
+
|
|
575
|
+
// Flip announce on and kick the share-config retry; the query
|
|
576
|
+
// transitions to `'ready'` once the doc syncs through.
|
|
577
|
+
announce = true;
|
|
578
|
+
repoB.shareConfigChanged();
|
|
579
|
+
// Use `waitForQueryState` instead of `progress.whenReady()` so that a
|
|
580
|
+
// transient `'unavailable'` from the dormant subduction source
|
|
581
|
+
// doesn't reject the wait before classical sync delivers.
|
|
582
|
+
await waitForQueryState(progress, ['ready'], { timeout: 1_000 });
|
|
583
|
+
expect(progress.peek().state).to.equal('ready');
|
|
584
|
+
expect(docB.doc()).to.deep.equal({ text: 'Hello world' });
|
|
585
|
+
});
|
|
586
|
+
});
|
|
587
|
+
|
|
588
|
+
describe('create2 tests', () => {
|
|
589
|
+
test('basic document creation', async () => {
|
|
590
|
+
const repo = new Repo({ network: [] });
|
|
591
|
+
|
|
592
|
+
const handle = await repo.create2<{ text: string }>();
|
|
593
|
+
expect(handle.doc()).to.deep.equal({});
|
|
594
|
+
|
|
595
|
+
handle.change((doc: any) => {
|
|
596
|
+
doc.text = 'Hello World';
|
|
597
|
+
});
|
|
598
|
+
|
|
599
|
+
expect(handle.doc()?.text).to.equal('Hello World');
|
|
600
|
+
});
|
|
601
|
+
|
|
602
|
+
test('document mutation', async () => {
|
|
603
|
+
const repo = new Repo({ network: [] });
|
|
604
|
+
|
|
605
|
+
const handle = await repo.create2<{ counter: number }>({ counter: 0 });
|
|
606
|
+
|
|
607
|
+
for (let i = 1; i <= 10; i++) {
|
|
608
|
+
handle.change((doc: any) => {
|
|
609
|
+
doc.counter = i;
|
|
610
|
+
});
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
expect(handle.doc()?.counter).to.equal(10);
|
|
614
|
+
});
|
|
615
|
+
|
|
616
|
+
test('import document and mutate', async () => {
|
|
617
|
+
const repo = new Repo({ network: [] });
|
|
618
|
+
|
|
619
|
+
const original = await repo.create2<{ text: string }>({ text: 'original' });
|
|
620
|
+
const blob = A.save(original.doc()!);
|
|
621
|
+
|
|
622
|
+
const imported = repo.import<{ text: string }>(blob);
|
|
623
|
+
expect(imported.doc()?.text).to.equal('original');
|
|
624
|
+
|
|
625
|
+
imported.change((doc: any) => {
|
|
626
|
+
doc.text = 'mutated';
|
|
627
|
+
});
|
|
628
|
+
expect(imported.doc()?.text).to.equal('mutated');
|
|
629
|
+
});
|
|
630
|
+
|
|
631
|
+
test('reload document with flush', async () => {
|
|
632
|
+
const path = createTmpPath();
|
|
633
|
+
const text = 'Hello World!';
|
|
634
|
+
let url: AutomergeUrl;
|
|
635
|
+
|
|
636
|
+
{
|
|
637
|
+
const level = createTestLevel(path);
|
|
638
|
+
const storage = await createLevelAdapter(level);
|
|
639
|
+
const repo = new Repo({ network: [], storage });
|
|
640
|
+
const handle = await repo.create2<{ text: string }>();
|
|
641
|
+
url = handle.url;
|
|
642
|
+
handle.change((doc: any) => {
|
|
643
|
+
doc.text = text;
|
|
644
|
+
});
|
|
645
|
+
await repo.flush([handle.documentId]);
|
|
646
|
+
await level.close();
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
{
|
|
650
|
+
const level = createTestLevel(path);
|
|
651
|
+
const storage = await createLevelAdapter(level);
|
|
652
|
+
const repo = new Repo({ network: [], storage });
|
|
653
|
+
const handle = await repo.find<{ text: string }>(url);
|
|
654
|
+
await handle.whenReady();
|
|
655
|
+
expect(handle.doc()?.text).to.equal(text);
|
|
656
|
+
await level.close();
|
|
657
|
+
}
|
|
658
|
+
});
|
|
659
|
+
|
|
660
|
+
test('reload document without flush', async () => {
|
|
661
|
+
const path = createTmpPath();
|
|
662
|
+
const text = 'Hello World!';
|
|
663
|
+
let url: AutomergeUrl;
|
|
664
|
+
|
|
665
|
+
{
|
|
666
|
+
const level = createTestLevel(path);
|
|
667
|
+
const storage = await createLevelAdapter(level);
|
|
668
|
+
const repo = new Repo({ network: [], storage });
|
|
669
|
+
const handle = await repo.create2<{ text: string }>();
|
|
670
|
+
url = handle.url;
|
|
671
|
+
handle.change((doc: any) => {
|
|
672
|
+
doc.text = text;
|
|
673
|
+
});
|
|
674
|
+
// No explicit flush - rely on auto-save.
|
|
675
|
+
await sleep(200);
|
|
676
|
+
await level.close();
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
{
|
|
680
|
+
const level = createTestLevel(path);
|
|
681
|
+
const storage = await createLevelAdapter(level);
|
|
682
|
+
const repo = new Repo({ network: [], storage });
|
|
683
|
+
const handle = await repo.find<{ text: string }>(url);
|
|
684
|
+
await handle.whenReady();
|
|
685
|
+
expect(handle.doc()?.text).to.equal(text);
|
|
686
|
+
}
|
|
687
|
+
});
|
|
519
688
|
});
|
|
520
689
|
|
|
521
690
|
describe('teleport', () => {
|
|
@@ -602,11 +771,25 @@ describe('AutomergeRepo', () => {
|
|
|
602
771
|
});
|
|
603
772
|
await connectPeers(spaceKey, teleportBuilder, peerWithDocs.peer, peer2);
|
|
604
773
|
|
|
605
|
-
const
|
|
606
|
-
|
|
607
|
-
|
|
774
|
+
const shouldNotFindProgress = peer2.repo.findWithProgress(docNotInRemoteCollection.url);
|
|
775
|
+
// The doc in the remote collection should sync. Can't use
|
|
776
|
+
// `repo.find()` / `progress.whenReady()` here: the dormant
|
|
777
|
+
// subduction source transitions to `'unavailable'` mid-flight
|
|
778
|
+
// (before classical-network sync delivers the doc), which both APIs
|
|
779
|
+
// surface as a rejection. `findInStates` ignores transient
|
|
780
|
+
// `'unavailable'` and only rejects on `'failed'`.
|
|
781
|
+
const shouldFindDoc = await findInStates<any>(peer2.repo, docInRemoteCollection.url, ['ready']);
|
|
608
782
|
expect(shouldFindDoc.doc()).to.deep.eq(docInRemoteCollection.doc());
|
|
609
|
-
|
|
783
|
+
expect(shouldFindDoc.doc()).to.deep.eq(docInRemoteCollection.doc());
|
|
784
|
+
// The doc that's NOT in any remote collection must remain unsynced.
|
|
785
|
+
// TODO(mykola): Fork's classical-network sync doesn't transition the
|
|
786
|
+
// query to `'unavailable'` while a dormant subduction source is
|
|
787
|
+
// attached, so we can only assert "stays in loading" rather than
|
|
788
|
+
// "reaches unavailable". Re-enable a positive `'unavailable'`
|
|
789
|
+
// assertion when the fork drives the source state correctly.
|
|
790
|
+
await expect.poll(() => shouldNotFindProgress.peek().state, { timeout: 500, interval: 50 }).toEqual('loading');
|
|
791
|
+
const shouldNotFindDoc = peer2.repo.handles[parseAutomergeUrl(docNotInRemoteCollection.url).documentId];
|
|
792
|
+
expect(shouldNotFindDoc.doc()).to.deep.equal({});
|
|
610
793
|
});
|
|
611
794
|
|
|
612
795
|
/**
|
|
@@ -636,12 +819,23 @@ describe('AutomergeRepo', () => {
|
|
|
636
819
|
await connectPeers(spaceKey, teleportBuilder, peerWithDocs.peer, peer2);
|
|
637
820
|
await connectPeers(anotherSpaceKey, teleportBuilder, peer2, peerFromAnotherSpace);
|
|
638
821
|
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
const
|
|
644
|
-
|
|
822
|
+
// Can't use `repo.find()` / `progress.whenReady()`: the dormant
|
|
823
|
+
// subduction source can transition to `'unavailable'` mid-flight
|
|
824
|
+
// (before classical sync delivers), which both APIs surface as a
|
|
825
|
+
// rejection. `findInStates` ignores transient `'unavailable'`.
|
|
826
|
+
const loaded = await findInStates<any>(peer2.repo, document.url, ['ready']);
|
|
827
|
+
expect(loaded.doc()).to.deep.equal(document.doc());
|
|
828
|
+
|
|
829
|
+
// peerFromAnotherSpace is connected to peer2 over a different spaceKey,
|
|
830
|
+
// so it must NOT receive the doc.
|
|
831
|
+
// TODO(mykola): Fork's classical-network query doesn't reach
|
|
832
|
+
// `'unavailable'`; assert "stays in loading" instead. Switch to a
|
|
833
|
+
// positive `'unavailable'` assertion once the fork drives source
|
|
834
|
+
// state correctly.
|
|
835
|
+
const otherSpaceProgress = peerFromAnotherSpace.repo.findWithProgress(document.url);
|
|
836
|
+
await expect.poll(() => otherSpaceProgress.peek().state, { timeout: 500, interval: 50 }).toEqual('loading');
|
|
837
|
+
const otherSpaceDoc = peerFromAnotherSpace.repo.handles[parseAutomergeUrl(document.url).documentId];
|
|
838
|
+
expect(otherSpaceDoc.doc()).to.deep.equal({});
|
|
645
839
|
});
|
|
646
840
|
|
|
647
841
|
test('collection-sync with chain of peers', async () => {
|
|
@@ -670,17 +864,72 @@ describe('AutomergeRepo', () => {
|
|
|
670
864
|
await connectPeers(spaceKey, teleportBuilder, peerWithDocs.peer, peer2);
|
|
671
865
|
await connectPeers(spaceKey, teleportBuilder, peer2, peer3);
|
|
672
866
|
|
|
673
|
-
const loadedDocument = await peer2.repo
|
|
674
|
-
await loadedDocument.
|
|
867
|
+
const loadedDocument = await findInStates(peer2.repo, document.url, FIND_STATES);
|
|
868
|
+
await expect.poll(() => loadedDocument.doc(), { timeout: 1000 }).toEqual(document.doc());
|
|
675
869
|
|
|
676
|
-
const doc = await peer3.repo
|
|
677
|
-
await doc.
|
|
678
|
-
|
|
870
|
+
const doc = await findInStates(peer3.repo, document.url, FIND_STATES);
|
|
871
|
+
await expect.poll(() => doc.doc(), { timeout: 1000 }).toEqual(document.doc());
|
|
872
|
+
});
|
|
873
|
+
|
|
874
|
+
test('document is passively replicated to connected peers', async () => {
|
|
875
|
+
const [spaceKey] = PublicKey.randomSequence();
|
|
876
|
+
|
|
877
|
+
const teleportBuilder = new TeleportBuilder();
|
|
878
|
+
onTestFinished(() => teleportBuilder.destroy());
|
|
879
|
+
|
|
880
|
+
const peer1 = await createTeleportTestPeer(teleportBuilder, spaceKey);
|
|
881
|
+
const peer2 = await createTeleportTestPeer(teleportBuilder, spaceKey);
|
|
882
|
+
|
|
883
|
+
const handle = peer1.repo.create();
|
|
884
|
+
handle.change((doc: any) => (doc.text = 'hello'));
|
|
885
|
+
await connectPeers(spaceKey, teleportBuilder, peer1, peer2);
|
|
886
|
+
|
|
887
|
+
await expect
|
|
888
|
+
.poll(async () => {
|
|
889
|
+
const doc = peer2.repo.handles[handle.documentId];
|
|
890
|
+
await doc.whenReady();
|
|
891
|
+
return doc.doc()!.text;
|
|
892
|
+
})
|
|
893
|
+
.toEqual('hello');
|
|
894
|
+
});
|
|
895
|
+
|
|
896
|
+
test('imported document is passively replicated to connected peers', async () => {
|
|
897
|
+
let blob: Uint8Array;
|
|
898
|
+
let documentId: DocumentId;
|
|
899
|
+
{
|
|
900
|
+
const repo = new Repo();
|
|
901
|
+
const handle = repo.create();
|
|
902
|
+
handle.change((doc: any) => (doc.text = 'hello'));
|
|
903
|
+
blob = A.save(handle.doc()!);
|
|
904
|
+
documentId = handle.documentId;
|
|
905
|
+
}
|
|
906
|
+
|
|
907
|
+
const [spaceKey] = PublicKey.randomSequence();
|
|
908
|
+
|
|
909
|
+
const teleportBuilder = new TeleportBuilder();
|
|
910
|
+
onTestFinished(() => teleportBuilder.destroy());
|
|
911
|
+
|
|
912
|
+
const peer1 = await createTeleportTestPeer(teleportBuilder, spaceKey);
|
|
913
|
+
const peer2 = await createTeleportTestPeer(teleportBuilder, spaceKey);
|
|
914
|
+
await connectPeers(spaceKey, teleportBuilder, peer1, peer2);
|
|
915
|
+
|
|
916
|
+
const handle = peer1.repo.import(blob, { docId: documentId });
|
|
917
|
+
await handle.whenReady();
|
|
918
|
+
|
|
919
|
+
await expect
|
|
920
|
+
.poll(
|
|
921
|
+
async () => {
|
|
922
|
+
const doc = peer2.repo.handles[handle.documentId];
|
|
923
|
+
await doc.whenReady();
|
|
924
|
+
return doc.doc()!.text;
|
|
925
|
+
},
|
|
926
|
+
{ timeout: 1_000 },
|
|
927
|
+
)
|
|
928
|
+
.toEqual('hello');
|
|
679
929
|
});
|
|
680
930
|
});
|
|
681
931
|
|
|
682
|
-
const createLevelAdapter = async () => {
|
|
683
|
-
const level = createTestLevel();
|
|
932
|
+
const createLevelAdapter = async (level = createTestLevel()) => {
|
|
684
933
|
const storage = new LevelDBStorageAdapter({ db: level.sublevel('automerge') });
|
|
685
934
|
await openAndClose(level, storage);
|
|
686
935
|
return storage;
|
|
@@ -702,19 +951,23 @@ describe('AutomergeRepo', () => {
|
|
|
702
951
|
};
|
|
703
952
|
});
|
|
704
953
|
|
|
954
|
+
type ShareConfig = Exclude<ConstructorParameters<typeof Repo>[0], undefined>['shareConfig'];
|
|
955
|
+
|
|
705
956
|
type ConnectedRepoOptions = {
|
|
706
957
|
storages?: StorageAdapterInterface[];
|
|
707
958
|
connectionStateProvider?: TestConnectionStateProvider;
|
|
708
959
|
sharePolicy?: SharePolicy;
|
|
960
|
+
shareConfig?: ShareConfig;
|
|
709
961
|
};
|
|
710
962
|
|
|
711
963
|
const createRepoTopology = async <Peers extends string[], Peer extends string = Peers[number]>(args: {
|
|
712
964
|
peers: Peers;
|
|
713
965
|
connections: [Peer, Peer][];
|
|
714
966
|
options?: ConnectedRepoOptions;
|
|
967
|
+
onMessage?: (message: Message) => void;
|
|
715
968
|
}) => {
|
|
716
969
|
const adapters = args.connections.map(
|
|
717
|
-
() => TestAdapter.createPair(args.options?.connectionStateProvider) as [TestAdapter, TestAdapter],
|
|
970
|
+
() => TestAdapter.createPair(args.options?.connectionStateProvider, args.onMessage) as [TestAdapter, TestAdapter],
|
|
718
971
|
);
|
|
719
972
|
const repos = args.peers.map((peerId, peerIndex) => {
|
|
720
973
|
const network = adapters
|
|
@@ -730,7 +983,8 @@ const createRepoTopology = async <Peers extends string[], Peer extends string =
|
|
|
730
983
|
peerId: peerId as PeerId,
|
|
731
984
|
storage: args.options?.storages?.[peerIndex],
|
|
732
985
|
network,
|
|
733
|
-
sharePolicy: args.options?.sharePolicy ??
|
|
986
|
+
sharePolicy: (args.options?.sharePolicy ?? args.options?.shareConfig) ? undefined : async () => true,
|
|
987
|
+
shareConfig: args.options?.shareConfig,
|
|
734
988
|
});
|
|
735
989
|
});
|
|
736
990
|
return { repos, adapters };
|
|
@@ -797,7 +1051,7 @@ const createTeleportTestPeer = async (
|
|
|
797
1051
|
});
|
|
798
1052
|
await echoAdapter.open();
|
|
799
1053
|
await echoAdapter.whenConnected();
|
|
800
|
-
await echoAdapter.addReplicator(meshAdapter);
|
|
1054
|
+
await echoAdapter.addReplicator(Context.default(), meshAdapter);
|
|
801
1055
|
const [teleport] = teleportBuilder.createPeers({ factory: () => new TeleportPeer() });
|
|
802
1056
|
return { repo, meshAdapter, teleport };
|
|
803
1057
|
};
|
|
@@ -816,3 +1070,7 @@ const connectPeers = async (
|
|
|
816
1070
|
};
|
|
817
1071
|
|
|
818
1072
|
type TeleportTestPeer = { repo: Repo; meshAdapter: MeshEchoReplicator; teleport: TeleportPeer };
|
|
1073
|
+
|
|
1074
|
+
export const createTmpPath = (): string => {
|
|
1075
|
+
return `/tmp/dxos-${PublicKey.random().toHex()}`;
|
|
1076
|
+
};
|