@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.
- package/README.md +8 -8
- package/dist/AutomergeUrl.d.ts +17 -5
- package/dist/AutomergeUrl.d.ts.map +1 -1
- package/dist/AutomergeUrl.js +71 -24
- package/dist/DocHandle.d.ts +87 -30
- package/dist/DocHandle.d.ts.map +1 -1
- package/dist/DocHandle.js +198 -48
- package/dist/FindProgress.d.ts +30 -0
- package/dist/FindProgress.d.ts.map +1 -0
- package/dist/FindProgress.js +1 -0
- package/dist/RemoteHeadsSubscriptions.d.ts +4 -5
- package/dist/RemoteHeadsSubscriptions.d.ts.map +1 -1
- package/dist/RemoteHeadsSubscriptions.js +4 -1
- package/dist/Repo.d.ts +46 -6
- package/dist/Repo.d.ts.map +1 -1
- package/dist/Repo.js +252 -67
- package/dist/helpers/abortable.d.ts +39 -0
- package/dist/helpers/abortable.d.ts.map +1 -0
- package/dist/helpers/abortable.js +45 -0
- package/dist/helpers/arraysAreEqual.d.ts.map +1 -1
- package/dist/helpers/bufferFromHex.d.ts +3 -0
- package/dist/helpers/bufferFromHex.d.ts.map +1 -0
- package/dist/helpers/bufferFromHex.js +13 -0
- package/dist/helpers/debounce.d.ts.map +1 -1
- package/dist/helpers/eventPromise.d.ts.map +1 -1
- package/dist/helpers/headsAreSame.d.ts +2 -2
- package/dist/helpers/headsAreSame.d.ts.map +1 -1
- package/dist/helpers/mergeArrays.d.ts +1 -1
- package/dist/helpers/mergeArrays.d.ts.map +1 -1
- package/dist/helpers/pause.d.ts.map +1 -1
- package/dist/helpers/tests/network-adapter-tests.d.ts.map +1 -1
- package/dist/helpers/tests/network-adapter-tests.js +13 -13
- package/dist/helpers/tests/storage-adapter-tests.d.ts +2 -2
- package/dist/helpers/tests/storage-adapter-tests.d.ts.map +1 -1
- package/dist/helpers/tests/storage-adapter-tests.js +25 -48
- package/dist/helpers/throttle.d.ts.map +1 -1
- package/dist/helpers/withTimeout.d.ts.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/network/messages.d.ts.map +1 -1
- package/dist/storage/StorageSubsystem.d.ts +15 -1
- package/dist/storage/StorageSubsystem.d.ts.map +1 -1
- package/dist/storage/StorageSubsystem.js +50 -14
- package/dist/synchronizer/CollectionSynchronizer.d.ts +4 -3
- package/dist/synchronizer/CollectionSynchronizer.d.ts.map +1 -1
- package/dist/synchronizer/CollectionSynchronizer.js +34 -15
- package/dist/synchronizer/DocSynchronizer.d.ts +3 -2
- package/dist/synchronizer/DocSynchronizer.d.ts.map +1 -1
- package/dist/synchronizer/DocSynchronizer.js +51 -27
- package/dist/synchronizer/Synchronizer.d.ts +11 -0
- package/dist/synchronizer/Synchronizer.d.ts.map +1 -1
- package/dist/types.d.ts +4 -1
- package/dist/types.d.ts.map +1 -1
- package/fuzz/fuzz.ts +3 -3
- package/package.json +3 -4
- package/src/AutomergeUrl.ts +101 -26
- package/src/DocHandle.ts +268 -58
- package/src/FindProgress.ts +48 -0
- package/src/RemoteHeadsSubscriptions.ts +11 -9
- package/src/Repo.ts +364 -74
- package/src/helpers/abortable.ts +61 -0
- package/src/helpers/bufferFromHex.ts +14 -0
- package/src/helpers/headsAreSame.ts +2 -2
- package/src/helpers/tests/network-adapter-tests.ts +14 -13
- package/src/helpers/tests/storage-adapter-tests.ts +44 -86
- package/src/index.ts +7 -0
- package/src/storage/StorageSubsystem.ts +66 -16
- package/src/synchronizer/CollectionSynchronizer.ts +37 -16
- package/src/synchronizer/DocSynchronizer.ts +59 -32
- package/src/synchronizer/Synchronizer.ts +14 -0
- package/src/types.ts +4 -1
- package/test/AutomergeUrl.test.ts +130 -0
- package/test/CollectionSynchronizer.test.ts +4 -4
- package/test/DocHandle.test.ts +255 -30
- package/test/DocSynchronizer.test.ts +10 -3
- package/test/Repo.test.ts +376 -203
- package/test/StorageSubsystem.test.ts +80 -1
- package/test/remoteHeads.test.ts +27 -12
|
@@ -4,13 +4,15 @@ import assert from "assert"
|
|
|
4
4
|
import fs from "fs"
|
|
5
5
|
import os from "os"
|
|
6
6
|
import path from "path"
|
|
7
|
-
import { describe, it } from "vitest"
|
|
7
|
+
import { describe, it, expect } from "vitest"
|
|
8
8
|
import { generateAutomergeUrl, parseAutomergeUrl } from "../src/AutomergeUrl.js"
|
|
9
9
|
import { PeerId, cbor } from "../src/index.js"
|
|
10
10
|
import { StorageSubsystem } from "../src/storage/StorageSubsystem.js"
|
|
11
11
|
import { StorageId } from "../src/storage/types.js"
|
|
12
12
|
import { DummyStorageAdapter } from "../src/helpers/DummyStorageAdapter.js"
|
|
13
13
|
import * as Uuid from "uuid"
|
|
14
|
+
import { chunkTypeFromKey } from "../src/storage/chunkTypeFromKey.js"
|
|
15
|
+
import { DocumentId } from "../src/types.js"
|
|
14
16
|
|
|
15
17
|
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "automerge-repo-tests"))
|
|
16
18
|
|
|
@@ -243,6 +245,83 @@ describe("StorageSubsystem", () => {
|
|
|
243
245
|
assert.strictEqual(id1, id2)
|
|
244
246
|
})
|
|
245
247
|
})
|
|
248
|
+
|
|
249
|
+
describe("loadDoc", () => {
|
|
250
|
+
it("maintains correct document state when loading chunks in order", async () => {
|
|
251
|
+
const storageAdapter = new DummyStorageAdapter()
|
|
252
|
+
const storage = new StorageSubsystem(storageAdapter)
|
|
253
|
+
|
|
254
|
+
// Create a document with multiple changes
|
|
255
|
+
const doc = A.init<{ foo: string }>()
|
|
256
|
+
const doc1 = A.change(doc, d => {
|
|
257
|
+
d.foo = "first"
|
|
258
|
+
})
|
|
259
|
+
const doc2 = A.change(doc1, d => {
|
|
260
|
+
d.foo = "second"
|
|
261
|
+
})
|
|
262
|
+
const doc3 = A.change(doc2, d => {
|
|
263
|
+
d.foo = "third"
|
|
264
|
+
})
|
|
265
|
+
|
|
266
|
+
// Save the document with multiple changes
|
|
267
|
+
const documentId = "test-doc" as DocumentId
|
|
268
|
+
await storage.saveDoc(documentId, doc3)
|
|
269
|
+
|
|
270
|
+
// Load the document
|
|
271
|
+
const loadedDoc = await storage.loadDoc<{ foo: string }>(documentId)
|
|
272
|
+
|
|
273
|
+
// Verify the document state is correct
|
|
274
|
+
expect(loadedDoc?.foo).toBe("third")
|
|
275
|
+
})
|
|
276
|
+
|
|
277
|
+
it("combines chunks with snapshot first", async () => {
|
|
278
|
+
const storageAdapter = new DummyStorageAdapter()
|
|
279
|
+
const storage = new StorageSubsystem(storageAdapter)
|
|
280
|
+
|
|
281
|
+
// Create a document with multiple changes
|
|
282
|
+
const doc = A.init<{ foo: string }>()
|
|
283
|
+
const doc1 = A.change(doc, d => {
|
|
284
|
+
d.foo = "first"
|
|
285
|
+
})
|
|
286
|
+
const doc2 = A.change(doc1, d => {
|
|
287
|
+
d.foo = Array(10000)
|
|
288
|
+
.fill(0)
|
|
289
|
+
.map(() =>
|
|
290
|
+
String.fromCharCode(Math.floor(Math.random() * 26) + 97)
|
|
291
|
+
)
|
|
292
|
+
.join("")
|
|
293
|
+
})
|
|
294
|
+
|
|
295
|
+
// Save the document with multiple changes
|
|
296
|
+
const documentId = "test-doc" as DocumentId
|
|
297
|
+
await storage.saveDoc(documentId, doc2)
|
|
298
|
+
|
|
299
|
+
const doc3 = A.change(doc2, d => {
|
|
300
|
+
d.foo = "third"
|
|
301
|
+
})
|
|
302
|
+
await storage.saveDoc(documentId, doc3)
|
|
303
|
+
|
|
304
|
+
// Load the document
|
|
305
|
+
const loadedDoc = await storage.loadDoc<{ foo: string }>(documentId)
|
|
306
|
+
|
|
307
|
+
// Verify the document state is correct
|
|
308
|
+
expect(loadedDoc?.foo).toBe(doc3.foo)
|
|
309
|
+
|
|
310
|
+
// Get the raw binary data from storage
|
|
311
|
+
const binary = await storage.loadDocData(documentId)
|
|
312
|
+
expect(binary).not.toBeNull()
|
|
313
|
+
if (!binary) return
|
|
314
|
+
|
|
315
|
+
// Verify the binary starts with the Automerge magic value
|
|
316
|
+
expect(binary[0]).toBe(0x85)
|
|
317
|
+
expect(binary[1]).toBe(0x6f)
|
|
318
|
+
expect(binary[2]).toBe(0x4a)
|
|
319
|
+
expect(binary[3]).toBe(0x83)
|
|
320
|
+
|
|
321
|
+
// Verify the chunk type is CHUNK_TYPE_DOCUMENT (0x00)
|
|
322
|
+
expect(binary[8]).toBe(0x00)
|
|
323
|
+
})
|
|
324
|
+
})
|
|
246
325
|
})
|
|
247
326
|
}
|
|
248
327
|
})
|
package/test/remoteHeads.test.ts
CHANGED
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
import { DummyStorageAdapter } from "../src/helpers/DummyStorageAdapter.js"
|
|
14
14
|
import { collectMessages } from "./helpers/collectMessages.js"
|
|
15
15
|
import { TestDoc } from "./types.js"
|
|
16
|
+
import { pause } from "../src/helpers/pause.js"
|
|
16
17
|
|
|
17
18
|
describe("DocHandle.remoteHeads", () => {
|
|
18
19
|
const TEST_ID = parseAutomergeUrl(generateAutomergeUrl()).documentId
|
|
@@ -128,13 +129,15 @@ describe("DocHandle.remoteHeads", () => {
|
|
|
128
129
|
const aliceDoc = alice.create<TestDoc>()
|
|
129
130
|
aliceDoc.change(d => (d.foo = "bar"))
|
|
130
131
|
|
|
132
|
+
await pause(50)
|
|
133
|
+
|
|
131
134
|
// bob waits for the document to arrive
|
|
132
|
-
const bobDoc = bob.find<TestDoc>(aliceDoc.url)
|
|
133
|
-
await bobDoc.whenReady()
|
|
135
|
+
const bobDoc = await bob.find<TestDoc>(aliceDoc.url)
|
|
134
136
|
|
|
135
137
|
// alice's service worker waits for the document to arrive
|
|
136
|
-
const aliceServiceWorkerDoc = aliceServiceWorker.find(
|
|
137
|
-
|
|
138
|
+
const aliceServiceWorkerDoc = await aliceServiceWorker.find(
|
|
139
|
+
aliceDoc.documentId
|
|
140
|
+
)
|
|
138
141
|
|
|
139
142
|
let aliceSeenByBobPromise = new Promise<DocHandleRemoteHeadsPayload>(
|
|
140
143
|
resolve => {
|
|
@@ -168,17 +171,21 @@ describe("DocHandle.remoteHeads", () => {
|
|
|
168
171
|
const bobDocB = bob.create<TestDoc>()
|
|
169
172
|
bobDocB.change(d => (d.foo = "B"))
|
|
170
173
|
|
|
174
|
+
await pause(50)
|
|
175
|
+
|
|
171
176
|
// alice opens doc A
|
|
172
|
-
const
|
|
177
|
+
const aliceDocAPromise = alice.find<TestDoc>(bobDocA.url)
|
|
173
178
|
|
|
174
179
|
const remoteHeadsChangedMessages = (
|
|
175
180
|
await collectMessages({
|
|
176
181
|
emitter: alice.networkSubsystem,
|
|
177
182
|
event: "message",
|
|
178
|
-
until:
|
|
183
|
+
until: aliceDocAPromise,
|
|
179
184
|
})
|
|
180
185
|
).filter(({ type }) => type === "remote-heads-changed")
|
|
181
186
|
|
|
187
|
+
const aliceDocA = await aliceDocAPromise
|
|
188
|
+
|
|
182
189
|
// we should only be notified of the head changes of doc A
|
|
183
190
|
assert(
|
|
184
191
|
remoteHeadsChangedMessages.every(
|
|
@@ -197,6 +204,8 @@ describe("DocHandle.remoteHeads", () => {
|
|
|
197
204
|
const bobDocB = bob.create<TestDoc>()
|
|
198
205
|
bobDocB.change(d => (d.foo = "B"))
|
|
199
206
|
|
|
207
|
+
await pause(50)
|
|
208
|
+
|
|
200
209
|
// alice opens the docs
|
|
201
210
|
const _aliceDocA = alice.find<TestDoc>(bobDocA.url)
|
|
202
211
|
const _aliceDocB = alice.find<TestDoc>(bobDocB.url)
|
|
@@ -209,19 +218,21 @@ describe("DocHandle.remoteHeads", () => {
|
|
|
209
218
|
// stored remote heads immediately.
|
|
210
219
|
|
|
211
220
|
// open doc and subscribe alice's second tab to bob's service worker
|
|
212
|
-
const
|
|
221
|
+
const alice2DocAPromise = alice2.find<TestDoc>(bobDocA.url)
|
|
213
222
|
alice2.subscribeToRemotes([bobServiceWorkerStorageId])
|
|
214
223
|
|
|
215
224
|
const remoteHeadsChangedMessages = (
|
|
216
225
|
await collectMessages({
|
|
217
226
|
emitter: alice2.networkSubsystem,
|
|
218
227
|
event: "message",
|
|
219
|
-
until:
|
|
228
|
+
until: alice2DocAPromise,
|
|
220
229
|
})
|
|
221
230
|
).filter(({ type }) => type === "remote-heads-changed")
|
|
222
231
|
|
|
232
|
+
const alice2DocA = await alice2DocAPromise
|
|
233
|
+
|
|
223
234
|
// we should only be notified of the head changes of doc A
|
|
224
|
-
assert.strictEqual(remoteHeadsChangedMessages.length,
|
|
235
|
+
assert.strictEqual(remoteHeadsChangedMessages.length, 1)
|
|
225
236
|
assert(
|
|
226
237
|
remoteHeadsChangedMessages.every(
|
|
227
238
|
d => d.documentId === alice2DocA.documentId
|
|
@@ -242,18 +253,22 @@ describe("DocHandle.remoteHeads", () => {
|
|
|
242
253
|
// alice subscribes to bob's service worker
|
|
243
254
|
alice.subscribeToRemotes([bobServiceWorkerStorageId])
|
|
244
255
|
|
|
256
|
+
await pause(50)
|
|
257
|
+
|
|
245
258
|
// alice opens doc A
|
|
246
|
-
const
|
|
259
|
+
const alice1DocAPromise = alice.find<TestDoc>(bobDocA.url)
|
|
247
260
|
|
|
248
261
|
const remoteHeadsChangedMessages = (
|
|
249
262
|
await collectMessages({
|
|
250
263
|
emitter: alice.networkSubsystem,
|
|
251
264
|
event: "message",
|
|
252
|
-
until:
|
|
265
|
+
until: alice1DocAPromise,
|
|
253
266
|
})
|
|
254
267
|
).filter(({ type }) => type === "remote-heads-changed")
|
|
255
268
|
|
|
256
|
-
|
|
269
|
+
const alice1DocA = await alice1DocAPromise
|
|
270
|
+
|
|
271
|
+
assert.strictEqual(remoteHeadsChangedMessages.length, 1)
|
|
257
272
|
assert(
|
|
258
273
|
remoteHeadsChangedMessages.every(
|
|
259
274
|
d => d.documentId === alice1DocA.documentId
|