@dxos/echo-pipeline 0.4.10-main.d4e372f → 0.4.10-main.d51f2c2

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 (60) hide show
  1. package/dist/lib/browser/{chunk-RTEEJ723.mjs → chunk-SYE4EK33.mjs} +30 -35
  2. package/dist/lib/browser/chunk-SYE4EK33.mjs.map +7 -0
  3. package/dist/lib/browser/index.mjs +279 -159
  4. package/dist/lib/browser/index.mjs.map +4 -4
  5. package/dist/lib/browser/meta.json +1 -1
  6. package/dist/lib/browser/testing/index.mjs +8 -2
  7. package/dist/lib/browser/testing/index.mjs.map +4 -4
  8. package/dist/lib/node/{chunk-7VZVCCNF.cjs → chunk-WCTX6RNS.cjs} +35 -40
  9. package/dist/lib/node/chunk-WCTX6RNS.cjs.map +7 -0
  10. package/dist/lib/node/index.cjs +285 -168
  11. package/dist/lib/node/index.cjs.map +4 -4
  12. package/dist/lib/node/meta.json +1 -1
  13. package/dist/lib/node/testing/index.cjs +18 -13
  14. package/dist/lib/node/testing/index.cjs.map +4 -4
  15. package/dist/types/src/automerge/automerge-doc-loader.d.ts +2 -0
  16. package/dist/types/src/automerge/automerge-doc-loader.d.ts.map +1 -1
  17. package/dist/types/src/automerge/automerge-host.d.ts +20 -10
  18. package/dist/types/src/automerge/automerge-host.d.ts.map +1 -1
  19. package/dist/types/src/automerge/index.d.ts +1 -0
  20. package/dist/types/src/automerge/index.d.ts.map +1 -1
  21. package/dist/types/src/automerge/level.test.d.ts +2 -0
  22. package/dist/types/src/automerge/level.test.d.ts.map +1 -0
  23. package/dist/types/src/automerge/leveldb-storage-adapter.d.ts +27 -0
  24. package/dist/types/src/automerge/leveldb-storage-adapter.d.ts.map +1 -0
  25. package/dist/types/src/automerge/migrations.d.ts +7 -0
  26. package/dist/types/src/automerge/migrations.d.ts.map +1 -0
  27. package/dist/types/src/automerge/reference.d.ts +15 -0
  28. package/dist/types/src/automerge/reference.d.ts.map +1 -0
  29. package/dist/types/src/automerge/storage-adapter.test.d.ts +2 -0
  30. package/dist/types/src/automerge/storage-adapter.test.d.ts.map +1 -0
  31. package/dist/types/src/automerge/types.d.ts +7 -2
  32. package/dist/types/src/automerge/types.d.ts.map +1 -1
  33. package/dist/types/src/metadata/metadata-store.d.ts +2 -1
  34. package/dist/types/src/metadata/metadata-store.d.ts.map +1 -1
  35. package/dist/types/src/space/space.d.ts +4 -8
  36. package/dist/types/src/space/space.d.ts.map +1 -1
  37. package/dist/types/src/testing/index.d.ts +1 -0
  38. package/dist/types/src/testing/index.d.ts.map +1 -1
  39. package/dist/types/src/testing/level.d.ts +3 -0
  40. package/dist/types/src/testing/level.d.ts.map +1 -0
  41. package/dist/types/src/testing/test-agent-builder.d.ts +2 -2
  42. package/package.json +33 -30
  43. package/src/automerge/automerge-doc-loader.ts +6 -0
  44. package/src/automerge/automerge-host.test.ts +19 -5
  45. package/src/automerge/automerge-host.ts +52 -33
  46. package/src/automerge/index.ts +1 -0
  47. package/src/automerge/level.test.ts +43 -0
  48. package/src/automerge/leveldb-storage-adapter.ts +105 -0
  49. package/src/automerge/migrations.ts +41 -0
  50. package/src/automerge/reference.ts +31 -0
  51. package/src/automerge/storage-adapter.test.ts +90 -0
  52. package/src/automerge/types.ts +7 -5
  53. package/src/db-host/data-service.ts +1 -1
  54. package/src/metadata/metadata-store.ts +17 -8
  55. package/src/space/space.test.ts +7 -7
  56. package/src/space/space.ts +6 -21
  57. package/src/testing/index.ts +1 -0
  58. package/src/testing/level.ts +11 -0
  59. package/dist/lib/browser/chunk-RTEEJ723.mjs.map +0 -7
  60. package/dist/lib/node/chunk-7VZVCCNF.cjs.map +0 -7
@@ -2,6 +2,7 @@ import "@dxos/node-std/globals";
2
2
  import {
3
3
  AuthExtension,
4
4
  AuthStatus,
5
+ Buffer,
5
6
  DataServiceImpl,
6
7
  MOCK_AUTH_PROVIDER,
7
8
  MOCK_AUTH_VERIFIER,
@@ -16,128 +17,105 @@ import {
16
17
  TimeframeClock,
17
18
  codec,
18
19
  createMappedFeedWriter,
20
+ hasInvitationExpired,
19
21
  mapFeedIndexesToTimeframe,
20
22
  mapTimeframeToFeedIndexes,
21
23
  startAfter,
22
24
  valueEncoding
23
- } from "./chunk-RTEEJ723.mjs";
25
+ } from "./chunk-SYE4EK33.mjs";
24
26
 
25
27
  // packages/core/echo/echo-pipeline/src/automerge/automerge-host.ts
26
28
  import { next as automerge, getHeads } from "@dxos/automerge/automerge";
27
29
  import { Repo } from "@dxos/automerge/automerge-repo";
28
- import { IndexedDBStorageAdapter } from "@dxos/automerge/automerge-repo-storage-indexeddb";
29
30
  import { Context } from "@dxos/context";
30
31
  import { PublicKey } from "@dxos/keys";
31
- import { log as log3 } from "@dxos/log";
32
+ import { log as log4 } from "@dxos/log";
32
33
  import { idCodec } from "@dxos/protocols";
33
- import { StorageType } from "@dxos/random-access-storage";
34
34
  import { trace } from "@dxos/tracing";
35
35
  import { ComplexMap, ComplexSet, defaultMap, mapValues } from "@dxos/util";
36
36
 
37
- // packages/core/echo/echo-pipeline/src/automerge/automerge-storage-adapter.ts
38
- import { arrayToBuffer, bufferToArray } from "@dxos/util";
39
- var AutomergeStorageAdapter = class {
40
- constructor(_directory) {
41
- this._directory = _directory;
37
+ // packages/core/echo/echo-pipeline/src/automerge/leveldb-storage-adapter.ts
38
+ var LevelDBStorageAdapter = class {
39
+ constructor(_params) {
40
+ this._params = _params;
42
41
  this._state = "opened";
43
42
  }
44
- async load(key) {
43
+ async load(keyArray) {
45
44
  if (this._state !== "opened") {
46
45
  return void 0;
47
46
  }
48
- const filename = this._getFilename(key);
49
- const file = this._directory.getOrCreateFile(filename);
50
- const { size } = await file.stat();
51
- if (!size || size === 0) {
52
- return void 0;
53
- }
54
- const buffer = await file.read(0, size);
55
- return bufferToArray(buffer);
47
+ return this._params.db.get(keyArray, {
48
+ ...encodingOptions
49
+ }).catch((err) => err.code === "LEVEL_NOT_FOUND" ? void 0 : Promise.reject(err));
56
50
  }
57
- async save(key, data) {
51
+ async save(keyArray, binary) {
58
52
  if (this._state !== "opened") {
59
53
  return void 0;
60
54
  }
61
- const filename = this._getFilename(key);
62
- const file = this._directory.getOrCreateFile(filename);
63
- await file.write(0, arrayToBuffer(data));
64
- await file.truncate?.(data.length);
65
- await file.flush?.();
55
+ await this._params.callbacks?.beforeSave?.(keyArray);
56
+ await this._params.db.put(keyArray, Buffer.from(binary), {
57
+ ...encodingOptions
58
+ });
59
+ await this._params.callbacks?.afterSave?.(keyArray);
66
60
  }
67
- async remove(key) {
61
+ async remove(keyArray) {
68
62
  if (this._state !== "opened") {
69
63
  return void 0;
70
64
  }
71
- const filename = this._getFilename(key);
72
- const file = this._directory.getOrCreateFile(filename);
73
- await file.destroy();
65
+ await this._params.db.del(keyArray, {
66
+ ...encodingOptions
67
+ });
74
68
  }
75
69
  async loadRange(keyPrefix) {
76
70
  if (this._state !== "opened") {
77
71
  return [];
78
72
  }
79
- const filename = this._getFilename(keyPrefix);
80
- const entries = await this._directory.list();
81
- return Promise.all(entries.filter((entry) => entry.startsWith(filename)).map(async (entry) => {
82
- const file = this._directory.getOrCreateFile(entry);
83
- const { size } = await file.stat();
84
- const buffer = await file.read(0, size);
85
- return {
86
- key: this._getKeyFromFilename(entry),
87
- data: bufferToArray(buffer)
88
- };
89
- }));
73
+ const result = [];
74
+ for await (const [key, value] of this._params.db.iterator({
75
+ gte: keyPrefix,
76
+ lte: [
77
+ ...keyPrefix,
78
+ "\uFFFF"
79
+ ],
80
+ ...encodingOptions
81
+ })) {
82
+ result.push({
83
+ key,
84
+ data: value
85
+ });
86
+ }
87
+ return result;
90
88
  }
91
89
  async removeRange(keyPrefix) {
92
90
  if (this._state !== "opened") {
93
91
  return void 0;
94
92
  }
95
- const filename = this._getFilename(keyPrefix);
96
- const entries = await this._directory.list();
97
- await Promise.all(entries.filter((entry) => entry.startsWith(filename)).map(async (entry) => {
98
- const file = this._directory.getOrCreateFile(entry);
99
- await file.destroy();
100
- }));
93
+ const batch = this._params.db.batch();
94
+ for await (const [key] of this._params.db.iterator({
95
+ gte: keyPrefix,
96
+ lte: [
97
+ ...keyPrefix,
98
+ "\uFFFF"
99
+ ],
100
+ ...encodingOptions
101
+ })) {
102
+ batch.del(key, {
103
+ ...encodingOptions
104
+ });
105
+ }
106
+ await batch.write();
101
107
  }
102
- async close() {
108
+ close() {
103
109
  this._state = "closed";
104
110
  }
105
- _getFilename(key) {
106
- return key.map((k) => k.replaceAll("%", "%25").replaceAll("-", "%2D")).join("-");
107
- }
108
- _getKeyFromFilename(filename) {
109
- return filename.split("-").map((k) => k.replaceAll("%2D", "-").replaceAll("%25", "%"));
110
- }
111
111
  };
112
-
113
- // packages/core/echo/echo-pipeline/src/automerge/automerge-storage–wrapper.ts
114
- var AutomergeStorageWrapper = class {
115
- constructor({ storage, callbacks }) {
116
- this._storage = storage;
117
- this._callbacks = callbacks;
118
- }
119
- async load(key) {
120
- return this._storage.load(key);
121
- }
122
- async save(key, value) {
123
- await this._callbacks.beforeSave?.(key);
124
- await this._storage.save(key, value);
125
- await this._callbacks.afterSave?.(key);
126
- }
127
- async remove(key) {
128
- return this._storage.remove(key);
129
- }
130
- async loadRange(keyPrefix) {
131
- return this._storage.loadRange(keyPrefix);
132
- }
133
- async removeRange(keyPrefix) {
134
- return this._storage.removeRange(keyPrefix);
135
- }
136
- async close() {
137
- if (this._storage instanceof AutomergeStorageAdapter) {
138
- return this._storage.close();
139
- }
140
- }
112
+ var keyEncoder = {
113
+ encode: (key) => Buffer.from(key.map((k) => k.replaceAll("%", "%25").replaceAll("-", "%2D")).join("-")),
114
+ decode: (key) => Buffer.from(key).toString().split("-").map((k) => k.replaceAll("%2D", "-").replaceAll("%25", "%"))
115
+ };
116
+ var encodingOptions = {
117
+ keyEncoding: keyEncoder,
118
+ valueEncoding: "buffer"
141
119
  };
142
120
 
143
121
  // packages/core/echo/echo-pipeline/src/automerge/local-host-network-adapter.ts
@@ -372,6 +350,118 @@ var MeshNetworkAdapter = class extends NetworkAdapter2 {
372
350
  }
373
351
  };
374
352
 
353
+ // packages/core/echo/echo-pipeline/src/automerge/migrations.ts
354
+ import { IndexedDBStorageAdapter } from "@dxos/automerge/automerge-repo-storage-indexeddb";
355
+ import { log as log3 } from "@dxos/log";
356
+ import { StorageType } from "@dxos/random-access-storage";
357
+
358
+ // packages/core/echo/echo-pipeline/src/automerge/automerge-storage-adapter.ts
359
+ import { arrayToBuffer, bufferToArray } from "@dxos/util";
360
+ var AutomergeStorageAdapter = class {
361
+ constructor(_directory) {
362
+ this._directory = _directory;
363
+ this._state = "opened";
364
+ }
365
+ async load(key) {
366
+ if (this._state !== "opened") {
367
+ return void 0;
368
+ }
369
+ const filename = this._getFilename(key);
370
+ const file = this._directory.getOrCreateFile(filename);
371
+ const { size } = await file.stat();
372
+ if (!size || size === 0) {
373
+ return void 0;
374
+ }
375
+ const buffer = await file.read(0, size);
376
+ return bufferToArray(buffer);
377
+ }
378
+ async save(key, data) {
379
+ if (this._state !== "opened") {
380
+ return void 0;
381
+ }
382
+ const filename = this._getFilename(key);
383
+ const file = this._directory.getOrCreateFile(filename);
384
+ await file.write(0, arrayToBuffer(data));
385
+ await file.truncate?.(data.length);
386
+ await file.flush?.();
387
+ }
388
+ async remove(key) {
389
+ if (this._state !== "opened") {
390
+ return void 0;
391
+ }
392
+ const filename = this._getFilename(key);
393
+ const file = this._directory.getOrCreateFile(filename);
394
+ await file.destroy();
395
+ }
396
+ async loadRange(keyPrefix) {
397
+ if (this._state !== "opened") {
398
+ return [];
399
+ }
400
+ const filename = this._getFilename(keyPrefix);
401
+ const entries = await this._directory.list();
402
+ return Promise.all(entries.filter((entry) => entry.startsWith(filename)).map(async (entry) => {
403
+ const file = this._directory.getOrCreateFile(entry);
404
+ const { size } = await file.stat();
405
+ const buffer = await file.read(0, size);
406
+ return {
407
+ key: this._getKeyFromFilename(entry),
408
+ data: bufferToArray(buffer)
409
+ };
410
+ }));
411
+ }
412
+ async removeRange(keyPrefix) {
413
+ if (this._state !== "opened") {
414
+ return void 0;
415
+ }
416
+ const filename = this._getFilename(keyPrefix);
417
+ const entries = await this._directory.list();
418
+ await Promise.all(entries.filter((entry) => entry.startsWith(filename)).map(async (entry) => {
419
+ const file = this._directory.getOrCreateFile(entry);
420
+ await file.destroy();
421
+ }));
422
+ }
423
+ async close() {
424
+ this._state = "closed";
425
+ }
426
+ _getFilename(key) {
427
+ return key.map((k) => k.replaceAll("%", "%25").replaceAll("-", "%2D")).join("-");
428
+ }
429
+ _getKeyFromFilename(filename) {
430
+ return filename.split("-").map((k) => k.replaceAll("%2D", "-").replaceAll("%25", "%"));
431
+ }
432
+ };
433
+
434
+ // packages/core/echo/echo-pipeline/src/automerge/migrations.ts
435
+ var __dxlog_file3 = "/home/runner/work/dxos/dxos/packages/core/echo/echo-pipeline/src/automerge/migrations.ts";
436
+ var levelMigration = async ({ db, directory }) => {
437
+ const isNewLevel = !await db.iterator({
438
+ ...encodingOptions
439
+ }).next();
440
+ if (!isNewLevel) {
441
+ return;
442
+ }
443
+ const oldStorageAdapter = directory.type === StorageType.IDB ? new IndexedDBStorageAdapter(directory.path, "data") : new AutomergeStorageAdapter(directory);
444
+ const chunks = await oldStorageAdapter.loadRange([]);
445
+ if (chunks.length === 0) {
446
+ return;
447
+ }
448
+ const batch = db.batch();
449
+ log3.info("found chunks on old storage adapter", {
450
+ chunks: chunks.length
451
+ }, {
452
+ F: __dxlog_file3,
453
+ L: 36,
454
+ S: void 0,
455
+ C: (f, a) => f(...a)
456
+ });
457
+ for (const { key, data } of await oldStorageAdapter.loadRange([])) {
458
+ data && batch.put(key, data, {
459
+ ...encodingOptions
460
+ });
461
+ }
462
+ await batch.write();
463
+ };
464
+
375
465
  // packages/core/echo/echo-pipeline/src/automerge/automerge-host.ts
376
466
  function _ts_decorate(decorators, target, key, desc) {
377
467
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
@@ -383,9 +473,9 @@ function _ts_decorate(decorators, target, key, desc) {
383
473
  r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
384
474
  return c > 3 && r && Object.defineProperty(target, key, r), r;
385
475
  }
386
- var __dxlog_file3 = "/home/runner/work/dxos/dxos/packages/core/echo/echo-pipeline/src/automerge/automerge-host.ts";
476
+ var __dxlog_file4 = "/home/runner/work/dxos/dxos/packages/core/echo/echo-pipeline/src/automerge/automerge-host.ts";
387
477
  var AutomergeHost = class {
388
- constructor({ directory, metadata }) {
478
+ constructor({ directory, db, metadata }) {
389
479
  this._ctx = new Context();
390
480
  /**
391
481
  * spaceKey -> deviceKey[]
@@ -393,19 +483,24 @@ var AutomergeHost = class {
393
483
  this._authorizedDevices = new ComplexMap(PublicKey.hash);
394
484
  this._updatingMetadata = /* @__PURE__ */ new Map();
395
485
  this._requestedDocs = /* @__PURE__ */ new Set();
486
+ this._directory = directory;
487
+ this._db = db;
396
488
  this._metadata = metadata;
397
- this._meshNetwork = new MeshNetworkAdapter();
398
- this._clientNetwork = new LocalHostNetworkAdapter();
399
- this._storage = new AutomergeStorageWrapper({
400
- storage: (
401
- // TODO(mykola): Delete specific handling of IDB storage.
402
- directory.type === StorageType.IDB ? new IndexedDBStorageAdapter(directory.path, "data") : new AutomergeStorageAdapter(directory)
403
- ),
489
+ }
490
+ async open() {
491
+ this._directory && await levelMigration({
492
+ db: await this._db,
493
+ directory: this._directory
494
+ });
495
+ this._storage = new LevelDBStorageAdapter({
496
+ db: await this._db,
404
497
  callbacks: {
405
498
  beforeSave: (params) => this._beforeSave(params)
406
499
  }
407
500
  });
408
501
  this._peerId = `host-${PublicKey.random().toHex()}`;
502
+ this._meshNetwork = new MeshNetworkAdapter();
503
+ this._clientNetwork = new LocalHostNetworkAdapter();
409
504
  this._repo = new Repo({
410
505
  peerId: this._peerId,
411
506
  network: [
@@ -425,13 +520,13 @@ var AutomergeHost = class {
425
520
  const doc = this._repo.handles[documentId]?.docSync();
426
521
  if (!doc) {
427
522
  const isRequested = this._requestedDocs.has(`automerge:${documentId}`);
428
- log3("doc share policy check", {
523
+ log4("doc share policy check", {
429
524
  peerId,
430
525
  documentId,
431
526
  isRequested
432
527
  }, {
433
- F: __dxlog_file3,
434
- L: 96,
528
+ F: __dxlog_file4,
529
+ L: 111,
435
530
  S: this,
436
531
  C: (f, a) => f(...a)
437
532
  });
@@ -440,12 +535,12 @@ var AutomergeHost = class {
440
535
  try {
441
536
  const spaceKey = getSpaceKeyFromDoc(doc);
442
537
  if (!spaceKey) {
443
- log3("space key not found for share policy check", {
538
+ log4("space key not found for share policy check", {
444
539
  peerId,
445
540
  documentId
446
541
  }, {
447
- F: __dxlog_file3,
448
- L: 103,
542
+ F: __dxlog_file4,
543
+ L: 118,
449
544
  S: this,
450
545
  C: (f, a) => f(...a)
451
546
  });
@@ -454,12 +549,12 @@ var AutomergeHost = class {
454
549
  const authorizedDevices = this._authorizedDevices.get(PublicKey.from(spaceKey));
455
550
  const deviceKeyHex = this.repo.peerMetadataByPeerId[peerId]?.dxos_deviceKey;
456
551
  if (!deviceKeyHex) {
457
- log3("device key not found for share policy check", {
552
+ log4("device key not found for share policy check", {
458
553
  peerId,
459
554
  documentId
460
555
  }, {
461
- F: __dxlog_file3,
462
- L: 112,
556
+ F: __dxlog_file4,
557
+ L: 127,
463
558
  S: this,
464
559
  C: (f, a) => f(...a)
465
560
  });
@@ -467,7 +562,7 @@ var AutomergeHost = class {
467
562
  }
468
563
  const deviceKey = PublicKey.from(deviceKeyHex);
469
564
  const isAuthorized = authorizedDevices?.has(deviceKey) ?? false;
470
- log3("share policy check", {
565
+ log4("share policy check", {
471
566
  localPeer: this._peerId,
472
567
  remotePeer: peerId,
473
568
  documentId,
@@ -475,16 +570,16 @@ var AutomergeHost = class {
475
570
  spaceKey,
476
571
  isAuthorized
477
572
  }, {
478
- F: __dxlog_file3,
479
- L: 118,
573
+ F: __dxlog_file4,
574
+ L: 133,
480
575
  S: this,
481
576
  C: (f, a) => f(...a)
482
577
  });
483
578
  return isAuthorized;
484
579
  } catch (err) {
485
- log3.catch(err, void 0, {
486
- F: __dxlog_file3,
487
- L: 128,
580
+ log4.catch(err, void 0, {
581
+ F: __dxlog_file4,
582
+ L: 143,
488
583
  S: this,
489
584
  C: (f, a) => f(...a)
490
585
  });
@@ -499,9 +594,15 @@ var AutomergeHost = class {
499
594
  this._repo.on("document", listener);
500
595
  this._ctx.onDispose(() => {
501
596
  this._repo.off("document", listener);
597
+ Object.values(this._repo.handles).forEach((handle) => handle.off("change"));
502
598
  });
503
599
  }
504
600
  }
601
+ async close() {
602
+ this._storage.close?.();
603
+ await this._clientNetwork.close();
604
+ await this._ctx.dispose();
605
+ }
505
606
  get repo() {
506
607
  return this._repo;
507
608
  }
@@ -514,9 +615,6 @@ var AutomergeHost = class {
514
615
  _onDocument(handle) {
515
616
  const listener = (event) => this._onUpdate(event);
516
617
  handle.on("change", listener);
517
- this._ctx.onDispose(() => {
518
- handle.off("change", listener);
519
- });
520
618
  }
521
619
  _onUpdate(event) {
522
620
  if (this._metadata == null) {
@@ -542,9 +640,9 @@ var AutomergeHost = class {
542
640
  const markingDirtyPromise = this._metadata.markDirty(idToLastHash).then(() => {
543
641
  this._updatingMetadata.delete(event.handle.documentId);
544
642
  }).catch((err) => {
545
- this._ctx.disposed && log3.catch(err, void 0, {
546
- F: __dxlog_file3,
547
- L: 188,
643
+ this._ctx.disposed && log4.catch(err, void 0, {
644
+ F: __dxlog_file4,
645
+ L: 207,
548
646
  S: this,
549
647
  C: (f, a) => f(...a)
550
648
  });
@@ -556,7 +654,7 @@ var AutomergeHost = class {
556
654
  state: handle.state,
557
655
  hasDoc: !!handle.docSync(),
558
656
  heads: handle.docSync() ? automerge.getHeads(handle.docSync()) : null,
559
- data: handle.docSync()?.doc && mapValues(handle.docSync()?.doc, (value, key) => {
657
+ data: handle.docSync() && mapValues(handle.docSync(), (value, key) => {
560
658
  try {
561
659
  switch (key) {
562
660
  case "access":
@@ -576,14 +674,13 @@ var AutomergeHost = class {
576
674
  _automergePeers() {
577
675
  return this._repo.peers;
578
676
  }
579
- async close() {
580
- await this._storage.close();
581
- await this._clientNetwork.close();
582
- await this._ctx.dispose();
583
- }
584
677
  //
585
678
  // Methods for client-services.
586
679
  //
680
+ async flush({ documentIds }) {
681
+ await Promise.all(documentIds?.map((id) => this._repo.find(id).whenReady()) ?? []);
682
+ await this._repo.flush(documentIds);
683
+ }
587
684
  syncRepo(request) {
588
685
  return this._clientNetwork.syncRepo(request);
589
686
  }
@@ -600,12 +697,12 @@ var AutomergeHost = class {
600
697
  return this._meshNetwork.createExtension();
601
698
  }
602
699
  authorizeDevice(spaceKey, deviceKey) {
603
- log3("authorizeDevice", {
700
+ log4("authorizeDevice", {
604
701
  spaceKey,
605
702
  deviceKey
606
703
  }, {
607
- F: __dxlog_file3,
608
- L: 255,
704
+ F: __dxlog_file4,
705
+ L: 274,
609
706
  S: this,
610
707
  C: (f, a) => f(...a)
611
708
  });
@@ -659,8 +756,8 @@ import { Event } from "@dxos/async";
659
756
  import { cancelWithContext } from "@dxos/context";
660
757
  import { warnAfterTimeout } from "@dxos/debug";
661
758
  import { invariant as invariant3 } from "@dxos/invariant";
662
- import { log as log4 } from "@dxos/log";
663
- var __dxlog_file4 = "/home/runner/work/dxos/dxos/packages/core/echo/echo-pipeline/src/automerge/automerge-doc-loader.ts";
759
+ import { log as log5 } from "@dxos/log";
760
+ var __dxlog_file5 = "/home/runner/work/dxos/dxos/packages/core/echo/echo-pipeline/src/automerge/automerge-doc-loader.ts";
664
761
  var AutomergeDocumentLoaderImpl = class {
665
762
  constructor(_spaceKey, _repo) {
666
763
  this._spaceKey = _spaceKey;
@@ -670,16 +767,21 @@ var AutomergeDocumentLoaderImpl = class {
670
767
  this._objectsPendingDocumentLoad = /* @__PURE__ */ new Set();
671
768
  this.onObjectDocumentLoaded = new Event();
672
769
  }
770
+ getAllHandles() {
771
+ return [
772
+ ...new Set(this._objectDocumentHandles.values())
773
+ ];
774
+ }
673
775
  async loadSpaceRootDocHandle(ctx, spaceState) {
674
776
  if (this._spaceRootDocHandle != null) {
675
777
  return;
676
778
  }
677
779
  if (!spaceState.rootUrl) {
678
- log4.error("Database opened with no rootUrl", {
780
+ log5.error("Database opened with no rootUrl", {
679
781
  spaceKey: this._spaceKey
680
782
  }, {
681
- F: __dxlog_file4,
682
- L: 60,
783
+ F: __dxlog_file5,
784
+ L: 66,
683
785
  S: this,
684
786
  C: (f, a) => f(...a)
685
787
  });
@@ -688,8 +790,8 @@ var AutomergeDocumentLoaderImpl = class {
688
790
  const existingDocHandle = await this._initDocHandle(ctx, spaceState.rootUrl);
689
791
  const doc = existingDocHandle.docSync();
690
792
  invariant3(doc, void 0, {
691
- F: __dxlog_file4,
692
- L: 65,
793
+ F: __dxlog_file5,
794
+ L: 71,
693
795
  S: this,
694
796
  A: [
695
797
  "doc",
@@ -704,8 +806,8 @@ var AutomergeDocumentLoaderImpl = class {
704
806
  }
705
807
  loadObjectDocument(objectId) {
706
808
  invariant3(this._spaceRootDocHandle, void 0, {
707
- F: __dxlog_file4,
708
- L: 74,
809
+ F: __dxlog_file5,
810
+ L: 80,
709
811
  S: this,
710
812
  A: [
711
813
  "this._spaceRootDocHandle",
@@ -717,8 +819,8 @@ var AutomergeDocumentLoaderImpl = class {
717
819
  }
718
820
  const spaceRootDoc = this._spaceRootDocHandle.docSync();
719
821
  invariant3(spaceRootDoc, void 0, {
720
- F: __dxlog_file4,
721
- L: 79,
822
+ F: __dxlog_file5,
823
+ L: 85,
722
824
  S: this,
723
825
  A: [
724
826
  "spaceRootDoc",
@@ -728,11 +830,11 @@ var AutomergeDocumentLoaderImpl = class {
728
830
  const documentUrl = (spaceRootDoc.links ?? {})[objectId];
729
831
  if (documentUrl == null) {
730
832
  this._objectsPendingDocumentLoad.add(objectId);
731
- log4.info("loading delayed until object links are initialized", {
833
+ log5.info("loading delayed until object links are initialized", {
732
834
  objectId
733
835
  }, {
734
- F: __dxlog_file4,
735
- L: 83,
836
+ F: __dxlog_file5,
837
+ L: 89,
736
838
  S: this,
737
839
  C: (f, a) => f(...a)
738
840
  });
@@ -752,8 +854,8 @@ var AutomergeDocumentLoaderImpl = class {
752
854
  }
753
855
  getSpaceRootDocHandle() {
754
856
  invariant3(this._spaceRootDocHandle, void 0, {
755
- F: __dxlog_file4,
756
- L: 101,
857
+ F: __dxlog_file5,
858
+ L: 107,
757
859
  S: this,
758
860
  A: [
759
861
  "this._spaceRootDocHandle",
@@ -764,8 +866,8 @@ var AutomergeDocumentLoaderImpl = class {
764
866
  }
765
867
  createDocumentForObject(objectId) {
766
868
  invariant3(this._spaceRootDocHandle, void 0, {
767
- F: __dxlog_file4,
768
- L: 106,
869
+ F: __dxlog_file5,
870
+ L: 112,
769
871
  S: this,
770
872
  A: [
771
873
  "this._spaceRootDocHandle",
@@ -803,30 +905,30 @@ var AutomergeDocumentLoaderImpl = class {
803
905
  };
804
906
  const objectDocumentHandle = this._objectDocumentHandles.get(objectId);
805
907
  if (objectDocumentHandle != null && objectDocumentHandle.url !== automergeUrl) {
806
- log4.warn("object already inlined in a different document, ignoring the link", {
908
+ log5.warn("object already inlined in a different document, ignoring the link", {
807
909
  ...logMeta,
808
910
  actualDocumentUrl: objectDocumentHandle.url
809
911
  }, {
810
- F: __dxlog_file4,
811
- L: 136,
912
+ F: __dxlog_file5,
913
+ L: 142,
812
914
  S: this,
813
915
  C: (f, a) => f(...a)
814
916
  });
815
917
  continue;
816
918
  }
817
919
  if (objectDocumentHandle?.url === automergeUrl) {
818
- log4.warn("object document was already loaded", logMeta, {
819
- F: __dxlog_file4,
820
- L: 143,
920
+ log5.warn("object document was already loaded", logMeta, {
921
+ F: __dxlog_file5,
922
+ L: 149,
821
923
  S: this,
822
924
  C: (f, a) => f(...a)
823
925
  });
824
926
  continue;
825
927
  }
826
928
  const handle = this._repo.find(automergeUrl);
827
- log4.debug("document loading triggered", logMeta, {
828
- F: __dxlog_file4,
829
- L: 147,
929
+ log5.debug("document loading triggered", logMeta, {
930
+ F: __dxlog_file5,
931
+ L: 153,
830
932
  S: this,
831
933
  C: (f, a) => f(...a)
832
934
  });
@@ -844,12 +946,12 @@ var AutomergeDocumentLoaderImpl = class {
844
946
  break;
845
947
  } catch (err) {
846
948
  if (`${err}`.includes("Timeout")) {
847
- log4.info("wraparound", {
949
+ log5.info("wraparound", {
848
950
  id: docHandle.documentId,
849
951
  state: docHandle.state
850
952
  }, {
851
- F: __dxlog_file4,
852
- L: 163,
953
+ F: __dxlog_file5,
954
+ L: 169,
853
955
  S: this,
854
956
  C: (f, a) => f(...a)
855
957
  });
@@ -889,9 +991,9 @@ var AutomergeDocumentLoaderImpl = class {
889
991
  docUrl: handle.url
890
992
  };
891
993
  if (this.onObjectDocumentLoaded.listenerCount() === 0) {
892
- log4.info("document loaded after all listeners were removed", logMeta, {
893
- F: __dxlog_file4,
894
- L: 199,
994
+ log5.info("document loaded after all listeners were removed", logMeta, {
995
+ F: __dxlog_file5,
996
+ L: 205,
895
997
  S: this,
896
998
  C: (f, a) => f(...a)
897
999
  });
@@ -899,9 +1001,9 @@ var AutomergeDocumentLoaderImpl = class {
899
1001
  }
900
1002
  const objectDocHandle = this._objectDocumentHandles.get(objectId);
901
1003
  if (objectDocHandle?.url !== handle.url) {
902
- log4.warn("object was rebound while a document was loading, discarding handle", logMeta, {
903
- F: __dxlog_file4,
904
- L: 204,
1004
+ log5.warn("object was rebound while a document was loading, discarding handle", logMeta, {
1005
+ F: __dxlog_file5,
1006
+ L: 210,
905
1007
  S: this,
906
1008
  C: (f, a) => f(...a)
907
1009
  });
@@ -913,14 +1015,14 @@ var AutomergeDocumentLoaderImpl = class {
913
1015
  });
914
1016
  } catch (err) {
915
1017
  const shouldRetryLoading = this.onObjectDocumentLoaded.listenerCount() > 0;
916
- log4.warn("failed to load a document", {
1018
+ log5.warn("failed to load a document", {
917
1019
  objectId,
918
1020
  automergeUrl: handle.url,
919
1021
  retryLoading: shouldRetryLoading,
920
1022
  err
921
1023
  }, {
922
- F: __dxlog_file4,
923
- L: 210,
1024
+ F: __dxlog_file5,
1025
+ L: 216,
924
1026
  S: this,
925
1027
  C: (f, a) => f(...a)
926
1028
  });
@@ -930,6 +1032,19 @@ var AutomergeDocumentLoaderImpl = class {
930
1032
  }
931
1033
  }
932
1034
  };
1035
+
1036
+ // packages/core/echo/echo-pipeline/src/automerge/reference.ts
1037
+ import { Reference } from "@dxos/echo-db";
1038
+ var REFERENCE_TYPE_TAG = "dxos.echo.model.document.Reference";
1039
+ var encodeReference = (reference) => ({
1040
+ "@type": REFERENCE_TYPE_TAG,
1041
+ // NOTE: Automerge do not support undefined values, so we need to use null instead.
1042
+ itemId: reference.itemId ?? null,
1043
+ protocol: reference.protocol ?? null,
1044
+ host: reference.host ?? null
1045
+ });
1046
+ var decodeReference = (value) => new Reference(value.itemId, value.protocol ?? void 0, value.host ?? void 0);
1047
+ var isEncodedReferenceObject = (value) => typeof value === "object" && value !== null && value["@type"] === REFERENCE_TYPE_TAG;
933
1048
  export {
934
1049
  AuthExtension,
935
1050
  AuthStatus,
@@ -943,6 +1058,7 @@ export {
943
1058
  MeshNetworkAdapter,
944
1059
  MetadataStore,
945
1060
  Pipeline,
1061
+ REFERENCE_TYPE_TAG,
946
1062
  SnapshotManager,
947
1063
  SnapshotStore,
948
1064
  Space,
@@ -952,7 +1068,11 @@ export {
952
1068
  TimeframeClock,
953
1069
  codec,
954
1070
  createMappedFeedWriter,
1071
+ decodeReference,
1072
+ encodeReference,
955
1073
  getSpaceKeyFromDoc,
1074
+ hasInvitationExpired,
1075
+ isEncodedReferenceObject,
956
1076
  mapFeedIndexesToTimeframe,
957
1077
  mapTimeframeToFeedIndexes,
958
1078
  startAfter,