@dxos/echo-pipeline 0.4.10-main.3e0c8a5 → 0.4.10-main.3f5e2d2

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 (74) hide show
  1. package/dist/lib/browser/{chunk-RTEEJ723.mjs → chunk-KMWJLYEQ.mjs} +77 -57
  2. package/dist/lib/browser/chunk-KMWJLYEQ.mjs.map +7 -0
  3. package/dist/lib/browser/index.mjs +395 -254
  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 +10 -2
  7. package/dist/lib/browser/testing/index.mjs.map +4 -4
  8. package/dist/lib/node/{chunk-7VZVCCNF.cjs → chunk-YZA42CKA.cjs} +82 -62
  9. package/dist/lib/node/chunk-YZA42CKA.cjs.map +7 -0
  10. package/dist/lib/node/index.cjs +423 -283
  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 +20 -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 +21 -18
  18. package/dist/types/src/automerge/automerge-host.d.ts.map +1 -1
  19. package/dist/types/src/automerge/automerge-repo.test.d.ts +2 -0
  20. package/dist/types/src/automerge/automerge-repo.test.d.ts.map +1 -0
  21. package/dist/types/src/automerge/index.d.ts +2 -0
  22. package/dist/types/src/automerge/index.d.ts.map +1 -1
  23. package/dist/types/src/automerge/level.test.d.ts +2 -0
  24. package/dist/types/src/automerge/level.test.d.ts.map +1 -0
  25. package/dist/types/src/automerge/leveldb-storage-adapter.d.ts +30 -0
  26. package/dist/types/src/automerge/leveldb-storage-adapter.d.ts.map +1 -0
  27. package/dist/types/src/automerge/local-host-network-adapter.d.ts +8 -1
  28. package/dist/types/src/automerge/local-host-network-adapter.d.ts.map +1 -1
  29. package/dist/types/src/automerge/migrations.d.ts +7 -0
  30. package/dist/types/src/automerge/migrations.d.ts.map +1 -0
  31. package/dist/types/src/automerge/reference.d.ts +15 -0
  32. package/dist/types/src/automerge/reference.d.ts.map +1 -0
  33. package/dist/types/src/automerge/storage-adapter.test.d.ts +2 -0
  34. package/dist/types/src/automerge/storage-adapter.test.d.ts.map +1 -0
  35. package/dist/types/src/automerge/types.d.ts +8 -2
  36. package/dist/types/src/automerge/types.d.ts.map +1 -1
  37. package/dist/types/src/metadata/metadata-store.d.ts +2 -1
  38. package/dist/types/src/metadata/metadata-store.d.ts.map +1 -1
  39. package/dist/types/src/space/control-pipeline.d.ts +3 -1
  40. package/dist/types/src/space/control-pipeline.d.ts.map +1 -1
  41. package/dist/types/src/space/space-manager.d.ts +3 -1
  42. package/dist/types/src/space/space-manager.d.ts.map +1 -1
  43. package/dist/types/src/space/space.d.ts +6 -9
  44. package/dist/types/src/space/space.d.ts.map +1 -1
  45. package/dist/types/src/testing/index.d.ts +1 -0
  46. package/dist/types/src/testing/index.d.ts.map +1 -1
  47. package/dist/types/src/testing/level.d.ts +3 -0
  48. package/dist/types/src/testing/level.d.ts.map +1 -0
  49. package/dist/types/src/testing/test-agent-builder.d.ts +2 -2
  50. package/dist/types/src/testing/test-agent-builder.d.ts.map +1 -1
  51. package/package.json +33 -30
  52. package/src/automerge/automerge-doc-loader.ts +9 -0
  53. package/src/automerge/automerge-host.test.ts +44 -21
  54. package/src/automerge/automerge-host.ts +93 -113
  55. package/src/automerge/automerge-repo.test.ts +61 -0
  56. package/src/automerge/index.ts +2 -0
  57. package/src/automerge/level.test.ts +82 -0
  58. package/src/automerge/leveldb-storage-adapter.ts +117 -0
  59. package/src/automerge/local-host-network-adapter.ts +19 -13
  60. package/src/automerge/migrations.ts +41 -0
  61. package/src/automerge/reference.ts +31 -0
  62. package/src/automerge/storage-adapter.test.ts +90 -0
  63. package/src/automerge/types.ts +8 -5
  64. package/src/db-host/data-service.ts +1 -1
  65. package/src/metadata/metadata-store.ts +17 -8
  66. package/src/space/control-pipeline.ts +11 -1
  67. package/src/space/space-manager.ts +4 -0
  68. package/src/space/space.test.ts +7 -7
  69. package/src/space/space.ts +17 -22
  70. package/src/testing/index.ts +1 -0
  71. package/src/testing/level.ts +12 -0
  72. package/src/testing/test-agent-builder.ts +1 -0
  73. package/dist/lib/browser/chunk-RTEEJ723.mjs.map +0 -7
  74. 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,142 +17,131 @@ 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-KMWJLYEQ.mjs";
24
26
 
25
27
  // packages/core/echo/echo-pipeline/src/automerge/automerge-host.ts
26
- import { next as automerge, getHeads } from "@dxos/automerge/automerge";
28
+ import { Event } from "@dxos/async";
29
+ import { next as automerge, getBackend } from "@dxos/automerge/automerge";
27
30
  import { Repo } from "@dxos/automerge/automerge-repo";
28
- import { IndexedDBStorageAdapter } from "@dxos/automerge/automerge-repo-storage-indexeddb";
29
31
  import { Context } from "@dxos/context";
32
+ import { invariant as invariant3 } from "@dxos/invariant";
30
33
  import { PublicKey } from "@dxos/keys";
31
34
  import { log as log3 } from "@dxos/log";
32
- import { idCodec } from "@dxos/protocols";
33
- import { StorageType } from "@dxos/random-access-storage";
34
35
  import { trace } from "@dxos/tracing";
35
36
  import { ComplexMap, ComplexSet, defaultMap, mapValues } from "@dxos/util";
36
37
 
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;
42
- this._state = "opened";
38
+ // packages/core/echo/echo-pipeline/src/automerge/leveldb-storage-adapter.ts
39
+ import { LifecycleState, Resource } from "@dxos/context";
40
+ var LevelDBStorageAdapter = class extends Resource {
41
+ constructor(_params) {
42
+ super();
43
+ this._params = _params;
43
44
  }
44
- async load(key) {
45
- if (this._state !== "opened") {
46
- return void 0;
47
- }
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;
45
+ async load(keyArray) {
46
+ try {
47
+ if (this._lifecycleState !== LifecycleState.OPEN) {
48
+ return void 0;
49
+ }
50
+ return await this._params.db.get(keyArray, {
51
+ ...encodingOptions
52
+ });
53
+ } catch (err) {
54
+ if (isLevelDbNotFoundError(err)) {
55
+ return void 0;
56
+ }
57
+ throw err;
53
58
  }
54
- const buffer = await file.read(0, size);
55
- return bufferToArray(buffer);
56
59
  }
57
- async save(key, data) {
58
- if (this._state !== "opened") {
60
+ async save(keyArray, binary) {
61
+ if (this._lifecycleState !== LifecycleState.OPEN) {
59
62
  return void 0;
60
63
  }
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?.();
64
+ const batch = this._params.db.batch();
65
+ await this._params.callbacks?.beforeSave?.({
66
+ path: keyArray,
67
+ batch
68
+ });
69
+ batch.put(keyArray, Buffer.from(binary), {
70
+ ...encodingOptions
71
+ });
72
+ await batch.write();
73
+ await this._params.callbacks?.afterSave?.(keyArray);
66
74
  }
67
- async remove(key) {
68
- if (this._state !== "opened") {
75
+ async remove(keyArray) {
76
+ if (this._lifecycleState !== LifecycleState.OPEN) {
69
77
  return void 0;
70
78
  }
71
- const filename = this._getFilename(key);
72
- const file = this._directory.getOrCreateFile(filename);
73
- await file.destroy();
79
+ await this._params.db.del(keyArray, {
80
+ ...encodingOptions
81
+ });
74
82
  }
75
83
  async loadRange(keyPrefix) {
76
- if (this._state !== "opened") {
84
+ if (this._lifecycleState !== LifecycleState.OPEN) {
77
85
  return [];
78
86
  }
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
- }));
87
+ const result = [];
88
+ for await (const [key, value] of this._params.db.iterator({
89
+ gte: keyPrefix,
90
+ lte: [
91
+ ...keyPrefix,
92
+ "\uFFFF"
93
+ ],
94
+ ...encodingOptions
95
+ })) {
96
+ result.push({
97
+ key,
98
+ data: value
99
+ });
100
+ }
101
+ return result;
90
102
  }
91
103
  async removeRange(keyPrefix) {
92
- if (this._state !== "opened") {
104
+ if (this._lifecycleState !== LifecycleState.OPEN) {
93
105
  return void 0;
94
106
  }
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
- }));
101
- }
102
- async close() {
103
- this._state = "closed";
104
- }
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
- };
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();
107
+ const batch = this._params.db.batch();
108
+ for await (const [key] of this._params.db.iterator({
109
+ gte: keyPrefix,
110
+ lte: [
111
+ ...keyPrefix,
112
+ "\uFFFF"
113
+ ],
114
+ ...encodingOptions
115
+ })) {
116
+ batch.del(key, {
117
+ ...encodingOptions
118
+ });
139
119
  }
120
+ await batch.write();
140
121
  }
141
122
  };
123
+ var keyEncoder = {
124
+ encode: (key) => Buffer.from(key.map((k) => k.replaceAll("%", "%25").replaceAll("-", "%2D")).join("-")),
125
+ decode: (key) => Buffer.from(key).toString().split("-").map((k) => k.replaceAll("%2D", "-").replaceAll("%25", "%"))
126
+ };
127
+ var encodingOptions = {
128
+ keyEncoding: keyEncoder,
129
+ valueEncoding: "buffer"
130
+ };
131
+ var isLevelDbNotFoundError = (err) => err.code === "LEVEL_NOT_FOUND";
142
132
 
143
133
  // packages/core/echo/echo-pipeline/src/automerge/local-host-network-adapter.ts
144
134
  import { Trigger } from "@dxos/async";
145
135
  import { NetworkAdapter, cbor } from "@dxos/automerge/automerge-repo";
146
136
  import { Stream } from "@dxos/codec-protobuf";
147
137
  import { invariant } from "@dxos/invariant";
148
- import { log } from "@dxos/log";
149
138
  var __dxlog_file = "/home/runner/work/dxos/dxos/packages/core/echo/echo-pipeline/src/automerge/local-host-network-adapter.ts";
150
139
  var LocalHostNetworkAdapter = class extends NetworkAdapter {
151
140
  constructor() {
152
141
  super(...arguments);
153
142
  this._peers = /* @__PURE__ */ new Map();
154
143
  this._connected = new Trigger();
144
+ this._isConnected = false;
155
145
  }
156
146
  /**
157
147
  * Emits `ready` event. That signals to `Repo` that it can start using the adapter.
@@ -161,15 +151,21 @@ var LocalHostNetworkAdapter = class extends NetworkAdapter {
161
151
  network: this
162
152
  });
163
153
  }
154
+ /**
155
+ * Called by `Repo` to connect to the network.
156
+ *
157
+ * @param peerId Our peer Id.
158
+ */
164
159
  connect(peerId) {
165
160
  this.peerId = peerId;
161
+ this._isConnected = true;
166
162
  this._connected.wake();
167
163
  }
168
164
  send(message) {
169
165
  const peer = this._peers.get(message.targetId);
170
166
  invariant(peer, "Peer not found.", {
171
167
  F: __dxlog_file,
172
- L: 45,
168
+ L: 51,
173
169
  S: this,
174
170
  A: [
175
171
  "peer",
@@ -184,12 +180,17 @@ var LocalHostNetworkAdapter = class extends NetworkAdapter {
184
180
  }
185
181
  disconnect() {
186
182
  }
183
+ async whenConnected() {
184
+ await this._connected.wait({
185
+ timeout: 1e4
186
+ });
187
+ }
187
188
  syncRepo({ id, syncMessage }) {
188
189
  const peerId = this._getPeerId(id);
189
190
  return new Stream(({ next, close }) => {
190
191
  invariant(!this._peers.has(peerId), "Peer already connected.", {
191
192
  F: __dxlog_file,
192
- L: 63,
193
+ L: 73,
193
194
  S: this,
194
195
  A: [
195
196
  "!this._peers.has(peerId)",
@@ -211,35 +212,47 @@ var LocalHostNetworkAdapter = class extends NetworkAdapter {
211
212
  });
212
213
  }
213
214
  });
214
- this._connected.wait({
215
- timeout: 1e3
216
- }).then(() => {
217
- this.emit("peer-candidate", {
218
- peerMetadata: {},
219
- peerId
220
- });
221
- }).catch((err) => log.catch(err, void 0, {
215
+ invariant(this._isConnected, void 0, {
222
216
  F: __dxlog_file,
223
- L: 88,
217
+ L: 90,
224
218
  S: this,
225
- C: (f, a) => f(...a)
226
- }));
219
+ A: [
220
+ "this._isConnected",
221
+ ""
222
+ ]
223
+ });
224
+ this.emit("peer-candidate", {
225
+ peerMetadata: {},
226
+ peerId
227
+ });
227
228
  });
228
229
  }
229
230
  async sendSyncMessage({ id, syncMessage }) {
230
- await this._connected.wait({
231
- timeout: 1e3
231
+ invariant(this._isConnected, void 0, {
232
+ F: __dxlog_file,
233
+ L: 99,
234
+ S: this,
235
+ A: [
236
+ "this._isConnected",
237
+ ""
238
+ ]
232
239
  });
233
240
  const message = cbor.decode(syncMessage);
234
241
  this.emit("message", message);
235
242
  }
236
243
  async getHostInfo() {
237
- await this._connected.wait({
238
- timeout: 1e3
244
+ invariant(this._isConnected, void 0, {
245
+ F: __dxlog_file,
246
+ L: 105,
247
+ S: this,
248
+ A: [
249
+ "this._isConnected",
250
+ ""
251
+ ]
239
252
  });
240
253
  invariant(this.peerId, "Peer id not set.", {
241
254
  F: __dxlog_file,
242
- L: 100,
255
+ L: 106,
243
256
  S: this,
244
257
  A: [
245
258
  "this.peerId",
@@ -259,7 +272,7 @@ var LocalHostNetworkAdapter = class extends NetworkAdapter {
259
272
  import { Trigger as Trigger2 } from "@dxos/async";
260
273
  import { NetworkAdapter as NetworkAdapter2, cbor as cbor2 } from "@dxos/automerge/automerge-repo";
261
274
  import { invariant as invariant2 } from "@dxos/invariant";
262
- import { log as log2 } from "@dxos/log";
275
+ import { log } from "@dxos/log";
263
276
  import { AutomergeReplicator } from "@dxos/teleport-extension-automerge-replicator";
264
277
  var __dxlog_file2 = "/home/runner/work/dxos/dxos/packages/core/echo/echo-pipeline/src/automerge/mesh-network-adapter.ts";
265
278
  var MeshNetworkAdapter = class extends NetworkAdapter2 {
@@ -294,7 +307,7 @@ var MeshNetworkAdapter = class extends NetworkAdapter2 {
294
307
  });
295
308
  extension.sendSyncMessage({
296
309
  payload: cbor2.encode(message)
297
- }).catch((err) => log2.catch(err, void 0, {
310
+ }).catch((err) => log.catch(err, void 0, {
298
311
  F: __dxlog_file2,
299
312
  L: 39,
300
313
  S: this,
@@ -319,7 +332,7 @@ var MeshNetworkAdapter = class extends NetworkAdapter2 {
319
332
  }, {
320
333
  onStartReplication: async (info, remotePeerId) => {
321
334
  await this._connected.wait();
322
- log2("onStartReplication", {
335
+ log("onStartReplication", {
323
336
  id: info.id,
324
337
  thisPeerId: this.peerId,
325
338
  remotePeerId: remotePeerId.toHex()
@@ -332,7 +345,7 @@ var MeshNetworkAdapter = class extends NetworkAdapter2 {
332
345
  if (!this._extensions.has(info.id)) {
333
346
  peerInfo = info;
334
347
  this._extensions.set(info.id, extension);
335
- log2("peer-candidate", {
348
+ log("peer-candidate", {
336
349
  id: info.id,
337
350
  thisPeerId: this.peerId,
338
351
  remotePeerId: remotePeerId.toHex()
@@ -372,6 +385,118 @@ var MeshNetworkAdapter = class extends NetworkAdapter2 {
372
385
  }
373
386
  };
374
387
 
388
+ // packages/core/echo/echo-pipeline/src/automerge/migrations.ts
389
+ import { IndexedDBStorageAdapter } from "@dxos/automerge/automerge-repo-storage-indexeddb";
390
+ import { log as log2 } from "@dxos/log";
391
+ import { StorageType } from "@dxos/random-access-storage";
392
+
393
+ // packages/core/echo/echo-pipeline/src/automerge/automerge-storage-adapter.ts
394
+ import { arrayToBuffer, bufferToArray } from "@dxos/util";
395
+ var AutomergeStorageAdapter = class {
396
+ constructor(_directory) {
397
+ this._directory = _directory;
398
+ this._state = "opened";
399
+ }
400
+ async load(key) {
401
+ if (this._state !== "opened") {
402
+ return void 0;
403
+ }
404
+ const filename = this._getFilename(key);
405
+ const file = this._directory.getOrCreateFile(filename);
406
+ const { size } = await file.stat();
407
+ if (!size || size === 0) {
408
+ return void 0;
409
+ }
410
+ const buffer = await file.read(0, size);
411
+ return bufferToArray(buffer);
412
+ }
413
+ async save(key, data) {
414
+ if (this._state !== "opened") {
415
+ return void 0;
416
+ }
417
+ const filename = this._getFilename(key);
418
+ const file = this._directory.getOrCreateFile(filename);
419
+ await file.write(0, arrayToBuffer(data));
420
+ await file.truncate?.(data.length);
421
+ await file.flush?.();
422
+ }
423
+ async remove(key) {
424
+ if (this._state !== "opened") {
425
+ return void 0;
426
+ }
427
+ const filename = this._getFilename(key);
428
+ const file = this._directory.getOrCreateFile(filename);
429
+ await file.destroy();
430
+ }
431
+ async loadRange(keyPrefix) {
432
+ if (this._state !== "opened") {
433
+ return [];
434
+ }
435
+ const filename = this._getFilename(keyPrefix);
436
+ const entries = await this._directory.list();
437
+ return Promise.all(entries.filter((entry) => entry.startsWith(filename)).map(async (entry) => {
438
+ const file = this._directory.getOrCreateFile(entry);
439
+ const { size } = await file.stat();
440
+ const buffer = await file.read(0, size);
441
+ return {
442
+ key: this._getKeyFromFilename(entry),
443
+ data: bufferToArray(buffer)
444
+ };
445
+ }));
446
+ }
447
+ async removeRange(keyPrefix) {
448
+ if (this._state !== "opened") {
449
+ return void 0;
450
+ }
451
+ const filename = this._getFilename(keyPrefix);
452
+ const entries = await this._directory.list();
453
+ await Promise.all(entries.filter((entry) => entry.startsWith(filename)).map(async (entry) => {
454
+ const file = this._directory.getOrCreateFile(entry);
455
+ await file.destroy();
456
+ }));
457
+ }
458
+ async close() {
459
+ this._state = "closed";
460
+ }
461
+ _getFilename(key) {
462
+ return key.map((k) => k.replaceAll("%", "%25").replaceAll("-", "%2D")).join("-");
463
+ }
464
+ _getKeyFromFilename(filename) {
465
+ return filename.split("-").map((k) => k.replaceAll("%2D", "-").replaceAll("%25", "%"));
466
+ }
467
+ };
468
+
469
+ // packages/core/echo/echo-pipeline/src/automerge/migrations.ts
470
+ var __dxlog_file3 = "/home/runner/work/dxos/dxos/packages/core/echo/echo-pipeline/src/automerge/migrations.ts";
471
+ var levelMigration = async ({ db, directory }) => {
472
+ const isNewLevel = !await db.iterator({
473
+ ...encodingOptions
474
+ }).next();
475
+ if (!isNewLevel) {
476
+ return;
477
+ }
478
+ const oldStorageAdapter = directory.type === StorageType.IDB ? new IndexedDBStorageAdapter(directory.path, "data") : new AutomergeStorageAdapter(directory);
479
+ const chunks = await oldStorageAdapter.loadRange([]);
480
+ if (chunks.length === 0) {
481
+ return;
482
+ }
483
+ const batch = db.batch();
484
+ log2.info("found chunks on old storage adapter", {
485
+ chunks: chunks.length
486
+ }, {
487
+ F: __dxlog_file3,
488
+ L: 36,
489
+ S: void 0,
490
+ C: (f, a) => f(...a)
491
+ });
492
+ for (const { key, data } of await oldStorageAdapter.loadRange([])) {
493
+ data && batch.put(key, data, {
494
+ ...encodingOptions
495
+ });
496
+ }
497
+ await batch.write();
498
+ };
499
+
375
500
  // packages/core/echo/echo-pipeline/src/automerge/automerge-host.ts
376
501
  function _ts_decorate(decorators, target, key, desc) {
377
502
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
@@ -383,29 +508,32 @@ function _ts_decorate(decorators, target, key, desc) {
383
508
  r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
384
509
  return c > 3 && r && Object.defineProperty(target, key, r), r;
385
510
  }
386
- var __dxlog_file3 = "/home/runner/work/dxos/dxos/packages/core/echo/echo-pipeline/src/automerge/automerge-host.ts";
511
+ var __dxlog_file4 = "/home/runner/work/dxos/dxos/packages/core/echo/echo-pipeline/src/automerge/automerge-host.ts";
387
512
  var AutomergeHost = class {
388
- constructor({ directory, metadata }) {
513
+ constructor({ directory, db, storageCallbacks }) {
389
514
  this._ctx = new Context();
390
515
  /**
391
516
  * spaceKey -> deviceKey[]
392
517
  */
393
518
  this._authorizedDevices = new ComplexMap(PublicKey.hash);
394
- this._updatingMetadata = /* @__PURE__ */ new Map();
395
519
  this._requestedDocs = /* @__PURE__ */ new Set();
396
- 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
- ),
404
- callbacks: {
405
- beforeSave: (params) => this._beforeSave(params)
406
- }
520
+ this._directory = directory;
521
+ this._db = db;
522
+ this._storageCallbacks = storageCallbacks;
523
+ }
524
+ async open() {
525
+ this._directory && await levelMigration({
526
+ db: this._db,
527
+ directory: this._directory
528
+ });
529
+ this._storage = new LevelDBStorageAdapter({
530
+ db: this._db,
531
+ callbacks: this._storageCallbacks
407
532
  });
533
+ await this._storage.open?.();
408
534
  this._peerId = `host-${PublicKey.random().toHex()}`;
535
+ this._meshNetwork = new MeshNetworkAdapter();
536
+ this._clientNetwork = new LocalHostNetworkAdapter();
409
537
  this._repo = new Repo({
410
538
  peerId: this._peerId,
411
539
  network: [
@@ -430,8 +558,8 @@ var AutomergeHost = class {
430
558
  documentId,
431
559
  isRequested
432
560
  }, {
433
- F: __dxlog_file3,
434
- L: 96,
561
+ F: __dxlog_file4,
562
+ L: 108,
435
563
  S: this,
436
564
  C: (f, a) => f(...a)
437
565
  });
@@ -444,8 +572,8 @@ var AutomergeHost = class {
444
572
  peerId,
445
573
  documentId
446
574
  }, {
447
- F: __dxlog_file3,
448
- L: 103,
575
+ F: __dxlog_file4,
576
+ L: 115,
449
577
  S: this,
450
578
  C: (f, a) => f(...a)
451
579
  });
@@ -458,8 +586,8 @@ var AutomergeHost = class {
458
586
  peerId,
459
587
  documentId
460
588
  }, {
461
- F: __dxlog_file3,
462
- L: 112,
589
+ F: __dxlog_file4,
590
+ L: 124,
463
591
  S: this,
464
592
  C: (f, a) => f(...a)
465
593
  });
@@ -475,16 +603,16 @@ var AutomergeHost = class {
475
603
  spaceKey,
476
604
  isAuthorized
477
605
  }, {
478
- F: __dxlog_file3,
479
- L: 118,
606
+ F: __dxlog_file4,
607
+ L: 130,
480
608
  S: this,
481
609
  C: (f, a) => f(...a)
482
610
  });
483
611
  return isAuthorized;
484
612
  } catch (err) {
485
613
  log3.catch(err, void 0, {
486
- F: __dxlog_file3,
487
- L: 128,
614
+ F: __dxlog_file4,
615
+ L: 140,
488
616
  S: this,
489
617
  C: (f, a) => f(...a)
490
618
  });
@@ -494,69 +622,22 @@ var AutomergeHost = class {
494
622
  });
495
623
  this._clientNetwork.ready();
496
624
  this._meshNetwork.ready();
497
- {
498
- const listener = ({ handle }) => this._onDocument(handle);
499
- this._repo.on("document", listener);
500
- this._ctx.onDispose(() => {
501
- this._repo.off("document", listener);
502
- });
503
- }
625
+ await this._clientNetwork.whenConnected();
626
+ }
627
+ async close() {
628
+ await this._storage.close?.();
629
+ await this._clientNetwork.close();
630
+ await this._ctx.dispose();
504
631
  }
505
632
  get repo() {
506
633
  return this._repo;
507
634
  }
508
- async _beforeSave(path) {
509
- const id = path[0];
510
- if (this._updatingMetadata.has(id)) {
511
- return this._updatingMetadata.get(id);
512
- }
513
- }
514
- _onDocument(handle) {
515
- const listener = (event) => this._onUpdate(event);
516
- handle.on("change", listener);
517
- this._ctx.onDispose(() => {
518
- handle.off("change", listener);
519
- });
520
- }
521
- _onUpdate(event) {
522
- if (this._metadata == null) {
523
- return;
524
- }
525
- const objectIds = getInlineChanges(event);
526
- if (objectIds.length === 0) {
527
- return;
528
- }
529
- const heads = getHeads(event.doc);
530
- const lastAvailableHash = heads.join("");
531
- if (!lastAvailableHash) {
532
- return;
533
- }
534
- const encodedIds = objectIds.map((objectId) => idCodec.encode({
535
- documentId: event.handle.documentId,
536
- objectId
537
- }));
538
- const idToLastHash = new Map(encodedIds.map((id) => [
539
- id,
540
- lastAvailableHash
541
- ]));
542
- const markingDirtyPromise = this._metadata.markDirty(idToLastHash).then(() => {
543
- this._updatingMetadata.delete(event.handle.documentId);
544
- }).catch((err) => {
545
- this._ctx.disposed && log3.catch(err, void 0, {
546
- F: __dxlog_file3,
547
- L: 188,
548
- S: this,
549
- C: (f, a) => f(...a)
550
- });
551
- });
552
- this._updatingMetadata.set(event.handle.documentId, markingDirtyPromise);
553
- }
554
635
  _automergeDocs() {
555
636
  return mapValues(this._repo.handles, (handle) => ({
556
637
  state: handle.state,
557
638
  hasDoc: !!handle.docSync(),
558
639
  heads: handle.docSync() ? automerge.getHeads(handle.docSync()) : null,
559
- data: handle.docSync()?.doc && mapValues(handle.docSync()?.doc, (value, key) => {
640
+ data: handle.docSync() && mapValues(handle.docSync(), (value, key) => {
560
641
  try {
561
642
  switch (key) {
562
643
  case "access":
@@ -576,14 +657,25 @@ var AutomergeHost = class {
576
657
  _automergePeers() {
577
658
  return this._repo.peers;
578
659
  }
579
- async close() {
580
- await this._storage.close();
581
- await this._clientNetwork.close();
582
- await this._ctx.dispose();
583
- }
584
660
  //
585
661
  // Methods for client-services.
586
662
  //
663
+ async flush({ states }) {
664
+ await Promise.all(states?.map(async ({ heads, documentId }) => {
665
+ invariant3(heads, "heads are required for flush", {
666
+ F: __dxlog_file4,
667
+ L: 200,
668
+ S: this,
669
+ A: [
670
+ "heads",
671
+ "'heads are required for flush'"
672
+ ]
673
+ });
674
+ const handle = this.repo.handles[documentId] ?? this._repo.find(documentId);
675
+ await waitForHeads(handle, heads);
676
+ }) ?? []);
677
+ await this._repo.flush(states?.map(({ documentId }) => documentId));
678
+ }
587
679
  syncRepo(request) {
588
680
  return this._clientNetwork.syncRepo(request);
589
681
  }
@@ -604,8 +696,8 @@ var AutomergeHost = class {
604
696
  spaceKey,
605
697
  deviceKey
606
698
  }, {
607
- F: __dxlog_file3,
608
- L: 255,
699
+ F: __dxlog_file4,
700
+ L: 230,
609
701
  S: this,
610
702
  C: (f, a) => f(...a)
611
703
  });
@@ -625,27 +717,14 @@ _ts_decorate([
625
717
  depth: null
626
718
  })
627
719
  ], AutomergeHost.prototype, "_automergePeers", null);
720
+ _ts_decorate([
721
+ trace.span({
722
+ showInBrowserTimeline: true
723
+ })
724
+ ], AutomergeHost.prototype, "flush", null);
628
725
  AutomergeHost = _ts_decorate([
629
726
  trace.resource()
630
727
  ], AutomergeHost);
631
- var getInlineChanges = (event) => {
632
- const inlineChangedObjectIds = /* @__PURE__ */ new Set();
633
- for (const { path } of event.patches) {
634
- if (path.length < 2) {
635
- continue;
636
- }
637
- switch (path[0]) {
638
- case "objects":
639
- if (path.length >= 2) {
640
- inlineChangedObjectIds.add(path[1]);
641
- }
642
- break;
643
- }
644
- }
645
- return [
646
- ...inlineChangedObjectIds
647
- ];
648
- };
649
728
  var getSpaceKeyFromDoc = (doc) => {
650
729
  const rawSpaceKey = doc.access?.spaceKey ?? doc.experimental_spaceKey;
651
730
  if (rawSpaceKey == null) {
@@ -653,14 +732,43 @@ var getSpaceKeyFromDoc = (doc) => {
653
732
  }
654
733
  return String(rawSpaceKey);
655
734
  };
735
+ var waitForHeads = async (handle, heads) => {
736
+ await handle.whenReady();
737
+ const unavailableHeads = new Set(heads);
738
+ await Event.wrap(handle, "change").waitForCondition(() => {
739
+ for (const changeHash of unavailableHeads.values()) {
740
+ if (changeIsPresentInDoc(handle.docSync(), changeHash)) {
741
+ unavailableHeads.delete(changeHash);
742
+ }
743
+ }
744
+ if (unavailableHeads.size === 0) {
745
+ return true;
746
+ }
747
+ return false;
748
+ });
749
+ };
750
+ var changeIsPresentInDoc = (doc, changeHash) => {
751
+ return !!getBackend(doc).getChangeByHash(changeHash);
752
+ };
656
753
 
657
754
  // packages/core/echo/echo-pipeline/src/automerge/automerge-doc-loader.ts
658
- import { Event } from "@dxos/async";
755
+ import { Event as Event2 } from "@dxos/async";
659
756
  import { cancelWithContext } from "@dxos/context";
660
757
  import { warnAfterTimeout } from "@dxos/debug";
661
- import { invariant as invariant3 } from "@dxos/invariant";
758
+ import { invariant as invariant4 } from "@dxos/invariant";
662
759
  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";
760
+ import { trace as trace2 } from "@dxos/tracing";
761
+ function _ts_decorate2(decorators, target, key, desc) {
762
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
763
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function")
764
+ r = Reflect.decorate(decorators, target, key, desc);
765
+ else
766
+ for (var i = decorators.length - 1; i >= 0; i--)
767
+ if (d = decorators[i])
768
+ r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
769
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
770
+ }
771
+ var __dxlog_file5 = "/home/runner/work/dxos/dxos/packages/core/echo/echo-pipeline/src/automerge/automerge-doc-loader.ts";
664
772
  var AutomergeDocumentLoaderImpl = class {
665
773
  constructor(_spaceKey, _repo) {
666
774
  this._spaceKey = _spaceKey;
@@ -668,7 +776,12 @@ var AutomergeDocumentLoaderImpl = class {
668
776
  this._spaceRootDocHandle = null;
669
777
  this._objectDocumentHandles = /* @__PURE__ */ new Map();
670
778
  this._objectsPendingDocumentLoad = /* @__PURE__ */ new Set();
671
- this.onObjectDocumentLoaded = new Event();
779
+ this.onObjectDocumentLoaded = new Event2();
780
+ }
781
+ getAllHandles() {
782
+ return [
783
+ ...new Set(this._objectDocumentHandles.values())
784
+ ];
672
785
  }
673
786
  async loadSpaceRootDocHandle(ctx, spaceState) {
674
787
  if (this._spaceRootDocHandle != null) {
@@ -678,8 +791,8 @@ var AutomergeDocumentLoaderImpl = class {
678
791
  log4.error("Database opened with no rootUrl", {
679
792
  spaceKey: this._spaceKey
680
793
  }, {
681
- F: __dxlog_file4,
682
- L: 60,
794
+ F: __dxlog_file5,
795
+ L: 69,
683
796
  S: this,
684
797
  C: (f, a) => f(...a)
685
798
  });
@@ -687,9 +800,9 @@ var AutomergeDocumentLoaderImpl = class {
687
800
  } else {
688
801
  const existingDocHandle = await this._initDocHandle(ctx, spaceState.rootUrl);
689
802
  const doc = existingDocHandle.docSync();
690
- invariant3(doc, void 0, {
691
- F: __dxlog_file4,
692
- L: 65,
803
+ invariant4(doc, void 0, {
804
+ F: __dxlog_file5,
805
+ L: 74,
693
806
  S: this,
694
807
  A: [
695
808
  "doc",
@@ -703,9 +816,9 @@ var AutomergeDocumentLoaderImpl = class {
703
816
  }
704
817
  }
705
818
  loadObjectDocument(objectId) {
706
- invariant3(this._spaceRootDocHandle, void 0, {
707
- F: __dxlog_file4,
708
- L: 74,
819
+ invariant4(this._spaceRootDocHandle, void 0, {
820
+ F: __dxlog_file5,
821
+ L: 83,
709
822
  S: this,
710
823
  A: [
711
824
  "this._spaceRootDocHandle",
@@ -716,9 +829,9 @@ var AutomergeDocumentLoaderImpl = class {
716
829
  return;
717
830
  }
718
831
  const spaceRootDoc = this._spaceRootDocHandle.docSync();
719
- invariant3(spaceRootDoc, void 0, {
720
- F: __dxlog_file4,
721
- L: 79,
832
+ invariant4(spaceRootDoc, void 0, {
833
+ F: __dxlog_file5,
834
+ L: 88,
722
835
  S: this,
723
836
  A: [
724
837
  "spaceRootDoc",
@@ -731,8 +844,8 @@ var AutomergeDocumentLoaderImpl = class {
731
844
  log4.info("loading delayed until object links are initialized", {
732
845
  objectId
733
846
  }, {
734
- F: __dxlog_file4,
735
- L: 83,
847
+ F: __dxlog_file5,
848
+ L: 92,
736
849
  S: this,
737
850
  C: (f, a) => f(...a)
738
851
  });
@@ -751,9 +864,9 @@ var AutomergeDocumentLoaderImpl = class {
751
864
  linksAwaitingLoad.forEach(([objectId]) => this._objectsPendingDocumentLoad.delete(objectId));
752
865
  }
753
866
  getSpaceRootDocHandle() {
754
- invariant3(this._spaceRootDocHandle, void 0, {
755
- F: __dxlog_file4,
756
- L: 101,
867
+ invariant4(this._spaceRootDocHandle, void 0, {
868
+ F: __dxlog_file5,
869
+ L: 110,
757
870
  S: this,
758
871
  A: [
759
872
  "this._spaceRootDocHandle",
@@ -763,9 +876,9 @@ var AutomergeDocumentLoaderImpl = class {
763
876
  return this._spaceRootDocHandle;
764
877
  }
765
878
  createDocumentForObject(objectId) {
766
- invariant3(this._spaceRootDocHandle, void 0, {
767
- F: __dxlog_file4,
768
- L: 106,
879
+ invariant4(this._spaceRootDocHandle, void 0, {
880
+ F: __dxlog_file5,
881
+ L: 115,
769
882
  S: this,
770
883
  A: [
771
884
  "this._spaceRootDocHandle",
@@ -807,8 +920,8 @@ var AutomergeDocumentLoaderImpl = class {
807
920
  ...logMeta,
808
921
  actualDocumentUrl: objectDocumentHandle.url
809
922
  }, {
810
- F: __dxlog_file4,
811
- L: 136,
923
+ F: __dxlog_file5,
924
+ L: 145,
812
925
  S: this,
813
926
  C: (f, a) => f(...a)
814
927
  });
@@ -816,8 +929,8 @@ var AutomergeDocumentLoaderImpl = class {
816
929
  }
817
930
  if (objectDocumentHandle?.url === automergeUrl) {
818
931
  log4.warn("object document was already loaded", logMeta, {
819
- F: __dxlog_file4,
820
- L: 143,
932
+ F: __dxlog_file5,
933
+ L: 152,
821
934
  S: this,
822
935
  C: (f, a) => f(...a)
823
936
  });
@@ -825,8 +938,8 @@ var AutomergeDocumentLoaderImpl = class {
825
938
  }
826
939
  const handle = this._repo.find(automergeUrl);
827
940
  log4.debug("document loading triggered", logMeta, {
828
- F: __dxlog_file4,
829
- L: 147,
941
+ F: __dxlog_file5,
942
+ L: 156,
830
943
  S: this,
831
944
  C: (f, a) => f(...a)
832
945
  });
@@ -848,8 +961,8 @@ var AutomergeDocumentLoaderImpl = class {
848
961
  id: docHandle.documentId,
849
962
  state: docHandle.state
850
963
  }, {
851
- F: __dxlog_file4,
852
- L: 163,
964
+ F: __dxlog_file5,
965
+ L: 172,
853
966
  S: this,
854
967
  C: (f, a) => f(...a)
855
968
  });
@@ -890,8 +1003,8 @@ var AutomergeDocumentLoaderImpl = class {
890
1003
  };
891
1004
  if (this.onObjectDocumentLoaded.listenerCount() === 0) {
892
1005
  log4.info("document loaded after all listeners were removed", logMeta, {
893
- F: __dxlog_file4,
894
- L: 199,
1006
+ F: __dxlog_file5,
1007
+ L: 208,
895
1008
  S: this,
896
1009
  C: (f, a) => f(...a)
897
1010
  });
@@ -900,8 +1013,8 @@ var AutomergeDocumentLoaderImpl = class {
900
1013
  const objectDocHandle = this._objectDocumentHandles.get(objectId);
901
1014
  if (objectDocHandle?.url !== handle.url) {
902
1015
  log4.warn("object was rebound while a document was loading, discarding handle", logMeta, {
903
- F: __dxlog_file4,
904
- L: 204,
1016
+ F: __dxlog_file5,
1017
+ L: 213,
905
1018
  S: this,
906
1019
  C: (f, a) => f(...a)
907
1020
  });
@@ -919,8 +1032,8 @@ var AutomergeDocumentLoaderImpl = class {
919
1032
  retryLoading: shouldRetryLoading,
920
1033
  err
921
1034
  }, {
922
- F: __dxlog_file4,
923
- L: 210,
1035
+ F: __dxlog_file5,
1036
+ L: 219,
924
1037
  S: this,
925
1038
  C: (f, a) => f(...a)
926
1039
  });
@@ -930,6 +1043,27 @@ var AutomergeDocumentLoaderImpl = class {
930
1043
  }
931
1044
  }
932
1045
  };
1046
+ _ts_decorate2([
1047
+ trace2.span({
1048
+ showInBrowserTimeline: true
1049
+ })
1050
+ ], AutomergeDocumentLoaderImpl.prototype, "loadSpaceRootDocHandle", null);
1051
+ AutomergeDocumentLoaderImpl = _ts_decorate2([
1052
+ trace2.resource()
1053
+ ], AutomergeDocumentLoaderImpl);
1054
+
1055
+ // packages/core/echo/echo-pipeline/src/automerge/reference.ts
1056
+ import { Reference } from "@dxos/echo-schema";
1057
+ var REFERENCE_TYPE_TAG = "dxos.echo.model.document.Reference";
1058
+ var encodeReference = (reference) => ({
1059
+ "@type": REFERENCE_TYPE_TAG,
1060
+ // NOTE: Automerge do not support undefined values, so we need to use null instead.
1061
+ itemId: reference.itemId ?? null,
1062
+ protocol: reference.protocol ?? null,
1063
+ host: reference.host ?? null
1064
+ });
1065
+ var decodeReference = (value) => new Reference(value.itemId, value.protocol ?? void 0, value.host ?? void 0);
1066
+ var isEncodedReferenceObject = (value) => typeof value === "object" && value !== null && value["@type"] === REFERENCE_TYPE_TAG;
933
1067
  export {
934
1068
  AuthExtension,
935
1069
  AuthStatus,
@@ -937,12 +1071,14 @@ export {
937
1071
  AutomergeHost,
938
1072
  AutomergeStorageAdapter,
939
1073
  DataServiceImpl,
1074
+ LevelDBStorageAdapter,
940
1075
  LocalHostNetworkAdapter,
941
1076
  MOCK_AUTH_PROVIDER,
942
1077
  MOCK_AUTH_VERIFIER,
943
1078
  MeshNetworkAdapter,
944
1079
  MetadataStore,
945
1080
  Pipeline,
1081
+ REFERENCE_TYPE_TAG,
946
1082
  SnapshotManager,
947
1083
  SnapshotStore,
948
1084
  Space,
@@ -952,7 +1088,12 @@ export {
952
1088
  TimeframeClock,
953
1089
  codec,
954
1090
  createMappedFeedWriter,
1091
+ decodeReference,
1092
+ encodeReference,
1093
+ encodingOptions,
955
1094
  getSpaceKeyFromDoc,
1095
+ hasInvitationExpired,
1096
+ isEncodedReferenceObject,
956
1097
  mapFeedIndexesToTimeframe,
957
1098
  mapTimeframeToFeedIndexes,
958
1099
  startAfter,