@automerge/automerge-repo 2.0.0-alpha.1 → 2.0.0-alpha.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/DocHandle.d.ts +71 -2
- package/dist/DocHandle.d.ts.map +1 -1
- package/dist/DocHandle.js +116 -2
- package/dist/Repo.d.ts +24 -0
- package/dist/Repo.d.ts.map +1 -1
- package/dist/Repo.js +94 -57
- 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/entrypoints/slim.d.ts +1 -0
- package/dist/entrypoints/slim.d.ts.map +1 -1
- package/dist/entrypoints/slim.js +2 -0
- 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 +19 -39
- package/dist/storage/StorageSubsystem.d.ts +11 -1
- package/dist/storage/StorageSubsystem.d.ts.map +1 -1
- package/dist/storage/StorageSubsystem.js +18 -3
- package/dist/synchronizer/CollectionSynchronizer.d.ts +13 -0
- package/dist/synchronizer/CollectionSynchronizer.d.ts.map +1 -1
- package/dist/synchronizer/CollectionSynchronizer.js +13 -6
- package/dist/synchronizer/DocSynchronizer.d.ts +7 -0
- package/dist/synchronizer/DocSynchronizer.d.ts.map +1 -1
- package/dist/synchronizer/DocSynchronizer.js +14 -0
- package/dist/synchronizer/Synchronizer.d.ts +8 -0
- package/dist/synchronizer/Synchronizer.d.ts.map +1 -1
- package/package.json +3 -3
- package/src/DocHandle.ts +137 -4
- package/src/Repo.ts +123 -56
- package/src/entrypoints/fullfat.ts +1 -2
- package/src/entrypoints/slim.ts +2 -0
- package/src/helpers/tests/storage-adapter-tests.ts +31 -62
- package/src/storage/StorageSubsystem.ts +26 -3
- package/src/synchronizer/CollectionSynchronizer.ts +23 -6
- package/src/synchronizer/DocSynchronizer.ts +15 -0
- package/src/synchronizer/Synchronizer.ts +9 -0
- package/test/DocHandle.test.ts +141 -0
- package/test/Repo.test.ts +73 -0
- package/test/StorageSubsystem.test.ts +17 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"slim.d.ts","sourceRoot":"","sources":["../../src/entrypoints/slim.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAA;AAC3B,OAAO,EAAE,oBAAoB,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAA"}
|
|
1
|
+
{"version":3,"file":"slim.d.ts","sourceRoot":"","sources":["../../src/entrypoints/slim.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAA;AAC3B,OAAO,EAAE,oBAAoB,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAA;AAEhF,OAAO,KAAK,SAAS,MAAM,2BAA2B,CAAA"}
|
package/dist/entrypoints/slim.js
CHANGED
|
@@ -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,CA0I3E;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,55 +1,46 @@
|
|
|
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());
|
|
@@ -62,10 +53,8 @@ export function runStorageAdapterTests(_setup, title) {
|
|
|
62
53
|
{ key: ["AAAAA", "sync-state", "xxxxx"], data: PAYLOAD_A() },
|
|
63
54
|
{ key: ["AAAAA", "sync-state", "zzzzz"], data: PAYLOAD_C() },
|
|
64
55
|
]));
|
|
65
|
-
teardown();
|
|
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"]);
|
|
@@ -75,33 +64,27 @@ export function runStorageAdapterTests(_setup, title) {
|
|
|
75
64
|
expect(actual).toStrictEqual(expect.not.arrayContaining([
|
|
76
65
|
{ key: ["BBBBB", "sync-state", "zzzzz"], data: PAYLOAD_C() },
|
|
77
66
|
]));
|
|
78
|
-
teardown();
|
|
79
67
|
});
|
|
80
68
|
});
|
|
81
69
|
describe("save and remove", () => {
|
|
82
|
-
it("after removing, should be empty", async () => {
|
|
83
|
-
const { adapter, teardown } = await setup();
|
|
70
|
+
it("after removing, should be empty", async ({ adapter }) => {
|
|
84
71
|
await adapter.save(["AAAAA", "snapshot", "xxxxx"], PAYLOAD_A());
|
|
85
72
|
await adapter.remove(["AAAAA", "snapshot", "xxxxx"]);
|
|
86
73
|
expect(await adapter.loadRange(["AAAAA"])).toStrictEqual([]);
|
|
87
74
|
expect(await adapter.load(["AAAAA", "snapshot", "xxxxx"])).toBeUndefined();
|
|
88
|
-
teardown();
|
|
89
75
|
});
|
|
90
76
|
});
|
|
91
77
|
describe("save and save", () => {
|
|
92
|
-
it("should overwrite data saved with the same key", async () => {
|
|
93
|
-
const { adapter, teardown } = await setup();
|
|
78
|
+
it("should overwrite data saved with the same key", async ({ adapter, }) => {
|
|
94
79
|
await adapter.save(["AAAAA", "sync-state", "xxxxx"], PAYLOAD_A());
|
|
95
80
|
await adapter.save(["AAAAA", "sync-state", "xxxxx"], PAYLOAD_B());
|
|
96
81
|
expect(await adapter.loadRange(["AAAAA", "sync-state"])).toStrictEqual([
|
|
97
82
|
{ key: ["AAAAA", "sync-state", "xxxxx"], data: PAYLOAD_B() },
|
|
98
83
|
]);
|
|
99
|
-
teardown();
|
|
100
84
|
});
|
|
101
85
|
});
|
|
102
86
|
describe("removeRange", () => {
|
|
103
|
-
it("should remove a range of records", async () => {
|
|
104
|
-
const { adapter, teardown } = await setup();
|
|
87
|
+
it("should remove a range of records", async ({ adapter }) => {
|
|
105
88
|
await adapter.save(["AAAAA", "sync-state", "xxxxx"], PAYLOAD_A());
|
|
106
89
|
await adapter.save(["AAAAA", "snapshot", "yyyyy"], PAYLOAD_B());
|
|
107
90
|
await adapter.save(["AAAAA", "sync-state", "zzzzz"], PAYLOAD_C());
|
|
@@ -109,10 +92,8 @@ export function runStorageAdapterTests(_setup, title) {
|
|
|
109
92
|
expect(await adapter.loadRange(["AAAAA"])).toStrictEqual([
|
|
110
93
|
{ key: ["AAAAA", "snapshot", "yyyyy"], data: PAYLOAD_B() },
|
|
111
94
|
]);
|
|
112
|
-
teardown();
|
|
113
95
|
});
|
|
114
|
-
it("should not remove records that don't match", async () => {
|
|
115
|
-
const { adapter, teardown } = await setup();
|
|
96
|
+
it("should not remove records that don't match", async ({ adapter }) => {
|
|
116
97
|
await adapter.save(["AAAAA", "sync-state", "xxxxx"], PAYLOAD_A());
|
|
117
98
|
await adapter.save(["BBBBB", "sync-state", "zzzzz"], PAYLOAD_B());
|
|
118
99
|
await adapter.removeRange(["AAAAA"]);
|
|
@@ -120,7 +101,6 @@ export function runStorageAdapterTests(_setup, title) {
|
|
|
120
101
|
expect(actual).toStrictEqual([
|
|
121
102
|
{ key: ["BBBBB", "sync-state", "zzzzz"], data: PAYLOAD_B() },
|
|
122
103
|
]);
|
|
123
|
-
teardown();
|
|
124
104
|
});
|
|
125
105
|
});
|
|
126
106
|
});
|
|
@@ -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;AAE5C,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;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"}
|
|
@@ -5,11 +5,12 @@ 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";
|
|
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() {
|
|
@@ -100,7 +102,14 @@ export class StorageSubsystem {
|
|
|
100
102
|
if (binary.length === 0)
|
|
101
103
|
return null;
|
|
102
104
|
// Load into an Automerge document
|
|
105
|
+
const start = performance.now();
|
|
103
106
|
const newDoc = A.loadIncremental(A.init(), binary);
|
|
107
|
+
const end = performance.now();
|
|
108
|
+
this.emit("document-loaded", {
|
|
109
|
+
documentId,
|
|
110
|
+
durationMillis: end - start,
|
|
111
|
+
...A.stats(newDoc),
|
|
112
|
+
});
|
|
104
113
|
// Record the latest heads for the document
|
|
105
114
|
this.#storedHeads.set(documentId, A.getHeads(newDoc));
|
|
106
115
|
return newDoc;
|
|
@@ -178,8 +187,14 @@ export class StorageSubsystem {
|
|
|
178
187
|
}
|
|
179
188
|
async loadSyncState(documentId, storageId) {
|
|
180
189
|
const key = [documentId, "sync-state", storageId];
|
|
181
|
-
|
|
182
|
-
|
|
190
|
+
try {
|
|
191
|
+
const loaded = await this.#storageAdapter.load(key);
|
|
192
|
+
return loaded ? A.decodeSyncState(loaded) : undefined;
|
|
193
|
+
}
|
|
194
|
+
catch (e) {
|
|
195
|
+
this.#log(`Error loading sync state for ${documentId} from ${storageId}`);
|
|
196
|
+
return undefined;
|
|
197
|
+
}
|
|
183
198
|
}
|
|
184
199
|
async saveSyncState(documentId, storageId, syncState) {
|
|
185
200
|
const key = [documentId, "sync-state", storageId];
|
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
import { Repo } from "../Repo.js";
|
|
2
2
|
import { DocMessage } from "../network/messages.js";
|
|
3
3
|
import { DocumentId, PeerId } from "../types.js";
|
|
4
|
+
import { DocSynchronizer } from "./DocSynchronizer.js";
|
|
4
5
|
import { Synchronizer } from "./Synchronizer.js";
|
|
5
6
|
/** A CollectionSynchronizer is responsible for synchronizing a DocCollection with peers. */
|
|
6
7
|
export declare class CollectionSynchronizer extends Synchronizer {
|
|
7
8
|
#private;
|
|
8
9
|
private repo;
|
|
10
|
+
/** A map of documentIds to their synchronizers */
|
|
11
|
+
/** @hidden */
|
|
12
|
+
docSynchronizers: Record<DocumentId, DocSynchronizer>;
|
|
9
13
|
constructor(repo: Repo);
|
|
10
14
|
/**
|
|
11
15
|
* When we receive a sync message for a document we haven't got in memory, we
|
|
@@ -23,5 +27,14 @@ export declare class CollectionSynchronizer extends Synchronizer {
|
|
|
23
27
|
removePeer(peerId: PeerId): void;
|
|
24
28
|
/** Returns a list of all connected peer ids */
|
|
25
29
|
get peers(): PeerId[];
|
|
30
|
+
metrics(): {
|
|
31
|
+
[key: string]: {
|
|
32
|
+
peers: PeerId[];
|
|
33
|
+
size: {
|
|
34
|
+
numOps: number;
|
|
35
|
+
numChanges: number;
|
|
36
|
+
};
|
|
37
|
+
};
|
|
38
|
+
};
|
|
26
39
|
}
|
|
27
40
|
//# sourceMappingURL=CollectionSynchronizer.d.ts.map
|
|
@@ -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;
|
|
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;IAsD9B;;;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"}
|
|
@@ -9,7 +9,8 @@ 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
|
constructor(repo) {
|
|
@@ -18,11 +19,11 @@ export class CollectionSynchronizer extends Synchronizer {
|
|
|
18
19
|
}
|
|
19
20
|
/** Returns a synchronizer for the given document, creating one if it doesn't already exist. */
|
|
20
21
|
#fetchDocSynchronizer(documentId) {
|
|
21
|
-
if (!this
|
|
22
|
+
if (!this.docSynchronizers[documentId]) {
|
|
22
23
|
const handle = this.repo.find(stringifyAutomergeUrl({ documentId }));
|
|
23
|
-
this
|
|
24
|
+
this.docSynchronizers[documentId] = this.#initDocSynchronizer(handle);
|
|
24
25
|
}
|
|
25
|
-
return this
|
|
26
|
+
return this.docSynchronizers[documentId];
|
|
26
27
|
}
|
|
27
28
|
/** Creates a new docSynchronizer and sets it up to propagate messages */
|
|
28
29
|
#initDocSynchronizer(handle) {
|
|
@@ -42,6 +43,7 @@ export class CollectionSynchronizer extends Synchronizer {
|
|
|
42
43
|
docSynchronizer.on("message", event => this.emit("message", event));
|
|
43
44
|
docSynchronizer.on("open-doc", event => this.emit("open-doc", event));
|
|
44
45
|
docSynchronizer.on("sync-state", event => this.emit("sync-state", event));
|
|
46
|
+
docSynchronizer.on("metrics", event => this.emit("metrics", event));
|
|
45
47
|
return docSynchronizer;
|
|
46
48
|
}
|
|
47
49
|
/** returns an array of peerIds that we share this document generously with */
|
|
@@ -98,7 +100,7 @@ export class CollectionSynchronizer extends Synchronizer {
|
|
|
98
100
|
return;
|
|
99
101
|
}
|
|
100
102
|
this.#peers.add(peerId);
|
|
101
|
-
for (const docSynchronizer of Object.values(this
|
|
103
|
+
for (const docSynchronizer of Object.values(this.docSynchronizers)) {
|
|
102
104
|
const { documentId } = docSynchronizer;
|
|
103
105
|
void this.repo.sharePolicy(peerId, documentId).then(okToShare => {
|
|
104
106
|
if (okToShare)
|
|
@@ -110,7 +112,7 @@ export class CollectionSynchronizer extends Synchronizer {
|
|
|
110
112
|
removePeer(peerId) {
|
|
111
113
|
log(`removing peer ${peerId}`);
|
|
112
114
|
this.#peers.delete(peerId);
|
|
113
|
-
for (const docSynchronizer of Object.values(this
|
|
115
|
+
for (const docSynchronizer of Object.values(this.docSynchronizers)) {
|
|
114
116
|
docSynchronizer.endSync(peerId);
|
|
115
117
|
}
|
|
116
118
|
}
|
|
@@ -118,4 +120,9 @@ export class CollectionSynchronizer extends Synchronizer {
|
|
|
118
120
|
get peers() {
|
|
119
121
|
return Array.from(this.#peers);
|
|
120
122
|
}
|
|
123
|
+
metrics() {
|
|
124
|
+
return Object.fromEntries(Object.entries(this.docSynchronizers).map(([documentId, synchronizer]) => {
|
|
125
|
+
return [documentId, synchronizer.metrics()];
|
|
126
|
+
}));
|
|
127
|
+
}
|
|
121
128
|
}
|
|
@@ -24,6 +24,13 @@ export declare class DocSynchronizer extends Synchronizer {
|
|
|
24
24
|
receiveMessage(message: RepoMessage): void;
|
|
25
25
|
receiveEphemeralMessage(message: EphemeralMessage): void;
|
|
26
26
|
receiveSyncMessage(message: SyncMessage | RequestMessage): void;
|
|
27
|
+
metrics(): {
|
|
28
|
+
peers: PeerId[];
|
|
29
|
+
size: {
|
|
30
|
+
numOps: number;
|
|
31
|
+
numChanges: number;
|
|
32
|
+
};
|
|
33
|
+
};
|
|
27
34
|
}
|
|
28
35
|
export {};
|
|
29
36
|
//# 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;gBAsBV,EAAE,MAAM,EAAE,eAAe,EAAE,EAAE,qBAAqB;IAyB9D,IAAI,UAAU,uCAEb;IAED,IAAI,UAAU,qCAEb;IAkID,OAAO,CAAC,MAAM,EAAE,MAAM;IAItB,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE;IAmD3B,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;
|
|
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;gBAsBV,EAAE,MAAM,EAAE,eAAe,EAAE,EAAE,qBAAqB;IAyB9D,IAAI,UAAU,uCAEb;IAED,IAAI,UAAU,qCAEb;IAkID,OAAO,CAAC,MAAM,EAAE,MAAM;IAItB,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE;IAmD3B,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;IAuFxD,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"}
|
|
@@ -252,7 +252,15 @@ export class DocSynchronizer extends Synchronizer {
|
|
|
252
252
|
}
|
|
253
253
|
this.#withSyncState(message.senderId, syncState => {
|
|
254
254
|
this.#handle.update(doc => {
|
|
255
|
+
const start = performance.now();
|
|
255
256
|
const [newDoc, newSyncState] = A.receiveSyncMessage(doc, syncState, message.data);
|
|
257
|
+
const end = performance.now();
|
|
258
|
+
this.emit("metrics", {
|
|
259
|
+
type: "receive-sync-message",
|
|
260
|
+
documentId: this.#handle.documentId,
|
|
261
|
+
durationMillis: end - start,
|
|
262
|
+
...A.stats(doc),
|
|
263
|
+
});
|
|
256
264
|
this.#setSyncState(message.senderId, newSyncState);
|
|
257
265
|
// respond to just this peer (as required)
|
|
258
266
|
this.#sendSyncMessage(message.senderId, doc);
|
|
@@ -286,4 +294,10 @@ export class DocSynchronizer extends Synchronizer {
|
|
|
286
294
|
}
|
|
287
295
|
this.#pendingSyncMessages = [];
|
|
288
296
|
}
|
|
297
|
+
metrics() {
|
|
298
|
+
return {
|
|
299
|
+
peers: this.#peers,
|
|
300
|
+
size: this.#handle.metrics(),
|
|
301
|
+
};
|
|
302
|
+
}
|
|
289
303
|
}
|
|
@@ -9,6 +9,7 @@ export interface SynchronizerEvents {
|
|
|
9
9
|
message: (payload: MessageContents) => void;
|
|
10
10
|
"sync-state": (payload: SyncStatePayload) => void;
|
|
11
11
|
"open-doc": (arg: OpenDocMessage) => void;
|
|
12
|
+
metrics: (arg: DocSyncMetrics) => void;
|
|
12
13
|
}
|
|
13
14
|
/** Notify the repo that the sync state has changed */
|
|
14
15
|
export interface SyncStatePayload {
|
|
@@ -16,4 +17,11 @@ export interface SyncStatePayload {
|
|
|
16
17
|
documentId: DocumentId;
|
|
17
18
|
syncState: SyncState;
|
|
18
19
|
}
|
|
20
|
+
export type DocSyncMetrics = {
|
|
21
|
+
type: "receive-sync-message";
|
|
22
|
+
documentId: DocumentId;
|
|
23
|
+
durationMillis: number;
|
|
24
|
+
numOps: number;
|
|
25
|
+
numChanges: number;
|
|
26
|
+
};
|
|
19
27
|
//# sourceMappingURL=Synchronizer.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Synchronizer.d.ts","sourceRoot":"","sources":["../../src/synchronizer/Synchronizer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAC5C,OAAO,EACL,eAAe,EACf,cAAc,EACd,WAAW,EACZ,MAAM,wBAAwB,CAAA;AAC/B,OAAO,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAA;AACrD,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAEhD,8BAAsB,YAAa,SAAQ,YAAY,CAAC,kBAAkB,CAAC;IACzE,QAAQ,CAAC,cAAc,CAAC,OAAO,EAAE,WAAW,GAAG,IAAI;CACpD;AAED,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,CAAC,OAAO,EAAE,eAAe,KAAK,IAAI,CAAA;IAC3C,YAAY,EAAE,CAAC,OAAO,EAAE,gBAAgB,KAAK,IAAI,CAAA;IACjD,UAAU,EAAE,CAAC,GAAG,EAAE,cAAc,KAAK,IAAI,CAAA;
|
|
1
|
+
{"version":3,"file":"Synchronizer.d.ts","sourceRoot":"","sources":["../../src/synchronizer/Synchronizer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAC5C,OAAO,EACL,eAAe,EACf,cAAc,EACd,WAAW,EACZ,MAAM,wBAAwB,CAAA;AAC/B,OAAO,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAA;AACrD,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAEhD,8BAAsB,YAAa,SAAQ,YAAY,CAAC,kBAAkB,CAAC;IACzE,QAAQ,CAAC,cAAc,CAAC,OAAO,EAAE,WAAW,GAAG,IAAI;CACpD;AAED,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,CAAC,OAAO,EAAE,eAAe,KAAK,IAAI,CAAA;IAC3C,YAAY,EAAE,CAAC,OAAO,EAAE,gBAAgB,KAAK,IAAI,CAAA;IACjD,UAAU,EAAE,CAAC,GAAG,EAAE,cAAc,KAAK,IAAI,CAAA;IACzC,OAAO,EAAE,CAAC,GAAG,EAAE,cAAc,KAAK,IAAI,CAAA;CACvC;AAED,uDAAuD;AACvD,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAA;IACd,UAAU,EAAE,UAAU,CAAA;IACtB,SAAS,EAAE,SAAS,CAAA;CACrB;AAED,MAAM,MAAM,cAAc,GAAG;IAC3B,IAAI,EAAE,sBAAsB,CAAA;IAC5B,UAAU,EAAE,UAAU,CAAA;IACtB,cAAc,EAAE,MAAM,CAAA;IACtB,MAAM,EAAE,MAAM,CAAA;IACd,UAAU,EAAE,MAAM,CAAA;CACnB,CAAA"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@automerge/automerge-repo",
|
|
3
|
-
"version": "2.0.0-alpha.
|
|
3
|
+
"version": "2.0.0-alpha.12",
|
|
4
4
|
"description": "A repository object to manage a collection of automerge documents",
|
|
5
5
|
"repository": "https://github.com/automerge/automerge-repo/tree/master/packages/automerge-repo",
|
|
6
6
|
"author": "Peter van Hardenberg <pvh@pvh.ca>",
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
"vite": "^5.0.8"
|
|
24
24
|
},
|
|
25
25
|
"dependencies": {
|
|
26
|
-
"@automerge/automerge": "^2.2.
|
|
26
|
+
"@automerge/automerge": "^2.2.8",
|
|
27
27
|
"bs58check": "^3.0.1",
|
|
28
28
|
"cbor-x": "^1.3.0",
|
|
29
29
|
"debug": "^4.3.4",
|
|
@@ -60,5 +60,5 @@
|
|
|
60
60
|
"publishConfig": {
|
|
61
61
|
"access": "public"
|
|
62
62
|
},
|
|
63
|
-
"gitHead": "
|
|
63
|
+
"gitHead": "8b016e42d2518ebb11eb148f52b9fb9a0b4467ff"
|
|
64
64
|
}
|