@dxos/echo-pipeline 0.5.8-main.f69e6e4 → 0.5.8

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 (46) hide show
  1. package/dist/lib/browser/{chunk-I2J5TTHJ.mjs → chunk-GANAND63.mjs} +23 -48
  2. package/dist/lib/browser/{chunk-I2J5TTHJ.mjs.map → chunk-GANAND63.mjs.map} +3 -3
  3. package/dist/lib/browser/index.mjs +26 -31
  4. package/dist/lib/browser/index.mjs.map +3 -3
  5. package/dist/lib/browser/meta.json +1 -1
  6. package/dist/lib/browser/testing/index.mjs +4 -99
  7. package/dist/lib/browser/testing/index.mjs.map +4 -4
  8. package/dist/lib/node/{chunk-QPCNQ4ZK.cjs → chunk-M475BGBI.cjs} +25 -51
  9. package/dist/lib/node/chunk-M475BGBI.cjs.map +7 -0
  10. package/dist/lib/node/index.cjs +47 -52
  11. package/dist/lib/node/index.cjs.map +3 -3
  12. package/dist/lib/node/meta.json +1 -1
  13. package/dist/lib/node/testing/index.cjs +13 -107
  14. package/dist/lib/node/testing/index.cjs.map +4 -4
  15. package/dist/types/src/automerge/automerge-doc-loader.d.ts +3 -7
  16. package/dist/types/src/automerge/automerge-doc-loader.d.ts.map +1 -1
  17. package/dist/types/src/automerge/automerge-host.d.ts.map +1 -1
  18. package/dist/types/src/automerge/migrations.d.ts.map +1 -1
  19. package/dist/types/src/space/space-manager.d.ts +2 -2
  20. package/dist/types/src/space/space-manager.d.ts.map +1 -1
  21. package/dist/types/src/space/space-protocol.d.ts +2 -2
  22. package/dist/types/src/space/space-protocol.d.ts.map +1 -1
  23. package/dist/types/src/space/space.d.ts +1 -9
  24. package/dist/types/src/space/space.d.ts.map +1 -1
  25. package/dist/types/src/testing/index.d.ts +0 -1
  26. package/dist/types/src/testing/index.d.ts.map +1 -1
  27. package/dist/types/src/testing/test-agent-builder.d.ts +2 -2
  28. package/dist/types/src/testing/test-agent-builder.d.ts.map +1 -1
  29. package/package.json +33 -33
  30. package/src/automerge/automerge-doc-loader.test.ts +2 -5
  31. package/src/automerge/automerge-doc-loader.ts +4 -6
  32. package/src/automerge/automerge-host.test.ts +553 -1
  33. package/src/automerge/automerge-host.ts +5 -12
  34. package/src/automerge/automerge-repo.test.ts +2 -450
  35. package/src/automerge/migrations.ts +1 -2
  36. package/src/automerge/storage-adapter.test.ts +15 -81
  37. package/src/space/space-manager.ts +4 -6
  38. package/src/space/space-protocol.test.ts +3 -3
  39. package/src/space/space-protocol.ts +3 -3
  40. package/src/space/space.ts +2 -32
  41. package/src/testing/index.ts +0 -1
  42. package/src/testing/test-agent-builder.ts +4 -4
  43. package/dist/lib/node/chunk-QPCNQ4ZK.cjs.map +0 -7
  44. package/dist/types/src/testing/test-network-adapter.d.ts +0 -18
  45. package/dist/types/src/testing/test-network-adapter.d.ts.map +0 -1
  46. package/src/testing/test-network-adapter.ts +0 -62
@@ -3,40 +3,16 @@
3
3
  //
4
4
 
5
5
  import { expect } from 'chai';
6
- import waitForExpect from 'wait-for-expect';
7
6
 
8
- import { asyncTimeout, sleep } from '@dxos/async';
9
7
  import { type Heads, change, clone, equals, from, getBackend, getHeads } from '@dxos/automerge/automerge';
10
- import { type Message, Repo, type PeerId, type DocumentId, type HandleState } from '@dxos/automerge/automerge-repo';
8
+ import { Repo } from '@dxos/automerge/automerge-repo';
11
9
  import { randomBytes } from '@dxos/crypto';
12
- import { PublicKey } from '@dxos/keys';
13
10
  import { createTestLevel } from '@dxos/kv-store/testing';
14
- import { TestBuilder as TeleportBuilder, TestPeer as TeleportPeer } from '@dxos/teleport/testing';
15
- import { afterTest, describe, openAndClose, test } from '@dxos/test';
11
+ import { describe, openAndClose, test } from '@dxos/test';
16
12
 
17
- import { EchoNetworkAdapter } from './echo-network-adapter';
18
13
  import { LevelDBStorageAdapter } from './leveldb-storage-adapter';
19
- import { MeshEchoReplicator } from './mesh-echo-replicator';
20
- import { TestAdapter } from '../testing';
21
14
 
22
15
  describe('AutomergeRepo', () => {
23
- test('change events', () => {
24
- const repo = new Repo({ network: [] });
25
- const handle = repo.create<{ field?: string }>();
26
-
27
- let valueDuringChange: string | undefined;
28
-
29
- handle.addListener('change', (doc) => {
30
- valueDuringChange = handle.docSync().field;
31
- });
32
-
33
- handle.change((doc: any) => {
34
- doc.field = 'value';
35
- });
36
-
37
- expect(valueDuringChange).to.eq('value');
38
- });
39
-
40
16
  test('flush', async () => {
41
17
  const level = createTestLevel();
42
18
  const storage = new LevelDBStorageAdapter({ db: level.sublevel('automerge') });
@@ -91,428 +67,4 @@ describe('AutomergeRepo', () => {
91
67
  expect(equals(a, b)).to.be.false;
92
68
  expect(equals(a, c)).to.be.true;
93
69
  });
94
-
95
- describe('network', () => {
96
- test('basic networking', async () => {
97
- const hostAdapter: TestAdapter = new TestAdapter({
98
- send: (message: Message) => clientAdapter.receive(message),
99
- });
100
- const clientAdapter: TestAdapter = new TestAdapter({
101
- send: (message: Message) => hostAdapter.receive(message),
102
- });
103
-
104
- const host = new Repo({
105
- network: [hostAdapter],
106
- });
107
- const client = new Repo({
108
- network: [clientAdapter],
109
- });
110
- hostAdapter.ready();
111
- clientAdapter.ready();
112
- await hostAdapter.onConnect.wait();
113
- await clientAdapter.onConnect.wait();
114
- hostAdapter.peerCandidate(clientAdapter.peerId!);
115
- clientAdapter.peerCandidate(hostAdapter.peerId!);
116
-
117
- const handle = host.create();
118
- const text = 'Hello world';
119
- handle.change((doc: any) => {
120
- doc.text = text;
121
- });
122
-
123
- const docOnClient = client.find(handle.url);
124
- expect((await asyncTimeout(docOnClient.doc(), 1000)).text).to.equal(text);
125
- });
126
-
127
- test('share policy gets enabled afterwards', async () => {
128
- const [hostAdapter, clientAdapter] = TestAdapter.createPair();
129
- let sharePolicy = false;
130
-
131
- const host = new Repo({
132
- network: [hostAdapter],
133
- peerId: 'host' as PeerId,
134
- sharePolicy: async () => sharePolicy,
135
- });
136
- const client = new Repo({
137
- network: [clientAdapter],
138
- peerId: 'client' as PeerId,
139
- sharePolicy: async () => sharePolicy,
140
- });
141
- hostAdapter.ready();
142
- clientAdapter.ready();
143
- await hostAdapter.onConnect.wait();
144
- await clientAdapter.onConnect.wait();
145
- hostAdapter.peerCandidate(clientAdapter.peerId!);
146
- clientAdapter.peerCandidate(hostAdapter.peerId!);
147
-
148
- const handle = host.create();
149
- const text = 'Hello world';
150
- handle.change((doc: any) => {
151
- doc.text = text;
152
- });
153
-
154
- {
155
- const docOnClient = client.find(handle.url);
156
- await asyncTimeout(docOnClient.whenReady(['unavailable']), 1000);
157
- }
158
-
159
- sharePolicy = true;
160
-
161
- {
162
- const docOnClient = client.find(handle.url);
163
- // TODO(mykola): We expect the document to be available here, but it's not.
164
- await asyncTimeout(docOnClient.whenReady(['unavailable']), 1000);
165
- }
166
- });
167
-
168
- test('two documents and share policy switching', async () => {
169
- const [hostAdapter, clientAdapter] = TestAdapter.createPair();
170
- const allowedDocs: DocumentId[] = [];
171
-
172
- const host: Repo = new Repo({
173
- network: [hostAdapter],
174
- peerId: 'host' as PeerId,
175
- sharePolicy: async (_, docId) => (docId ? allowedDocs.includes(docId) && !!host.handles[docId] : false),
176
- });
177
-
178
- const client: Repo = new Repo({
179
- network: [clientAdapter],
180
- peerId: 'client' as PeerId,
181
- sharePolicy: async (_, docId) => (docId ? allowedDocs.includes(docId) && !!client.handles[docId] : false),
182
- });
183
-
184
- const firstHandle = host.create();
185
- firstHandle.change((doc: any) => (doc.text = 'Hello world'));
186
- await host.find(firstHandle.url).whenReady();
187
- allowedDocs.push(firstHandle.documentId);
188
-
189
- {
190
- // Initiate connection.
191
- hostAdapter.ready();
192
- clientAdapter.ready();
193
- await hostAdapter.onConnect.wait();
194
- await clientAdapter.onConnect.wait();
195
- hostAdapter.peerCandidate(clientAdapter.peerId!);
196
- clientAdapter.peerCandidate(hostAdapter.peerId!);
197
- }
198
-
199
- {
200
- const firstDocOnClient = client.find(firstHandle.url);
201
- await asyncTimeout(firstDocOnClient.whenReady(), 1000);
202
- expect(firstDocOnClient.docSync().text).to.equal('Hello world');
203
- }
204
-
205
- const secondHandle = host.create();
206
- secondHandle.change((doc: any) => (doc.text = 'Hello world'));
207
- await host.find(secondHandle.url).whenReady();
208
- allowedDocs.push(secondHandle.documentId);
209
-
210
- {
211
- const secondDocOnClient = client.find(secondHandle.url);
212
- await asyncTimeout(secondDocOnClient.whenReady(), 1000);
213
- expect(secondDocOnClient.docSync().text).to.equal('Hello world');
214
- }
215
- });
216
-
217
- test('recovering from a lost connection', async () => {
218
- let connectionState: 'on' | 'off' = 'on';
219
-
220
- const hostAdapter: TestAdapter = new TestAdapter({
221
- send: (message: Message) => connectionState === 'on' && sleep(10).then(() => clientAdapter.receive(message)),
222
- });
223
- const clientAdapter: TestAdapter = new TestAdapter({
224
- send: (message: Message) => connectionState === 'on' && sleep(10).then(() => hostAdapter.receive(message)),
225
- });
226
-
227
- const host = new Repo({
228
- network: [hostAdapter],
229
- });
230
- const client = new Repo({
231
- network: [clientAdapter],
232
- });
233
-
234
- // Establish connection.
235
- hostAdapter.ready();
236
- clientAdapter.ready();
237
- await hostAdapter.onConnect.wait();
238
- await clientAdapter.onConnect.wait();
239
- hostAdapter.peerCandidate(clientAdapter.peerId!);
240
- clientAdapter.peerCandidate(hostAdapter.peerId!);
241
-
242
- const handle = host.create();
243
- const docOnClient = client.find(handle.url);
244
- {
245
- const sanityText = 'Hello world';
246
- handle.change((doc: any) => {
247
- doc.sanityText = sanityText;
248
- });
249
-
250
- expect((await asyncTimeout(docOnClient.doc(), 1000)).sanityText).to.equal(sanityText);
251
- }
252
-
253
- // Disrupt connection.
254
- const offlineText = 'This has been written while the connection was off';
255
- {
256
- connectionState = 'off';
257
-
258
- handle.change((doc: any) => {
259
- doc.offlineText = offlineText;
260
- });
261
-
262
- await sleep(100);
263
- expect((await asyncTimeout(docOnClient.doc(), 1000)).offlineText).to.be.undefined;
264
- }
265
-
266
- // Re-establish connection.
267
- const onlineText = 'This has been written after the connection was re-established';
268
- {
269
- connectionState = 'on';
270
- hostAdapter.peerDisconnected(clientAdapter.peerId!);
271
- clientAdapter.peerDisconnected(hostAdapter.peerId!);
272
- hostAdapter.peerCandidate(clientAdapter.peerId!);
273
- clientAdapter.peerCandidate(hostAdapter.peerId!);
274
-
275
- handle.change((doc: any) => {
276
- doc.onlineText = onlineText;
277
- });
278
- await sleep(100);
279
- expect((await asyncTimeout(docOnClient.doc(), 1000)).onlineText).to.equal(onlineText);
280
- expect((await asyncTimeout(docOnClient.doc(), 1000)).offlineText).to.equal(offlineText);
281
- }
282
- });
283
-
284
- test('integration test with teleport', async () => {
285
- const [spaceKey] = PublicKey.randomSequence();
286
-
287
- const createAutomergeRepo = async () => {
288
- const meshAdapter = new MeshEchoReplicator();
289
- const echoAdapter = new EchoNetworkAdapter({
290
- getContainingSpaceForDocument: async () => spaceKey,
291
- });
292
- const repo = new Repo({
293
- network: [echoAdapter],
294
- });
295
- await echoAdapter.open();
296
- await echoAdapter.whenConnected();
297
- await echoAdapter.addReplicator(meshAdapter);
298
- return { repo, meshAdapter };
299
- };
300
- const peer1 = await createAutomergeRepo();
301
- const peer2 = await createAutomergeRepo();
302
-
303
- const handle = peer1.repo.create();
304
-
305
- const teleportBuilder = new TeleportBuilder();
306
- afterTest(() => teleportBuilder.destroy());
307
-
308
- const [teleportPeer1, teleportPeer2] = teleportBuilder.createPeers({ factory: () => new TeleportPeer() });
309
- {
310
- // Initiate connection.
311
- const [connection1, connection2] = await teleportBuilder.connect(teleportPeer1, teleportPeer2);
312
- connection1.teleport.addExtension('automerge', peer1.meshAdapter.createExtension());
313
- connection2.teleport.addExtension('automerge', peer2.meshAdapter.createExtension());
314
-
315
- // Test connection.
316
- const text = 'Hello world';
317
- handle.change((doc: any) => {
318
- doc.text = text;
319
- });
320
- await waitForExpect(async () => {
321
- const docOnPeer2 = peer2.repo.find(handle.url);
322
- const doc = await asyncTimeout(docOnPeer2.doc(), 1000);
323
- expect(doc.text).to.eq(text);
324
- }, 1000);
325
- }
326
-
327
- const offlineText = 'This has been written while the connection was off';
328
- {
329
- // Disconnect peers.
330
- await teleportBuilder.disconnect(teleportPeer1, teleportPeer2);
331
-
332
- // Make offline changes.
333
- const offlineText = 'This has been written while the connection was off';
334
- handle.change((doc: any) => {
335
- doc.offlineText = offlineText;
336
- });
337
- const docOnPeer2 = peer2.repo.find(handle.url);
338
- await sleep(100);
339
- expect((await asyncTimeout(docOnPeer2.doc(), 1000)).offlineText).to.be.undefined;
340
- }
341
-
342
- {
343
- const docOnPeer2 = peer2.repo.find(handle.url);
344
- const receivedUpdate = new Promise((resolve, reject) => docOnPeer2.once('heads-changed', resolve));
345
-
346
- // Reconnect peers.
347
- const [connection1, connection2] = await teleportBuilder.connect(teleportPeer1, teleportPeer2);
348
- connection1.teleport.addExtension('automerge', peer1.meshAdapter.createExtension());
349
- connection2.teleport.addExtension('automerge', peer2.meshAdapter.createExtension());
350
-
351
- // Wait for offline changes to be synced.
352
- await receivedUpdate;
353
- await docOnPeer2.whenReady();
354
- expect((await asyncTimeout(docOnPeer2.doc(), 1000)).offlineText).to.eq(offlineText);
355
-
356
- // Test connection.
357
- const onlineText = 'This has been written after the connection was re-established';
358
- const receivedOnlineUpdate = new Promise((resolve, reject) => docOnPeer2.once('heads-changed', resolve));
359
- handle.change((doc: any) => {
360
- doc.onlineText = onlineText;
361
- });
362
- await receivedOnlineUpdate;
363
- await docOnPeer2.whenReady();
364
- expect((await asyncTimeout(docOnPeer2.doc(), 1000)).onlineText).to.eq(onlineText);
365
- }
366
- });
367
-
368
- test('replication though a 4 peer chain', async () => {
369
- const pairAB = TestAdapter.createPair();
370
- const pairBC = TestAdapter.createPair();
371
- const pairCD = TestAdapter.createPair();
372
-
373
- const repoA = new Repo({
374
- peerId: 'A' as any,
375
- network: [pairAB[0]],
376
- sharePolicy: async () => true,
377
- });
378
- const _repoB = new Repo({
379
- peerId: 'B' as any,
380
- network: [pairAB[1], pairBC[0]],
381
- sharePolicy: async () => true,
382
- });
383
- const _repoC = new Repo({
384
- peerId: 'C' as any,
385
- network: [pairBC[1], pairCD[0]],
386
- sharePolicy: async () => true,
387
- });
388
- const repoD = new Repo({
389
- peerId: 'D' as any,
390
- network: [pairCD[1]],
391
- sharePolicy: async () => true,
392
- });
393
-
394
- for (const pair of [pairAB, pairBC, pairCD]) {
395
- pair[0].ready();
396
- pair[1].ready();
397
- await pair[0].onConnect.wait();
398
- await pair[1].onConnect.wait();
399
- pair[0].peerCandidate(pair[1].peerId!);
400
- pair[1].peerCandidate(pair[0].peerId!);
401
- }
402
-
403
- const docA = repoA.create();
404
- // NOTE: Doesn't work if the doc is empty.
405
- docA.change((doc: any) => {
406
- doc.text = 'Hello world';
407
- });
408
-
409
- // If we wait here for replication to finish naturally, the test will pass.
410
-
411
- const docD = repoD.find(docA.url);
412
-
413
- await docD.whenReady();
414
- });
415
-
416
- test('replication though a 3 peer chain', async () => {
417
- const pairAB = TestAdapter.createPair();
418
- const pairBC = TestAdapter.createPair();
419
-
420
- const repoA = new Repo({
421
- peerId: 'A' as any,
422
- network: [pairAB[0]],
423
- sharePolicy: async () => true,
424
- });
425
- const repoB = new Repo({
426
- peerId: 'B' as any,
427
- network: [pairAB[1], pairBC[0]],
428
- sharePolicy: async () => true,
429
- });
430
- const repoC = new Repo({
431
- peerId: 'C' as any,
432
- network: [pairBC[1]],
433
- sharePolicy: async () => true,
434
- });
435
-
436
- for (const pair of [pairAB, pairBC]) {
437
- pair[0].ready();
438
- pair[1].ready();
439
- await pair[0].onConnect.wait();
440
- await pair[1].onConnect.wait();
441
- pair[0].peerCandidate(pair[1].peerId!);
442
- pair[1].peerCandidate(pair[0].peerId!);
443
- }
444
-
445
- const docA = repoA.create();
446
- // NOTE: Doesn't work if the doc is empty.
447
- docA.change((doc: any) => {
448
- doc.text = 'Hello world';
449
- });
450
-
451
- const _docB = repoB.find(docA.url);
452
- const docC = repoC.find(docA.url);
453
-
454
- await docC.whenReady();
455
- });
456
-
457
- test('replicate document after request', async () => {
458
- const [adapter1, adapter2] = TestAdapter.createPair();
459
- const repoA = new Repo({
460
- peerId: 'A' as any,
461
- network: [adapter1],
462
- sharePolicy: async () => true,
463
- });
464
- const repoB = new Repo({
465
- peerId: 'B' as any,
466
- network: [adapter2],
467
- sharePolicy: async () => true,
468
- });
469
-
470
- const unavailable: HandleState = 'unavailable';
471
-
472
- {
473
- // Connect repos.
474
- adapter1.ready();
475
- adapter2.ready();
476
- await adapter1.onConnect.wait();
477
- await adapter2.onConnect.wait();
478
- }
479
-
480
- const docA = repoA.create();
481
- // NOTE: Doesn't work if the doc is empty.
482
- docA.change((doc: any) => {
483
- doc.text = 'Hello world';
484
- });
485
-
486
- const docB = repoB.find(docA.url);
487
- {
488
- // Request document from repoB.
489
- await asyncTimeout(docB.whenReady([unavailable]), 1_000);
490
- }
491
-
492
- {
493
- // Failing to find a document.
494
- // await (docB.whenReady([unavailable]), 1_000);
495
- }
496
-
497
- {
498
- adapter1.peerCandidate(adapter2.peerId!);
499
- await sleep(100);
500
- adapter2.peerCandidate(adapter1.peerId!);
501
- }
502
-
503
- {
504
- await asyncTimeout(docB.whenReady([unavailable]), 1_000);
505
- }
506
-
507
- {
508
- // Note: Retry hack.
509
- adapter1.peerDisconnected(adapter2.peerId!);
510
- adapter2.peerDisconnected(adapter1.peerId!);
511
- adapter1.peerCandidate(adapter2.peerId!);
512
- adapter2.peerCandidate(adapter1.peerId!);
513
-
514
- await asyncTimeout(docB.whenReady(), 1_000);
515
- }
516
- });
517
- });
518
70
  });
@@ -12,7 +12,7 @@ import { AutomergeStorageAdapter } from './automerge-storage-adapter';
12
12
  import { encodingOptions } from './leveldb-storage-adapter';
13
13
 
14
14
  export const levelMigration = async ({ db, directory }: { db: SublevelDB; directory: Directory }) => {
15
- // Note: Make auto-migration from previous storage to leveldb here.
15
+ // Note: Make automigration from previous storage to leveldb here.
16
16
  const isNewLevel = !(await db
17
17
  .iterator<StorageKey, Uint8Array>({
18
18
  ...encodingOptions,
@@ -32,7 +32,6 @@ export const levelMigration = async ({ db, directory }: { db: SublevelDB; direct
32
32
  if (chunks.length === 0) {
33
33
  return;
34
34
  }
35
-
36
35
  const batch = db.batch();
37
36
  log.info('found chunks on old storage adapter', { chunks: chunks.length });
38
37
  for (const { key, data } of await oldStorageAdapter.loadRange([])) {
@@ -5,24 +5,18 @@
5
5
  import { expect } from 'chai';
6
6
 
7
7
  import { type StorageAdapterInterface } from '@dxos/automerge/automerge-repo';
8
- import { randomBytes } from '@dxos/crypto';
9
8
  import { PublicKey } from '@dxos/keys';
10
9
  import { createTestLevel } from '@dxos/kv-store/testing';
11
10
  import { StorageType, createStorage } from '@dxos/random-access-storage';
12
- import { afterTest, describe, test } from '@dxos/test';
13
- import { arrayToBuffer, bufferToArray, type MaybePromise } from '@dxos/util';
11
+ import { afterTest, describe, openAndClose, test } from '@dxos/test';
12
+ import { type MaybePromise } from '@dxos/util';
14
13
 
15
14
  import { AutomergeStorageAdapter } from './automerge-storage-adapter';
16
15
  import { LevelDBStorageAdapter } from './leveldb-storage-adapter';
17
16
 
18
17
  const runTests = (
19
18
  testNamespace: string,
20
- /** Run per test. Expects automatic clean-up with `afterTest`. */
21
- createAdapter: (root?: string) => MaybePromise<{
22
- adapter: StorageAdapterInterface;
23
- /** Would be called automatically with `afterTest`. Exposed for mid-test clean-up. */
24
- close: () => MaybePromise<void>;
25
- }>,
19
+ /** Run per test. Expects automatic clean-up with `afterTest`. */ createAdapter: () => MaybePromise<StorageAdapterInterface>,
26
20
  ) => {
27
21
  describe(testNamespace, () => {
28
22
  const chunks = [
@@ -33,14 +27,14 @@ const runTests = (
33
27
  ];
34
28
 
35
29
  test('should store and retrieve data', async () => {
36
- const { adapter } = await createAdapter();
30
+ const adapter = await createAdapter();
37
31
 
38
32
  await adapter.save(chunks[0].key, chunks[0].data);
39
33
  expect(await adapter.load(chunks[0].key)).to.deep.equal(chunks[0].data);
40
34
  });
41
35
 
42
36
  test('loadRange return inputs with correct prefixes', async () => {
43
- const { adapter } = await createAdapter();
37
+ const adapter = await createAdapter();
44
38
 
45
39
  for (const chunk of chunks) {
46
40
  await adapter.save(chunk.key, chunk.data);
@@ -52,7 +46,7 @@ const runTests = (
52
46
  });
53
47
 
54
48
  test('deletion works', async () => {
55
- const { adapter } = await createAdapter();
49
+ const adapter = await createAdapter();
56
50
 
57
51
  for (const chunk of chunks) {
58
52
  await adapter.save(chunk.key, chunk.data);
@@ -70,87 +64,27 @@ const runTests = (
70
64
  expect(await adapter.load(['a', 'b', 'c', '2'])).to.deep.equal(chunks[1].data);
71
65
  expect(await adapter.load(['a', 'b', 'd', '3'])).to.be.undefined;
72
66
  });
73
-
74
- test('loadRange', async () => {
75
- const root = `/tmp/${randomBytes(16).toString('hex')}`;
76
- {
77
- const { adapter, close } = await createAdapter(root);
78
- await adapter.save(['test', '1'], bufferToArray(Buffer.from('one')));
79
- await adapter.save(['test', '2'], bufferToArray(Buffer.from('two')));
80
- await adapter.save(['bar', '1'], bufferToArray(Buffer.from('bar')));
81
- await close();
82
- }
83
-
84
- {
85
- const { adapter } = await createAdapter(root);
86
- const range = await adapter.loadRange(['test']);
87
- expect(range.map((chunk) => arrayToBuffer(chunk.data!).toString())).to.deep.eq(['one', 'two']);
88
- expect(range.map((chunk) => chunk.key)).to.deep.eq([
89
- ['test', '1'],
90
- ['test', '2'],
91
- ]);
92
- }
93
- });
94
-
95
- test('removeRange', async () => {
96
- const root = `/tmp/${randomBytes(16).toString('hex')}`;
97
- {
98
- const { adapter, close } = await createAdapter(root);
99
- await adapter.save(['test', '1'], bufferToArray(Buffer.from('one')));
100
- await adapter.save(['test', '2'], bufferToArray(Buffer.from('two')));
101
- await adapter.save(['bar', '1'], bufferToArray(Buffer.from('bar')));
102
- await close();
103
- }
104
-
105
- {
106
- const { adapter } = await createAdapter(root);
107
- await adapter.removeRange(['test']);
108
- const range = await adapter.loadRange(['test']);
109
- expect(range.map((chunk) => arrayToBuffer(chunk.data!).toString())).to.deep.eq([]);
110
- const range2 = await adapter.loadRange(['bar']);
111
- expect(range2.map((chunk) => arrayToBuffer(chunk.data!).toString())).to.deep.eq(['bar']);
112
- expect(range2.map((chunk) => chunk.key)).to.deep.eq([['bar', '1']]);
113
- }
114
- });
115
67
  });
116
68
  };
117
69
 
118
70
  /**
119
71
  * Run tests for AutomergeStorageAdapter.
120
72
  */
121
- runTests('AutomergeStorageAdapter', (root?: string) => {
122
- const storage = createStorage({ type: root ? StorageType.NODE : StorageType.RAM, root });
73
+ runTests('AutomergeStorageAdapter', () => {
74
+ const storage = createStorage({ type: StorageType.RAM });
75
+ afterTest(() => storage.close());
123
76
  const dir = storage.createDirectory('automerge');
124
77
  const adapter = new AutomergeStorageAdapter(dir);
125
-
126
- const close = async () => {
127
- await adapter.close();
128
- await storage.close();
129
- };
130
- afterTest(close);
131
-
132
- return {
133
- adapter,
134
- close,
135
- };
78
+ afterTest(() => adapter.close());
79
+ return adapter;
136
80
  });
137
81
 
138
82
  /**
139
83
  * Run tests for LevelDBStorageAdapter.
140
84
  */
141
- runTests('LevelDBStorageAdapter', async (root?: string) => {
142
- const level = createTestLevel(root);
143
- await level.open();
85
+ runTests('LevelDBStorageAdapter', async () => {
86
+ const level = createTestLevel();
144
87
  const adapter = new LevelDBStorageAdapter({ db: level.sublevel('automerge') });
145
- await adapter.open?.();
146
-
147
- const close = async () => {
148
- await adapter.close();
149
- await level.close();
150
- };
151
- afterTest(close);
152
- return {
153
- adapter,
154
- close,
155
- };
88
+ await openAndClose(level, adapter as any);
89
+ return adapter;
156
90
  });
@@ -8,7 +8,7 @@ import { failUndefined } from '@dxos/debug';
8
8
  import { type FeedStore } from '@dxos/feed-store';
9
9
  import { PublicKey } from '@dxos/keys';
10
10
  import { log } from '@dxos/log';
11
- import { type SwarmNetworkManager } from '@dxos/network-manager';
11
+ import { type NetworkManager } from '@dxos/network-manager';
12
12
  import { trace } from '@dxos/protocols';
13
13
  import type { FeedMessage } from '@dxos/protocols/proto/dxos/echo/feed';
14
14
  import { type SpaceMetadata } from '@dxos/protocols/proto/dxos/echo/metadata';
@@ -16,14 +16,14 @@ import { type Teleport } from '@dxos/teleport';
16
16
  import { type BlobStore } from '@dxos/teleport-extension-object-sync';
17
17
  import { ComplexMap } from '@dxos/util';
18
18
 
19
- import { Space, createIdFromSpaceKey } from './space';
19
+ import { Space } from './space';
20
20
  import { SpaceProtocol, type SwarmIdentity } from './space-protocol';
21
21
  import { SnapshotManager, type SnapshotStore } from '../db-host';
22
22
  import { type MetadataStore } from '../metadata';
23
23
 
24
24
  export type SpaceManagerParams = {
25
25
  feedStore: FeedStore<FeedMessage>;
26
- networkManager: SwarmNetworkManager;
26
+ networkManager: NetworkManager;
27
27
  metadataStore: MetadataStore;
28
28
 
29
29
  /**
@@ -54,7 +54,7 @@ export type ConstructSpaceParams = {
54
54
  export class SpaceManager {
55
55
  private readonly _spaces = new ComplexMap<PublicKey, Space>(PublicKey.hash);
56
56
  private readonly _feedStore: FeedStore<FeedMessage>;
57
- private readonly _networkManager: SwarmNetworkManager;
57
+ private readonly _networkManager: NetworkManager;
58
58
  private readonly _metadataStore: MetadataStore;
59
59
  private readonly _snapshotStore: SnapshotStore;
60
60
  private readonly _blobStore: BlobStore;
@@ -98,7 +98,6 @@ export class SpaceManager {
98
98
  const genesisFeed = await this._feedStore.openFeed(metadata.genesisFeedKey ?? failUndefined());
99
99
 
100
100
  const spaceKey = metadata.key;
101
- const spaceId = await createIdFromSpaceKey(spaceKey);
102
101
  const protocol = new SpaceProtocol({
103
102
  topic: spaceKey,
104
103
  swarmIdentity,
@@ -110,7 +109,6 @@ export class SpaceManager {
110
109
  const snapshotManager = new SnapshotManager(this._snapshotStore, this._blobStore, protocol.blobSync);
111
110
 
112
111
  const space = new Space({
113
- id: spaceId,
114
112
  spaceKey,
115
113
  protocol,
116
114
  genesisFeed,