@automerge/automerge-repo 2.0.0-alpha.6 → 2.0.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. package/README.md +8 -8
  2. package/dist/AutomergeUrl.d.ts +17 -5
  3. package/dist/AutomergeUrl.d.ts.map +1 -1
  4. package/dist/AutomergeUrl.js +71 -24
  5. package/dist/DocHandle.d.ts +87 -30
  6. package/dist/DocHandle.d.ts.map +1 -1
  7. package/dist/DocHandle.js +198 -48
  8. package/dist/FindProgress.d.ts +30 -0
  9. package/dist/FindProgress.d.ts.map +1 -0
  10. package/dist/FindProgress.js +1 -0
  11. package/dist/RemoteHeadsSubscriptions.d.ts +4 -5
  12. package/dist/RemoteHeadsSubscriptions.d.ts.map +1 -1
  13. package/dist/RemoteHeadsSubscriptions.js +4 -1
  14. package/dist/Repo.d.ts +46 -6
  15. package/dist/Repo.d.ts.map +1 -1
  16. package/dist/Repo.js +252 -67
  17. package/dist/helpers/abortable.d.ts +39 -0
  18. package/dist/helpers/abortable.d.ts.map +1 -0
  19. package/dist/helpers/abortable.js +45 -0
  20. package/dist/helpers/arraysAreEqual.d.ts.map +1 -1
  21. package/dist/helpers/bufferFromHex.d.ts +3 -0
  22. package/dist/helpers/bufferFromHex.d.ts.map +1 -0
  23. package/dist/helpers/bufferFromHex.js +13 -0
  24. package/dist/helpers/debounce.d.ts.map +1 -1
  25. package/dist/helpers/eventPromise.d.ts.map +1 -1
  26. package/dist/helpers/headsAreSame.d.ts +2 -2
  27. package/dist/helpers/headsAreSame.d.ts.map +1 -1
  28. package/dist/helpers/mergeArrays.d.ts +1 -1
  29. package/dist/helpers/mergeArrays.d.ts.map +1 -1
  30. package/dist/helpers/pause.d.ts.map +1 -1
  31. package/dist/helpers/tests/network-adapter-tests.d.ts.map +1 -1
  32. package/dist/helpers/tests/network-adapter-tests.js +13 -13
  33. package/dist/helpers/tests/storage-adapter-tests.d.ts +2 -2
  34. package/dist/helpers/tests/storage-adapter-tests.d.ts.map +1 -1
  35. package/dist/helpers/tests/storage-adapter-tests.js +25 -48
  36. package/dist/helpers/throttle.d.ts.map +1 -1
  37. package/dist/helpers/withTimeout.d.ts.map +1 -1
  38. package/dist/index.d.ts +2 -1
  39. package/dist/index.d.ts.map +1 -1
  40. package/dist/index.js +1 -1
  41. package/dist/network/messages.d.ts.map +1 -1
  42. package/dist/storage/StorageSubsystem.d.ts +15 -1
  43. package/dist/storage/StorageSubsystem.d.ts.map +1 -1
  44. package/dist/storage/StorageSubsystem.js +50 -14
  45. package/dist/synchronizer/CollectionSynchronizer.d.ts +4 -3
  46. package/dist/synchronizer/CollectionSynchronizer.d.ts.map +1 -1
  47. package/dist/synchronizer/CollectionSynchronizer.js +34 -15
  48. package/dist/synchronizer/DocSynchronizer.d.ts +3 -2
  49. package/dist/synchronizer/DocSynchronizer.d.ts.map +1 -1
  50. package/dist/synchronizer/DocSynchronizer.js +51 -27
  51. package/dist/synchronizer/Synchronizer.d.ts +11 -0
  52. package/dist/synchronizer/Synchronizer.d.ts.map +1 -1
  53. package/dist/types.d.ts +4 -1
  54. package/dist/types.d.ts.map +1 -1
  55. package/fuzz/fuzz.ts +3 -3
  56. package/package.json +3 -4
  57. package/src/AutomergeUrl.ts +101 -26
  58. package/src/DocHandle.ts +268 -58
  59. package/src/FindProgress.ts +48 -0
  60. package/src/RemoteHeadsSubscriptions.ts +11 -9
  61. package/src/Repo.ts +364 -74
  62. package/src/helpers/abortable.ts +61 -0
  63. package/src/helpers/bufferFromHex.ts +14 -0
  64. package/src/helpers/headsAreSame.ts +2 -2
  65. package/src/helpers/tests/network-adapter-tests.ts +14 -13
  66. package/src/helpers/tests/storage-adapter-tests.ts +44 -86
  67. package/src/index.ts +7 -0
  68. package/src/storage/StorageSubsystem.ts +66 -16
  69. package/src/synchronizer/CollectionSynchronizer.ts +37 -16
  70. package/src/synchronizer/DocSynchronizer.ts +59 -32
  71. package/src/synchronizer/Synchronizer.ts +14 -0
  72. package/src/types.ts +4 -1
  73. package/test/AutomergeUrl.test.ts +130 -0
  74. package/test/CollectionSynchronizer.test.ts +4 -4
  75. package/test/DocHandle.test.ts +255 -30
  76. package/test/DocSynchronizer.test.ts +10 -3
  77. package/test/Repo.test.ts +376 -203
  78. package/test/StorageSubsystem.test.ts +80 -1
  79. 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
- // Bob receives the document
34
- await eventPromise(bobRepo, "document");
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())?.foo, "bar");
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((await aliceHandle.doc())?.foo, "baz");
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 eventPromises([bobRepo, charlieRepo], "document");
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((await bobHandle.doc())?.foo, "bar");
85
- assert.equal((await charlieHandle.doc())?.foo, "bar");
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((await bobHandle.doc())?.foo, "baz");
93
- assert.equal((await charlieHandle.doc())?.foo, "baz");
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(_setup: SetupFn, title?: string): void;
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;AAQvF,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,OAAO,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CA+K5E;AAID,MAAM,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC;IAClC,OAAO,EAAE,uBAAuB,CAAA;IAChC,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAA;CACtB,CAAC,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
- export function runStorageAdapterTests(_setup, title) {
7
- const setup = async () => {
8
- const { adapter, teardown = NO_OP } = await _setup();
9
- return { adapter, teardown };
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(expect.arrayContaining([
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(expect.arrayContaining([
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(expect.arrayContaining([
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
  });
@@ -1 +1 @@
1
- {"version":3,"file":"throttle.d.ts","sourceRoot":"","sources":["../../src/helpers/throttle.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,eAAO,MAAM,QAAQ,GAAI,CAAC,SAAS,CAAC,GAAG,IAAI,EAAE,UAAU,CAAC,CAAC,CAAC,KAAK,UAAU,CAAC,CAAC,CAAC,MACtE,CAAC,SACE,MAAM,eAKa,UAAU,CAAC,CAAC,CAAC,SAQxC,CAAA"}
1
+ {"version":3,"file":"throttle.d.ts","sourceRoot":"","sources":["../../src/helpers/throttle.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,eAAO,MAAM,QAAQ,GAAI,CAAC,SAAS,CAAC,GAAG,IAAI,EAAE,UAAU,CAAC,CAAC,CAAC,KAAK,UAAU,CAAC,CAAC,CAAC,EAC1E,IAAI,CAAC,EACL,OAAO,MAAM,MAKI,GAAG,MAAM,UAAU,CAAC,CAAC,CAAC,SAQxC,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"withTimeout.d.ts","sourceRoot":"","sources":["../../src/helpers/withTimeout.ts"],"names":[],"mappings":"AACA;;;GAGG;AACH,eAAO,MAAM,WAAW,GAAU,CAAC,WACxB,OAAO,CAAC,CAAC,CAAC,KAChB,MAAM,KACR,OAAO,CAAC,CAAC,CAaX,CAAA;AAED,qBAAa,YAAa,SAAQ,KAAK;gBACzB,OAAO,EAAE,MAAM;CAI5B"}
1
+ {"version":3,"file":"withTimeout.d.ts","sourceRoot":"","sources":["../../src/helpers/withTimeout.ts"],"names":[],"mappings":"AACA;;;GAGG;AACH,eAAO,MAAM,WAAW,GAAU,CAAC,EACjC,SAAS,OAAO,CAAC,CAAC,CAAC,EACnB,GAAG,MAAM,KACR,OAAO,CAAC,CAAC,CAaX,CAAA;AAED,qBAAa,YAAa,SAAQ,KAAK;gBACzB,OAAO,EAAE,MAAM;CAI5B"}
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";
@@ -38,6 +38,7 @@ export * as cbor from "./helpers/cbor.js";
38
38
  export type { DocHandleChangePayload, DocHandleDeletePayload, DocHandleEncodedChangePayload, DocHandleEphemeralMessagePayload, DocHandleRemoteHeadsPayload, DocHandleEvents, DocHandleOptions, DocHandleOutboundEphemeralMessagePayload, HandleState, } from "./DocHandle.js";
39
39
  export type { DeleteDocumentPayload, DocumentPayload, RepoConfig, RepoEvents, SharePolicy, } from "./Repo.js";
40
40
  export type { NetworkAdapterEvents, OpenPayload, PeerCandidatePayload, PeerDisconnectedPayload, PeerMetadata, } from "./network/NetworkAdapterInterface.js";
41
+ export type { NetworkSubsystemEvents, PeerPayload, } from "./network/NetworkSubsystem.js";
41
42
  export type { DocumentUnavailableMessage, EphemeralMessage, Message, RepoMessage, RequestMessage, SyncMessage, } from "./network/messages.js";
42
43
  export type { Chunk, ChunkInfo, ChunkType, StorageKey, StorageId, } from "./storage/types.js";
43
44
  export * from "./types.js";
@@ -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,GACrB,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"}
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,sBAAsB,EACtB,WAAW,GACZ,MAAM,+BAA+B,CAAA;AAEtC,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";
@@ -1 +1 @@
1
- {"version":3,"file":"messages.d.ts","sourceRoot":"","sources":["../../src/network/messages.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"messages.d.ts","sourceRoot":"","sources":["../../src/network/messages.ts"],"names":[],"mappings":"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"}
@@ -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>;
@@ -30,6 +39,10 @@ export declare class StorageSubsystem {
30
39
  namespace: string,
31
40
  /** Key to remove. Typically a UUID or other unique identifier, but could be any string. */
32
41
  key: string): Promise<void>;
42
+ /**
43
+ * Loads and combines document chunks from storage, with snapshots first.
44
+ */
45
+ loadDocData(documentId: DocumentId): Promise<Uint8Array | null>;
33
46
  /**
34
47
  * Loads the Automerge document with the given ID from storage.
35
48
  */
@@ -49,4 +62,5 @@ export declare class StorageSubsystem {
49
62
  loadSyncState(documentId: DocumentId, storageId: StorageId): Promise<A.SyncState | undefined>;
50
63
  saveSyncState(documentId: DocumentId, storageId: StorageId, syncState: A.SyncState): Promise<void>;
51
64
  }
65
+ export {};
52
66
  //# 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;AAK7D;;;GAGG;AACH,qBAAa,gBAAgB;;gBAef,cAAc,EAAE,uBAAuB;IAI7C,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;IAmClE;;;;;;OAMG;IACG,OAAO,CAAC,UAAU,EAAE,UAAU,EAAE,GAAG,EAAE,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAazE;;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"}
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;AAG7D,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,WAAW,CAAC,UAAU,EAAE,UAAU,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;IAgDrE;;OAEG;IACG,OAAO,CAAC,CAAC,EAAE,UAAU,EAAE,UAAU,GAAG,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;IAqBlE;;;;;;OAMG;IACG,OAAO,CAAC,UAAU,EAAE,UAAU,EAAE,GAAG,EAAE,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAezE;;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"}
@@ -3,13 +3,14 @@ import debug from "debug";
3
3
  import { headsAreSame } from "../helpers/headsAreSame.js";
4
4
  import { mergeArrays } from "../helpers/mergeArrays.js";
5
5
  import { keyHash, headsHash } from "./keyHash.js";
6
- import { chunkTypeFromKey } from "./chunkTypeFromKey.js";
7
6
  import * as Uuid from "uuid";
7
+ import { EventEmitter } from "eventemitter3";
8
+ import { encodeHeads } from "../AutomergeUrl.js";
8
9
  /**
9
10
  * The storage subsystem is responsible for saving and loading Automerge documents to and from
10
11
  * storage adapter. It also provides a generic key/value storage interface for other uses.
11
12
  */
12
- export class StorageSubsystem {
13
+ export class StorageSubsystem extends EventEmitter {
13
14
  /** The storage adapter to use for saving and loading documents */
14
15
  #storageAdapter;
15
16
  /** Record of the latest heads we've loaded or saved for each document */
@@ -20,6 +21,7 @@ export class StorageSubsystem {
20
21
  #compacting = false;
21
22
  #log = debug(`automerge-repo:storage-subsystem`);
22
23
  constructor(storageAdapter) {
24
+ super();
23
25
  this.#storageAdapter = storageAdapter;
24
26
  }
25
27
  async id() {
@@ -73,34 +75,68 @@ export class StorageSubsystem {
73
75
  }
74
76
  // AUTOMERGE DOCUMENT STORAGE
75
77
  /**
76
- * Loads the Automerge document with the given ID from storage.
78
+ * Loads and combines document chunks from storage, with snapshots first.
77
79
  */
78
- async loadDoc(documentId) {
79
- // Load all the chunks for this document
80
- const chunks = await this.#storageAdapter.loadRange([documentId]);
80
+ async loadDocData(documentId) {
81
+ // Load snapshots first
82
+ const snapshotChunks = await this.#storageAdapter.loadRange([
83
+ documentId,
84
+ "snapshot",
85
+ ]);
86
+ const incrementalChunks = await this.#storageAdapter.loadRange([
87
+ documentId,
88
+ "incremental",
89
+ ]);
81
90
  const binaries = [];
82
91
  const chunkInfos = [];
83
- for (const chunk of chunks) {
84
- // chunks might have been deleted in the interim
92
+ // Process snapshots first
93
+ for (const chunk of snapshotChunks) {
85
94
  if (chunk.data === undefined)
86
95
  continue;
87
- const chunkType = chunkTypeFromKey(chunk.key);
88
- if (chunkType == null)
96
+ chunkInfos.push({
97
+ key: chunk.key,
98
+ type: "snapshot",
99
+ size: chunk.data.length,
100
+ });
101
+ binaries.push(chunk.data);
102
+ }
103
+ // Then process incrementals
104
+ for (const chunk of incrementalChunks) {
105
+ if (chunk.data === undefined)
89
106
  continue;
90
107
  chunkInfos.push({
91
108
  key: chunk.key,
92
- type: chunkType,
109
+ type: "incremental",
93
110
  size: chunk.data.length,
94
111
  });
95
112
  binaries.push(chunk.data);
96
113
  }
114
+ // Store chunk infos for future reference
97
115
  this.#chunkInfos.set(documentId, chunkInfos);
116
+ // If no chunks were found, return null
117
+ if (binaries.length === 0) {
118
+ return null;
119
+ }
98
120
  // Merge the chunks into a single binary
99
- const binary = mergeArrays(binaries);
100
- if (binary.length === 0)
121
+ return mergeArrays(binaries);
122
+ }
123
+ /**
124
+ * Loads the Automerge document with the given ID from storage.
125
+ */
126
+ async loadDoc(documentId) {
127
+ // Load and combine chunks
128
+ const binary = await this.loadDocData(documentId);
129
+ if (!binary)
101
130
  return null;
102
131
  // Load into an Automerge document
132
+ const start = performance.now();
103
133
  const newDoc = A.loadIncremental(A.init(), binary);
134
+ const end = performance.now();
135
+ this.emit("document-loaded", {
136
+ documentId,
137
+ durationMillis: end - start,
138
+ ...A.stats(newDoc),
139
+ });
104
140
  // Record the latest heads for the document
105
141
  this.#storedHeads.set(documentId, A.getHeads(newDoc));
106
142
  return newDoc;
@@ -201,7 +237,7 @@ export class StorageSubsystem {
201
237
  return true;
202
238
  }
203
239
  const newHeads = A.getHeads(doc);
204
- if (headsAreSame(newHeads, oldHeads)) {
240
+ if (headsAreSame(encodeHeads(newHeads), encodeHeads(oldHeads))) {
205
241
  // the document hasn't changed
206
242
  return false;
207
243
  }
@@ -1,6 +1,7 @@
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";
4
5
  import { DocSynchronizer } from "./DocSynchronizer.js";
5
6
  import { Synchronizer } from "./Synchronizer.js";
6
7
  /** A CollectionSynchronizer is responsible for synchronizing a DocCollection with peers. */
@@ -10,7 +11,7 @@ export declare class CollectionSynchronizer extends Synchronizer {
10
11
  /** A map of documentIds to their synchronizers */
11
12
  /** @hidden */
12
13
  docSynchronizers: Record<DocumentId, DocSynchronizer>;
13
- constructor(repo: Repo);
14
+ constructor(repo: Repo, denylist?: AutomergeUrl[]);
14
15
  /**
15
16
  * When we receive a sync message for a document we haven't got in memory, we
16
17
  * register it with the repo and start synchronizing
@@ -19,7 +20,7 @@ export declare class CollectionSynchronizer extends Synchronizer {
19
20
  /**
20
21
  * Starts synchronizing the given document with all peers that we share it generously with.
21
22
  */
22
- addDocument(documentId: DocumentId): void;
23
+ addDocument(handle: DocHandle<unknown>): void;
23
24
  removeDocument(documentId: DocumentId): void;
24
25
  /** Adds a peer and maybe starts synchronizing with them */
25
26
  addPeer(peerId: PeerId): void;
@@ -1 +1 @@
1
- {"version":3,"file":"CollectionSynchronizer.d.ts","sourceRoot":"","sources":["../../src/synchronizer/CollectionSynchronizer.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAA;AACjC,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAA;AACnD,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAChD,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAA;AACtD,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAIhD,4FAA4F;AAC5F,qBAAa,sBAAuB,SAAQ,YAAY;;IAW1C,OAAO,CAAC,IAAI;IAPxB,kDAAkD;IAClD,cAAc;IACd,gBAAgB,EAAE,MAAM,CAAC,UAAU,EAAE,eAAe,CAAC,CAAK;gBAKtC,IAAI,EAAE,IAAI;IAqD9B;;;OAGG;IACG,cAAc,CAAC,OAAO,EAAE,UAAU;IAyBxC;;OAEG;IACH,WAAW,CAAC,UAAU,EAAE,UAAU;IAalC,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
+ {"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 { stringifyAutomergeUrl } from "../AutomergeUrl.js";
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");
@@ -13,22 +13,25 @@ export class CollectionSynchronizer extends Synchronizer {
13
13
  docSynchronizers = {};
14
14
  /** Used to determine if the document is know to the Collection and a synchronizer exists or is being set up */
15
15
  #docSetUp = {};
16
- constructor(repo) {
16
+ #denylist;
17
+ constructor(repo, denylist = []) {
17
18
  super();
18
19
  this.repo = repo;
20
+ this.#denylist = denylist.map(url => parseAutomergeUrl(url).documentId);
19
21
  }
20
22
  /** Returns a synchronizer for the given document, creating one if it doesn't already exist. */
21
- #fetchDocSynchronizer(documentId) {
22
- if (!this.docSynchronizers[documentId]) {
23
- const handle = this.repo.find(stringifyAutomergeUrl({ documentId }));
24
- this.docSynchronizers[documentId] = this.#initDocSynchronizer(handle);
23
+ #fetchDocSynchronizer(handle) {
24
+ if (!this.docSynchronizers[handle.documentId]) {
25
+ this.docSynchronizers[handle.documentId] =
26
+ this.#initDocSynchronizer(handle);
25
27
  }
26
- return this.docSynchronizers[documentId];
28
+ return this.docSynchronizers[handle.documentId];
27
29
  }
28
30
  /** Creates a new docSynchronizer and sets it up to propagate messages */
29
31
  #initDocSynchronizer(handle) {
30
32
  const docSynchronizer = new DocSynchronizer({
31
33
  handle,
34
+ peerId: this.repo.networkSubsystem.peerId,
32
35
  onLoadSyncState: async (peerId) => {
33
36
  if (!this.repo.storageSubsystem) {
34
37
  return;
@@ -43,6 +46,7 @@ export class CollectionSynchronizer extends Synchronizer {
43
46
  docSynchronizer.on("message", event => this.emit("message", event));
44
47
  docSynchronizer.on("open-doc", event => this.emit("open-doc", event));
45
48
  docSynchronizer.on("sync-state", event => this.emit("sync-state", event));
49
+ docSynchronizer.on("metrics", event => this.emit("metrics", event));
46
50
  return docSynchronizer;
47
51
  }
48
52
  /** returns an array of peerIds that we share this document generously with */
@@ -67,24 +71,39 @@ export class CollectionSynchronizer extends Synchronizer {
67
71
  if (!documentId) {
68
72
  throw new Error("received a message with an invalid documentId");
69
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
+ }
70
86
  this.#docSetUp[documentId] = true;
71
- const docSynchronizer = this.#fetchDocSynchronizer(documentId);
87
+ const handle = await this.repo.find(documentId, {
88
+ allowableStates: ["ready", "unavailable", "requesting"],
89
+ });
90
+ const docSynchronizer = this.#fetchDocSynchronizer(handle);
72
91
  docSynchronizer.receiveMessage(message);
73
92
  // Initiate sync with any new peers
74
93
  const peers = await this.#documentGenerousPeers(documentId);
75
- docSynchronizer.beginSync(peers.filter(peerId => !docSynchronizer.hasPeer(peerId)));
94
+ void docSynchronizer.beginSync(peers.filter(peerId => !docSynchronizer.hasPeer(peerId)));
76
95
  }
77
96
  /**
78
97
  * Starts synchronizing the given document with all peers that we share it generously with.
79
98
  */
80
- addDocument(documentId) {
99
+ addDocument(handle) {
81
100
  // HACK: this is a hack to prevent us from adding the same document twice
82
- if (this.#docSetUp[documentId]) {
101
+ if (this.#docSetUp[handle.documentId]) {
83
102
  return;
84
103
  }
85
- const docSynchronizer = this.#fetchDocSynchronizer(documentId);
86
- void this.#documentGenerousPeers(documentId).then(peers => {
87
- docSynchronizer.beginSync(peers);
104
+ const docSynchronizer = this.#fetchDocSynchronizer(handle);
105
+ void this.#documentGenerousPeers(handle.documentId).then(peers => {
106
+ void docSynchronizer.beginSync(peers);
88
107
  });
89
108
  }
90
109
  // TODO: implement this
@@ -103,7 +122,7 @@ export class CollectionSynchronizer extends Synchronizer {
103
122
  const { documentId } = docSynchronizer;
104
123
  void this.repo.sharePolicy(peerId, documentId).then(okToShare => {
105
124
  if (okToShare)
106
- docSynchronizer.beginSync([peerId]);
125
+ void docSynchronizer.beginSync([peerId]);
107
126
  });
108
127
  }
109
128
  }
@@ -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,11 +16,11 @@ 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;