@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
package/test/DocHandle.test.ts
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
import * as A from "@automerge/automerge/next"
|
|
2
2
|
import assert from "assert"
|
|
3
3
|
import { decode } from "cbor-x"
|
|
4
|
-
import { describe, it, vi } from "vitest"
|
|
5
|
-
import {
|
|
4
|
+
import { describe, expect, it, vi } from "vitest"
|
|
5
|
+
import {
|
|
6
|
+
encodeHeads,
|
|
7
|
+
generateAutomergeUrl,
|
|
8
|
+
parseAutomergeUrl,
|
|
9
|
+
} from "../src/AutomergeUrl.js"
|
|
6
10
|
import { eventPromise } from "../src/helpers/eventPromise.js"
|
|
7
11
|
import { pause } from "../src/helpers/pause.js"
|
|
8
12
|
import { DocHandle, DocHandleChangePayload } from "../src/index.js"
|
|
@@ -34,7 +38,7 @@ describe("DocHandle", () => {
|
|
|
34
38
|
handle.update(doc => docFromMockStorage(doc))
|
|
35
39
|
|
|
36
40
|
assert.equal(handle.isReady(), true)
|
|
37
|
-
const doc =
|
|
41
|
+
const doc = handle.doc()
|
|
38
42
|
assert.equal(doc?.foo, "bar")
|
|
39
43
|
})
|
|
40
44
|
|
|
@@ -46,13 +50,13 @@ describe("DocHandle", () => {
|
|
|
46
50
|
handle.update(doc => docFromMockStorage(doc))
|
|
47
51
|
|
|
48
52
|
assert.equal(handle.isReady(), true)
|
|
49
|
-
const doc =
|
|
50
|
-
assert.deepEqual(doc, handle.
|
|
53
|
+
const doc = handle.doc()
|
|
54
|
+
assert.deepEqual(doc, handle.doc())
|
|
51
55
|
})
|
|
52
56
|
|
|
53
|
-
it("should
|
|
57
|
+
it("should throw an exception if we access the doc before ready", async () => {
|
|
54
58
|
const handle = new DocHandle<TestDoc>(TEST_ID)
|
|
55
|
-
assert.
|
|
59
|
+
assert.throws(() => handle.doc())
|
|
56
60
|
})
|
|
57
61
|
|
|
58
62
|
it("should not return a doc until ready", async () => {
|
|
@@ -62,26 +66,185 @@ describe("DocHandle", () => {
|
|
|
62
66
|
// simulate loading from storage
|
|
63
67
|
handle.update(doc => docFromMockStorage(doc))
|
|
64
68
|
|
|
65
|
-
const doc =
|
|
69
|
+
const doc = handle.doc()
|
|
66
70
|
|
|
67
71
|
assert.equal(handle.isReady(), true)
|
|
68
72
|
assert.equal(doc?.foo, "bar")
|
|
69
73
|
})
|
|
70
74
|
|
|
75
|
+
/** HISTORY TRAVERSAL
|
|
76
|
+
* This API is relatively alpha-ish but we're already
|
|
77
|
+
* doing things in our own apps that are fairly ambitious
|
|
78
|
+
* by routing around to a lower-level API.
|
|
79
|
+
* This is an attempt to wrap up the existing practice
|
|
80
|
+
* in a slightly more supportable set of APIs but should be
|
|
81
|
+
* considered provisional: expect further improvements.
|
|
82
|
+
*/
|
|
83
|
+
|
|
71
84
|
it("should return the heads when requested", async () => {
|
|
72
85
|
const handle = setup()
|
|
73
86
|
handle.change(d => (d.foo = "bar"))
|
|
74
87
|
assert.equal(handle.isReady(), true)
|
|
75
88
|
|
|
76
|
-
const heads = A.getHeads(handle.
|
|
89
|
+
const heads = encodeHeads(A.getHeads(handle.doc()))
|
|
77
90
|
assert.notDeepEqual(handle.heads(), [])
|
|
78
91
|
assert.deepEqual(heads, handle.heads())
|
|
79
92
|
})
|
|
80
93
|
|
|
81
|
-
it("should
|
|
94
|
+
it("should throw an if the heads aren't loaded", async () => {
|
|
82
95
|
const handle = new DocHandle<TestDoc>(TEST_ID)
|
|
83
96
|
assert.equal(handle.isReady(), false)
|
|
84
|
-
|
|
97
|
+
expect(() => handle.heads()).toThrow("DocHandle is not ready")
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
it("should return the history when requested", async () => {
|
|
101
|
+
const handle = setup()
|
|
102
|
+
handle.change(d => (d.foo = "bar"))
|
|
103
|
+
handle.change(d => (d.foo = "baz"))
|
|
104
|
+
assert.equal(handle.isReady(), true)
|
|
105
|
+
|
|
106
|
+
const history = handle.history()
|
|
107
|
+
assert.deepEqual(handle.history().length, 2)
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
it("should return a commit from the history", async () => {
|
|
111
|
+
const handle = setup()
|
|
112
|
+
handle.change(d => (d.foo = "zero"))
|
|
113
|
+
handle.change(d => (d.foo = "one"))
|
|
114
|
+
handle.change(d => (d.foo = "two"))
|
|
115
|
+
handle.change(d => (d.foo = "three"))
|
|
116
|
+
assert.equal(handle.isReady(), true)
|
|
117
|
+
|
|
118
|
+
const history = handle.history()
|
|
119
|
+
const viewHandle = handle.view(history[1])
|
|
120
|
+
assert.deepEqual(await viewHandle.doc(), { foo: "one" })
|
|
121
|
+
})
|
|
122
|
+
|
|
123
|
+
it("should support fixed heads from construction", async () => {
|
|
124
|
+
const handle = setup()
|
|
125
|
+
handle.change(d => (d.foo = "zero"))
|
|
126
|
+
handle.change(d => (d.foo = "one"))
|
|
127
|
+
|
|
128
|
+
const history = handle.history()
|
|
129
|
+
const viewHandle = new DocHandle<TestDoc>(TEST_ID, { heads: history[0] })
|
|
130
|
+
viewHandle.update(() => A.clone(handle.doc()!))
|
|
131
|
+
viewHandle.doneLoading()
|
|
132
|
+
|
|
133
|
+
assert.deepEqual(await viewHandle.doc(), { foo: "zero" })
|
|
134
|
+
})
|
|
135
|
+
|
|
136
|
+
it("should prevent changes on fixed-heads handles", async () => {
|
|
137
|
+
const handle = setup()
|
|
138
|
+
handle.change(d => (d.foo = "zero"))
|
|
139
|
+
const viewHandle = handle.view(handle.heads()!)
|
|
140
|
+
|
|
141
|
+
assert.throws(() => viewHandle.change(d => (d.foo = "one")))
|
|
142
|
+
assert.throws(() =>
|
|
143
|
+
viewHandle.changeAt(handle.heads()!, d => (d.foo = "one"))
|
|
144
|
+
)
|
|
145
|
+
assert.throws(() => viewHandle.merge(handle))
|
|
146
|
+
})
|
|
147
|
+
|
|
148
|
+
it("should return fixed heads from heads()", async () => {
|
|
149
|
+
const handle = setup()
|
|
150
|
+
handle.change(d => (d.foo = "zero"))
|
|
151
|
+
const originalHeads = handle.heads()!
|
|
152
|
+
|
|
153
|
+
handle.change(d => (d.foo = "one"))
|
|
154
|
+
const viewHandle = handle.view(originalHeads)
|
|
155
|
+
|
|
156
|
+
assert.deepEqual(viewHandle.heads(), originalHeads)
|
|
157
|
+
assert.notDeepEqual(viewHandle.heads(), handle.heads())
|
|
158
|
+
})
|
|
159
|
+
|
|
160
|
+
it("should return diffs", async () => {
|
|
161
|
+
const handle = setup()
|
|
162
|
+
handle.change(d => (d.foo = "zero"))
|
|
163
|
+
handle.change(d => (d.foo = "one"))
|
|
164
|
+
handle.change(d => (d.foo = "two"))
|
|
165
|
+
handle.change(d => (d.foo = "three"))
|
|
166
|
+
assert.equal(handle.isReady(), true)
|
|
167
|
+
|
|
168
|
+
const history = handle.history()
|
|
169
|
+
const patches = handle.diff(history[1])
|
|
170
|
+
assert.deepEqual(patches, [
|
|
171
|
+
{ action: "put", path: ["foo"], value: "" },
|
|
172
|
+
{ action: "splice", path: ["foo", 0], value: "one" },
|
|
173
|
+
])
|
|
174
|
+
})
|
|
175
|
+
|
|
176
|
+
it("should support arbitrary diffs too", async () => {
|
|
177
|
+
const handle = setup()
|
|
178
|
+
handle.change(d => (d.foo = "zero"))
|
|
179
|
+
handle.change(d => (d.foo = "one"))
|
|
180
|
+
handle.change(d => (d.foo = "two"))
|
|
181
|
+
handle.change(d => (d.foo = "three"))
|
|
182
|
+
assert.equal(handle.isReady(), true)
|
|
183
|
+
|
|
184
|
+
const history = handle.history()
|
|
185
|
+
const patches = handle.diff(history[1], history[3])
|
|
186
|
+
assert.deepEqual(patches, [
|
|
187
|
+
{ action: "put", path: ["foo"], value: "" },
|
|
188
|
+
{ action: "splice", path: ["foo", 0], value: "three" },
|
|
189
|
+
])
|
|
190
|
+
const backPatches = handle.diff(history[3], history[1])
|
|
191
|
+
assert.deepEqual(backPatches, [
|
|
192
|
+
{ action: "put", path: ["foo"], value: "" },
|
|
193
|
+
{ action: "splice", path: ["foo", 0], value: "one" },
|
|
194
|
+
])
|
|
195
|
+
})
|
|
196
|
+
|
|
197
|
+
it("should support diffing against another handle", async () => {
|
|
198
|
+
const handle = setup()
|
|
199
|
+
handle.change(d => (d.foo = "zero"))
|
|
200
|
+
const viewHandle = handle.view(handle.heads()!)
|
|
201
|
+
|
|
202
|
+
handle.change(d => (d.foo = "one"))
|
|
203
|
+
|
|
204
|
+
const patches = viewHandle.diff(handle)
|
|
205
|
+
assert.deepEqual(patches, [
|
|
206
|
+
{ action: "put", path: ["foo"], value: "" },
|
|
207
|
+
{ action: "splice", path: ["foo", 0], value: "one" },
|
|
208
|
+
])
|
|
209
|
+
})
|
|
210
|
+
|
|
211
|
+
// TODO: alexg -- should i remove this test? should this fail or no?
|
|
212
|
+
it.skip("should fail diffing against unrelated handles", async () => {
|
|
213
|
+
const handle1 = setup()
|
|
214
|
+
const handle2 = setup()
|
|
215
|
+
|
|
216
|
+
handle1.change(d => (d.foo = "zero"))
|
|
217
|
+
handle2.change(d => (d.foo = "one"))
|
|
218
|
+
|
|
219
|
+
assert.throws(() => handle1.diff(handle2))
|
|
220
|
+
})
|
|
221
|
+
|
|
222
|
+
it("should allow direct access to decoded changes", async () => {
|
|
223
|
+
const handle = setup()
|
|
224
|
+
const time = Date.now()
|
|
225
|
+
handle.change(d => (d.foo = "foo"), { message: "commitMessage" })
|
|
226
|
+
assert.equal(handle.isReady(), true)
|
|
227
|
+
|
|
228
|
+
const metadata = handle.metadata()
|
|
229
|
+
assert.deepEqual(metadata.message, "commitMessage")
|
|
230
|
+
// NOTE: I'm not testing time because of https://github.com/automerge/automerge/issues/965
|
|
231
|
+
// but it does round-trip successfully!
|
|
232
|
+
})
|
|
233
|
+
|
|
234
|
+
it("should allow direct access to a specific decoded change", async () => {
|
|
235
|
+
const handle = setup()
|
|
236
|
+
const time = Date.now()
|
|
237
|
+
handle.change(d => (d.foo = "foo"), { message: "commitMessage" })
|
|
238
|
+
handle.change(d => (d.foo = "foo"), { message: "commitMessage2" })
|
|
239
|
+
handle.change(d => (d.foo = "foo"), { message: "commitMessage3" })
|
|
240
|
+
handle.change(d => (d.foo = "foo"), { message: "commitMessage4" })
|
|
241
|
+
assert.equal(handle.isReady(), true)
|
|
242
|
+
|
|
243
|
+
const history = handle.history()
|
|
244
|
+
const metadata = handle.metadata(history[0][0])
|
|
245
|
+
assert.deepEqual(metadata.message, "commitMessage")
|
|
246
|
+
// NOTE: I'm not testing time because of https://github.com/automerge/automerge/issues/965
|
|
247
|
+
// but it does round-trip successfully!
|
|
85
248
|
})
|
|
86
249
|
|
|
87
250
|
/**
|
|
@@ -96,8 +259,6 @@ describe("DocHandle", () => {
|
|
|
96
259
|
const handle = new DocHandle<TestDoc>(TEST_ID)
|
|
97
260
|
assert.equal(handle.isReady(), false)
|
|
98
261
|
|
|
99
|
-
handle.doc()
|
|
100
|
-
|
|
101
262
|
assert(vi.getTimerCount() > timerCount)
|
|
102
263
|
|
|
103
264
|
// simulate loading from storage
|
|
@@ -122,7 +283,7 @@ describe("DocHandle", () => {
|
|
|
122
283
|
assert.equal(handle.isReady(), true)
|
|
123
284
|
handle.change(d => (d.foo = "pizza"))
|
|
124
285
|
|
|
125
|
-
const doc =
|
|
286
|
+
const doc = handle.doc()
|
|
126
287
|
assert.equal(doc?.foo, "pizza")
|
|
127
288
|
})
|
|
128
289
|
|
|
@@ -132,7 +293,9 @@ describe("DocHandle", () => {
|
|
|
132
293
|
// we don't have it in storage, so we request it from the network
|
|
133
294
|
handle.request()
|
|
134
295
|
|
|
135
|
-
|
|
296
|
+
await expect(() => {
|
|
297
|
+
handle.doc()
|
|
298
|
+
}).toThrowError("DocHandle is not ready")
|
|
136
299
|
assert.equal(handle.isReady(), false)
|
|
137
300
|
assert.throws(() => handle.change(_ => {}))
|
|
138
301
|
})
|
|
@@ -148,7 +311,7 @@ describe("DocHandle", () => {
|
|
|
148
311
|
return A.change(doc, d => (d.foo = "bar"))
|
|
149
312
|
})
|
|
150
313
|
|
|
151
|
-
const doc =
|
|
314
|
+
const doc = handle.doc()
|
|
152
315
|
assert.equal(handle.isReady(), true)
|
|
153
316
|
assert.equal(doc?.foo, "bar")
|
|
154
317
|
})
|
|
@@ -164,7 +327,7 @@ describe("DocHandle", () => {
|
|
|
164
327
|
doc.foo = "bar"
|
|
165
328
|
})
|
|
166
329
|
|
|
167
|
-
const doc =
|
|
330
|
+
const doc = handle.doc()
|
|
168
331
|
assert.equal(doc?.foo, "bar")
|
|
169
332
|
|
|
170
333
|
const changePayload = await p
|
|
@@ -189,7 +352,7 @@ describe("DocHandle", () => {
|
|
|
189
352
|
|
|
190
353
|
const p = new Promise<void>(resolve =>
|
|
191
354
|
handle.once("change", ({ handle, doc }) => {
|
|
192
|
-
assert.equal(handle.
|
|
355
|
+
assert.equal(handle.doc()?.foo, doc.foo)
|
|
193
356
|
|
|
194
357
|
resolve()
|
|
195
358
|
})
|
|
@@ -226,7 +389,7 @@ describe("DocHandle", () => {
|
|
|
226
389
|
doc.foo = "baz"
|
|
227
390
|
})
|
|
228
391
|
|
|
229
|
-
const doc =
|
|
392
|
+
const doc = handle.doc()
|
|
230
393
|
assert.equal(doc?.foo, "baz")
|
|
231
394
|
|
|
232
395
|
return p
|
|
@@ -241,7 +404,7 @@ describe("DocHandle", () => {
|
|
|
241
404
|
})
|
|
242
405
|
|
|
243
406
|
await p
|
|
244
|
-
const doc =
|
|
407
|
+
const doc = handle.doc()
|
|
245
408
|
assert.equal(doc?.foo, "bar")
|
|
246
409
|
})
|
|
247
410
|
|
|
@@ -261,11 +424,7 @@ describe("DocHandle", () => {
|
|
|
261
424
|
// set docHandle time out after 5 ms
|
|
262
425
|
const handle = new DocHandle<TestDoc>(TEST_ID, { timeoutDelay: 5 })
|
|
263
426
|
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
assert.equal(doc, undefined)
|
|
267
|
-
|
|
268
|
-
assert.equal(handle.state, "unavailable")
|
|
427
|
+
expect(() => handle.doc()).toThrowError("DocHandle is not ready")
|
|
269
428
|
})
|
|
270
429
|
|
|
271
430
|
it("should not time out if the document is loaded in time", async () => {
|
|
@@ -276,11 +435,11 @@ describe("DocHandle", () => {
|
|
|
276
435
|
handle.update(doc => docFromMockStorage(doc))
|
|
277
436
|
|
|
278
437
|
// now it should not time out
|
|
279
|
-
const doc =
|
|
438
|
+
const doc = handle.doc()
|
|
280
439
|
assert.equal(doc?.foo, "bar")
|
|
281
440
|
})
|
|
282
441
|
|
|
283
|
-
it("should
|
|
442
|
+
it("should throw an exception if loading from the network times out", async () => {
|
|
284
443
|
// set docHandle time out after 5 ms
|
|
285
444
|
const handle = new DocHandle<TestDoc>(TEST_ID, { timeoutDelay: 5 })
|
|
286
445
|
|
|
@@ -290,8 +449,7 @@ describe("DocHandle", () => {
|
|
|
290
449
|
// there's no update
|
|
291
450
|
await pause(10)
|
|
292
451
|
|
|
293
|
-
|
|
294
|
-
assert.equal(doc, undefined)
|
|
452
|
+
expect(() => handle.doc()).toThrowError("DocHandle is not ready")
|
|
295
453
|
})
|
|
296
454
|
|
|
297
455
|
it("should not time out if the document is updated in time", async () => {
|
|
@@ -309,7 +467,7 @@ describe("DocHandle", () => {
|
|
|
309
467
|
// now it should not time out
|
|
310
468
|
await pause(5)
|
|
311
469
|
|
|
312
|
-
const doc =
|
|
470
|
+
const doc = handle.doc()
|
|
313
471
|
assert.equal(doc?.foo, "bar")
|
|
314
472
|
})
|
|
315
473
|
|
|
@@ -362,4 +520,71 @@ describe("DocHandle", () => {
|
|
|
362
520
|
assert.deepStrictEqual(decode(data), message)
|
|
363
521
|
})
|
|
364
522
|
})
|
|
523
|
+
|
|
524
|
+
it("should cache view handles based on heads", async () => {
|
|
525
|
+
// Create and setup a document with some data
|
|
526
|
+
const handle = setup()
|
|
527
|
+
handle.change(doc => {
|
|
528
|
+
doc.foo = "Hello"
|
|
529
|
+
})
|
|
530
|
+
const heads1 = handle.heads()
|
|
531
|
+
|
|
532
|
+
// Make another change to get a different set of heads
|
|
533
|
+
handle.change(doc => {
|
|
534
|
+
doc.foo = "Hello, World!"
|
|
535
|
+
})
|
|
536
|
+
|
|
537
|
+
// Create a view at the first set of heads
|
|
538
|
+
const view1 = handle.view(heads1)
|
|
539
|
+
|
|
540
|
+
// Request the same view again
|
|
541
|
+
const view2 = handle.view(heads1)
|
|
542
|
+
|
|
543
|
+
// Verify we got the same handle instance back (cached version)
|
|
544
|
+
expect(view1).toBe(view2)
|
|
545
|
+
|
|
546
|
+
// Verify the contents are correct
|
|
547
|
+
expect(view1.doc().foo).toBe("Hello")
|
|
548
|
+
|
|
549
|
+
// Test with a different set of heads
|
|
550
|
+
const view3 = handle.view(handle.heads())
|
|
551
|
+
expect(view3).not.toBe(view1)
|
|
552
|
+
expect(view3.doc().foo).toBe("Hello, World!")
|
|
553
|
+
})
|
|
554
|
+
|
|
555
|
+
it("should improve performance when requesting the same view multiple times", () => {
|
|
556
|
+
// Create and setup a document with some data
|
|
557
|
+
const handle = setup()
|
|
558
|
+
handle.change(doc => {
|
|
559
|
+
doc.foo = "Hello"
|
|
560
|
+
})
|
|
561
|
+
const heads = handle.heads()
|
|
562
|
+
|
|
563
|
+
// First, measure time without cache (first access)
|
|
564
|
+
const startTimeNoCached = performance.now()
|
|
565
|
+
const firstView = handle.view(heads)
|
|
566
|
+
const endTimeNoCached = performance.now()
|
|
567
|
+
|
|
568
|
+
// Now measure with cache (subsequent accesses)
|
|
569
|
+
const startTimeCached = performance.now()
|
|
570
|
+
for (let i = 0; i < 100; i++) {
|
|
571
|
+
handle.view(heads)
|
|
572
|
+
}
|
|
573
|
+
const endTimeCached = performance.now()
|
|
574
|
+
|
|
575
|
+
// Assert that all views are the same instance
|
|
576
|
+
for (let i = 0; i < 10; i++) {
|
|
577
|
+
expect(handle.view(heads)).toBe(firstView)
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
// Calculate average times
|
|
581
|
+
const timeForFirstAccess = endTimeNoCached - startTimeNoCached
|
|
582
|
+
const timeForCachedAccesses = (endTimeCached - startTimeCached) / 100
|
|
583
|
+
|
|
584
|
+
console.log(`Time for first view (no cache): ${timeForFirstAccess}ms`)
|
|
585
|
+
console.log(`Average time per cached view: ${timeForCachedAccesses}ms`)
|
|
586
|
+
|
|
587
|
+
// Cached access should be significantly faster
|
|
588
|
+
expect(timeForCachedAccesses).toBeLessThan(timeForFirstAccess / 10)
|
|
589
|
+
})
|
|
365
590
|
})
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
import assert from "assert"
|
|
2
2
|
import { describe, it } from "vitest"
|
|
3
3
|
import { next as Automerge } from "@automerge/automerge"
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
encodeHeads,
|
|
6
|
+
generateAutomergeUrl,
|
|
7
|
+
parseAutomergeUrl,
|
|
8
|
+
} from "../src/AutomergeUrl.js"
|
|
5
9
|
import { DocHandle } from "../src/DocHandle.js"
|
|
6
10
|
import { eventPromise } from "../src/helpers/eventPromise.js"
|
|
7
11
|
import {
|
|
@@ -67,11 +71,14 @@ describe("DocSynchronizer", () => {
|
|
|
67
71
|
|
|
68
72
|
assert.equal(message1.peerId, "alice")
|
|
69
73
|
assert.equal(message1.documentId, handle.documentId)
|
|
70
|
-
assert.
|
|
74
|
+
assert.deepStrictEqual(message1.syncState.lastSentHeads, [])
|
|
71
75
|
|
|
72
76
|
assert.equal(message2.peerId, "alice")
|
|
73
77
|
assert.equal(message2.documentId, handle.documentId)
|
|
74
|
-
assert.
|
|
78
|
+
assert.deepStrictEqual(
|
|
79
|
+
encodeHeads(message2.syncState.lastSentHeads),
|
|
80
|
+
handle.heads()
|
|
81
|
+
)
|
|
75
82
|
})
|
|
76
83
|
|
|
77
84
|
it("still syncs with a peer after it disconnects and reconnects", async () => {
|