@automerge/automerge-repo 2.0.0-alpha.2 → 2.0.0-alpha.22
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/README.md +5 -6
- package/dist/AutomergeUrl.d.ts +17 -5
- package/dist/AutomergeUrl.d.ts.map +1 -1
- package/dist/AutomergeUrl.js +71 -24
- package/dist/DocHandle.d.ts +89 -20
- package/dist/DocHandle.d.ts.map +1 -1
- package/dist/DocHandle.js +189 -28
- package/dist/FindProgress.d.ts +30 -0
- package/dist/FindProgress.d.ts.map +1 -0
- package/dist/FindProgress.js +1 -0
- package/dist/RemoteHeadsSubscriptions.d.ts +4 -5
- package/dist/RemoteHeadsSubscriptions.d.ts.map +1 -1
- package/dist/RemoteHeadsSubscriptions.js +4 -1
- package/dist/Repo.d.ts +44 -6
- package/dist/Repo.d.ts.map +1 -1
- package/dist/Repo.js +226 -87
- package/dist/entrypoints/fullfat.d.ts +1 -0
- package/dist/entrypoints/fullfat.d.ts.map +1 -1
- package/dist/entrypoints/fullfat.js +1 -2
- package/dist/helpers/abortable.d.ts +39 -0
- package/dist/helpers/abortable.d.ts.map +1 -0
- package/dist/helpers/abortable.js +45 -0
- package/dist/helpers/bufferFromHex.d.ts +3 -0
- package/dist/helpers/bufferFromHex.d.ts.map +1 -0
- package/dist/helpers/bufferFromHex.js +13 -0
- package/dist/helpers/headsAreSame.d.ts +2 -2
- package/dist/helpers/headsAreSame.d.ts.map +1 -1
- package/dist/helpers/mergeArrays.d.ts +1 -1
- package/dist/helpers/mergeArrays.d.ts.map +1 -1
- package/dist/helpers/tests/network-adapter-tests.d.ts.map +1 -1
- package/dist/helpers/tests/network-adapter-tests.js +13 -13
- package/dist/helpers/tests/storage-adapter-tests.d.ts +2 -2
- package/dist/helpers/tests/storage-adapter-tests.d.ts.map +1 -1
- package/dist/helpers/tests/storage-adapter-tests.js +25 -48
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/storage/StorageSubsystem.d.ts +11 -1
- package/dist/storage/StorageSubsystem.d.ts.map +1 -1
- package/dist/storage/StorageSubsystem.js +20 -4
- package/dist/synchronizer/CollectionSynchronizer.d.ts +17 -3
- package/dist/synchronizer/CollectionSynchronizer.d.ts.map +1 -1
- package/dist/synchronizer/CollectionSynchronizer.js +43 -18
- package/dist/synchronizer/DocSynchronizer.d.ts +10 -2
- package/dist/synchronizer/DocSynchronizer.d.ts.map +1 -1
- package/dist/synchronizer/DocSynchronizer.js +30 -8
- package/dist/synchronizer/Synchronizer.d.ts +11 -0
- package/dist/synchronizer/Synchronizer.d.ts.map +1 -1
- package/dist/types.d.ts +4 -1
- package/dist/types.d.ts.map +1 -1
- package/fuzz/fuzz.ts +3 -3
- package/package.json +3 -3
- package/src/AutomergeUrl.ts +101 -26
- package/src/DocHandle.ts +256 -38
- package/src/FindProgress.ts +48 -0
- package/src/RemoteHeadsSubscriptions.ts +11 -9
- package/src/Repo.ts +310 -95
- package/src/entrypoints/fullfat.ts +1 -2
- package/src/helpers/abortable.ts +61 -0
- package/src/helpers/bufferFromHex.ts +14 -0
- package/src/helpers/headsAreSame.ts +2 -2
- package/src/helpers/tests/network-adapter-tests.ts +14 -13
- package/src/helpers/tests/storage-adapter-tests.ts +44 -86
- package/src/index.ts +2 -0
- package/src/storage/StorageSubsystem.ts +29 -4
- package/src/synchronizer/CollectionSynchronizer.ts +56 -19
- package/src/synchronizer/DocSynchronizer.ts +34 -9
- package/src/synchronizer/Synchronizer.ts +14 -0
- package/src/types.ts +4 -1
- package/test/AutomergeUrl.test.ts +130 -0
- package/test/CollectionSynchronizer.test.ts +4 -4
- package/test/DocHandle.test.ts +189 -29
- package/test/DocSynchronizer.test.ts +10 -3
- package/test/Repo.test.ts +377 -191
- package/test/StorageSubsystem.test.ts +17 -0
- package/test/remoteHeads.test.ts +27 -12
|
@@ -30,23 +30,23 @@ export function runNetworkAdapterTests(_setup, title) {
|
|
|
30
30
|
const bobRepo = new Repo({ network: b, peerId: bob });
|
|
31
31
|
// Alice creates a document
|
|
32
32
|
const aliceHandle = aliceRepo.create();
|
|
33
|
-
//
|
|
34
|
-
await
|
|
35
|
-
const bobHandle = bobRepo.find(aliceHandle.url);
|
|
33
|
+
// TODO: ... let connections complete. this shouldn't be necessary.
|
|
34
|
+
await pause(50);
|
|
35
|
+
const bobHandle = await bobRepo.find(aliceHandle.url);
|
|
36
36
|
// Alice changes the document
|
|
37
37
|
aliceHandle.change(d => {
|
|
38
38
|
d.foo = "bar";
|
|
39
39
|
});
|
|
40
40
|
// Bob receives the change
|
|
41
41
|
await eventPromise(bobHandle, "change");
|
|
42
|
-
assert.equal((await bobHandle.doc()
|
|
42
|
+
assert.equal((await bobHandle).doc()?.foo, "bar");
|
|
43
43
|
// Bob changes the document
|
|
44
44
|
bobHandle.change(d => {
|
|
45
45
|
d.foo = "baz";
|
|
46
46
|
});
|
|
47
47
|
// Alice receives the change
|
|
48
48
|
await eventPromise(aliceHandle, "change");
|
|
49
|
-
assert.equal(
|
|
49
|
+
assert.equal(aliceHandle.doc().foo, "baz");
|
|
50
50
|
};
|
|
51
51
|
// Run the test in both directions, in case they're different types of adapters
|
|
52
52
|
{
|
|
@@ -72,25 +72,25 @@ export function runNetworkAdapterTests(_setup, title) {
|
|
|
72
72
|
const aliceHandle = aliceRepo.create();
|
|
73
73
|
const docUrl = aliceHandle.url;
|
|
74
74
|
// Bob and Charlie receive the document
|
|
75
|
-
await
|
|
76
|
-
const bobHandle = bobRepo.find(docUrl);
|
|
77
|
-
const charlieHandle = charlieRepo.find(docUrl);
|
|
75
|
+
await pause(50);
|
|
76
|
+
const bobHandle = await bobRepo.find(docUrl);
|
|
77
|
+
const charlieHandle = await charlieRepo.find(docUrl);
|
|
78
78
|
// Alice changes the document
|
|
79
79
|
aliceHandle.change(d => {
|
|
80
80
|
d.foo = "bar";
|
|
81
81
|
});
|
|
82
82
|
// Bob and Charlie receive the change
|
|
83
83
|
await eventPromises([bobHandle, charlieHandle], "change");
|
|
84
|
-
assert.equal(
|
|
85
|
-
assert.equal(
|
|
84
|
+
assert.equal(bobHandle.doc().foo, "bar");
|
|
85
|
+
assert.equal(charlieHandle.doc().foo, "bar");
|
|
86
86
|
// Charlie changes the document
|
|
87
87
|
charlieHandle.change(d => {
|
|
88
88
|
d.foo = "baz";
|
|
89
89
|
});
|
|
90
90
|
// Alice and Bob receive the change
|
|
91
91
|
await eventPromises([aliceHandle, bobHandle], "change");
|
|
92
|
-
assert.equal(
|
|
93
|
-
assert.equal(
|
|
92
|
+
assert.equal(bobHandle.doc().foo, "baz");
|
|
93
|
+
assert.equal(charlieHandle.doc().foo, "baz");
|
|
94
94
|
teardown();
|
|
95
95
|
});
|
|
96
96
|
it("can broadcast a message", async () => {
|
|
@@ -101,7 +101,7 @@ export function runNetworkAdapterTests(_setup, title) {
|
|
|
101
101
|
const charlieRepo = new Repo({ network: c, peerId: charlie });
|
|
102
102
|
await eventPromises([aliceRepo, bobRepo, charlieRepo].map(r => r.networkSubsystem), "peer");
|
|
103
103
|
const aliceHandle = aliceRepo.create();
|
|
104
|
-
const charlieHandle = charlieRepo.find(aliceHandle.url);
|
|
104
|
+
const charlieHandle = await charlieRepo.find(aliceHandle.url);
|
|
105
105
|
// pause to give charlie a chance to let alice know it wants the doc
|
|
106
106
|
await pause(100);
|
|
107
107
|
const alicePresenceData = { presence: "alice" };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { StorageAdapterInterface } from "../../storage/StorageAdapterInterface.js";
|
|
2
|
-
export declare function runStorageAdapterTests(
|
|
2
|
+
export declare function runStorageAdapterTests(setup: SetupFn, title?: string): void;
|
|
3
3
|
export type SetupFn = () => Promise<{
|
|
4
4
|
adapter: StorageAdapterInterface;
|
|
5
|
-
teardown?: () => void
|
|
5
|
+
teardown?: () => void | Promise<void>;
|
|
6
6
|
}>;
|
|
7
7
|
//# sourceMappingURL=storage-adapter-tests.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"storage-adapter-tests.d.ts","sourceRoot":"","sources":["../../../src/helpers/tests/storage-adapter-tests.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,0CAA0C,CAAA;
|
|
1
|
+
{"version":3,"file":"storage-adapter-tests.d.ts","sourceRoot":"","sources":["../../../src/helpers/tests/storage-adapter-tests.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,0CAA0C,CAAA;AAcvF,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CA+H3E;AAID,MAAM,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC;IAClC,OAAO,EAAE,uBAAuB,CAAA;IAChC,QAAQ,CAAC,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;CACtC,CAAC,CAAA"}
|
|
@@ -1,107 +1,87 @@
|
|
|
1
|
-
import { describe, expect, it } from "vitest";
|
|
1
|
+
import { describe, expect, beforeEach, it as _it } from "vitest";
|
|
2
2
|
const PAYLOAD_A = () => new Uint8Array([0, 1, 127, 99, 154, 235]);
|
|
3
3
|
const PAYLOAD_B = () => new Uint8Array([1, 76, 160, 53, 57, 10, 230]);
|
|
4
4
|
const PAYLOAD_C = () => new Uint8Array([2, 111, 74, 131, 236, 96, 142, 193]);
|
|
5
5
|
const LARGE_PAYLOAD = new Uint8Array(100000).map(() => Math.random() * 256);
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
6
|
+
const it = (_it);
|
|
7
|
+
export function runStorageAdapterTests(setup, title) {
|
|
8
|
+
beforeEach(async (ctx) => {
|
|
9
|
+
const { adapter, teardown = NO_OP } = await setup();
|
|
10
|
+
ctx.adapter = adapter;
|
|
11
|
+
return teardown;
|
|
12
|
+
});
|
|
11
13
|
describe(`Storage adapter acceptance tests ${title ? `(${title})` : ""}`, () => {
|
|
12
14
|
describe("load", () => {
|
|
13
|
-
it("should return undefined if there is no data", async () => {
|
|
14
|
-
const { adapter, teardown } = await setup();
|
|
15
|
+
it("should return undefined if there is no data", async ({ adapter }) => {
|
|
15
16
|
const actual = await adapter.load(["AAAAA", "sync-state", "xxxxx"]);
|
|
16
17
|
expect(actual).toBeUndefined();
|
|
17
|
-
teardown();
|
|
18
18
|
});
|
|
19
19
|
});
|
|
20
20
|
describe("save and load", () => {
|
|
21
|
-
it("should return data that was saved", async () => {
|
|
22
|
-
const { adapter, teardown } = await setup();
|
|
21
|
+
it("should return data that was saved", async ({ adapter }) => {
|
|
23
22
|
await adapter.save(["storage-adapter-id"], PAYLOAD_A());
|
|
24
23
|
const actual = await adapter.load(["storage-adapter-id"]);
|
|
25
24
|
expect(actual).toStrictEqual(PAYLOAD_A());
|
|
26
|
-
teardown();
|
|
27
25
|
});
|
|
28
|
-
it("should work with composite keys", async () => {
|
|
29
|
-
const { adapter, teardown } = await setup();
|
|
26
|
+
it("should work with composite keys", async ({ adapter }) => {
|
|
30
27
|
await adapter.save(["AAAAA", "sync-state", "xxxxx"], PAYLOAD_A());
|
|
31
28
|
const actual = await adapter.load(["AAAAA", "sync-state", "xxxxx"]);
|
|
32
29
|
expect(actual).toStrictEqual(PAYLOAD_A());
|
|
33
|
-
teardown();
|
|
34
30
|
});
|
|
35
|
-
it("should work with a large payload", async () => {
|
|
36
|
-
const { adapter, teardown } = await setup();
|
|
31
|
+
it("should work with a large payload", async ({ adapter }) => {
|
|
37
32
|
await adapter.save(["AAAAA", "sync-state", "xxxxx"], LARGE_PAYLOAD);
|
|
38
33
|
const actual = await adapter.load(["AAAAA", "sync-state", "xxxxx"]);
|
|
39
34
|
expect(actual).toStrictEqual(LARGE_PAYLOAD);
|
|
40
|
-
teardown();
|
|
41
35
|
});
|
|
42
36
|
});
|
|
43
37
|
describe("loadRange", () => {
|
|
44
|
-
it("should return an empty array if there is no data", async () => {
|
|
45
|
-
const { adapter, teardown } = await setup();
|
|
38
|
+
it("should return an empty array if there is no data", async ({ adapter, }) => {
|
|
46
39
|
expect(await adapter.loadRange(["AAAAA"])).toStrictEqual([]);
|
|
47
|
-
teardown();
|
|
48
40
|
});
|
|
49
41
|
});
|
|
50
42
|
describe("save and loadRange", () => {
|
|
51
|
-
it("should return all the data that matches the key", async () => {
|
|
52
|
-
const { adapter, teardown } = await setup();
|
|
43
|
+
it("should return all the data that matches the key", async ({ adapter, }) => {
|
|
53
44
|
await adapter.save(["AAAAA", "sync-state", "xxxxx"], PAYLOAD_A());
|
|
54
45
|
await adapter.save(["AAAAA", "snapshot", "yyyyy"], PAYLOAD_B());
|
|
55
46
|
await adapter.save(["AAAAA", "sync-state", "zzzzz"], PAYLOAD_C());
|
|
56
|
-
expect(await adapter.loadRange(["AAAAA"])).toStrictEqual(
|
|
47
|
+
expect(await adapter.loadRange(["AAAAA"])).toStrictEqual([
|
|
57
48
|
{ key: ["AAAAA", "sync-state", "xxxxx"], data: PAYLOAD_A() },
|
|
58
49
|
{ key: ["AAAAA", "snapshot", "yyyyy"], data: PAYLOAD_B() },
|
|
59
50
|
{ key: ["AAAAA", "sync-state", "zzzzz"], data: PAYLOAD_C() },
|
|
60
|
-
])
|
|
61
|
-
expect(await adapter.loadRange(["AAAAA", "sync-state"])).toStrictEqual(
|
|
51
|
+
]);
|
|
52
|
+
expect(await adapter.loadRange(["AAAAA", "sync-state"])).toStrictEqual([
|
|
62
53
|
{ key: ["AAAAA", "sync-state", "xxxxx"], data: PAYLOAD_A() },
|
|
63
54
|
{ key: ["AAAAA", "sync-state", "zzzzz"], data: PAYLOAD_C() },
|
|
64
|
-
])
|
|
65
|
-
teardown();
|
|
55
|
+
]);
|
|
66
56
|
});
|
|
67
|
-
it("should only load values that match they key", async () => {
|
|
68
|
-
const { adapter, teardown } = await setup();
|
|
57
|
+
it("should only load values that match they key", async ({ adapter }) => {
|
|
69
58
|
await adapter.save(["AAAAA", "sync-state", "xxxxx"], PAYLOAD_A());
|
|
70
59
|
await adapter.save(["BBBBB", "sync-state", "zzzzz"], PAYLOAD_C());
|
|
71
60
|
const actual = await adapter.loadRange(["AAAAA"]);
|
|
72
|
-
expect(actual).toStrictEqual(
|
|
61
|
+
expect(actual).toStrictEqual([
|
|
73
62
|
{ key: ["AAAAA", "sync-state", "xxxxx"], data: PAYLOAD_A() },
|
|
74
|
-
])
|
|
75
|
-
expect(actual).toStrictEqual(expect.not.arrayContaining([
|
|
76
|
-
{ key: ["BBBBB", "sync-state", "zzzzz"], data: PAYLOAD_C() },
|
|
77
|
-
]));
|
|
78
|
-
teardown();
|
|
63
|
+
]);
|
|
79
64
|
});
|
|
80
65
|
});
|
|
81
66
|
describe("save and remove", () => {
|
|
82
|
-
it("after removing, should be empty", async () => {
|
|
83
|
-
const { adapter, teardown } = await setup();
|
|
67
|
+
it("after removing, should be empty", async ({ adapter }) => {
|
|
84
68
|
await adapter.save(["AAAAA", "snapshot", "xxxxx"], PAYLOAD_A());
|
|
85
69
|
await adapter.remove(["AAAAA", "snapshot", "xxxxx"]);
|
|
86
70
|
expect(await adapter.loadRange(["AAAAA"])).toStrictEqual([]);
|
|
87
71
|
expect(await adapter.load(["AAAAA", "snapshot", "xxxxx"])).toBeUndefined();
|
|
88
|
-
teardown();
|
|
89
72
|
});
|
|
90
73
|
});
|
|
91
74
|
describe("save and save", () => {
|
|
92
|
-
it("should overwrite data saved with the same key", async () => {
|
|
93
|
-
const { adapter, teardown } = await setup();
|
|
75
|
+
it("should overwrite data saved with the same key", async ({ adapter, }) => {
|
|
94
76
|
await adapter.save(["AAAAA", "sync-state", "xxxxx"], PAYLOAD_A());
|
|
95
77
|
await adapter.save(["AAAAA", "sync-state", "xxxxx"], PAYLOAD_B());
|
|
96
78
|
expect(await adapter.loadRange(["AAAAA", "sync-state"])).toStrictEqual([
|
|
97
79
|
{ key: ["AAAAA", "sync-state", "xxxxx"], data: PAYLOAD_B() },
|
|
98
80
|
]);
|
|
99
|
-
teardown();
|
|
100
81
|
});
|
|
101
82
|
});
|
|
102
83
|
describe("removeRange", () => {
|
|
103
|
-
it("should remove a range of records", async () => {
|
|
104
|
-
const { adapter, teardown } = await setup();
|
|
84
|
+
it("should remove a range of records", async ({ adapter }) => {
|
|
105
85
|
await adapter.save(["AAAAA", "sync-state", "xxxxx"], PAYLOAD_A());
|
|
106
86
|
await adapter.save(["AAAAA", "snapshot", "yyyyy"], PAYLOAD_B());
|
|
107
87
|
await adapter.save(["AAAAA", "sync-state", "zzzzz"], PAYLOAD_C());
|
|
@@ -109,10 +89,8 @@ export function runStorageAdapterTests(_setup, title) {
|
|
|
109
89
|
expect(await adapter.loadRange(["AAAAA"])).toStrictEqual([
|
|
110
90
|
{ key: ["AAAAA", "snapshot", "yyyyy"], data: PAYLOAD_B() },
|
|
111
91
|
]);
|
|
112
|
-
teardown();
|
|
113
92
|
});
|
|
114
|
-
it("should not remove records that don't match", async () => {
|
|
115
|
-
const { adapter, teardown } = await setup();
|
|
93
|
+
it("should not remove records that don't match", async ({ adapter }) => {
|
|
116
94
|
await adapter.save(["AAAAA", "sync-state", "xxxxx"], PAYLOAD_A());
|
|
117
95
|
await adapter.save(["BBBBB", "sync-state", "zzzzz"], PAYLOAD_B());
|
|
118
96
|
await adapter.removeRange(["AAAAA"]);
|
|
@@ -120,7 +98,6 @@ export function runStorageAdapterTests(_setup, title) {
|
|
|
120
98
|
expect(actual).toStrictEqual([
|
|
121
99
|
{ key: ["BBBBB", "sync-state", "zzzzz"], data: PAYLOAD_B() },
|
|
122
100
|
]);
|
|
123
|
-
teardown();
|
|
124
101
|
});
|
|
125
102
|
});
|
|
126
103
|
});
|
package/dist/index.d.ts
CHANGED
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
* ```
|
|
27
27
|
*/
|
|
28
28
|
export { DocHandle } from "./DocHandle.js";
|
|
29
|
-
export { isValidAutomergeUrl, isValidDocumentId, parseAutomergeUrl, stringifyAutomergeUrl, interpretAsDocumentId, generateAutomergeUrl, } from "./AutomergeUrl.js";
|
|
29
|
+
export { isValidAutomergeUrl, isValidDocumentId, parseAutomergeUrl, stringifyAutomergeUrl, interpretAsDocumentId, generateAutomergeUrl, encodeHeads, decodeHeads, } from "./AutomergeUrl.js";
|
|
30
30
|
export { Repo } from "./Repo.js";
|
|
31
31
|
export { NetworkAdapter } from "./network/NetworkAdapter.js";
|
|
32
32
|
export type { NetworkAdapterInterface } from "./network/NetworkAdapterInterface.js";
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAC1C,OAAO,EACL,mBAAmB,EACnB,iBAAiB,EACjB,iBAAiB,EACjB,qBAAqB,EACrB,qBAAqB,EACrB,oBAAoB,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAC1C,OAAO,EACL,mBAAmB,EACnB,iBAAiB,EACjB,iBAAiB,EACjB,qBAAqB,EACrB,qBAAqB,EACrB,oBAAoB,EACpB,WAAW,EACX,WAAW,GACZ,MAAM,mBAAmB,CAAA;AAC1B,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAChC,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAA;AAC5D,YAAY,EAAE,uBAAuB,EAAE,MAAM,sCAAsC,CAAA;AACnF,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAA;AACrD,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAA;AAC5D,YAAY,EAAE,uBAAuB,EAAE,MAAM,sCAAsC,CAAA;AAEnF,eAAe;AACf,OAAO,KAAK,IAAI,MAAM,mBAAmB,CAAA;AAIzC,YAAY,EACV,sBAAsB,EACtB,sBAAsB,EACtB,6BAA6B,EAC7B,gCAAgC,EAChC,2BAA2B,EAC3B,eAAe,EACf,gBAAgB,EAChB,wCAAwC,EACxC,WAAW,GACZ,MAAM,gBAAgB,CAAA;AAEvB,YAAY,EACV,qBAAqB,EACrB,eAAe,EACf,UAAU,EACV,UAAU,EACV,WAAW,GACZ,MAAM,WAAW,CAAA;AAElB,YAAY,EACV,oBAAoB,EACpB,WAAW,EACX,oBAAoB,EACpB,uBAAuB,EACvB,YAAY,GACb,MAAM,sCAAsC,CAAA;AAE7C,YAAY,EACV,0BAA0B,EAC1B,gBAAgB,EAChB,OAAO,EACP,WAAW,EACX,cAAc,EACd,WAAW,GACZ,MAAM,uBAAuB,CAAA;AAE9B,YAAY,EACV,KAAK,EACL,SAAS,EACT,SAAS,EACT,UAAU,EACV,SAAS,GACV,MAAM,oBAAoB,CAAA;AAE3B,cAAc,YAAY,CAAA;AAG1B,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,gCAAgC,CAAA;AAGnE,YAAY,EACV,GAAG,EACH,KAAK,EACL,KAAK,EACL,aAAa,EACb,IAAI,EACJ,OAAO,EACP,MAAM,EACN,QAAQ,EACR,IAAI,EACJ,OAAO,EACP,SAAS,EACT,SAAS,EACT,MAAM,GACP,MAAM,gCAAgC,CAAA;AAIvC,OAAO,EACL,UAAU,EACV,aAAa,EACb,YAAY,EACZ,IAAI,EACJ,YAAY,GACb,MAAM,gCAAgC,CAAA;AAKvC,OAAO,EACL,SAAS,EACT,iBAAiB,EACjB,MAAM,EACN,UAAU,EACV,QAAQ,EACR,QAAQ,EACR,IAAI,EACJ,MAAM,GACP,MAAM,gCAAgC,CAAA"}
|
package/dist/index.js
CHANGED
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
* ```
|
|
27
27
|
*/
|
|
28
28
|
export { DocHandle } from "./DocHandle.js";
|
|
29
|
-
export { isValidAutomergeUrl, isValidDocumentId, parseAutomergeUrl, stringifyAutomergeUrl, interpretAsDocumentId, generateAutomergeUrl, } from "./AutomergeUrl.js";
|
|
29
|
+
export { isValidAutomergeUrl, isValidDocumentId, parseAutomergeUrl, stringifyAutomergeUrl, interpretAsDocumentId, generateAutomergeUrl, encodeHeads, decodeHeads, } from "./AutomergeUrl.js";
|
|
30
30
|
export { Repo } from "./Repo.js";
|
|
31
31
|
export { NetworkAdapter } from "./network/NetworkAdapter.js";
|
|
32
32
|
export { isRepoMessage } from "./network/messages.js";
|
|
@@ -2,11 +2,20 @@ import * as A from "@automerge/automerge/slim/next";
|
|
|
2
2
|
import { type DocumentId } from "../types.js";
|
|
3
3
|
import { StorageAdapterInterface } from "./StorageAdapterInterface.js";
|
|
4
4
|
import { StorageId } from "./types.js";
|
|
5
|
+
import { EventEmitter } from "eventemitter3";
|
|
6
|
+
type StorageSubsystemEvents = {
|
|
7
|
+
"document-loaded": (arg: {
|
|
8
|
+
documentId: DocumentId;
|
|
9
|
+
durationMillis: number;
|
|
10
|
+
numOps: number;
|
|
11
|
+
numChanges: number;
|
|
12
|
+
}) => void;
|
|
13
|
+
};
|
|
5
14
|
/**
|
|
6
15
|
* The storage subsystem is responsible for saving and loading Automerge documents to and from
|
|
7
16
|
* storage adapter. It also provides a generic key/value storage interface for other uses.
|
|
8
17
|
*/
|
|
9
|
-
export declare class StorageSubsystem {
|
|
18
|
+
export declare class StorageSubsystem extends EventEmitter<StorageSubsystemEvents> {
|
|
10
19
|
#private;
|
|
11
20
|
constructor(storageAdapter: StorageAdapterInterface);
|
|
12
21
|
id(): Promise<StorageId>;
|
|
@@ -49,4 +58,5 @@ export declare class StorageSubsystem {
|
|
|
49
58
|
loadSyncState(documentId: DocumentId, storageId: StorageId): Promise<A.SyncState | undefined>;
|
|
50
59
|
saveSyncState(documentId: DocumentId, storageId: StorageId, syncState: A.SyncState): Promise<void>;
|
|
51
60
|
}
|
|
61
|
+
export {};
|
|
52
62
|
//# sourceMappingURL=StorageSubsystem.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"StorageSubsystem.d.ts","sourceRoot":"","sources":["../../src/storage/StorageSubsystem.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,gCAAgC,CAAA;AAInD,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,aAAa,CAAA;AAC7C,OAAO,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAA;AACtE,OAAO,EAAyB,SAAS,EAAE,MAAM,YAAY,CAAA;
|
|
1
|
+
{"version":3,"file":"StorageSubsystem.d.ts","sourceRoot":"","sources":["../../src/storage/StorageSubsystem.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,gCAAgC,CAAA;AAInD,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,aAAa,CAAA;AAC7C,OAAO,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAA;AACtE,OAAO,EAAyB,SAAS,EAAE,MAAM,YAAY,CAAA;AAI7D,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAG5C,KAAK,sBAAsB,GAAG;IAC5B,iBAAiB,EAAE,CAAC,GAAG,EAAE;QACvB,UAAU,EAAE,UAAU,CAAA;QACtB,cAAc,EAAE,MAAM,CAAA;QACtB,MAAM,EAAE,MAAM,CAAA;QACd,UAAU,EAAE,MAAM,CAAA;KACnB,KAAK,IAAI,CAAA;CACX,CAAA;AAED;;;GAGG;AACH,qBAAa,gBAAiB,SAAQ,YAAY,CAAC,sBAAsB,CAAC;;gBAe5D,cAAc,EAAE,uBAAuB;IAK7C,EAAE,IAAI,OAAO,CAAC,SAAS,CAAC;IA2B9B,kCAAkC;IAC5B,IAAI;IACR,iFAAiF;IACjF,SAAS,EAAE,MAAM;IAEjB,yFAAyF;IACzF,GAAG,EAAE,MAAM,GACV,OAAO,CAAC,UAAU,GAAG,SAAS,CAAC;IAKlC,gCAAgC;IAC1B,IAAI;IACR,iFAAiF;IACjF,SAAS,EAAE,MAAM;IAEjB,yFAAyF;IACzF,GAAG,EAAE,MAAM;IAEX,sCAAsC;IACtC,IAAI,EAAE,UAAU,GACf,OAAO,CAAC,IAAI,CAAC;IAKhB,oCAAoC;IAC9B,MAAM;IACV,iFAAiF;IACjF,SAAS,EAAE,MAAM;IAEjB,2FAA2F;IAC3F,GAAG,EAAE,MAAM,GACV,OAAO,CAAC,IAAI,CAAC;IAOhB;;OAEG;IACG,OAAO,CAAC,CAAC,EAAE,UAAU,EAAE,UAAU,GAAG,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;IA0ClE;;;;;;OAMG;IACG,OAAO,CAAC,UAAU,EAAE,UAAU,EAAE,GAAG,EAAE,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAczE;;OAEG;IACG,SAAS,CAAC,UAAU,EAAE,UAAU;IAkEhC,aAAa,CACjB,UAAU,EAAE,UAAU,EACtB,SAAS,EAAE,SAAS,GACnB,OAAO,CAAC,CAAC,CAAC,SAAS,GAAG,SAAS,CAAC;IAW7B,aAAa,CACjB,UAAU,EAAE,UAAU,EACtB,SAAS,EAAE,SAAS,EACpB,SAAS,EAAE,CAAC,CAAC,SAAS,GACrB,OAAO,CAAC,IAAI,CAAC;CA8CjB"}
|
|
@@ -5,11 +5,13 @@ import { mergeArrays } from "../helpers/mergeArrays.js";
|
|
|
5
5
|
import { keyHash, headsHash } from "./keyHash.js";
|
|
6
6
|
import { chunkTypeFromKey } from "./chunkTypeFromKey.js";
|
|
7
7
|
import * as Uuid from "uuid";
|
|
8
|
+
import { EventEmitter } from "eventemitter3";
|
|
9
|
+
import { encodeHeads } from "../AutomergeUrl.js";
|
|
8
10
|
/**
|
|
9
11
|
* The storage subsystem is responsible for saving and loading Automerge documents to and from
|
|
10
12
|
* storage adapter. It also provides a generic key/value storage interface for other uses.
|
|
11
13
|
*/
|
|
12
|
-
export class StorageSubsystem {
|
|
14
|
+
export class StorageSubsystem extends EventEmitter {
|
|
13
15
|
/** The storage adapter to use for saving and loading documents */
|
|
14
16
|
#storageAdapter;
|
|
15
17
|
/** Record of the latest heads we've loaded or saved for each document */
|
|
@@ -20,6 +22,7 @@ export class StorageSubsystem {
|
|
|
20
22
|
#compacting = false;
|
|
21
23
|
#log = debug(`automerge-repo:storage-subsystem`);
|
|
22
24
|
constructor(storageAdapter) {
|
|
25
|
+
super();
|
|
23
26
|
this.#storageAdapter = storageAdapter;
|
|
24
27
|
}
|
|
25
28
|
async id() {
|
|
@@ -100,7 +103,14 @@ export class StorageSubsystem {
|
|
|
100
103
|
if (binary.length === 0)
|
|
101
104
|
return null;
|
|
102
105
|
// Load into an Automerge document
|
|
106
|
+
const start = performance.now();
|
|
103
107
|
const newDoc = A.loadIncremental(A.init(), binary);
|
|
108
|
+
const end = performance.now();
|
|
109
|
+
this.emit("document-loaded", {
|
|
110
|
+
documentId,
|
|
111
|
+
durationMillis: end - start,
|
|
112
|
+
...A.stats(newDoc),
|
|
113
|
+
});
|
|
104
114
|
// Record the latest heads for the document
|
|
105
115
|
this.#storedHeads.set(documentId, A.getHeads(newDoc));
|
|
106
116
|
return newDoc;
|
|
@@ -178,8 +188,14 @@ export class StorageSubsystem {
|
|
|
178
188
|
}
|
|
179
189
|
async loadSyncState(documentId, storageId) {
|
|
180
190
|
const key = [documentId, "sync-state", storageId];
|
|
181
|
-
|
|
182
|
-
|
|
191
|
+
try {
|
|
192
|
+
const loaded = await this.#storageAdapter.load(key);
|
|
193
|
+
return loaded ? A.decodeSyncState(loaded) : undefined;
|
|
194
|
+
}
|
|
195
|
+
catch (e) {
|
|
196
|
+
this.#log(`Error loading sync state for ${documentId} from ${storageId}`);
|
|
197
|
+
return undefined;
|
|
198
|
+
}
|
|
183
199
|
}
|
|
184
200
|
async saveSyncState(documentId, storageId, syncState) {
|
|
185
201
|
const key = [documentId, "sync-state", storageId];
|
|
@@ -195,7 +211,7 @@ export class StorageSubsystem {
|
|
|
195
211
|
return true;
|
|
196
212
|
}
|
|
197
213
|
const newHeads = A.getHeads(doc);
|
|
198
|
-
if (headsAreSame(newHeads, oldHeads)) {
|
|
214
|
+
if (headsAreSame(encodeHeads(newHeads), encodeHeads(oldHeads))) {
|
|
199
215
|
// the document hasn't changed
|
|
200
216
|
return false;
|
|
201
217
|
}
|
|
@@ -1,12 +1,17 @@
|
|
|
1
|
+
import { DocHandle } from "../DocHandle.js";
|
|
1
2
|
import { Repo } from "../Repo.js";
|
|
2
3
|
import { DocMessage } from "../network/messages.js";
|
|
3
|
-
import { DocumentId, PeerId } from "../types.js";
|
|
4
|
+
import { AutomergeUrl, DocumentId, PeerId } from "../types.js";
|
|
5
|
+
import { DocSynchronizer } from "./DocSynchronizer.js";
|
|
4
6
|
import { Synchronizer } from "./Synchronizer.js";
|
|
5
7
|
/** A CollectionSynchronizer is responsible for synchronizing a DocCollection with peers. */
|
|
6
8
|
export declare class CollectionSynchronizer extends Synchronizer {
|
|
7
9
|
#private;
|
|
8
10
|
private repo;
|
|
9
|
-
|
|
11
|
+
/** A map of documentIds to their synchronizers */
|
|
12
|
+
/** @hidden */
|
|
13
|
+
docSynchronizers: Record<DocumentId, DocSynchronizer>;
|
|
14
|
+
constructor(repo: Repo, denylist?: AutomergeUrl[]);
|
|
10
15
|
/**
|
|
11
16
|
* When we receive a sync message for a document we haven't got in memory, we
|
|
12
17
|
* register it with the repo and start synchronizing
|
|
@@ -15,7 +20,7 @@ export declare class CollectionSynchronizer extends Synchronizer {
|
|
|
15
20
|
/**
|
|
16
21
|
* Starts synchronizing the given document with all peers that we share it generously with.
|
|
17
22
|
*/
|
|
18
|
-
addDocument(
|
|
23
|
+
addDocument(handle: DocHandle<unknown>): void;
|
|
19
24
|
removeDocument(documentId: DocumentId): void;
|
|
20
25
|
/** Adds a peer and maybe starts synchronizing with them */
|
|
21
26
|
addPeer(peerId: PeerId): void;
|
|
@@ -23,5 +28,14 @@ export declare class CollectionSynchronizer extends Synchronizer {
|
|
|
23
28
|
removePeer(peerId: PeerId): void;
|
|
24
29
|
/** Returns a list of all connected peer ids */
|
|
25
30
|
get peers(): PeerId[];
|
|
31
|
+
metrics(): {
|
|
32
|
+
[key: string]: {
|
|
33
|
+
peers: PeerId[];
|
|
34
|
+
size: {
|
|
35
|
+
numOps: number;
|
|
36
|
+
numChanges: number;
|
|
37
|
+
};
|
|
38
|
+
};
|
|
39
|
+
};
|
|
26
40
|
}
|
|
27
41
|
//# sourceMappingURL=CollectionSynchronizer.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CollectionSynchronizer.d.ts","sourceRoot":"","sources":["../../src/synchronizer/CollectionSynchronizer.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"CollectionSynchronizer.d.ts","sourceRoot":"","sources":["../../src/synchronizer/CollectionSynchronizer.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAE3C,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAA;AACjC,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAA;AACnD,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAC9D,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAA;AACtD,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAIhD,4FAA4F;AAC5F,qBAAa,sBAAuB,SAAQ,YAAY;;IAa1C,OAAO,CAAC,IAAI;IATxB,kDAAkD;IAClD,cAAc;IACd,gBAAgB,EAAE,MAAM,CAAC,UAAU,EAAE,eAAe,CAAC,CAAK;gBAOtC,IAAI,EAAE,IAAI,EAAE,QAAQ,GAAE,YAAY,EAAO;IAwD7D;;;OAGG;IACG,cAAc,CAAC,OAAO,EAAE,UAAU;IAyCxC;;OAEG;IACH,WAAW,CAAC,MAAM,EAAE,SAAS,CAAC,OAAO,CAAC;IAatC,cAAc,CAAC,UAAU,EAAE,UAAU;IAIrC,2DAA2D;IAC3D,OAAO,CAAC,MAAM,EAAE,MAAM;IAgBtB,uDAAuD;IACvD,UAAU,CAAC,MAAM,EAAE,MAAM;IASzB,+CAA+C;IAC/C,IAAI,KAAK,IAAI,MAAM,EAAE,CAEpB;IAED,OAAO,IAAI;QACT,CAAC,GAAG,EAAE,MAAM,GAAG;YACb,KAAK,EAAE,MAAM,EAAE,CAAA;YACf,IAAI,EAAE;gBAAE,MAAM,EAAE,MAAM,CAAC;gBAAC,UAAU,EAAE,MAAM,CAAA;aAAE,CAAA;SAC7C,CAAA;KACF;CASF"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import debug from "debug";
|
|
2
|
-
import {
|
|
2
|
+
import { parseAutomergeUrl } from "../AutomergeUrl.js";
|
|
3
3
|
import { DocSynchronizer } from "./DocSynchronizer.js";
|
|
4
4
|
import { Synchronizer } from "./Synchronizer.js";
|
|
5
5
|
const log = debug("automerge-repo:collectionsync");
|
|
@@ -9,25 +9,29 @@ export class CollectionSynchronizer extends Synchronizer {
|
|
|
9
9
|
/** The set of peers we are connected with */
|
|
10
10
|
#peers = new Set();
|
|
11
11
|
/** A map of documentIds to their synchronizers */
|
|
12
|
-
|
|
12
|
+
/** @hidden */
|
|
13
|
+
docSynchronizers = {};
|
|
13
14
|
/** Used to determine if the document is know to the Collection and a synchronizer exists or is being set up */
|
|
14
15
|
#docSetUp = {};
|
|
15
|
-
|
|
16
|
+
#denylist;
|
|
17
|
+
constructor(repo, denylist = []) {
|
|
16
18
|
super();
|
|
17
19
|
this.repo = repo;
|
|
20
|
+
this.#denylist = denylist.map(url => parseAutomergeUrl(url).documentId);
|
|
18
21
|
}
|
|
19
22
|
/** Returns a synchronizer for the given document, creating one if it doesn't already exist. */
|
|
20
|
-
#fetchDocSynchronizer(
|
|
21
|
-
if (!this
|
|
22
|
-
|
|
23
|
-
|
|
23
|
+
#fetchDocSynchronizer(handle) {
|
|
24
|
+
if (!this.docSynchronizers[handle.documentId]) {
|
|
25
|
+
this.docSynchronizers[handle.documentId] =
|
|
26
|
+
this.#initDocSynchronizer(handle);
|
|
24
27
|
}
|
|
25
|
-
return this
|
|
28
|
+
return this.docSynchronizers[handle.documentId];
|
|
26
29
|
}
|
|
27
30
|
/** Creates a new docSynchronizer and sets it up to propagate messages */
|
|
28
31
|
#initDocSynchronizer(handle) {
|
|
29
32
|
const docSynchronizer = new DocSynchronizer({
|
|
30
33
|
handle,
|
|
34
|
+
peerId: this.repo.networkSubsystem.peerId,
|
|
31
35
|
onLoadSyncState: async (peerId) => {
|
|
32
36
|
if (!this.repo.storageSubsystem) {
|
|
33
37
|
return;
|
|
@@ -42,6 +46,7 @@ export class CollectionSynchronizer extends Synchronizer {
|
|
|
42
46
|
docSynchronizer.on("message", event => this.emit("message", event));
|
|
43
47
|
docSynchronizer.on("open-doc", event => this.emit("open-doc", event));
|
|
44
48
|
docSynchronizer.on("sync-state", event => this.emit("sync-state", event));
|
|
49
|
+
docSynchronizer.on("metrics", event => this.emit("metrics", event));
|
|
45
50
|
return docSynchronizer;
|
|
46
51
|
}
|
|
47
52
|
/** returns an array of peerIds that we share this document generously with */
|
|
@@ -66,24 +71,39 @@ export class CollectionSynchronizer extends Synchronizer {
|
|
|
66
71
|
if (!documentId) {
|
|
67
72
|
throw new Error("received a message with an invalid documentId");
|
|
68
73
|
}
|
|
74
|
+
if (this.#denylist.includes(documentId)) {
|
|
75
|
+
this.emit("metrics", {
|
|
76
|
+
type: "doc-denied",
|
|
77
|
+
documentId,
|
|
78
|
+
});
|
|
79
|
+
this.emit("message", {
|
|
80
|
+
type: "doc-unavailable",
|
|
81
|
+
documentId,
|
|
82
|
+
targetId: message.senderId,
|
|
83
|
+
});
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
69
86
|
this.#docSetUp[documentId] = true;
|
|
70
|
-
const
|
|
87
|
+
const handle = await this.repo.find(documentId, {
|
|
88
|
+
allowableStates: ["ready", "unavailable", "requesting"],
|
|
89
|
+
});
|
|
90
|
+
const docSynchronizer = this.#fetchDocSynchronizer(handle);
|
|
71
91
|
docSynchronizer.receiveMessage(message);
|
|
72
92
|
// Initiate sync with any new peers
|
|
73
93
|
const peers = await this.#documentGenerousPeers(documentId);
|
|
74
|
-
docSynchronizer.beginSync(peers.filter(peerId => !docSynchronizer.hasPeer(peerId)));
|
|
94
|
+
void docSynchronizer.beginSync(peers.filter(peerId => !docSynchronizer.hasPeer(peerId)));
|
|
75
95
|
}
|
|
76
96
|
/**
|
|
77
97
|
* Starts synchronizing the given document with all peers that we share it generously with.
|
|
78
98
|
*/
|
|
79
|
-
addDocument(
|
|
99
|
+
addDocument(handle) {
|
|
80
100
|
// HACK: this is a hack to prevent us from adding the same document twice
|
|
81
|
-
if (this.#docSetUp[documentId]) {
|
|
101
|
+
if (this.#docSetUp[handle.documentId]) {
|
|
82
102
|
return;
|
|
83
103
|
}
|
|
84
|
-
const docSynchronizer = this.#fetchDocSynchronizer(
|
|
85
|
-
void this.#documentGenerousPeers(documentId).then(peers => {
|
|
86
|
-
docSynchronizer.beginSync(peers);
|
|
104
|
+
const docSynchronizer = this.#fetchDocSynchronizer(handle);
|
|
105
|
+
void this.#documentGenerousPeers(handle.documentId).then(peers => {
|
|
106
|
+
void docSynchronizer.beginSync(peers);
|
|
87
107
|
});
|
|
88
108
|
}
|
|
89
109
|
// TODO: implement this
|
|
@@ -98,11 +118,11 @@ export class CollectionSynchronizer extends Synchronizer {
|
|
|
98
118
|
return;
|
|
99
119
|
}
|
|
100
120
|
this.#peers.add(peerId);
|
|
101
|
-
for (const docSynchronizer of Object.values(this
|
|
121
|
+
for (const docSynchronizer of Object.values(this.docSynchronizers)) {
|
|
102
122
|
const { documentId } = docSynchronizer;
|
|
103
123
|
void this.repo.sharePolicy(peerId, documentId).then(okToShare => {
|
|
104
124
|
if (okToShare)
|
|
105
|
-
docSynchronizer.beginSync([peerId]);
|
|
125
|
+
void docSynchronizer.beginSync([peerId]);
|
|
106
126
|
});
|
|
107
127
|
}
|
|
108
128
|
}
|
|
@@ -110,7 +130,7 @@ export class CollectionSynchronizer extends Synchronizer {
|
|
|
110
130
|
removePeer(peerId) {
|
|
111
131
|
log(`removing peer ${peerId}`);
|
|
112
132
|
this.#peers.delete(peerId);
|
|
113
|
-
for (const docSynchronizer of Object.values(this
|
|
133
|
+
for (const docSynchronizer of Object.values(this.docSynchronizers)) {
|
|
114
134
|
docSynchronizer.endSync(peerId);
|
|
115
135
|
}
|
|
116
136
|
}
|
|
@@ -118,4 +138,9 @@ export class CollectionSynchronizer extends Synchronizer {
|
|
|
118
138
|
get peers() {
|
|
119
139
|
return Array.from(this.#peers);
|
|
120
140
|
}
|
|
141
|
+
metrics() {
|
|
142
|
+
return Object.fromEntries(Object.entries(this.docSynchronizers).map(([documentId, synchronizer]) => {
|
|
143
|
+
return [documentId, synchronizer.metrics()];
|
|
144
|
+
}));
|
|
145
|
+
}
|
|
121
146
|
}
|
|
@@ -6,6 +6,7 @@ import { Synchronizer } from "./Synchronizer.js";
|
|
|
6
6
|
type PeerDocumentStatus = "unknown" | "has" | "unavailable" | "wants";
|
|
7
7
|
interface DocSynchronizerConfig {
|
|
8
8
|
handle: DocHandle<unknown>;
|
|
9
|
+
peerId: PeerId;
|
|
9
10
|
onLoadSyncState?: (peerId: PeerId) => Promise<A.SyncState | undefined>;
|
|
10
11
|
}
|
|
11
12
|
/**
|
|
@@ -15,15 +16,22 @@ interface DocSynchronizerConfig {
|
|
|
15
16
|
export declare class DocSynchronizer extends Synchronizer {
|
|
16
17
|
#private;
|
|
17
18
|
syncDebounceRate: number;
|
|
18
|
-
constructor({ handle, onLoadSyncState }: DocSynchronizerConfig);
|
|
19
|
+
constructor({ handle, peerId, onLoadSyncState }: DocSynchronizerConfig);
|
|
19
20
|
get peerStates(): Record<PeerId, PeerDocumentStatus>;
|
|
20
21
|
get documentId(): import("../types.js").DocumentId;
|
|
21
22
|
hasPeer(peerId: PeerId): boolean;
|
|
22
|
-
beginSync(peerIds: PeerId[]): void
|
|
23
|
+
beginSync(peerIds: PeerId[]): Promise<void>;
|
|
23
24
|
endSync(peerId: PeerId): void;
|
|
24
25
|
receiveMessage(message: RepoMessage): void;
|
|
25
26
|
receiveEphemeralMessage(message: EphemeralMessage): void;
|
|
26
27
|
receiveSyncMessage(message: SyncMessage | RequestMessage): void;
|
|
28
|
+
metrics(): {
|
|
29
|
+
peers: PeerId[];
|
|
30
|
+
size: {
|
|
31
|
+
numOps: number;
|
|
32
|
+
numChanges: number;
|
|
33
|
+
};
|
|
34
|
+
};
|
|
27
35
|
}
|
|
28
36
|
export {};
|
|
29
37
|
//# sourceMappingURL=DocSynchronizer.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DocSynchronizer.d.ts","sourceRoot":"","sources":["../../src/synchronizer/DocSynchronizer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,gCAAgC,CAAA;AAGnD,OAAO,EACL,SAAS,EAKV,MAAM,iBAAiB,CAAA;AACxB,OAAO,EAEL,gBAAgB,EAEhB,WAAW,EACX,cAAc,EACd,WAAW,EAEZ,MAAM,wBAAwB,CAAA;AAC/B,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AACpC,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAGhD,KAAK,kBAAkB,GAAG,SAAS,GAAG,KAAK,GAAG,aAAa,GAAG,OAAO,CAAA;AAOrE,UAAU,qBAAqB;IAC7B,MAAM,EAAE,SAAS,CAAC,OAAO,CAAC,CAAA;IAC1B,eAAe,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,SAAS,GAAG,SAAS,CAAC,CAAA;CACvE;AAED;;;GAGG;AACH,qBAAa,eAAgB,SAAQ,YAAY;;IAE/C,gBAAgB,SAAM;
|
|
1
|
+
{"version":3,"file":"DocSynchronizer.d.ts","sourceRoot":"","sources":["../../src/synchronizer/DocSynchronizer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,gCAAgC,CAAA;AAGnD,OAAO,EACL,SAAS,EAKV,MAAM,iBAAiB,CAAA;AACxB,OAAO,EAEL,gBAAgB,EAEhB,WAAW,EACX,cAAc,EACd,WAAW,EAEZ,MAAM,wBAAwB,CAAA;AAC/B,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AACpC,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAGhD,KAAK,kBAAkB,GAAG,SAAS,GAAG,KAAK,GAAG,aAAa,GAAG,OAAO,CAAA;AAOrE,UAAU,qBAAqB;IAC7B,MAAM,EAAE,SAAS,CAAC,OAAO,CAAC,CAAA;IAC1B,MAAM,EAAE,MAAM,CAAA;IACd,eAAe,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,SAAS,GAAG,SAAS,CAAC,CAAA;CACvE;AAED;;;GAGG;AACH,qBAAa,eAAgB,SAAQ,YAAY;;IAE/C,gBAAgB,SAAM;gBAyBV,EAAE,MAAM,EAAE,MAAM,EAAE,eAAe,EAAE,EAAE,qBAAqB;IA0BtE,IAAI,UAAU,uCAEb;IAED,IAAI,UAAU,qCAEb;IAiID,OAAO,CAAC,MAAM,EAAE,MAAM;IAIhB,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE;IAwDjC,OAAO,CAAC,MAAM,EAAE,MAAM;IAKtB,cAAc,CAAC,OAAO,EAAE,WAAW;IAkBnC,uBAAuB,CAAC,OAAO,EAAE,gBAAgB;IAuBjD,kBAAkB,CAAC,OAAO,EAAE,WAAW,GAAG,cAAc;IAwFxD,OAAO,IAAI;QAAE,KAAK,EAAE,MAAM,EAAE,CAAC;QAAC,IAAI,EAAE;YAAE,MAAM,EAAE,MAAM,CAAC;YAAC,UAAU,EAAE,MAAM,CAAA;SAAE,CAAA;KAAE;CAM7E"}
|