@automerge/automerge-repo 2.0.0-alpha.6 → 2.0.0-collectionsync-alpha.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.
- package/dist/CollectionHandle.d.ts +14 -0
- package/dist/CollectionHandle.d.ts.map +1 -0
- package/dist/CollectionHandle.js +37 -0
- package/dist/DocHandle.d.ts +67 -2
- package/dist/DocHandle.d.ts.map +1 -1
- package/dist/DocHandle.js +113 -2
- package/dist/DocUrl.d.ts +47 -0
- package/dist/DocUrl.d.ts.map +1 -0
- package/dist/DocUrl.js +72 -0
- package/dist/EphemeralData.d.ts +20 -0
- package/dist/EphemeralData.d.ts.map +1 -0
- package/dist/EphemeralData.js +1 -0
- package/dist/Repo.d.ts +28 -7
- package/dist/Repo.d.ts.map +1 -1
- package/dist/Repo.js +142 -143
- package/dist/ferigan.d.ts +51 -0
- package/dist/ferigan.d.ts.map +1 -0
- package/dist/ferigan.js +98 -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/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/network/NetworkSubsystem.d.ts +1 -0
- package/dist/network/NetworkSubsystem.d.ts.map +1 -1
- package/dist/network/NetworkSubsystem.js +3 -0
- package/dist/network/messages.d.ts +7 -1
- package/dist/network/messages.d.ts.map +1 -1
- package/dist/network/messages.js +2 -1
- package/dist/src/DocHandle.d.ts +182 -0
- package/dist/src/DocHandle.d.ts.map +1 -0
- package/dist/src/DocHandle.js +405 -0
- package/dist/src/DocUrl.d.ts +49 -0
- package/dist/src/DocUrl.d.ts.map +1 -0
- package/dist/src/DocUrl.js +72 -0
- package/dist/src/EphemeralData.d.ts +19 -0
- package/dist/src/EphemeralData.d.ts.map +1 -0
- package/dist/src/EphemeralData.js +1 -0
- package/dist/src/Repo.d.ts +74 -0
- package/dist/src/Repo.d.ts.map +1 -0
- package/dist/src/Repo.js +208 -0
- package/dist/src/helpers/arraysAreEqual.d.ts +2 -0
- package/dist/src/helpers/arraysAreEqual.d.ts.map +1 -0
- package/dist/src/helpers/arraysAreEqual.js +2 -0
- package/dist/src/helpers/cbor.d.ts +4 -0
- package/dist/src/helpers/cbor.d.ts.map +1 -0
- package/dist/src/helpers/cbor.js +8 -0
- package/dist/src/helpers/eventPromise.d.ts +11 -0
- package/dist/src/helpers/eventPromise.d.ts.map +1 -0
- package/dist/src/helpers/eventPromise.js +7 -0
- package/dist/src/helpers/headsAreSame.d.ts +2 -0
- package/dist/src/helpers/headsAreSame.d.ts.map +1 -0
- package/dist/src/helpers/headsAreSame.js +4 -0
- package/dist/src/helpers/mergeArrays.d.ts +2 -0
- package/dist/src/helpers/mergeArrays.d.ts.map +1 -0
- package/dist/src/helpers/mergeArrays.js +15 -0
- package/dist/src/helpers/pause.d.ts +6 -0
- package/dist/src/helpers/pause.d.ts.map +1 -0
- package/dist/src/helpers/pause.js +10 -0
- package/dist/src/helpers/tests/network-adapter-tests.d.ts +21 -0
- package/dist/src/helpers/tests/network-adapter-tests.d.ts.map +1 -0
- package/dist/src/helpers/tests/network-adapter-tests.js +122 -0
- package/dist/src/helpers/withTimeout.d.ts +12 -0
- package/dist/src/helpers/withTimeout.d.ts.map +1 -0
- package/dist/src/helpers/withTimeout.js +24 -0
- package/dist/src/index.d.ts +53 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +40 -0
- package/dist/src/network/NetworkAdapter.d.ts +26 -0
- package/dist/src/network/NetworkAdapter.d.ts.map +1 -0
- package/dist/src/network/NetworkAdapter.js +4 -0
- package/dist/src/network/NetworkSubsystem.d.ts +23 -0
- package/dist/src/network/NetworkSubsystem.d.ts.map +1 -0
- package/dist/src/network/NetworkSubsystem.js +120 -0
- package/dist/src/network/messages.d.ts +85 -0
- package/dist/src/network/messages.d.ts.map +1 -0
- package/dist/src/network/messages.js +23 -0
- package/dist/src/storage/StorageAdapter.d.ts +14 -0
- package/dist/src/storage/StorageAdapter.d.ts.map +1 -0
- package/dist/src/storage/StorageAdapter.js +1 -0
- package/dist/src/storage/StorageSubsystem.d.ts +12 -0
- package/dist/src/storage/StorageSubsystem.d.ts.map +1 -0
- package/dist/src/storage/StorageSubsystem.js +145 -0
- package/dist/src/synchronizer/CollectionSynchronizer.d.ts +25 -0
- package/dist/src/synchronizer/CollectionSynchronizer.d.ts.map +1 -0
- package/dist/src/synchronizer/CollectionSynchronizer.js +106 -0
- package/dist/src/synchronizer/DocSynchronizer.d.ts +29 -0
- package/dist/src/synchronizer/DocSynchronizer.d.ts.map +1 -0
- package/dist/src/synchronizer/DocSynchronizer.js +263 -0
- package/dist/src/synchronizer/Synchronizer.d.ts +9 -0
- package/dist/src/synchronizer/Synchronizer.d.ts.map +1 -0
- package/dist/src/synchronizer/Synchronizer.js +2 -0
- package/dist/src/types.d.ts +16 -0
- package/dist/src/types.d.ts.map +1 -0
- package/dist/src/types.js +1 -0
- package/dist/storage/StorageAdapter.d.ts +9 -0
- package/dist/storage/StorageAdapter.d.ts.map +1 -1
- package/dist/storage/StorageAdapter.js +33 -0
- package/dist/storage/StorageSubsystem.d.ts +12 -2
- package/dist/storage/StorageSubsystem.d.ts.map +1 -1
- package/dist/storage/StorageSubsystem.js +42 -100
- package/dist/synchronizer/CollectionSynchronizer.d.ts +4 -2
- package/dist/synchronizer/CollectionSynchronizer.d.ts.map +1 -1
- package/dist/synchronizer/CollectionSynchronizer.js +28 -15
- package/dist/synchronizer/DocSynchronizer.d.ts +6 -5
- package/dist/synchronizer/DocSynchronizer.d.ts.map +1 -1
- package/dist/synchronizer/DocSynchronizer.js +76 -178
- package/dist/synchronizer/Synchronizer.d.ts +11 -0
- package/dist/synchronizer/Synchronizer.d.ts.map +1 -1
- package/dist/test/CollectionSynchronizer.test.d.ts +2 -0
- package/dist/test/CollectionSynchronizer.test.d.ts.map +1 -0
- package/dist/test/CollectionSynchronizer.test.js +57 -0
- package/dist/test/DocHandle.test.d.ts +2 -0
- package/dist/test/DocHandle.test.d.ts.map +1 -0
- package/dist/test/DocHandle.test.js +238 -0
- package/dist/test/DocSynchronizer.test.d.ts +2 -0
- package/dist/test/DocSynchronizer.test.d.ts.map +1 -0
- package/dist/test/DocSynchronizer.test.js +111 -0
- package/dist/test/Network.test.d.ts +2 -0
- package/dist/test/Network.test.d.ts.map +1 -0
- package/dist/test/Network.test.js +11 -0
- package/dist/test/Repo.test.d.ts +2 -0
- package/dist/test/Repo.test.d.ts.map +1 -0
- package/dist/test/Repo.test.js +568 -0
- package/dist/test/StorageSubsystem.test.d.ts +2 -0
- package/dist/test/StorageSubsystem.test.d.ts.map +1 -0
- package/dist/test/StorageSubsystem.test.js +56 -0
- package/dist/test/helpers/DummyNetworkAdapter.d.ts +9 -0
- package/dist/test/helpers/DummyNetworkAdapter.d.ts.map +1 -0
- package/dist/test/helpers/DummyNetworkAdapter.js +15 -0
- package/dist/test/helpers/DummyStorageAdapter.d.ts +16 -0
- package/dist/test/helpers/DummyStorageAdapter.d.ts.map +1 -0
- package/dist/test/helpers/DummyStorageAdapter.js +33 -0
- package/dist/test/helpers/generate-large-object.d.ts +5 -0
- package/dist/test/helpers/generate-large-object.d.ts.map +1 -0
- package/dist/test/helpers/generate-large-object.js +9 -0
- package/dist/test/helpers/getRandomItem.d.ts +2 -0
- package/dist/test/helpers/getRandomItem.d.ts.map +1 -0
- package/dist/test/helpers/getRandomItem.js +4 -0
- package/dist/test/types.d.ts +4 -0
- package/dist/test/types.d.ts.map +1 -0
- package/dist/test/types.js +1 -0
- package/package.json +3 -3
- package/src/CollectionHandle.ts +54 -0
- package/src/DocHandle.ts +133 -4
- package/src/Repo.ts +192 -183
- package/src/ferigan.ts +184 -0
- package/src/helpers/tests/storage-adapter-tests.ts +31 -62
- package/src/index.ts +2 -0
- package/src/network/NetworkSubsystem.ts +4 -0
- package/src/network/messages.ts +11 -2
- package/src/storage/StorageAdapter.ts +42 -0
- package/src/storage/StorageSubsystem.ts +59 -119
- package/src/synchronizer/CollectionSynchronizer.ts +34 -26
- package/src/synchronizer/DocSynchronizer.ts +84 -231
- package/src/synchronizer/Synchronizer.ts +14 -0
- package/test/CollectionSynchronizer.test.ts +4 -2
- package/test/DocHandle.test.ts +141 -0
- package/test/DocSynchronizer.test.ts +6 -1
- package/test/RemoteHeadsSubscriptions.test.ts +1 -1
- package/test/Repo.test.ts +225 -117
- package/test/StorageSubsystem.test.ts +20 -16
- package/test/remoteHeads.test.ts +1 -1
package/src/ferigan.ts
ADDED
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import { EventEmitter } from "eventemitter3"
|
|
2
|
+
import { MessageContents, RepoMessage } from "./network/messages.js"
|
|
3
|
+
import { AutomergeUrl } from "./types.js"
|
|
4
|
+
import { Repo } from "./Repo.js"
|
|
5
|
+
|
|
6
|
+
const URL_RE = /automerge:([\w\d+/=]+)(?:\?([\w\d+/=&]*))*/
|
|
7
|
+
|
|
8
|
+
export interface Ferigan extends EventEmitter<FeriganEvents> {
|
|
9
|
+
receiveMessage(message: RepoMessage): Promise<void>
|
|
10
|
+
load(
|
|
11
|
+
doc: ChangeLogId,
|
|
12
|
+
since: ChangeHash[]
|
|
13
|
+
): AsyncIterableIterator<Progress<ChangeLog | undefined>>
|
|
14
|
+
loadCollection(
|
|
15
|
+
doc: ChangeLogId
|
|
16
|
+
): AsyncIterableIterator<Progress<Index | undefined>>
|
|
17
|
+
append(
|
|
18
|
+
doc: ChangeLogId,
|
|
19
|
+
parents: ChangeHash[],
|
|
20
|
+
changes: Uint8Array
|
|
21
|
+
): Promise<void>
|
|
22
|
+
replace(
|
|
23
|
+
doc: ChangeLogId,
|
|
24
|
+
start: ChangeHash,
|
|
25
|
+
end: ChangeHash,
|
|
26
|
+
changes: Uint8Array
|
|
27
|
+
): Promise<void>
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function makeFerigan(repo: Repo): Ferigan {
|
|
31
|
+
function fakeProgress<T>(): AsyncIterableIterator<Progress<T>> {
|
|
32
|
+
return (async function*() {
|
|
33
|
+
yield { type: "synchronizing_index" }
|
|
34
|
+
})()
|
|
35
|
+
}
|
|
36
|
+
class FakeFerigan extends EventEmitter<FeriganEvents> {
|
|
37
|
+
#repo: Repo
|
|
38
|
+
|
|
39
|
+
constructor(repo: Repo) {
|
|
40
|
+
super()
|
|
41
|
+
this.#repo = repo
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
async receiveMessage(message: RepoMessage): Promise<void> { }
|
|
45
|
+
load(
|
|
46
|
+
doc: ChangeLogId
|
|
47
|
+
): AsyncIterableIterator<Progress<ChangeLog | undefined>> {
|
|
48
|
+
return fakeProgress()
|
|
49
|
+
}
|
|
50
|
+
async *loadCollection(
|
|
51
|
+
doc: ChangeLogId
|
|
52
|
+
): AsyncIterableIterator<Progress<Index | undefined>> {
|
|
53
|
+
yield { type: "synchronizing_index" }
|
|
54
|
+
|
|
55
|
+
const index: Index = { rootUrl: doc as AutomergeUrl, entries: [] }
|
|
56
|
+
|
|
57
|
+
function findLinks(
|
|
58
|
+
obj: unknown
|
|
59
|
+
): { url: string }[] {
|
|
60
|
+
const links: { url: string; }[] = []
|
|
61
|
+
|
|
62
|
+
const traverse = (value: unknown): void => {
|
|
63
|
+
if (typeof value === "string") {
|
|
64
|
+
const url = parseUrl(value)
|
|
65
|
+
if (url != null) {
|
|
66
|
+
links.push(url)
|
|
67
|
+
}
|
|
68
|
+
} else if (Array.isArray(value)) {
|
|
69
|
+
value.forEach(traverse)
|
|
70
|
+
} else if (typeof value === "object" && value !== null) {
|
|
71
|
+
Object.values(value).forEach(traverse)
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
traverse(obj)
|
|
76
|
+
return links
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const indexDoc = this.#repo.find(doc as AutomergeUrl)
|
|
80
|
+
const handlesToProcess = [indexDoc]
|
|
81
|
+
|
|
82
|
+
while (handlesToProcess.length > 0) {
|
|
83
|
+
const handle = handlesToProcess.pop()
|
|
84
|
+
if (!handle) {
|
|
85
|
+
continue
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const doc = await handle.doc()
|
|
89
|
+
if (!doc) {
|
|
90
|
+
continue
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
handle.on("change", change => {
|
|
94
|
+
for (const patch of change.patches) {
|
|
95
|
+
if (patch.action === "splice") {
|
|
96
|
+
const possibleUrl = parseUrl(patch.value)
|
|
97
|
+
if (possibleUrl != null) {
|
|
98
|
+
this.emit("indexChanged", {
|
|
99
|
+
indexUrl: indexDoc.url,
|
|
100
|
+
change: {
|
|
101
|
+
type: "add",
|
|
102
|
+
url: possibleUrl.url,
|
|
103
|
+
}
|
|
104
|
+
})
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
const links = findLinks(doc)
|
|
111
|
+
for (const { url: urlStr } of links) {
|
|
112
|
+
const url = urlStr as AutomergeUrl
|
|
113
|
+
if (index.entries.some(entry => entry === url)) {
|
|
114
|
+
continue
|
|
115
|
+
}
|
|
116
|
+
index.entries.push(url)
|
|
117
|
+
|
|
118
|
+
const childHandle = this.#repo.find(url as AutomergeUrl)
|
|
119
|
+
handlesToProcess.push(childHandle)
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
yield { type: "done", value: index }
|
|
123
|
+
}
|
|
124
|
+
append(
|
|
125
|
+
doc: ChangeLogId,
|
|
126
|
+
parents: ChangeHash[],
|
|
127
|
+
changes: Uint8Array
|
|
128
|
+
): Promise<void> {
|
|
129
|
+
return Promise.resolve()
|
|
130
|
+
}
|
|
131
|
+
replace(
|
|
132
|
+
doc: ChangeLogId,
|
|
133
|
+
start: ChangeHash,
|
|
134
|
+
end: ChangeHash,
|
|
135
|
+
changes: Uint8Array
|
|
136
|
+
): Promise<void> {
|
|
137
|
+
return Promise.resolve()
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
return new FakeFerigan(repo)
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
interface FeriganEvents {
|
|
144
|
+
message: (event: {message: MessageContents}) => void
|
|
145
|
+
changed: (event: {changedLog: ChangeLogId}) => void
|
|
146
|
+
indexChanged: (event: {indexUrl: ChangeLogId, change: IndexChange}) => void
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
type IndexChange = {
|
|
150
|
+
type: "add"
|
|
151
|
+
url: AutomergeUrl
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
type ChangeLogId = string
|
|
155
|
+
|
|
156
|
+
type ChangeHash = string
|
|
157
|
+
|
|
158
|
+
type ChangeLog = { start: ChangeHash; end: ChangeHash; changes: Uint8Array }
|
|
159
|
+
|
|
160
|
+
export type Progress<T> =
|
|
161
|
+
| { type: "synchronizing_index" }
|
|
162
|
+
| {
|
|
163
|
+
type: "synchronizing_docs"
|
|
164
|
+
progress: number
|
|
165
|
+
total: number
|
|
166
|
+
}
|
|
167
|
+
| { type: "done"; value: T }
|
|
168
|
+
|
|
169
|
+
export type Index = {
|
|
170
|
+
rootUrl: AutomergeUrl,
|
|
171
|
+
entries: AutomergeUrl[],
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function parseUrl(
|
|
175
|
+
urlStr: string
|
|
176
|
+
): { url: AutomergeUrl } | undefined {
|
|
177
|
+
const match = urlStr.match(URL_RE)
|
|
178
|
+
if (!match) {
|
|
179
|
+
return undefined
|
|
180
|
+
}
|
|
181
|
+
const url = new URL(match[0])
|
|
182
|
+
const normalisedUrl = `automerge:${url.pathname}`
|
|
183
|
+
return { url: normalisedUrl as AutomergeUrl }
|
|
184
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { describe, expect, it } from "vitest"
|
|
1
|
+
import { describe, expect, beforeEach, it as _it } from "vitest"
|
|
2
2
|
|
|
3
3
|
import type { StorageAdapterInterface } from "../../storage/StorageAdapterInterface.js"
|
|
4
4
|
|
|
@@ -8,72 +8,61 @@ const PAYLOAD_C = () => new Uint8Array([2, 111, 74, 131, 236, 96, 142, 193])
|
|
|
8
8
|
|
|
9
9
|
const LARGE_PAYLOAD = new Uint8Array(100000).map(() => Math.random() * 256)
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
11
|
+
type AdapterTestContext = {
|
|
12
|
+
adapter: StorageAdapterInterface
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const it = _it<AdapterTestContext>
|
|
16
|
+
|
|
17
|
+
export function runStorageAdapterTests(setup: SetupFn, title?: string): void {
|
|
18
|
+
beforeEach<AdapterTestContext>(async ctx => {
|
|
19
|
+
const { adapter, teardown = NO_OP } = await setup()
|
|
20
|
+
ctx.adapter = adapter
|
|
21
|
+
return teardown
|
|
22
|
+
})
|
|
16
23
|
|
|
17
24
|
describe(`Storage adapter acceptance tests ${
|
|
18
25
|
title ? `(${title})` : ""
|
|
19
26
|
}`, () => {
|
|
20
27
|
describe("load", () => {
|
|
21
|
-
it("should return undefined if there is no data", async () => {
|
|
22
|
-
const { adapter, teardown } = await setup()
|
|
23
|
-
|
|
28
|
+
it("should return undefined if there is no data", async ({ adapter }) => {
|
|
24
29
|
const actual = await adapter.load(["AAAAA", "sync-state", "xxxxx"])
|
|
25
30
|
expect(actual).toBeUndefined()
|
|
26
|
-
|
|
27
|
-
teardown()
|
|
28
31
|
})
|
|
29
32
|
})
|
|
30
33
|
|
|
31
34
|
describe("save and load", () => {
|
|
32
|
-
it("should return data that was saved", async () => {
|
|
33
|
-
const { adapter, teardown } = await setup()
|
|
34
|
-
|
|
35
|
+
it("should return data that was saved", async ({ adapter }) => {
|
|
35
36
|
await adapter.save(["storage-adapter-id"], PAYLOAD_A())
|
|
36
37
|
const actual = await adapter.load(["storage-adapter-id"])
|
|
37
38
|
expect(actual).toStrictEqual(PAYLOAD_A())
|
|
38
|
-
|
|
39
|
-
teardown()
|
|
40
39
|
})
|
|
41
40
|
|
|
42
|
-
it("should work with composite keys", async () => {
|
|
43
|
-
const { adapter, teardown } = await setup()
|
|
44
|
-
|
|
41
|
+
it("should work with composite keys", async ({ adapter }) => {
|
|
45
42
|
await adapter.save(["AAAAA", "sync-state", "xxxxx"], PAYLOAD_A())
|
|
46
43
|
const actual = await adapter.load(["AAAAA", "sync-state", "xxxxx"])
|
|
47
44
|
expect(actual).toStrictEqual(PAYLOAD_A())
|
|
48
|
-
|
|
49
|
-
teardown()
|
|
50
45
|
})
|
|
51
46
|
|
|
52
|
-
it("should work with a large payload", async () => {
|
|
53
|
-
const { adapter, teardown } = await setup()
|
|
54
|
-
|
|
47
|
+
it("should work with a large payload", async ({ adapter }) => {
|
|
55
48
|
await adapter.save(["AAAAA", "sync-state", "xxxxx"], LARGE_PAYLOAD)
|
|
56
49
|
const actual = await adapter.load(["AAAAA", "sync-state", "xxxxx"])
|
|
57
50
|
expect(actual).toStrictEqual(LARGE_PAYLOAD)
|
|
58
|
-
|
|
59
|
-
teardown()
|
|
60
51
|
})
|
|
61
52
|
})
|
|
62
53
|
|
|
63
54
|
describe("loadRange", () => {
|
|
64
|
-
it("should return an empty array if there is no data", async (
|
|
65
|
-
|
|
66
|
-
|
|
55
|
+
it("should return an empty array if there is no data", async ({
|
|
56
|
+
adapter,
|
|
57
|
+
}) => {
|
|
67
58
|
expect(await adapter.loadRange(["AAAAA"])).toStrictEqual([])
|
|
68
|
-
|
|
69
|
-
teardown()
|
|
70
59
|
})
|
|
71
60
|
})
|
|
72
61
|
|
|
73
62
|
describe("save and loadRange", () => {
|
|
74
|
-
it("should return all the data that matches the key", async (
|
|
75
|
-
|
|
76
|
-
|
|
63
|
+
it("should return all the data that matches the key", async ({
|
|
64
|
+
adapter,
|
|
65
|
+
}) => {
|
|
77
66
|
await adapter.save(["AAAAA", "sync-state", "xxxxx"], PAYLOAD_A())
|
|
78
67
|
await adapter.save(["AAAAA", "snapshot", "yyyyy"], PAYLOAD_B())
|
|
79
68
|
await adapter.save(["AAAAA", "sync-state", "zzzzz"], PAYLOAD_C())
|
|
@@ -92,13 +81,9 @@ export function runStorageAdapterTests(_setup: SetupFn, title?: string): void {
|
|
|
92
81
|
{ key: ["AAAAA", "sync-state", "zzzzz"], data: PAYLOAD_C() },
|
|
93
82
|
])
|
|
94
83
|
)
|
|
95
|
-
|
|
96
|
-
teardown()
|
|
97
84
|
})
|
|
98
85
|
|
|
99
|
-
it("should only load values that match they key", async () => {
|
|
100
|
-
const { adapter, teardown } = await setup()
|
|
101
|
-
|
|
86
|
+
it("should only load values that match they key", async ({ adapter }) => {
|
|
102
87
|
await adapter.save(["AAAAA", "sync-state", "xxxxx"], PAYLOAD_A())
|
|
103
88
|
await adapter.save(["BBBBB", "sync-state", "zzzzz"], PAYLOAD_C())
|
|
104
89
|
|
|
@@ -113,15 +98,11 @@ export function runStorageAdapterTests(_setup: SetupFn, title?: string): void {
|
|
|
113
98
|
{ key: ["BBBBB", "sync-state", "zzzzz"], data: PAYLOAD_C() },
|
|
114
99
|
])
|
|
115
100
|
)
|
|
116
|
-
|
|
117
|
-
teardown()
|
|
118
101
|
})
|
|
119
102
|
})
|
|
120
103
|
|
|
121
104
|
describe("save and remove", () => {
|
|
122
|
-
it("after removing, should be empty", async () => {
|
|
123
|
-
const { adapter, teardown } = await setup()
|
|
124
|
-
|
|
105
|
+
it("after removing, should be empty", async ({ adapter }) => {
|
|
125
106
|
await adapter.save(["AAAAA", "snapshot", "xxxxx"], PAYLOAD_A())
|
|
126
107
|
await adapter.remove(["AAAAA", "snapshot", "xxxxx"])
|
|
127
108
|
|
|
@@ -129,30 +110,24 @@ export function runStorageAdapterTests(_setup: SetupFn, title?: string): void {
|
|
|
129
110
|
expect(
|
|
130
111
|
await adapter.load(["AAAAA", "snapshot", "xxxxx"])
|
|
131
112
|
).toBeUndefined()
|
|
132
|
-
|
|
133
|
-
teardown()
|
|
134
113
|
})
|
|
135
114
|
})
|
|
136
115
|
|
|
137
116
|
describe("save and save", () => {
|
|
138
|
-
it("should overwrite data saved with the same key", async (
|
|
139
|
-
|
|
140
|
-
|
|
117
|
+
it("should overwrite data saved with the same key", async ({
|
|
118
|
+
adapter,
|
|
119
|
+
}) => {
|
|
141
120
|
await adapter.save(["AAAAA", "sync-state", "xxxxx"], PAYLOAD_A())
|
|
142
121
|
await adapter.save(["AAAAA", "sync-state", "xxxxx"], PAYLOAD_B())
|
|
143
122
|
|
|
144
123
|
expect(await adapter.loadRange(["AAAAA", "sync-state"])).toStrictEqual([
|
|
145
124
|
{ key: ["AAAAA", "sync-state", "xxxxx"], data: PAYLOAD_B() },
|
|
146
125
|
])
|
|
147
|
-
|
|
148
|
-
teardown()
|
|
149
126
|
})
|
|
150
127
|
})
|
|
151
128
|
|
|
152
129
|
describe("removeRange", () => {
|
|
153
|
-
it("should remove a range of records", async () => {
|
|
154
|
-
const { adapter, teardown } = await setup()
|
|
155
|
-
|
|
130
|
+
it("should remove a range of records", async ({ adapter }) => {
|
|
156
131
|
await adapter.save(["AAAAA", "sync-state", "xxxxx"], PAYLOAD_A())
|
|
157
132
|
await adapter.save(["AAAAA", "snapshot", "yyyyy"], PAYLOAD_B())
|
|
158
133
|
await adapter.save(["AAAAA", "sync-state", "zzzzz"], PAYLOAD_C())
|
|
@@ -162,13 +137,9 @@ export function runStorageAdapterTests(_setup: SetupFn, title?: string): void {
|
|
|
162
137
|
expect(await adapter.loadRange(["AAAAA"])).toStrictEqual([
|
|
163
138
|
{ key: ["AAAAA", "snapshot", "yyyyy"], data: PAYLOAD_B() },
|
|
164
139
|
])
|
|
165
|
-
|
|
166
|
-
teardown()
|
|
167
140
|
})
|
|
168
141
|
|
|
169
|
-
it("should not remove records that don't match", async () => {
|
|
170
|
-
const { adapter, teardown } = await setup()
|
|
171
|
-
|
|
142
|
+
it("should not remove records that don't match", async ({ adapter }) => {
|
|
172
143
|
await adapter.save(["AAAAA", "sync-state", "xxxxx"], PAYLOAD_A())
|
|
173
144
|
await adapter.save(["BBBBB", "sync-state", "zzzzz"], PAYLOAD_B())
|
|
174
145
|
|
|
@@ -178,8 +149,6 @@ export function runStorageAdapterTests(_setup: SetupFn, title?: string): void {
|
|
|
178
149
|
expect(actual).toStrictEqual([
|
|
179
150
|
{ key: ["BBBBB", "sync-state", "zzzzz"], data: PAYLOAD_B() },
|
|
180
151
|
])
|
|
181
|
-
|
|
182
|
-
teardown()
|
|
183
152
|
})
|
|
184
153
|
})
|
|
185
154
|
})
|
|
@@ -189,5 +158,5 @@ const NO_OP = () => {}
|
|
|
189
158
|
|
|
190
159
|
export type SetupFn = () => Promise<{
|
|
191
160
|
adapter: StorageAdapterInterface
|
|
192
|
-
teardown?: () => void
|
|
161
|
+
teardown?: () => void | Promise<void>
|
|
193
162
|
}>
|
package/src/index.ts
CHANGED
|
@@ -41,6 +41,8 @@ export type { NetworkAdapterInterface } from "./network/NetworkAdapterInterface.
|
|
|
41
41
|
export { isRepoMessage } from "./network/messages.js"
|
|
42
42
|
export { StorageAdapter } from "./storage/StorageAdapter.js"
|
|
43
43
|
export type { StorageAdapterInterface } from "./storage/StorageAdapterInterface.js"
|
|
44
|
+
export type { Index, Progress } from "./ferigan.js"
|
|
45
|
+
export { CollectionHandle } from "./CollectionHandle.js"
|
|
44
46
|
|
|
45
47
|
/** @hidden **/
|
|
46
48
|
export * as cbor from "./helpers/cbor.js"
|
|
@@ -38,6 +38,10 @@ export class NetworkSubsystem extends EventEmitter<NetworkSubsystemEvents> {
|
|
|
38
38
|
adapters.forEach(a => this.addNetworkAdapter(a))
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
+
get peers(): PeerId[] {
|
|
42
|
+
return Array.from(Object.keys(this.#adaptersByPeer)) as PeerId[]
|
|
43
|
+
}
|
|
44
|
+
|
|
41
45
|
disconnect() {
|
|
42
46
|
this.adapters.forEach(a => a.disconnect())
|
|
43
47
|
}
|
package/src/network/messages.ts
CHANGED
|
@@ -130,6 +130,14 @@ export type RepoMessage =
|
|
|
130
130
|
| DocumentUnavailableMessage
|
|
131
131
|
| RemoteSubscriptionControlMessage
|
|
132
132
|
| RemoteHeadsChanged
|
|
133
|
+
| BeelayMessage
|
|
134
|
+
|
|
135
|
+
export type BeelayMessage = {
|
|
136
|
+
type: "beelay"
|
|
137
|
+
senderId: PeerId
|
|
138
|
+
targetId: PeerId
|
|
139
|
+
message: Uint8Array
|
|
140
|
+
}
|
|
133
141
|
|
|
134
142
|
/** These are message types that are handled by the {@link CollectionSynchronizer}.*/
|
|
135
143
|
export type DocMessage =
|
|
@@ -167,10 +175,11 @@ export const isRepoMessage = (message: Message): message is RepoMessage =>
|
|
|
167
175
|
isRequestMessage(message) ||
|
|
168
176
|
isDocumentUnavailableMessage(message) ||
|
|
169
177
|
isRemoteSubscriptionControlMessage(message) ||
|
|
170
|
-
isRemoteHeadsChanged(message)
|
|
178
|
+
isRemoteHeadsChanged(message) ||
|
|
179
|
+
true
|
|
171
180
|
|
|
172
181
|
// prettier-ignore
|
|
173
|
-
export const isDocumentUnavailableMessage = (msg: Message): msg is DocumentUnavailableMessage =>
|
|
182
|
+
export const isDocumentUnavailableMessage = (msg: Message): msg is DocumentUnavailableMessage =>
|
|
174
183
|
msg.type === "doc-unavailable"
|
|
175
184
|
|
|
176
185
|
export const isRequestMessage = (msg: Message): msg is RequestMessage =>
|
|
@@ -34,3 +34,45 @@ export abstract class StorageAdapter implements StorageAdapterInterface {
|
|
|
34
34
|
/** Remove all values with keys that start with `keyPrefix` */
|
|
35
35
|
abstract removeRange(keyPrefix: StorageKey): Promise<void>
|
|
36
36
|
}
|
|
37
|
+
|
|
38
|
+
export class InMemoryStorageAdapter extends StorageAdapter {
|
|
39
|
+
#data: Record<string, Uint8Array> = {}
|
|
40
|
+
|
|
41
|
+
#keyToString(key: string[]): string {
|
|
42
|
+
return key.join(".")
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
#stringToKey(key: string): string[] {
|
|
46
|
+
return key.split(".")
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
async loadRange(keyPrefix: StorageKey): Promise<Chunk[]> {
|
|
50
|
+
const range = Object.entries(this.#data)
|
|
51
|
+
.filter(([key, _]) => key.startsWith(this.#keyToString(keyPrefix)))
|
|
52
|
+
.map(([key, data]) => ({ key: this.#stringToKey(key), data }))
|
|
53
|
+
return Promise.resolve(range)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
async removeRange(keyPrefix: string[]): Promise<void> {
|
|
57
|
+
Object.entries(this.#data)
|
|
58
|
+
.filter(([key, _]) => key.startsWith(this.#keyToString(keyPrefix)))
|
|
59
|
+
.forEach(([key, _]) => delete this.#data[key])
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
async load(key: string[]): Promise<Uint8Array | undefined> {
|
|
63
|
+
return new Promise(resolve => resolve(this.#data[this.#keyToString(key)]))
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
async save(key: string[], binary: Uint8Array) {
|
|
67
|
+
this.#data[this.#keyToString(key)] = binary
|
|
68
|
+
return Promise.resolve()
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
async remove(key: string[]) {
|
|
72
|
+
delete this.#data[this.#keyToString(key)]
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
keys() {
|
|
76
|
+
return Object.keys(this.#data)
|
|
77
|
+
}
|
|
78
|
+
}
|