@dxos/echo-pipeline 0.6.13 → 0.6.14-main.1366248

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 (129) hide show
  1. package/dist/lib/browser/{chunk-UKXIJW43.mjs → chunk-RRKGEIVZ.mjs} +102 -52
  2. package/dist/lib/browser/chunk-RRKGEIVZ.mjs.map +7 -0
  3. package/dist/lib/browser/index.mjs +3476 -17
  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 +17 -7
  7. package/dist/lib/browser/testing/index.mjs.map +3 -3
  8. package/dist/lib/node/{chunk-7HHYCGUR.cjs → chunk-6KAVD3GU.cjs} +116 -64
  9. package/dist/lib/node/chunk-6KAVD3GU.cjs.map +7 -0
  10. package/dist/lib/node/index.cjs +3454 -35
  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 +25 -15
  14. package/dist/lib/node/testing/index.cjs.map +3 -3
  15. package/dist/lib/node-esm/chunk-4QES5F4H.mjs +2052 -0
  16. package/dist/lib/node-esm/chunk-4QES5F4H.mjs.map +7 -0
  17. package/dist/lib/{browser/chunk-MPWFDDQK.mjs → node-esm/index.mjs} +1716 -335
  18. package/dist/lib/node-esm/index.mjs.map +7 -0
  19. package/dist/lib/node-esm/meta.json +1 -0
  20. package/dist/lib/node-esm/testing/index.mjs +562 -0
  21. package/dist/lib/node-esm/testing/index.mjs.map +7 -0
  22. package/dist/types/src/automerge/automerge-host.d.ts +24 -1
  23. package/dist/types/src/automerge/automerge-host.d.ts.map +1 -1
  24. package/dist/types/src/automerge/collection-synchronizer.d.ts +2 -0
  25. package/dist/types/src/automerge/collection-synchronizer.d.ts.map +1 -1
  26. package/dist/types/src/automerge/echo-network-adapter.d.ts.map +1 -1
  27. package/dist/types/src/automerge/echo-replicator.d.ts +3 -3
  28. package/dist/types/src/automerge/echo-replicator.d.ts.map +1 -1
  29. package/dist/types/src/automerge/mesh-echo-replicator-connection.d.ts +3 -3
  30. package/dist/types/src/automerge/mesh-echo-replicator-connection.d.ts.map +1 -1
  31. package/dist/types/src/automerge/mesh-echo-replicator.d.ts.map +1 -1
  32. package/dist/types/src/automerge/space-collection.d.ts +3 -2
  33. package/dist/types/src/automerge/space-collection.d.ts.map +1 -1
  34. package/dist/types/src/db-host/automerge-metrics.d.ts +11 -0
  35. package/dist/types/src/db-host/automerge-metrics.d.ts.map +1 -0
  36. package/dist/types/src/db-host/data-service.d.ts +3 -2
  37. package/dist/types/src/db-host/data-service.d.ts.map +1 -1
  38. package/dist/types/src/db-host/database-root.d.ts +20 -0
  39. package/dist/types/src/db-host/database-root.d.ts.map +1 -0
  40. package/dist/types/src/db-host/documents-iterator.d.ts +7 -0
  41. package/dist/types/src/db-host/documents-iterator.d.ts.map +1 -0
  42. package/dist/types/src/db-host/echo-host.d.ts +73 -0
  43. package/dist/types/src/db-host/echo-host.d.ts.map +1 -0
  44. package/dist/types/src/db-host/index.d.ts +5 -0
  45. package/dist/types/src/db-host/index.d.ts.map +1 -1
  46. package/dist/types/src/db-host/migration.d.ts +8 -0
  47. package/dist/types/src/db-host/migration.d.ts.map +1 -0
  48. package/dist/types/src/db-host/query-service.d.ts +25 -0
  49. package/dist/types/src/db-host/query-service.d.ts.map +1 -0
  50. package/dist/types/src/db-host/query-state.d.ts +41 -0
  51. package/dist/types/src/db-host/query-state.d.ts.map +1 -0
  52. package/dist/types/src/db-host/space-state-manager.d.ts +23 -0
  53. package/dist/types/src/db-host/space-state-manager.d.ts.map +1 -0
  54. package/dist/types/src/edge/echo-edge-replicator.d.ts +24 -0
  55. package/dist/types/src/edge/echo-edge-replicator.d.ts.map +1 -0
  56. package/dist/types/src/edge/echo-edge-replicator.test.d.ts +2 -0
  57. package/dist/types/src/edge/echo-edge-replicator.test.d.ts.map +1 -0
  58. package/dist/types/src/edge/index.d.ts +2 -0
  59. package/dist/types/src/edge/index.d.ts.map +1 -0
  60. package/dist/types/src/index.d.ts +1 -0
  61. package/dist/types/src/index.d.ts.map +1 -1
  62. package/dist/types/src/metadata/metadata-store.d.ts +4 -1
  63. package/dist/types/src/metadata/metadata-store.d.ts.map +1 -1
  64. package/dist/types/src/space/space-protocol.d.ts.map +1 -1
  65. package/dist/types/src/space/space.d.ts +1 -0
  66. package/dist/types/src/space/space.d.ts.map +1 -1
  67. package/dist/types/src/testing/test-agent-builder.d.ts +3 -1
  68. package/dist/types/src/testing/test-agent-builder.d.ts.map +1 -1
  69. package/dist/types/src/testing/test-replicator.d.ts +4 -4
  70. package/dist/types/src/testing/test-replicator.d.ts.map +1 -1
  71. package/package.json +41 -56
  72. package/src/automerge/automerge-host.test.ts +8 -9
  73. package/src/automerge/automerge-host.ts +46 -7
  74. package/src/automerge/automerge-repo.test.ts +18 -16
  75. package/src/automerge/collection-synchronizer.test.ts +10 -5
  76. package/src/automerge/collection-synchronizer.ts +17 -6
  77. package/src/automerge/echo-data-monitor.test.ts +1 -3
  78. package/src/automerge/echo-network-adapter.test.ts +4 -3
  79. package/src/automerge/echo-network-adapter.ts +5 -4
  80. package/src/automerge/echo-replicator.ts +3 -3
  81. package/src/automerge/mesh-echo-replicator-connection.ts +10 -9
  82. package/src/automerge/mesh-echo-replicator.ts +2 -1
  83. package/src/automerge/space-collection.ts +3 -2
  84. package/src/automerge/storage-adapter.test.ts +2 -3
  85. package/src/db-host/automerge-metrics.ts +38 -0
  86. package/src/db-host/data-service.ts +29 -14
  87. package/src/db-host/database-root.ts +87 -0
  88. package/src/db-host/documents-iterator.ts +73 -0
  89. package/src/db-host/documents-synchronizer.test.ts +2 -2
  90. package/src/db-host/echo-host.ts +257 -0
  91. package/src/db-host/index.ts +6 -1
  92. package/src/db-host/migration.ts +57 -0
  93. package/src/db-host/query-service.ts +209 -0
  94. package/src/db-host/query-state.ts +214 -0
  95. package/src/db-host/space-state-manager.ts +90 -0
  96. package/src/edge/echo-edge-replicator.test.ts +96 -0
  97. package/src/edge/echo-edge-replicator.ts +343 -0
  98. package/src/edge/index.ts +5 -0
  99. package/src/index.ts +1 -0
  100. package/src/metadata/metadata-store.ts +22 -2
  101. package/src/pipeline/pipeline-stress.test.ts +44 -47
  102. package/src/pipeline/pipeline.test.ts +3 -4
  103. package/src/space/control-pipeline.test.ts +2 -3
  104. package/src/space/control-pipeline.ts +10 -1
  105. package/src/space/replication.browser.test.ts +2 -8
  106. package/src/space/space-manager.browser.test.ts +6 -5
  107. package/src/space/space-protocol.browser.test.ts +29 -34
  108. package/src/space/space-protocol.test.ts +37 -27
  109. package/src/space/space-protocol.ts +0 -4
  110. package/src/space/space.test.ts +30 -11
  111. package/src/space/space.ts +7 -2
  112. package/src/testing/test-agent-builder.ts +17 -5
  113. package/src/testing/test-replicator.ts +3 -3
  114. package/dist/lib/browser/chunk-MPWFDDQK.mjs.map +0 -7
  115. package/dist/lib/browser/chunk-UKXIJW43.mjs.map +0 -7
  116. package/dist/lib/browser/chunk-XPCF2V5U.mjs +0 -31
  117. package/dist/lib/browser/chunk-XPCF2V5U.mjs.map +0 -7
  118. package/dist/lib/browser/light.mjs +0 -32
  119. package/dist/lib/browser/light.mjs.map +0 -7
  120. package/dist/lib/node/chunk-5DH4KR2S.cjs +0 -2148
  121. package/dist/lib/node/chunk-5DH4KR2S.cjs.map +0 -7
  122. package/dist/lib/node/chunk-7HHYCGUR.cjs.map +0 -7
  123. package/dist/lib/node/chunk-DZVH7HDD.cjs +0 -43
  124. package/dist/lib/node/chunk-DZVH7HDD.cjs.map +0 -7
  125. package/dist/lib/node/light.cjs +0 -52
  126. package/dist/lib/node/light.cjs.map +0 -7
  127. package/dist/types/src/light.d.ts +0 -4
  128. package/dist/types/src/light.d.ts.map +0 -1
  129. package/src/light.ts +0 -7
@@ -1,17 +1,4 @@
1
1
  import "@dxos/node-std/globals";
2
- import {
3
- AutomergeHost,
4
- DataServiceImpl,
5
- DocumentsSynchronizer,
6
- EchoDataMonitor,
7
- LevelDBStorageAdapter,
8
- MeshEchoReplicator,
9
- deriveCollectionIdFromSpaceId,
10
- diffCollectionState,
11
- encodingOptions,
12
- getSpaceIdFromCollectionId,
13
- getSpaceKeyFromDoc
14
- } from "./chunk-MPWFDDQK.mjs";
15
2
  import {
16
3
  AuthExtension,
17
4
  AuthStatus,
@@ -27,16 +14,3480 @@ import {
27
14
  SpaceProtocolSession,
28
15
  TimeframeClock,
29
16
  codec,
17
+ createIdFromSpaceKey,
30
18
  createMappedFeedWriter,
31
19
  hasInvitationExpired,
32
20
  mapFeedIndexesToTimeframe,
33
21
  mapTimeframeToFeedIndexes,
34
22
  startAfter,
35
23
  valueEncoding
36
- } from "./chunk-UKXIJW43.mjs";
37
- import {
38
- createIdFromSpaceKey
39
- } from "./chunk-XPCF2V5U.mjs";
24
+ } from "./chunk-RRKGEIVZ.mjs";
25
+
26
+ // packages/core/echo/echo-pipeline/src/db-host/data-service.ts
27
+ import { UpdateScheduler as UpdateScheduler2 } from "@dxos/async";
28
+ import { Stream } from "@dxos/codec-protobuf/stream";
29
+ import { invariant as invariant7 } from "@dxos/invariant";
30
+ import { SpaceId as SpaceId2 } from "@dxos/keys";
31
+ import { log as log7 } from "@dxos/log";
32
+
33
+ // packages/core/echo/echo-pipeline/src/db-host/documents-synchronizer.ts
34
+ import { UpdateScheduler } from "@dxos/async";
35
+ import { next as A } from "@dxos/automerge/automerge";
36
+ import { Resource } from "@dxos/context";
37
+ import { invariant } from "@dxos/invariant";
38
+ import { log } from "@dxos/log";
39
+ var __dxlog_file = "/home/runner/work/dxos/dxos/packages/core/echo/echo-pipeline/src/db-host/documents-synchronizer.ts";
40
+ var MAX_UPDATE_FREQ = 10;
41
+ var DocumentsSynchronizer = class extends Resource {
42
+ constructor(_params) {
43
+ super();
44
+ this._params = _params;
45
+ this._syncStates = /* @__PURE__ */ new Map();
46
+ this._pendingUpdates = /* @__PURE__ */ new Set();
47
+ this._sendUpdatesJob = void 0;
48
+ }
49
+ addDocuments(documentIds, retryCounter = 0) {
50
+ if (retryCounter > 3) {
51
+ log.warn("Failed to load document, retry limit reached", {
52
+ documentIds
53
+ }, {
54
+ F: __dxlog_file,
55
+ L: 49,
56
+ S: this,
57
+ C: (f, a) => f(...a)
58
+ });
59
+ return;
60
+ }
61
+ for (const documentId of documentIds) {
62
+ const doc = this._params.repo.find(documentId);
63
+ doc.whenReady().then(() => {
64
+ this._startSync(doc);
65
+ this._pendingUpdates.add(doc.documentId);
66
+ this._sendUpdatesJob.trigger();
67
+ }).catch((error) => {
68
+ log.warn("Failed to load document, wraparound", {
69
+ documentId,
70
+ error
71
+ }, {
72
+ F: __dxlog_file,
73
+ L: 63,
74
+ S: this,
75
+ C: (f, a) => f(...a)
76
+ });
77
+ this.addDocuments([
78
+ documentId
79
+ ], retryCounter + 1);
80
+ });
81
+ }
82
+ }
83
+ removeDocuments(documentIds) {
84
+ for (const documentId of documentIds) {
85
+ this._syncStates.get(documentId)?.clearSubscriptions?.();
86
+ this._syncStates.delete(documentId);
87
+ this._pendingUpdates.delete(documentId);
88
+ }
89
+ }
90
+ async _open() {
91
+ this._sendUpdatesJob = new UpdateScheduler(this._ctx, this._checkAndSendUpdates.bind(this), {
92
+ maxFrequency: MAX_UPDATE_FREQ
93
+ });
94
+ }
95
+ async _close() {
96
+ await this._sendUpdatesJob.join();
97
+ this._syncStates.clear();
98
+ }
99
+ update(updates) {
100
+ for (const { documentId, mutation, isNew } of updates) {
101
+ if (isNew) {
102
+ const doc = this._params.repo.find(documentId);
103
+ doc.update((doc2) => A.loadIncremental(doc2, mutation));
104
+ this._startSync(doc);
105
+ } else {
106
+ this._writeMutation(documentId, mutation);
107
+ }
108
+ }
109
+ }
110
+ _startSync(doc) {
111
+ if (this._syncStates.has(doc.documentId)) {
112
+ log.info("Document already being synced", {
113
+ documentId: doc.documentId
114
+ }, {
115
+ F: __dxlog_file,
116
+ L: 102,
117
+ S: this,
118
+ C: (f, a) => f(...a)
119
+ });
120
+ return;
121
+ }
122
+ const syncState = {
123
+ handle: doc
124
+ };
125
+ this._subscribeForChanges(syncState);
126
+ this._syncStates.set(doc.documentId, syncState);
127
+ }
128
+ _subscribeForChanges(syncState) {
129
+ const handler = () => {
130
+ this._pendingUpdates.add(syncState.handle.documentId);
131
+ this._sendUpdatesJob.trigger();
132
+ };
133
+ syncState.handle.on("heads-changed", handler);
134
+ syncState.clearSubscriptions = () => syncState.handle.off("heads-changed", handler);
135
+ }
136
+ async _checkAndSendUpdates() {
137
+ const updates = [];
138
+ const docsWithPendingUpdates = Array.from(this._pendingUpdates);
139
+ this._pendingUpdates.clear();
140
+ for (const documentId of docsWithPendingUpdates) {
141
+ const update = this._getPendingChanges(documentId);
142
+ if (update) {
143
+ updates.push({
144
+ documentId,
145
+ mutation: update
146
+ });
147
+ }
148
+ }
149
+ if (updates.length > 0) {
150
+ this._params.sendUpdates({
151
+ updates
152
+ });
153
+ }
154
+ }
155
+ _getPendingChanges(documentId) {
156
+ const syncState = this._syncStates.get(documentId);
157
+ invariant(syncState, "Sync state for document not found", {
158
+ F: __dxlog_file,
159
+ L: 143,
160
+ S: this,
161
+ A: [
162
+ "syncState",
163
+ "'Sync state for document not found'"
164
+ ]
165
+ });
166
+ const doc = syncState.handle.docSync();
167
+ if (!doc) {
168
+ return;
169
+ }
170
+ const mutation = syncState.lastSentHead ? A.saveSince(doc, syncState.lastSentHead) : A.save(doc);
171
+ if (mutation.length === 0) {
172
+ return;
173
+ }
174
+ syncState.lastSentHead = A.getHeads(doc);
175
+ return mutation;
176
+ }
177
+ _writeMutation(documentId, mutation) {
178
+ const syncState = this._syncStates.get(documentId);
179
+ invariant(syncState, "Sync state for document not found", {
180
+ F: __dxlog_file,
181
+ L: 158,
182
+ S: this,
183
+ A: [
184
+ "syncState",
185
+ "'Sync state for document not found'"
186
+ ]
187
+ });
188
+ syncState.handle.update((doc) => {
189
+ const headsBefore = A.getHeads(doc);
190
+ const newDoc = A.loadIncremental(doc, mutation);
191
+ if (A.equals(headsBefore, syncState.lastSentHead)) {
192
+ syncState.lastSentHead = A.getHeads(newDoc);
193
+ }
194
+ return newDoc;
195
+ });
196
+ }
197
+ };
198
+
199
+ // packages/core/echo/echo-pipeline/src/automerge/automerge-host.ts
200
+ import { Event as Event2, asyncTimeout } from "@dxos/async";
201
+ import { getBackend, getHeads, isAutomerge, equals as headsEquals, save } from "@dxos/automerge/automerge";
202
+ import { Repo } from "@dxos/automerge/automerge-repo";
203
+ import { Context, Resource as Resource4, cancelWithContext } from "@dxos/context";
204
+ import { invariant as invariant3 } from "@dxos/invariant";
205
+ import { PublicKey } from "@dxos/keys";
206
+ import { log as log4 } from "@dxos/log";
207
+ import { objectPointerCodec } from "@dxos/protocols";
208
+ import { trace } from "@dxos/tracing";
209
+
210
+ // packages/core/echo/echo-pipeline/src/automerge/collection-synchronizer.ts
211
+ import { asyncReturn, Event, scheduleTask, scheduleTaskInterval } from "@dxos/async";
212
+ import { next as am } from "@dxos/automerge/automerge";
213
+ import { Resource as Resource2 } from "@dxos/context";
214
+ import { log as log2 } from "@dxos/log";
215
+ import { defaultMap } from "@dxos/util";
216
+ var __dxlog_file2 = "/home/runner/work/dxos/dxos/packages/core/echo/echo-pipeline/src/automerge/collection-synchronizer.ts";
217
+ var MIN_QUERY_INTERVAL = 5e3;
218
+ var POLL_INTERVAL = 3e4;
219
+ var CollectionSynchronizer = class extends Resource2 {
220
+ constructor(params) {
221
+ super();
222
+ /**
223
+ * CollectionId -> State.
224
+ */
225
+ this._perCollectionStates = /* @__PURE__ */ new Map();
226
+ this._connectedPeers = /* @__PURE__ */ new Set();
227
+ this.remoteStateUpdated = new Event();
228
+ this._sendCollectionState = params.sendCollectionState;
229
+ this._queryCollectionState = params.queryCollectionState;
230
+ this._shouldSyncCollection = params.shouldSyncCollection;
231
+ }
232
+ async _open(ctx) {
233
+ scheduleTaskInterval(this._ctx, async () => {
234
+ for (const collectionId of this._perCollectionStates.keys()) {
235
+ this.refreshCollection(collectionId);
236
+ await asyncReturn();
237
+ }
238
+ }, POLL_INTERVAL);
239
+ }
240
+ getRegisteredCollectionIds() {
241
+ return [
242
+ ...this._perCollectionStates.keys()
243
+ ];
244
+ }
245
+ getLocalCollectionState(collectionId) {
246
+ return this._getPerCollectionState(collectionId).localState;
247
+ }
248
+ setLocalCollectionState(collectionId, state) {
249
+ log2("setLocalCollectionState", {
250
+ collectionId,
251
+ state
252
+ }, {
253
+ F: __dxlog_file2,
254
+ L: 68,
255
+ S: this,
256
+ C: (f, a) => f(...a)
257
+ });
258
+ this._getPerCollectionState(collectionId).localState = state;
259
+ queueMicrotask(async () => {
260
+ if (!this._ctx.disposed) {
261
+ this._refreshInterestedPeers(collectionId);
262
+ this.refreshCollection(collectionId);
263
+ }
264
+ });
265
+ }
266
+ getRemoteCollectionStates(collectionId) {
267
+ return this._getPerCollectionState(collectionId).remoteStates;
268
+ }
269
+ refreshCollection(collectionId) {
270
+ let scheduleAnotherRefresh = false;
271
+ const state = this._getPerCollectionState(collectionId);
272
+ for (const peerId of this._connectedPeers) {
273
+ if (state.interestedPeers.has(peerId)) {
274
+ const lastQueried = state.lastQueried.get(peerId) ?? 0;
275
+ if (Date.now() - lastQueried > MIN_QUERY_INTERVAL) {
276
+ state.lastQueried.set(peerId, Date.now());
277
+ this._queryCollectionState(collectionId, peerId);
278
+ } else {
279
+ scheduleAnotherRefresh = true;
280
+ }
281
+ }
282
+ }
283
+ if (scheduleAnotherRefresh) {
284
+ scheduleTask(this._ctx, () => this.refreshCollection(collectionId), MIN_QUERY_INTERVAL);
285
+ }
286
+ }
287
+ /**
288
+ * Callback when a connection to a peer is established.
289
+ */
290
+ onConnectionOpen(peerId) {
291
+ this._connectedPeers.add(peerId);
292
+ queueMicrotask(async () => {
293
+ if (this._ctx.disposed) {
294
+ return;
295
+ }
296
+ for (const [collectionId, state] of this._perCollectionStates.entries()) {
297
+ if (this._shouldSyncCollection(collectionId, peerId)) {
298
+ state.interestedPeers.add(peerId);
299
+ state.lastQueried.set(peerId, Date.now());
300
+ this._queryCollectionState(collectionId, peerId);
301
+ }
302
+ }
303
+ });
304
+ }
305
+ /**
306
+ * Callback when a connection to a peer is closed.
307
+ */
308
+ onConnectionClosed(peerId) {
309
+ this._connectedPeers.delete(peerId);
310
+ for (const perCollectionState of this._perCollectionStates.values()) {
311
+ perCollectionState.remoteStates.delete(peerId);
312
+ }
313
+ }
314
+ /**
315
+ * Callback when a peer queries the state of a collection.
316
+ */
317
+ onCollectionStateQueried(collectionId, peerId) {
318
+ const perCollectionState = this._getPerCollectionState(collectionId);
319
+ if (perCollectionState.localState) {
320
+ this._sendCollectionState(collectionId, peerId, perCollectionState.localState);
321
+ }
322
+ }
323
+ /**
324
+ * Callback when a peer sends the state of a collection.
325
+ */
326
+ onRemoteStateReceived(collectionId, peerId, state) {
327
+ log2("onRemoteStateReceived", {
328
+ collectionId,
329
+ peerId,
330
+ state
331
+ }, {
332
+ F: __dxlog_file2,
333
+ L: 148,
334
+ S: this,
335
+ C: (f, a) => f(...a)
336
+ });
337
+ validateCollectionState(state);
338
+ const perCollectionState = this._getPerCollectionState(collectionId);
339
+ perCollectionState.remoteStates.set(peerId, state);
340
+ this.remoteStateUpdated.emit({
341
+ peerId,
342
+ collectionId
343
+ });
344
+ }
345
+ _getPerCollectionState(collectionId) {
346
+ return defaultMap(this._perCollectionStates, collectionId, () => ({
347
+ localState: void 0,
348
+ remoteStates: /* @__PURE__ */ new Map(),
349
+ interestedPeers: /* @__PURE__ */ new Set(),
350
+ lastQueried: /* @__PURE__ */ new Map()
351
+ }));
352
+ }
353
+ _refreshInterestedPeers(collectionId) {
354
+ for (const peerId of this._connectedPeers) {
355
+ if (this._shouldSyncCollection(collectionId, peerId)) {
356
+ this._getPerCollectionState(collectionId).interestedPeers.add(peerId);
357
+ } else {
358
+ this._getPerCollectionState(collectionId).interestedPeers.delete(peerId);
359
+ }
360
+ }
361
+ }
362
+ };
363
+ var diffCollectionState = (local, remote) => {
364
+ const allDocuments = /* @__PURE__ */ new Set([
365
+ ...Object.keys(local.documents),
366
+ ...Object.keys(remote.documents)
367
+ ]);
368
+ const missingOnRemote = [];
369
+ const missingOnLocal = [];
370
+ const different = [];
371
+ for (const documentId of allDocuments) {
372
+ if (!local.documents[documentId]) {
373
+ missingOnLocal.push(documentId);
374
+ } else if (!remote.documents[documentId]) {
375
+ missingOnRemote.push(documentId);
376
+ } else if (!am.equals(local.documents[documentId], remote.documents[documentId])) {
377
+ different.push(documentId);
378
+ }
379
+ }
380
+ return {
381
+ missingOnRemote,
382
+ missingOnLocal,
383
+ different
384
+ };
385
+ };
386
+ var validateCollectionState = (state) => {
387
+ Object.entries(state.documents).forEach(([documentId, heads]) => {
388
+ if (!isValidDocumentId(documentId)) {
389
+ throw new Error(`Invalid documentId: ${documentId}`);
390
+ }
391
+ if (Array.isArray(heads) && heads.some((head) => typeof head !== "string")) {
392
+ throw new Error(`Invalid heads: ${heads}`);
393
+ }
394
+ });
395
+ };
396
+ var isValidDocumentId = (documentId) => {
397
+ return typeof documentId === "string" && !documentId.includes(":");
398
+ };
399
+
400
+ // packages/core/echo/echo-pipeline/src/automerge/echo-network-adapter.ts
401
+ import { synchronized, Trigger } from "@dxos/async";
402
+ import { NetworkAdapter } from "@dxos/automerge/automerge-repo";
403
+ import { LifecycleState } from "@dxos/context";
404
+ import { invariant as invariant2 } from "@dxos/invariant";
405
+ import { log as log3 } from "@dxos/log";
406
+ import { nonNullable } from "@dxos/util";
407
+
408
+ // packages/core/echo/echo-pipeline/src/automerge/network-protocol.ts
409
+ import { MESSAGE_TYPE_COLLECTION_QUERY, MESSAGE_TYPE_COLLECTION_STATE } from "@dxos/protocols";
410
+ var isCollectionQueryMessage = (message) => message.type === MESSAGE_TYPE_COLLECTION_QUERY;
411
+ var isCollectionStateMessage = (message) => message.type === MESSAGE_TYPE_COLLECTION_STATE;
412
+
413
+ // packages/core/echo/echo-pipeline/src/automerge/echo-network-adapter.ts
414
+ function _ts_decorate(decorators, target, key, desc) {
415
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
416
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
417
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
418
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
419
+ }
420
+ var __dxlog_file3 = "/home/runner/work/dxos/dxos/packages/core/echo/echo-pipeline/src/automerge/echo-network-adapter.ts";
421
+ var EchoNetworkAdapter = class extends NetworkAdapter {
422
+ constructor(_params) {
423
+ super();
424
+ this._params = _params;
425
+ this._replicators = /* @__PURE__ */ new Set();
426
+ this._connections = /* @__PURE__ */ new Map();
427
+ this._lifecycleState = LifecycleState.CLOSED;
428
+ this._connected = new Trigger();
429
+ }
430
+ connect(peerId, peerMetadata) {
431
+ this.peerId = peerId;
432
+ this.peerMetadata = peerMetadata;
433
+ this._connected.wake();
434
+ }
435
+ send(message) {
436
+ this._send(message);
437
+ }
438
+ disconnect() {
439
+ }
440
+ async open() {
441
+ if (this._lifecycleState === LifecycleState.OPEN) {
442
+ return;
443
+ }
444
+ this._lifecycleState = LifecycleState.OPEN;
445
+ log3("emit ready", void 0, {
446
+ F: __dxlog_file3,
447
+ L: 82,
448
+ S: this,
449
+ C: (f, a) => f(...a)
450
+ });
451
+ this.emit("ready", {
452
+ network: this
453
+ });
454
+ }
455
+ async close() {
456
+ if (this._lifecycleState === LifecycleState.CLOSED) {
457
+ return this;
458
+ }
459
+ for (const replicator of this._replicators) {
460
+ await replicator.disconnect();
461
+ }
462
+ this._replicators.clear();
463
+ this._lifecycleState = LifecycleState.CLOSED;
464
+ }
465
+ async whenConnected() {
466
+ await this._connected.wait({
467
+ timeout: 1e4
468
+ });
469
+ }
470
+ async addReplicator(replicator) {
471
+ invariant2(this._lifecycleState === LifecycleState.OPEN, void 0, {
472
+ F: __dxlog_file3,
473
+ L: 108,
474
+ S: this,
475
+ A: [
476
+ "this._lifecycleState === LifecycleState.OPEN",
477
+ ""
478
+ ]
479
+ });
480
+ invariant2(this.peerId, void 0, {
481
+ F: __dxlog_file3,
482
+ L: 109,
483
+ S: this,
484
+ A: [
485
+ "this.peerId",
486
+ ""
487
+ ]
488
+ });
489
+ invariant2(!this._replicators.has(replicator), void 0, {
490
+ F: __dxlog_file3,
491
+ L: 110,
492
+ S: this,
493
+ A: [
494
+ "!this._replicators.has(replicator)",
495
+ ""
496
+ ]
497
+ });
498
+ this._replicators.add(replicator);
499
+ await replicator.connect({
500
+ peerId: this.peerId,
501
+ onConnectionOpen: this._onConnectionOpen.bind(this),
502
+ onConnectionClosed: this._onConnectionClosed.bind(this),
503
+ onConnectionAuthScopeChanged: this._onConnectionAuthScopeChanged.bind(this),
504
+ isDocumentInRemoteCollection: this._params.isDocumentInRemoteCollection,
505
+ getContainingSpaceForDocument: this._params.getContainingSpaceForDocument,
506
+ getContainingSpaceIdForDocument: async (documentId) => {
507
+ const key = await this._params.getContainingSpaceForDocument(documentId);
508
+ return key ? createIdFromSpaceKey(key) : null;
509
+ }
510
+ });
511
+ }
512
+ async removeReplicator(replicator) {
513
+ invariant2(this._lifecycleState === LifecycleState.OPEN, void 0, {
514
+ F: __dxlog_file3,
515
+ L: 129,
516
+ S: this,
517
+ A: [
518
+ "this._lifecycleState === LifecycleState.OPEN",
519
+ ""
520
+ ]
521
+ });
522
+ invariant2(this._replicators.has(replicator), void 0, {
523
+ F: __dxlog_file3,
524
+ L: 130,
525
+ S: this,
526
+ A: [
527
+ "this._replicators.has(replicator)",
528
+ ""
529
+ ]
530
+ });
531
+ await replicator.disconnect();
532
+ this._replicators.delete(replicator);
533
+ }
534
+ async shouldAdvertise(peerId, params) {
535
+ const connection = this._connections.get(peerId);
536
+ if (!connection) {
537
+ return false;
538
+ }
539
+ return connection.connection.shouldAdvertise(params);
540
+ }
541
+ shouldSyncCollection(peerId, params) {
542
+ const connection = this._connections.get(peerId);
543
+ if (!connection) {
544
+ return false;
545
+ }
546
+ return connection.connection.shouldSyncCollection(params);
547
+ }
548
+ queryCollectionState(collectionId, targetId) {
549
+ const message = {
550
+ type: "collection-query",
551
+ senderId: this.peerId,
552
+ targetId,
553
+ collectionId
554
+ };
555
+ this._send(message);
556
+ }
557
+ sendCollectionState(collectionId, targetId, state) {
558
+ const message = {
559
+ type: "collection-state",
560
+ senderId: this.peerId,
561
+ targetId,
562
+ collectionId,
563
+ state
564
+ };
565
+ this._send(message);
566
+ }
567
+ _send(message) {
568
+ const connectionEntry = this._connections.get(message.targetId);
569
+ if (!connectionEntry) {
570
+ throw new Error("Connection not found.");
571
+ }
572
+ const writeStart = Date.now();
573
+ connectionEntry.writer.write(message).then(() => {
574
+ const durationMs = Date.now() - writeStart;
575
+ this._params.monitor?.recordMessageSent(message, durationMs);
576
+ }).catch((err) => {
577
+ if (connectionEntry.isOpen) {
578
+ log3.catch(err, void 0, {
579
+ F: __dxlog_file3,
580
+ L: 190,
581
+ S: this,
582
+ C: (f, a) => f(...a)
583
+ });
584
+ }
585
+ this._params.monitor?.recordMessageSendingFailed(message);
586
+ });
587
+ }
588
+ // TODO(dmaretskyi): Remove.
589
+ getPeersInterestedInCollection(collectionId) {
590
+ return Array.from(this._connections.values()).map((connection) => {
591
+ return connection.connection.shouldSyncCollection({
592
+ collectionId
593
+ }) ? connection.connection.peerId : null;
594
+ }).filter(nonNullable);
595
+ }
596
+ _onConnectionOpen(connection) {
597
+ log3("Connection opened", {
598
+ peerId: connection.peerId
599
+ }, {
600
+ F: __dxlog_file3,
601
+ L: 208,
602
+ S: this,
603
+ C: (f, a) => f(...a)
604
+ });
605
+ invariant2(!this._connections.has(connection.peerId), void 0, {
606
+ F: __dxlog_file3,
607
+ L: 209,
608
+ S: this,
609
+ A: [
610
+ "!this._connections.has(connection.peerId as PeerId)",
611
+ ""
612
+ ]
613
+ });
614
+ const reader = connection.readable.getReader();
615
+ const writer = connection.writable.getWriter();
616
+ const connectionEntry = {
617
+ connection,
618
+ reader,
619
+ writer,
620
+ isOpen: true
621
+ };
622
+ this._connections.set(connection.peerId, connectionEntry);
623
+ queueMicrotask(async () => {
624
+ try {
625
+ while (true) {
626
+ const { done, value } = await reader.read();
627
+ if (done) {
628
+ break;
629
+ }
630
+ this._onMessage(value);
631
+ }
632
+ } catch (err) {
633
+ if (connectionEntry.isOpen) {
634
+ log3.catch(err, void 0, {
635
+ F: __dxlog_file3,
636
+ L: 228,
637
+ S: this,
638
+ C: (f, a) => f(...a)
639
+ });
640
+ }
641
+ }
642
+ });
643
+ log3("emit peer-candidate", {
644
+ peerId: connection.peerId
645
+ }, {
646
+ F: __dxlog_file3,
647
+ L: 233,
648
+ S: this,
649
+ C: (f, a) => f(...a)
650
+ });
651
+ this._emitPeerCandidate(connection);
652
+ this._params.monitor?.recordPeerConnected(connection.peerId);
653
+ }
654
+ _onMessage(message) {
655
+ if (isCollectionQueryMessage(message)) {
656
+ this._params.onCollectionStateQueried(message.collectionId, message.senderId);
657
+ } else if (isCollectionStateMessage(message)) {
658
+ this._params.onCollectionStateReceived(message.collectionId, message.senderId, message.state);
659
+ } else {
660
+ this.emit("message", message);
661
+ }
662
+ this._params.monitor?.recordMessageReceived(message);
663
+ }
664
+ /**
665
+ * Trigger doc-synchronizer shared documents set recalculation. Happens on peer-candidate.
666
+ * TODO(y): replace with a proper API call when sharePolicy update becomes supported by automerge-repo
667
+ */
668
+ _onConnectionAuthScopeChanged(connection) {
669
+ log3("Connection auth scope changed", {
670
+ peerId: connection.peerId
671
+ }, {
672
+ F: __dxlog_file3,
673
+ L: 254,
674
+ S: this,
675
+ C: (f, a) => f(...a)
676
+ });
677
+ const entry = this._connections.get(connection.peerId);
678
+ invariant2(entry, void 0, {
679
+ F: __dxlog_file3,
680
+ L: 256,
681
+ S: this,
682
+ A: [
683
+ "entry",
684
+ ""
685
+ ]
686
+ });
687
+ this.emit("peer-disconnected", {
688
+ peerId: connection.peerId
689
+ });
690
+ this._emitPeerCandidate(connection);
691
+ }
692
+ _onConnectionClosed(connection) {
693
+ log3("Connection closed", {
694
+ peerId: connection.peerId
695
+ }, {
696
+ F: __dxlog_file3,
697
+ L: 262,
698
+ S: this,
699
+ C: (f, a) => f(...a)
700
+ });
701
+ const entry = this._connections.get(connection.peerId);
702
+ invariant2(entry, void 0, {
703
+ F: __dxlog_file3,
704
+ L: 264,
705
+ S: this,
706
+ A: [
707
+ "entry",
708
+ ""
709
+ ]
710
+ });
711
+ entry.isOpen = false;
712
+ this.emit("peer-disconnected", {
713
+ peerId: connection.peerId
714
+ });
715
+ this._params.monitor?.recordPeerDisconnected(connection.peerId);
716
+ void entry.reader.cancel().catch((err) => log3.catch(err, void 0, {
717
+ F: __dxlog_file3,
718
+ L: 270,
719
+ S: this,
720
+ C: (f, a) => f(...a)
721
+ }));
722
+ void entry.writer.abort().catch((err) => log3.catch(err, void 0, {
723
+ F: __dxlog_file3,
724
+ L: 271,
725
+ S: this,
726
+ C: (f, a) => f(...a)
727
+ }));
728
+ this._connections.delete(connection.peerId);
729
+ }
730
+ _emitPeerCandidate(connection) {
731
+ this.emit("peer-candidate", {
732
+ peerId: connection.peerId,
733
+ peerMetadata: createEchoPeerMetadata()
734
+ });
735
+ }
736
+ };
737
+ _ts_decorate([
738
+ synchronized
739
+ ], EchoNetworkAdapter.prototype, "open", null);
740
+ _ts_decorate([
741
+ synchronized
742
+ ], EchoNetworkAdapter.prototype, "close", null);
743
+ _ts_decorate([
744
+ synchronized
745
+ ], EchoNetworkAdapter.prototype, "addReplicator", null);
746
+ _ts_decorate([
747
+ synchronized
748
+ ], EchoNetworkAdapter.prototype, "removeReplicator", null);
749
+ var createEchoPeerMetadata = () => ({
750
+ // TODO(dmaretskyi): Refactor this.
751
+ dxos_peerSource: "EchoNetworkAdapter"
752
+ });
753
+ var isEchoPeerMetadata = (metadata) => metadata?.dxos_peerSource === "EchoNetworkAdapter";
754
+
755
+ // packages/core/echo/echo-pipeline/src/automerge/heads-store.ts
756
+ import { headsEncoding } from "@dxos/indexing";
757
+ var HeadsStore = class {
758
+ constructor({ db }) {
759
+ this._db = db;
760
+ }
761
+ setHeads(documentId, heads, batch) {
762
+ batch.put(documentId, heads, {
763
+ sublevel: this._db,
764
+ keyEncoding: "utf8",
765
+ valueEncoding: headsEncoding
766
+ });
767
+ }
768
+ // TODO(dmaretskyi): Make batched.
769
+ async getHeads(documentIds) {
770
+ return this._db.getMany(documentIds, {
771
+ keyEncoding: "utf8",
772
+ valueEncoding: headsEncoding
773
+ });
774
+ }
775
+ };
776
+
777
+ // packages/core/echo/echo-pipeline/src/automerge/leveldb-storage-adapter.ts
778
+ import { LifecycleState as LifecycleState2, Resource as Resource3 } from "@dxos/context";
779
+ var LevelDBStorageAdapter = class extends Resource3 {
780
+ constructor(_params) {
781
+ super();
782
+ this._params = _params;
783
+ }
784
+ async load(keyArray) {
785
+ try {
786
+ if (this._lifecycleState !== LifecycleState2.OPEN) {
787
+ return void 0;
788
+ }
789
+ const startMs = Date.now();
790
+ const chunk = await this._params.db.get(keyArray, {
791
+ ...encodingOptions
792
+ });
793
+ this._params.monitor?.recordBytesLoaded(chunk.byteLength);
794
+ this._params.monitor?.recordLoadDuration(Date.now() - startMs);
795
+ return chunk;
796
+ } catch (err) {
797
+ if (isLevelDbNotFoundError(err)) {
798
+ return void 0;
799
+ }
800
+ throw err;
801
+ }
802
+ }
803
+ async save(keyArray, binary) {
804
+ if (this._lifecycleState !== LifecycleState2.OPEN) {
805
+ return void 0;
806
+ }
807
+ const startMs = Date.now();
808
+ const batch = this._params.db.batch();
809
+ await this._params.callbacks?.beforeSave?.({
810
+ path: keyArray,
811
+ batch
812
+ });
813
+ batch.put(keyArray, Buffer.from(binary), {
814
+ ...encodingOptions
815
+ });
816
+ await batch.write();
817
+ this._params.monitor?.recordBytesStored(binary.byteLength);
818
+ await this._params.callbacks?.afterSave?.(keyArray);
819
+ this._params.monitor?.recordStoreDuration(Date.now() - startMs);
820
+ }
821
+ async remove(keyArray) {
822
+ if (this._lifecycleState !== LifecycleState2.OPEN) {
823
+ return void 0;
824
+ }
825
+ await this._params.db.del(keyArray, {
826
+ ...encodingOptions
827
+ });
828
+ }
829
+ async loadRange(keyPrefix) {
830
+ if (this._lifecycleState !== LifecycleState2.OPEN) {
831
+ return [];
832
+ }
833
+ const startMs = Date.now();
834
+ const result = [];
835
+ for await (const [key, value] of this._params.db.iterator({
836
+ gte: keyPrefix,
837
+ lte: [
838
+ ...keyPrefix,
839
+ "\uFFFF"
840
+ ],
841
+ ...encodingOptions
842
+ })) {
843
+ result.push({
844
+ key,
845
+ data: value
846
+ });
847
+ this._params.monitor?.recordBytesLoaded(value.byteLength);
848
+ }
849
+ this._params.monitor?.recordLoadDuration(Date.now() - startMs);
850
+ return result;
851
+ }
852
+ async removeRange(keyPrefix) {
853
+ if (this._lifecycleState !== LifecycleState2.OPEN) {
854
+ return void 0;
855
+ }
856
+ const batch = this._params.db.batch();
857
+ for await (const [key] of this._params.db.iterator({
858
+ gte: keyPrefix,
859
+ lte: [
860
+ ...keyPrefix,
861
+ "\uFFFF"
862
+ ],
863
+ ...encodingOptions
864
+ })) {
865
+ batch.del(key, {
866
+ ...encodingOptions
867
+ });
868
+ }
869
+ await batch.write();
870
+ }
871
+ };
872
+ var keyEncoder = {
873
+ encode: (key) => Buffer.from(key.map((k) => k.replaceAll("%", "%25").replaceAll("-", "%2D")).join("-")),
874
+ decode: (key) => Buffer.from(key).toString().split("-").map((k) => k.replaceAll("%2D", "-").replaceAll("%25", "%")),
875
+ format: "buffer"
876
+ };
877
+ var encodingOptions = {
878
+ keyEncoding: keyEncoder,
879
+ valueEncoding: "buffer"
880
+ };
881
+ var isLevelDbNotFoundError = (err) => err.code === "LEVEL_NOT_FOUND";
882
+
883
+ // packages/core/echo/echo-pipeline/src/automerge/automerge-host.ts
884
+ function _ts_decorate2(decorators, target, key, desc) {
885
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
886
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
887
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
888
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
889
+ }
890
+ var __dxlog_file4 = "/home/runner/work/dxos/dxos/packages/core/echo/echo-pipeline/src/automerge/automerge-host.ts";
891
+ var AutomergeHost = class extends Resource4 {
892
+ constructor({ db, indexMetadataStore, dataMonitor }) {
893
+ super();
894
+ this._collectionSynchronizer = new CollectionSynchronizer({
895
+ queryCollectionState: this._queryCollectionState.bind(this),
896
+ sendCollectionState: this._sendCollectionState.bind(this),
897
+ shouldSyncCollection: this._shouldSyncCollection.bind(this)
898
+ });
899
+ this.collectionStateUpdated = new Event2();
900
+ this._db = db;
901
+ this._storage = new LevelDBStorageAdapter({
902
+ db: db.sublevel("automerge"),
903
+ callbacks: {
904
+ beforeSave: async (params) => this._beforeSave(params),
905
+ afterSave: async (key) => this._afterSave(key)
906
+ },
907
+ monitor: dataMonitor
908
+ });
909
+ this._echoNetworkAdapter = new EchoNetworkAdapter({
910
+ getContainingSpaceForDocument: this._getContainingSpaceForDocument.bind(this),
911
+ isDocumentInRemoteCollection: this._isDocumentInRemoteCollection.bind(this),
912
+ onCollectionStateQueried: this._onCollectionStateQueried.bind(this),
913
+ onCollectionStateReceived: this._onCollectionStateReceived.bind(this),
914
+ monitor: dataMonitor
915
+ });
916
+ this._headsStore = new HeadsStore({
917
+ db: db.sublevel("heads")
918
+ });
919
+ this._indexMetadataStore = indexMetadataStore;
920
+ }
921
+ async _open() {
922
+ this._peerId = `host-${PublicKey.random().toHex()}`;
923
+ await this._storage.open?.();
924
+ this._repo = new Repo({
925
+ peerId: this._peerId,
926
+ sharePolicy: this._sharePolicy.bind(this),
927
+ storage: this._storage,
928
+ network: [
929
+ // Upstream swarm.
930
+ this._echoNetworkAdapter
931
+ ]
932
+ });
933
+ Event2.wrap(this._echoNetworkAdapter, "peer-candidate").on(this._ctx, (e) => this._onPeerConnected(e.peerId));
934
+ Event2.wrap(this._echoNetworkAdapter, "peer-disconnected").on(this._ctx, (e) => this._onPeerDisconnected(e.peerId));
935
+ this._collectionSynchronizer.remoteStateUpdated.on(this._ctx, ({ collectionId, peerId }) => {
936
+ this._onRemoteCollectionStateUpdated(collectionId, peerId);
937
+ this.collectionStateUpdated.emit({
938
+ collectionId
939
+ });
940
+ });
941
+ await this._echoNetworkAdapter.open();
942
+ await this._collectionSynchronizer.open();
943
+ await this._echoNetworkAdapter.open();
944
+ await this._echoNetworkAdapter.whenConnected();
945
+ }
946
+ async _close() {
947
+ await this._collectionSynchronizer.close();
948
+ await this._storage.close?.();
949
+ await this._echoNetworkAdapter.close();
950
+ await this._ctx.dispose();
951
+ }
952
+ /**
953
+ * @deprecated To be abstracted away.
954
+ */
955
+ get repo() {
956
+ return this._repo;
957
+ }
958
+ get peerId() {
959
+ return this._peerId;
960
+ }
961
+ get loadedDocsCount() {
962
+ return Object.keys(this._repo.handles).length;
963
+ }
964
+ async addReplicator(replicator) {
965
+ await this._echoNetworkAdapter.addReplicator(replicator);
966
+ }
967
+ async removeReplicator(replicator) {
968
+ await this._echoNetworkAdapter.removeReplicator(replicator);
969
+ }
970
+ /**
971
+ * Loads the document handle from the repo and waits for it to be ready.
972
+ */
973
+ async loadDoc(ctx, documentId, opts) {
974
+ let handle;
975
+ if (typeof documentId === "string") {
976
+ handle = this._repo.handles[documentId];
977
+ }
978
+ if (!handle) {
979
+ handle = this._repo.find(documentId);
980
+ }
981
+ if (!handle.isReady()) {
982
+ if (!opts?.timeout) {
983
+ await cancelWithContext(ctx, handle.whenReady());
984
+ } else {
985
+ await cancelWithContext(ctx, asyncTimeout(handle.whenReady(), opts.timeout));
986
+ }
987
+ }
988
+ return handle;
989
+ }
990
+ /**
991
+ * Create new persisted document.
992
+ */
993
+ createDoc(initialValue, opts) {
994
+ if (opts?.preserveHistory) {
995
+ if (!isAutomerge(initialValue)) {
996
+ throw new TypeError("Initial value must be an Automerge document");
997
+ }
998
+ return this._repo.import(save(initialValue));
999
+ } else {
1000
+ return this._repo.create(initialValue);
1001
+ }
1002
+ }
1003
+ async waitUntilHeadsReplicated(heads) {
1004
+ const entries = heads.entries;
1005
+ if (!entries?.length) {
1006
+ return;
1007
+ }
1008
+ const documentIds = entries.map((entry) => entry.documentId);
1009
+ const documentHeads = await this.getHeads(documentIds);
1010
+ const headsToWait = entries.filter((entry, index) => {
1011
+ const targetHeads = entry.heads;
1012
+ if (!targetHeads || targetHeads.length === 0) {
1013
+ return false;
1014
+ }
1015
+ const currentHeads = documentHeads[index];
1016
+ return !(currentHeads !== null && headsEquals(currentHeads, targetHeads));
1017
+ });
1018
+ if (headsToWait.length > 0) {
1019
+ await Promise.all(headsToWait.map(async (entry, index) => {
1020
+ const handle = await this.loadDoc(Context.default(void 0, {
1021
+ F: __dxlog_file4,
1022
+ L: 230
1023
+ }), entry.documentId);
1024
+ await waitForHeads(handle, entry.heads);
1025
+ }));
1026
+ }
1027
+ await this._repo.flush(documentIds.filter((documentId) => !!this._repo.handles[documentId]));
1028
+ }
1029
+ async reIndexHeads(documentIds) {
1030
+ for (const documentId of documentIds) {
1031
+ log4.info("re-indexing heads for document", {
1032
+ documentId
1033
+ }, {
1034
+ F: __dxlog_file4,
1035
+ L: 242,
1036
+ S: this,
1037
+ C: (f, a) => f(...a)
1038
+ });
1039
+ const handle = this._repo.find(documentId);
1040
+ await handle.whenReady([
1041
+ "ready",
1042
+ "requesting"
1043
+ ]);
1044
+ if (handle.inState([
1045
+ "requesting"
1046
+ ])) {
1047
+ log4.warn("document is not available locally, skipping", {
1048
+ documentId
1049
+ }, {
1050
+ F: __dxlog_file4,
1051
+ L: 246,
1052
+ S: this,
1053
+ C: (f, a) => f(...a)
1054
+ });
1055
+ continue;
1056
+ }
1057
+ const doc = handle.docSync();
1058
+ invariant3(doc, void 0, {
1059
+ F: __dxlog_file4,
1060
+ L: 251,
1061
+ S: this,
1062
+ A: [
1063
+ "doc",
1064
+ ""
1065
+ ]
1066
+ });
1067
+ const heads = getHeads(doc);
1068
+ const batch = this._db.batch();
1069
+ this._headsStore.setHeads(documentId, heads, batch);
1070
+ await batch.write();
1071
+ }
1072
+ log4.info("done re-indexing heads", void 0, {
1073
+ F: __dxlog_file4,
1074
+ L: 258,
1075
+ S: this,
1076
+ C: (f, a) => f(...a)
1077
+ });
1078
+ }
1079
+ // TODO(dmaretskyi): Share based on HALO permissions and space affinity.
1080
+ // Hosts, running in the worker, don't share documents unless requested by other peers.
1081
+ // NOTE: If both peers return sharePolicy=false the replication will not happen
1082
+ // https://github.com/automerge/automerge-repo/pull/292
1083
+ async _sharePolicy(peerId, documentId) {
1084
+ if (peerId.startsWith("client-")) {
1085
+ return false;
1086
+ }
1087
+ if (!documentId) {
1088
+ return false;
1089
+ }
1090
+ const peerMetadata = this.repo.peerMetadataByPeerId[peerId];
1091
+ if (isEchoPeerMetadata(peerMetadata)) {
1092
+ return this._echoNetworkAdapter.shouldAdvertise(peerId, {
1093
+ documentId
1094
+ });
1095
+ }
1096
+ return false;
1097
+ }
1098
+ async _beforeSave({ path, batch }) {
1099
+ const handle = this._repo.handles[path[0]];
1100
+ if (!handle) {
1101
+ return;
1102
+ }
1103
+ const doc = handle.docSync();
1104
+ if (!doc) {
1105
+ return;
1106
+ }
1107
+ const heads = getHeads(doc);
1108
+ this._headsStore.setHeads(handle.documentId, heads, batch);
1109
+ const spaceKey = getSpaceKeyFromDoc(doc) ?? void 0;
1110
+ const objectIds = Object.keys(doc.objects ?? {});
1111
+ const encodedIds = objectIds.map((objectId) => objectPointerCodec.encode({
1112
+ documentId: handle.documentId,
1113
+ objectId,
1114
+ spaceKey
1115
+ }));
1116
+ const idToLastHash = new Map(encodedIds.map((id) => [
1117
+ id,
1118
+ heads
1119
+ ]));
1120
+ this._indexMetadataStore.markDirty(idToLastHash, batch);
1121
+ }
1122
+ _shouldSyncCollection(collectionId, peerId) {
1123
+ const peerMetadata = this._repo.peerMetadataByPeerId[peerId];
1124
+ if (isEchoPeerMetadata(peerMetadata)) {
1125
+ return this._echoNetworkAdapter.shouldSyncCollection(peerId, {
1126
+ collectionId
1127
+ });
1128
+ }
1129
+ return false;
1130
+ }
1131
+ /**
1132
+ * Called by AutomergeStorageAdapter after levelDB batch commit.
1133
+ */
1134
+ async _afterSave(path) {
1135
+ this._indexMetadataStore.notifyMarkedDirty();
1136
+ const documentId = path[0];
1137
+ const document = this._repo.handles[documentId]?.docSync();
1138
+ if (document) {
1139
+ const heads = getHeads(document);
1140
+ this._onHeadsChanged(documentId, heads);
1141
+ }
1142
+ }
1143
+ _automergePeers() {
1144
+ return this._repo.peers;
1145
+ }
1146
+ async _isDocumentInRemoteCollection(params) {
1147
+ for (const collectionId of this._collectionSynchronizer.getRegisteredCollectionIds()) {
1148
+ const remoteCollections = this._collectionSynchronizer.getRemoteCollectionStates(collectionId);
1149
+ const remotePeerDocs = remoteCollections.get(params.peerId)?.documents;
1150
+ if (remotePeerDocs && params.documentId in remotePeerDocs) {
1151
+ return true;
1152
+ }
1153
+ }
1154
+ return false;
1155
+ }
1156
+ async _getContainingSpaceForDocument(documentId) {
1157
+ const doc = this._repo.handles[documentId]?.docSync();
1158
+ if (!doc) {
1159
+ return null;
1160
+ }
1161
+ const spaceKeyHex = getSpaceKeyFromDoc(doc);
1162
+ if (!spaceKeyHex) {
1163
+ return null;
1164
+ }
1165
+ return PublicKey.from(spaceKeyHex);
1166
+ }
1167
+ /**
1168
+ * Flush documents to disk.
1169
+ */
1170
+ async flush({ documentIds } = {}) {
1171
+ const loadedDocuments = documentIds?.filter((documentId) => !!this._repo.handles[documentId]);
1172
+ await this._repo.flush(loadedDocuments);
1173
+ }
1174
+ async getHeads(documentIds) {
1175
+ const result = [];
1176
+ const storeRequestIds = [];
1177
+ const storeResultIndices = [];
1178
+ for (const documentId of documentIds) {
1179
+ const doc = this._repo.handles[documentId]?.docSync();
1180
+ if (doc) {
1181
+ result.push(getHeads(doc));
1182
+ } else {
1183
+ storeRequestIds.push(documentId);
1184
+ storeResultIndices.push(result.length);
1185
+ result.push(void 0);
1186
+ }
1187
+ }
1188
+ if (storeRequestIds.length > 0) {
1189
+ const storedHeads = await this._headsStore.getHeads(storeRequestIds);
1190
+ for (let i = 0; i < storedHeads.length; i++) {
1191
+ result[storeResultIndices[i]] = storedHeads[i];
1192
+ }
1193
+ }
1194
+ return result;
1195
+ }
1196
+ //
1197
+ // Collection sync.
1198
+ //
1199
+ getLocalCollectionState(collectionId) {
1200
+ return this._collectionSynchronizer.getLocalCollectionState(collectionId);
1201
+ }
1202
+ getRemoteCollectionStates(collectionId) {
1203
+ return this._collectionSynchronizer.getRemoteCollectionStates(collectionId);
1204
+ }
1205
+ refreshCollection(collectionId) {
1206
+ this._collectionSynchronizer.refreshCollection(collectionId);
1207
+ }
1208
+ async getCollectionSyncState(collectionId) {
1209
+ const result = {
1210
+ peers: []
1211
+ };
1212
+ const localState = this.getLocalCollectionState(collectionId);
1213
+ const remoteState = this.getRemoteCollectionStates(collectionId);
1214
+ if (!localState) {
1215
+ return result;
1216
+ }
1217
+ for (const [peerId, state] of remoteState) {
1218
+ const diff = diffCollectionState(localState, state);
1219
+ result.peers.push({
1220
+ peerId,
1221
+ missingOnRemote: diff.missingOnRemote.length,
1222
+ missingOnLocal: diff.missingOnLocal.length,
1223
+ differentDocuments: diff.different.length,
1224
+ localDocumentCount: Object.keys(localState.documents).length,
1225
+ remoteDocumentCount: Object.keys(state.documents).length
1226
+ });
1227
+ }
1228
+ return result;
1229
+ }
1230
+ /**
1231
+ * Update the local collection state based on the locally stored document heads.
1232
+ */
1233
+ async updateLocalCollectionState(collectionId, documentIds) {
1234
+ const heads = await this.getHeads(documentIds);
1235
+ const documents = Object.fromEntries(heads.map((heads2, index) => [
1236
+ documentIds[index],
1237
+ heads2 ?? []
1238
+ ]));
1239
+ this._collectionSynchronizer.setLocalCollectionState(collectionId, {
1240
+ documents
1241
+ });
1242
+ }
1243
+ _onCollectionStateQueried(collectionId, peerId) {
1244
+ this._collectionSynchronizer.onCollectionStateQueried(collectionId, peerId);
1245
+ }
1246
+ _onCollectionStateReceived(collectionId, peerId, state) {
1247
+ this._collectionSynchronizer.onRemoteStateReceived(collectionId, peerId, decodeCollectionState(state));
1248
+ }
1249
+ _queryCollectionState(collectionId, peerId) {
1250
+ this._echoNetworkAdapter.queryCollectionState(collectionId, peerId);
1251
+ }
1252
+ _sendCollectionState(collectionId, peerId, state) {
1253
+ this._echoNetworkAdapter.sendCollectionState(collectionId, peerId, encodeCollectionState(state));
1254
+ }
1255
+ _onPeerConnected(peerId) {
1256
+ this._collectionSynchronizer.onConnectionOpen(peerId);
1257
+ }
1258
+ _onPeerDisconnected(peerId) {
1259
+ this._collectionSynchronizer.onConnectionClosed(peerId);
1260
+ }
1261
+ _onRemoteCollectionStateUpdated(collectionId, peerId) {
1262
+ const localState = this._collectionSynchronizer.getLocalCollectionState(collectionId);
1263
+ const remoteState = this._collectionSynchronizer.getRemoteCollectionStates(collectionId).get(peerId);
1264
+ if (!localState || !remoteState) {
1265
+ return;
1266
+ }
1267
+ const { different, missingOnLocal, missingOnRemote } = diffCollectionState(localState, remoteState);
1268
+ const toReplicate = [
1269
+ ...missingOnLocal,
1270
+ ...missingOnRemote,
1271
+ ...different
1272
+ ];
1273
+ if (toReplicate.length === 0) {
1274
+ return;
1275
+ }
1276
+ log4.info("replication documents after collection sync", {
1277
+ count: toReplicate.length
1278
+ }, {
1279
+ F: __dxlog_file4,
1280
+ L: 486,
1281
+ S: this,
1282
+ C: (f, a) => f(...a)
1283
+ });
1284
+ for (const documentId of toReplicate) {
1285
+ this._repo.find(documentId);
1286
+ }
1287
+ }
1288
+ _onHeadsChanged(documentId, heads) {
1289
+ const collectionsChanged = /* @__PURE__ */ new Set();
1290
+ for (const collectionId of this._collectionSynchronizer.getRegisteredCollectionIds()) {
1291
+ const state = this._collectionSynchronizer.getLocalCollectionState(collectionId);
1292
+ if (state?.documents[documentId]) {
1293
+ const newState = structuredClone(state);
1294
+ newState.documents[documentId] = heads;
1295
+ this._collectionSynchronizer.setLocalCollectionState(collectionId, newState);
1296
+ collectionsChanged.add(collectionId);
1297
+ }
1298
+ }
1299
+ for (const collectionId of collectionsChanged) {
1300
+ this.collectionStateUpdated.emit({
1301
+ collectionId
1302
+ });
1303
+ }
1304
+ }
1305
+ };
1306
+ _ts_decorate2([
1307
+ trace.info()
1308
+ ], AutomergeHost.prototype, "_peerId", void 0);
1309
+ _ts_decorate2([
1310
+ trace.info({
1311
+ depth: null
1312
+ })
1313
+ ], AutomergeHost.prototype, "_automergePeers", null);
1314
+ _ts_decorate2([
1315
+ trace.span({
1316
+ showInBrowserTimeline: true
1317
+ })
1318
+ ], AutomergeHost.prototype, "flush", null);
1319
+ AutomergeHost = _ts_decorate2([
1320
+ trace.resource()
1321
+ ], AutomergeHost);
1322
+ var getSpaceKeyFromDoc = (doc) => {
1323
+ const rawSpaceKey = doc.access?.spaceKey ?? doc.experimental_spaceKey;
1324
+ if (rawSpaceKey == null) {
1325
+ return null;
1326
+ }
1327
+ return String(rawSpaceKey);
1328
+ };
1329
+ var waitForHeads = async (handle, heads) => {
1330
+ const unavailableHeads = new Set(heads);
1331
+ await handle.whenReady();
1332
+ await Event2.wrap(handle, "change").waitForCondition(() => {
1333
+ for (const changeHash of unavailableHeads.values()) {
1334
+ if (changeIsPresentInDoc(handle.docSync(), changeHash)) {
1335
+ unavailableHeads.delete(changeHash);
1336
+ }
1337
+ }
1338
+ return unavailableHeads.size === 0;
1339
+ });
1340
+ };
1341
+ var changeIsPresentInDoc = (doc, changeHash) => {
1342
+ return !!getBackend(doc).getChangeByHash(changeHash);
1343
+ };
1344
+ var decodeCollectionState = (state) => {
1345
+ invariant3(typeof state === "object" && state !== null, "Invalid state", {
1346
+ F: __dxlog_file4,
1347
+ L: 544,
1348
+ S: void 0,
1349
+ A: [
1350
+ "typeof state === 'object' && state !== null",
1351
+ "'Invalid state'"
1352
+ ]
1353
+ });
1354
+ return state;
1355
+ };
1356
+ var encodeCollectionState = (state) => {
1357
+ return state;
1358
+ };
1359
+
1360
+ // packages/core/echo/echo-pipeline/src/automerge/mesh-echo-replicator.ts
1361
+ import { invariant as invariant6 } from "@dxos/invariant";
1362
+ import { PublicKey as PublicKey2 } from "@dxos/keys";
1363
+ import { log as log6 } from "@dxos/log";
1364
+ import { ComplexSet, defaultMap as defaultMap2 } from "@dxos/util";
1365
+
1366
+ // packages/core/echo/echo-pipeline/src/automerge/mesh-echo-replicator-connection.ts
1367
+ import * as A2 from "@dxos/automerge/automerge";
1368
+ import { cbor } from "@dxos/automerge/automerge-repo";
1369
+ import { Resource as Resource5 } from "@dxos/context";
1370
+ import { invariant as invariant4 } from "@dxos/invariant";
1371
+ import { log as log5 } from "@dxos/log";
1372
+ import { AutomergeReplicator } from "@dxos/teleport-extension-automerge-replicator";
1373
+ var __dxlog_file5 = "/home/runner/work/dxos/dxos/packages/core/echo/echo-pipeline/src/automerge/mesh-echo-replicator-connection.ts";
1374
+ var DEFAULT_FACTORY = (params) => new AutomergeReplicator(...params);
1375
+ var MeshReplicatorConnection = class extends Resource5 {
1376
+ constructor(_params) {
1377
+ super();
1378
+ this._params = _params;
1379
+ this.remoteDeviceKey = null;
1380
+ this._remotePeerId = null;
1381
+ this._isEnabled = false;
1382
+ let readableStreamController;
1383
+ this.readable = new ReadableStream({
1384
+ start: (controller) => {
1385
+ readableStreamController = controller;
1386
+ this._ctx.onDispose(() => controller.close());
1387
+ }
1388
+ });
1389
+ this.writable = new WritableStream({
1390
+ write: async (message, controller) => {
1391
+ invariant4(this._isEnabled, "Writing to a disabled connection", {
1392
+ F: __dxlog_file5,
1393
+ L: 50,
1394
+ S: this,
1395
+ A: [
1396
+ "this._isEnabled",
1397
+ "'Writing to a disabled connection'"
1398
+ ]
1399
+ });
1400
+ try {
1401
+ logSendSync(message);
1402
+ await this.replicatorExtension.sendSyncMessage({
1403
+ payload: cbor.encode(message)
1404
+ });
1405
+ } catch (err) {
1406
+ controller.error(err);
1407
+ this._disconnectIfEnabled();
1408
+ }
1409
+ }
1410
+ });
1411
+ const createAutomergeReplicator = this._params.replicatorFactory ?? DEFAULT_FACTORY;
1412
+ this.replicatorExtension = createAutomergeReplicator([
1413
+ {
1414
+ peerId: this._params.ownPeerId
1415
+ },
1416
+ {
1417
+ onStartReplication: async (info, remotePeerId) => {
1418
+ this.remoteDeviceKey = remotePeerId;
1419
+ this._remotePeerId = info.id;
1420
+ log5("onStartReplication", {
1421
+ id: info.id,
1422
+ thisPeerId: this.peerId,
1423
+ remotePeerId: remotePeerId.toHex()
1424
+ }, {
1425
+ F: __dxlog_file5,
1426
+ L: 85,
1427
+ S: this,
1428
+ C: (f, a) => f(...a)
1429
+ });
1430
+ this._params.onRemoteConnected();
1431
+ },
1432
+ onSyncMessage: async ({ payload }) => {
1433
+ if (!this._isEnabled) {
1434
+ return;
1435
+ }
1436
+ const message = cbor.decode(payload);
1437
+ readableStreamController.enqueue(message);
1438
+ },
1439
+ onClose: async () => {
1440
+ this._disconnectIfEnabled();
1441
+ }
1442
+ }
1443
+ ]);
1444
+ }
1445
+ _disconnectIfEnabled() {
1446
+ if (this._isEnabled) {
1447
+ this._params.onRemoteDisconnected();
1448
+ }
1449
+ }
1450
+ get peerId() {
1451
+ invariant4(this._remotePeerId != null, "Remote peer has not connected yet.", {
1452
+ F: __dxlog_file5,
1453
+ L: 111,
1454
+ S: this,
1455
+ A: [
1456
+ "this._remotePeerId != null",
1457
+ "'Remote peer has not connected yet.'"
1458
+ ]
1459
+ });
1460
+ return this._remotePeerId;
1461
+ }
1462
+ async shouldAdvertise(params) {
1463
+ return this._params.shouldAdvertise(params);
1464
+ }
1465
+ shouldSyncCollection(params) {
1466
+ return this._params.shouldSyncCollection(params);
1467
+ }
1468
+ /**
1469
+ * Start exchanging messages with the remote peer.
1470
+ * Call after the remote peer has connected.
1471
+ */
1472
+ enable() {
1473
+ invariant4(this._remotePeerId != null, "Remote peer has not connected yet.", {
1474
+ F: __dxlog_file5,
1475
+ L: 128,
1476
+ S: this,
1477
+ A: [
1478
+ "this._remotePeerId != null",
1479
+ "'Remote peer has not connected yet.'"
1480
+ ]
1481
+ });
1482
+ this._isEnabled = true;
1483
+ }
1484
+ /**
1485
+ * Stop exchanging messages with the remote peer.
1486
+ */
1487
+ disable() {
1488
+ this._isEnabled = false;
1489
+ }
1490
+ };
1491
+ var logSendSync = (message) => {
1492
+ log5("sendSyncMessage", () => {
1493
+ const decodedSyncMessage = message.type === "sync" && message.data ? A2.decodeSyncMessage(message.data) : void 0;
1494
+ return {
1495
+ sync: decodedSyncMessage && {
1496
+ headsLength: decodedSyncMessage.heads.length,
1497
+ requesting: decodedSyncMessage.need.length > 0,
1498
+ sendingChanges: decodedSyncMessage.changes.length > 0
1499
+ },
1500
+ type: message.type,
1501
+ from: message.senderId,
1502
+ to: message.targetId
1503
+ };
1504
+ }, {
1505
+ F: __dxlog_file5,
1506
+ L: 141,
1507
+ S: void 0,
1508
+ C: (f, a) => f(...a)
1509
+ });
1510
+ };
1511
+
1512
+ // packages/core/echo/echo-pipeline/src/automerge/space-collection.ts
1513
+ import { invariant as invariant5 } from "@dxos/invariant";
1514
+ import { SpaceId } from "@dxos/keys";
1515
+ var __dxlog_file6 = "/home/runner/work/dxos/dxos/packages/core/echo/echo-pipeline/src/automerge/space-collection.ts";
1516
+ var deriveCollectionIdFromSpaceId = (spaceId) => `space:${spaceId}`;
1517
+ var getSpaceIdFromCollectionId = (collectionId) => {
1518
+ const spaceId = collectionId.replace(/^space:/, "");
1519
+ invariant5(SpaceId.isValid(spaceId), void 0, {
1520
+ F: __dxlog_file6,
1521
+ L: 13,
1522
+ S: void 0,
1523
+ A: [
1524
+ "SpaceId.isValid(spaceId)",
1525
+ ""
1526
+ ]
1527
+ });
1528
+ return spaceId;
1529
+ };
1530
+
1531
+ // packages/core/echo/echo-pipeline/src/automerge/mesh-echo-replicator.ts
1532
+ var __dxlog_file7 = "/home/runner/work/dxos/dxos/packages/core/echo/echo-pipeline/src/automerge/mesh-echo-replicator.ts";
1533
+ var MeshEchoReplicator = class {
1534
+ constructor() {
1535
+ this._connections = /* @__PURE__ */ new Set();
1536
+ /**
1537
+ * Using automerge peerId as a key.
1538
+ */
1539
+ this._connectionsPerPeer = /* @__PURE__ */ new Map();
1540
+ /**
1541
+ * spaceId -> deviceKey[]
1542
+ */
1543
+ this._authorizedDevices = /* @__PURE__ */ new Map();
1544
+ this._context = null;
1545
+ }
1546
+ async connect(context) {
1547
+ this._context = context;
1548
+ }
1549
+ async disconnect() {
1550
+ for (const connection of this._connectionsPerPeer.values()) {
1551
+ this._context?.onConnectionClosed(connection);
1552
+ }
1553
+ for (const connection of this._connections) {
1554
+ await connection.close();
1555
+ }
1556
+ this._connections.clear();
1557
+ this._connectionsPerPeer.clear();
1558
+ this._context = null;
1559
+ }
1560
+ createExtension(extensionFactory) {
1561
+ invariant6(this._context, void 0, {
1562
+ F: __dxlog_file7,
1563
+ L: 57,
1564
+ S: this,
1565
+ A: [
1566
+ "this._context",
1567
+ ""
1568
+ ]
1569
+ });
1570
+ const connection = new MeshReplicatorConnection({
1571
+ ownPeerId: this._context.peerId,
1572
+ replicatorFactory: extensionFactory,
1573
+ onRemoteConnected: async () => {
1574
+ log6("onRemoteConnected", {
1575
+ peerId: connection.peerId
1576
+ }, {
1577
+ F: __dxlog_file7,
1578
+ L: 63,
1579
+ S: this,
1580
+ C: (f, a) => f(...a)
1581
+ });
1582
+ invariant6(this._context, void 0, {
1583
+ F: __dxlog_file7,
1584
+ L: 64,
1585
+ S: this,
1586
+ A: [
1587
+ "this._context",
1588
+ ""
1589
+ ]
1590
+ });
1591
+ if (this._connectionsPerPeer.has(connection.peerId)) {
1592
+ this._context.onConnectionAuthScopeChanged(connection);
1593
+ } else {
1594
+ this._connectionsPerPeer.set(connection.peerId, connection);
1595
+ this._context.onConnectionOpen(connection);
1596
+ connection.enable();
1597
+ }
1598
+ },
1599
+ onRemoteDisconnected: async () => {
1600
+ log6("onRemoteDisconnected", {
1601
+ peerId: connection.peerId
1602
+ }, {
1603
+ F: __dxlog_file7,
1604
+ L: 75,
1605
+ S: this,
1606
+ C: (f, a) => f(...a)
1607
+ });
1608
+ this._context?.onConnectionClosed(connection);
1609
+ this._connectionsPerPeer.delete(connection.peerId);
1610
+ connection.disable();
1611
+ this._connections.delete(connection);
1612
+ },
1613
+ shouldAdvertise: async (params) => {
1614
+ log6("shouldAdvertise", {
1615
+ peerId: connection.peerId,
1616
+ documentId: params.documentId
1617
+ }, {
1618
+ F: __dxlog_file7,
1619
+ L: 82,
1620
+ S: this,
1621
+ C: (f, a) => f(...a)
1622
+ });
1623
+ invariant6(this._context, void 0, {
1624
+ F: __dxlog_file7,
1625
+ L: 83,
1626
+ S: this,
1627
+ A: [
1628
+ "this._context",
1629
+ ""
1630
+ ]
1631
+ });
1632
+ try {
1633
+ const spaceKey = await this._context.getContainingSpaceForDocument(params.documentId);
1634
+ if (!spaceKey) {
1635
+ const remoteDocumentExists = await this._context.isDocumentInRemoteCollection({
1636
+ documentId: params.documentId,
1637
+ peerId: connection.peerId
1638
+ });
1639
+ log6("document not found locally for share policy check, accepting the remote document", {
1640
+ peerId: connection.peerId,
1641
+ documentId: params.documentId,
1642
+ remoteDocumentExists
1643
+ }, {
1644
+ F: __dxlog_file7,
1645
+ L: 91,
1646
+ S: this,
1647
+ C: (f, a) => f(...a)
1648
+ });
1649
+ return remoteDocumentExists;
1650
+ }
1651
+ const spaceId = await createIdFromSpaceKey(spaceKey);
1652
+ const authorizedDevices = this._authorizedDevices.get(spaceId);
1653
+ if (!connection.remoteDeviceKey) {
1654
+ log6("device key not found for share policy check", {
1655
+ peerId: connection.peerId,
1656
+ documentId: params.documentId
1657
+ }, {
1658
+ F: __dxlog_file7,
1659
+ L: 107,
1660
+ S: this,
1661
+ C: (f, a) => f(...a)
1662
+ });
1663
+ return false;
1664
+ }
1665
+ const isAuthorized = authorizedDevices?.has(connection.remoteDeviceKey) ?? false;
1666
+ log6("share policy check", {
1667
+ localPeer: this._context.peerId,
1668
+ remotePeer: connection.peerId,
1669
+ documentId: params.documentId,
1670
+ deviceKey: connection.remoteDeviceKey,
1671
+ spaceKey,
1672
+ isAuthorized
1673
+ }, {
1674
+ F: __dxlog_file7,
1675
+ L: 115,
1676
+ S: this,
1677
+ C: (f, a) => f(...a)
1678
+ });
1679
+ return isAuthorized;
1680
+ } catch (err) {
1681
+ log6.catch(err, void 0, {
1682
+ F: __dxlog_file7,
1683
+ L: 125,
1684
+ S: this,
1685
+ C: (f, a) => f(...a)
1686
+ });
1687
+ return false;
1688
+ }
1689
+ },
1690
+ shouldSyncCollection: ({ collectionId }) => {
1691
+ const spaceId = getSpaceIdFromCollectionId(collectionId);
1692
+ const authorizedDevices = this._authorizedDevices.get(spaceId);
1693
+ if (!connection.remoteDeviceKey) {
1694
+ log6("device key not found for collection sync check", {
1695
+ peerId: connection.peerId,
1696
+ collectionId
1697
+ }, {
1698
+ F: __dxlog_file7,
1699
+ L: 135,
1700
+ S: this,
1701
+ C: (f, a) => f(...a)
1702
+ });
1703
+ return false;
1704
+ }
1705
+ const isAuthorized = authorizedDevices?.has(connection.remoteDeviceKey) ?? false;
1706
+ return isAuthorized;
1707
+ }
1708
+ });
1709
+ this._connections.add(connection);
1710
+ return connection.replicatorExtension;
1711
+ }
1712
+ async authorizeDevice(spaceKey, deviceKey) {
1713
+ log6("authorizeDevice", {
1714
+ spaceKey,
1715
+ deviceKey
1716
+ }, {
1717
+ F: __dxlog_file7,
1718
+ L: 152,
1719
+ S: this,
1720
+ C: (f, a) => f(...a)
1721
+ });
1722
+ const spaceId = await createIdFromSpaceKey(spaceKey);
1723
+ defaultMap2(this._authorizedDevices, spaceId, () => new ComplexSet(PublicKey2.hash)).add(deviceKey);
1724
+ for (const connection of this._connections) {
1725
+ if (connection.remoteDeviceKey && connection.remoteDeviceKey.equals(deviceKey)) {
1726
+ if (this._connectionsPerPeer.has(connection.peerId)) {
1727
+ this._context?.onConnectionAuthScopeChanged(connection);
1728
+ }
1729
+ }
1730
+ }
1731
+ }
1732
+ };
1733
+
1734
+ // packages/core/echo/echo-pipeline/src/automerge/echo-data-monitor.ts
1735
+ import { trace as trace2 } from "@dxos/tracing";
1736
+ import { CircularBuffer, mapValues, SlidingWindowSummary } from "@dxos/util";
1737
+ function _ts_decorate3(decorators, target, key, desc) {
1738
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1739
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
1740
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
1741
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
1742
+ }
1743
+ var PER_SECOND_RATE_AVG_WINDOW_SIZE = 5;
1744
+ var DEFAULT_AVG_WINDOW_SIZE = 25;
1745
+ var EchoDataMonitor = class {
1746
+ constructor(_params = {
1747
+ timeSeriesLength: 30
1748
+ }) {
1749
+ this._params = _params;
1750
+ this._lastTick = 0;
1751
+ this._activeCounters = createLocalCounters();
1752
+ this._localTimeSeries = createLocalTimeSeries();
1753
+ this._storageAverages = createStorageAverages();
1754
+ this._replicationAverages = createNetworkAverages();
1755
+ this._sizeByMessageType = {};
1756
+ this._lastReceivedMessages = new CircularBuffer(100);
1757
+ this._lastSentMessages = new CircularBuffer(100);
1758
+ this._connectionsCount = 0;
1759
+ }
1760
+ tick(timeMs) {
1761
+ this._advanceTimeWindow(timeMs - this._lastTick);
1762
+ this._lastTick = timeMs;
1763
+ }
1764
+ computeStats() {
1765
+ return {
1766
+ meta: {
1767
+ rateAverageOverSeconds: PER_SECOND_RATE_AVG_WINDOW_SIZE
1768
+ },
1769
+ storage: {
1770
+ reads: {
1771
+ payloadSize: this._storageAverages.loadedChunkSize.average(),
1772
+ opDuration: this._storageAverages.loadDuration.average(),
1773
+ countPerSecond: this._storageAverages.loadsPerSecond.average()
1774
+ },
1775
+ writes: {
1776
+ payloadSize: this._storageAverages.storedChunkSize.average(),
1777
+ opDuration: this._storageAverages.storeDuration.average(),
1778
+ countPerSecond: this._storageAverages.storesPerSecond.average()
1779
+ }
1780
+ },
1781
+ replicator: {
1782
+ connections: this._connectionsCount,
1783
+ receivedMessages: {
1784
+ payloadSize: this._replicationAverages.receivedMessageSize.average(),
1785
+ countPerSecond: this._replicationAverages.receivedPerSecond.average()
1786
+ },
1787
+ sentMessages: {
1788
+ payloadSize: this._replicationAverages.sentMessageSize.average(),
1789
+ opDuration: this._replicationAverages.sendDuration.average(),
1790
+ countPerSecond: this._replicationAverages.sentPerSecond.average(),
1791
+ failedPerSecond: this._replicationAverages.sendsFailedPerSecond.average()
1792
+ },
1793
+ countByMessageType: this._computeMessageHistogram("type"),
1794
+ avgSizeByMessageType: mapValues(this._sizeByMessageType, (summary) => summary.average())
1795
+ }
1796
+ };
1797
+ }
1798
+ get connectionsCount() {
1799
+ return this._connectionsCount;
1800
+ }
1801
+ /**
1802
+ * @internal
1803
+ */
1804
+ get lastPerSecondStats() {
1805
+ return this._lastCompleteCounters;
1806
+ }
1807
+ /**
1808
+ * @internal
1809
+ */
1810
+ get timeSeries() {
1811
+ return {
1812
+ ...this._localTimeSeries.storage,
1813
+ ...this._localTimeSeries.replication
1814
+ };
1815
+ }
1816
+ /**
1817
+ * @internal
1818
+ */
1819
+ get messagesByPeerId() {
1820
+ return this._computeMessageHistogram("peerId");
1821
+ }
1822
+ _advanceTimeWindow(millisPassed) {
1823
+ const oldMetrics = Object.freeze(this._activeCounters);
1824
+ this._activeCounters = createLocalCounters();
1825
+ this._lastCompleteCounters = oldMetrics;
1826
+ for (const peerId of Object.keys(oldMetrics.byPeerId)) {
1827
+ this._activeCounters.byPeerId[peerId] = createMessageCounter();
1828
+ }
1829
+ this._addToTimeSeries(oldMetrics.replication, this._localTimeSeries.replication);
1830
+ this._addToTimeSeries(oldMetrics.storage, this._localTimeSeries.storage);
1831
+ if (Math.abs(millisPassed - 1e3) < 100) {
1832
+ this._reportPerSecondRate(oldMetrics);
1833
+ }
1834
+ }
1835
+ _addToTimeSeries(values, timeSeries) {
1836
+ for (const [key, value] of Object.entries(values)) {
1837
+ const values2 = timeSeries[key];
1838
+ values2.push(value);
1839
+ if (values2.length > this._params.timeSeriesLength) {
1840
+ values2.shift();
1841
+ }
1842
+ }
1843
+ }
1844
+ _reportPerSecondRate(metrics) {
1845
+ const toReport = [
1846
+ [
1847
+ "storage.load",
1848
+ metrics.storage.loadedChunks,
1849
+ this._storageAverages.loadsPerSecond
1850
+ ],
1851
+ [
1852
+ "storage.store",
1853
+ metrics.storage.storedChunks,
1854
+ this._storageAverages.storesPerSecond
1855
+ ],
1856
+ [
1857
+ "network.receive",
1858
+ metrics.replication.received,
1859
+ this._replicationAverages.receivedPerSecond
1860
+ ],
1861
+ [
1862
+ "network.send",
1863
+ metrics.replication.sent,
1864
+ this._replicationAverages.sentPerSecond
1865
+ ]
1866
+ ];
1867
+ for (const [metricName, metric, summary] of toReport) {
1868
+ summary.record(metric);
1869
+ if (metric > 0) {
1870
+ trace2.metrics.distribution(`dxos.echo.${metricName}-rate`, metric);
1871
+ trace2.metrics.increment(`dxos.echo.${metricName}`, 1, {
1872
+ tags: {
1873
+ status: "busy"
1874
+ }
1875
+ });
1876
+ } else {
1877
+ trace2.metrics.increment(`dxos.echo.${metricName}`, 1, {
1878
+ tags: {
1879
+ status: "idle"
1880
+ }
1881
+ });
1882
+ }
1883
+ }
1884
+ this._replicationAverages.sendsFailedPerSecond.record(metrics.replication.failed);
1885
+ }
1886
+ recordPeerConnected(peerId) {
1887
+ this._activeCounters.byPeerId[peerId] = createMessageCounter();
1888
+ this._connectionsCount++;
1889
+ }
1890
+ recordPeerDisconnected(peerId) {
1891
+ this._connectionsCount--;
1892
+ delete this._activeCounters.byPeerId[peerId];
1893
+ }
1894
+ recordBytesStored(count) {
1895
+ this._activeCounters.storage.storedChunks++;
1896
+ this._activeCounters.storage.storedBytes += count;
1897
+ this._storageAverages.storedChunkSize.record(count);
1898
+ trace2.metrics.distribution("dxos.echo.storage.bytes-stored", count, {
1899
+ unit: "bytes"
1900
+ });
1901
+ }
1902
+ recordLoadDuration(durationMs) {
1903
+ this._storageAverages.loadDuration.record(durationMs);
1904
+ }
1905
+ recordStoreDuration(durationMs) {
1906
+ this._storageAverages.storeDuration.record(durationMs);
1907
+ }
1908
+ recordBytesLoaded(count) {
1909
+ this._activeCounters.storage.loadedChunks++;
1910
+ this._activeCounters.storage.loadedBytes += count;
1911
+ this._storageAverages.loadedChunkSize.record(count);
1912
+ trace2.metrics.distribution("dxos.echo.storage.bytes-loaded", count, {
1913
+ unit: "bytes"
1914
+ });
1915
+ }
1916
+ recordMessageSent(message, duration) {
1917
+ let metricsGroupName;
1918
+ const bytes = getByteCount(message);
1919
+ const tags = {
1920
+ type: message.type
1921
+ };
1922
+ if (isAutomergeProtocolMessage(message)) {
1923
+ this._activeCounters.replication.sent++;
1924
+ this._replicationAverages.sendDuration.record(duration);
1925
+ this._replicationAverages.sentMessageSize.record(bytes);
1926
+ metricsGroupName = "replication";
1927
+ } else {
1928
+ metricsGroupName = "collection-sync";
1929
+ }
1930
+ trace2.metrics.distribution(`dxos.echo.${metricsGroupName}.bytes-sent`, bytes, {
1931
+ unit: "bytes",
1932
+ tags
1933
+ });
1934
+ trace2.metrics.distribution(`dxos.echo.${metricsGroupName}.send-duration`, duration, {
1935
+ unit: "millisecond",
1936
+ tags
1937
+ });
1938
+ trace2.metrics.increment(`dxos.echo.${metricsGroupName}.send-status`, 1, {
1939
+ tags: {
1940
+ ...tags,
1941
+ success: true
1942
+ }
1943
+ });
1944
+ const { messageSize, messageCounts } = this._getStatsForType(message);
1945
+ messageSize.record(bytes);
1946
+ messageCounts.sent++;
1947
+ this._lastSentMessages.push({
1948
+ type: message.type,
1949
+ peerId: message.targetId
1950
+ });
1951
+ }
1952
+ recordMessageReceived(message) {
1953
+ const bytes = getByteCount(message);
1954
+ const tags = {
1955
+ type: message.type
1956
+ };
1957
+ if (isAutomergeProtocolMessage(message)) {
1958
+ this._activeCounters.replication.received++;
1959
+ this._replicationAverages.receivedMessageSize.record(bytes);
1960
+ trace2.metrics.distribution("dxos.echo.replication.bytes-received", bytes, {
1961
+ unit: "bytes",
1962
+ tags
1963
+ });
1964
+ } else {
1965
+ trace2.metrics.distribution("dxos.echo.collection-sync.bytes-received", bytes, {
1966
+ unit: "bytes",
1967
+ tags
1968
+ });
1969
+ }
1970
+ const { messageSize, messageCounts } = this._getStatsForType(message);
1971
+ messageSize.record(bytes);
1972
+ messageCounts.received++;
1973
+ this._lastReceivedMessages.push({
1974
+ type: message.type,
1975
+ peerId: message.senderId
1976
+ });
1977
+ }
1978
+ recordMessageSendingFailed(message) {
1979
+ const tags = {
1980
+ type: message.type,
1981
+ success: false
1982
+ };
1983
+ if (isAutomergeProtocolMessage(message)) {
1984
+ this._activeCounters.replication.failed++;
1985
+ trace2.metrics.increment("dxos.echo.replication.send-status", 1, {
1986
+ unit: "bytes",
1987
+ tags
1988
+ });
1989
+ } else {
1990
+ trace2.metrics.increment("dxos.echo.collection-sync.send-status", 1, {
1991
+ unit: "bytes",
1992
+ tags
1993
+ });
1994
+ }
1995
+ const { messageCounts } = this._getStatsForType(message);
1996
+ messageCounts.failed++;
1997
+ }
1998
+ _getStatsForType(message) {
1999
+ const messageSize = this._sizeByMessageType[message.type] ??= createSlidingWindow();
2000
+ const messageCounts = this._activeCounters.byType[message.type] ??= createMessageCounter();
2001
+ return {
2002
+ messageCounts,
2003
+ messageSize
2004
+ };
2005
+ }
2006
+ _computeMessageHistogram(groupKey) {
2007
+ const result = {};
2008
+ for (const receivedMessage of this._lastReceivedMessages) {
2009
+ const counters = result[receivedMessage[groupKey]] ??= {
2010
+ received: 0,
2011
+ sent: 0
2012
+ };
2013
+ counters.received++;
2014
+ }
2015
+ for (const receivedMessage of this._lastSentMessages) {
2016
+ const counters = result[receivedMessage[groupKey]] ??= {
2017
+ received: 0,
2018
+ sent: 0
2019
+ };
2020
+ counters.sent++;
2021
+ }
2022
+ return result;
2023
+ }
2024
+ };
2025
+ EchoDataMonitor = _ts_decorate3([
2026
+ trace2.resource()
2027
+ ], EchoDataMonitor);
2028
+ var isAutomergeProtocolMessage = (message) => {
2029
+ return !(isCollectionQueryMessage(message) || isCollectionStateMessage(message));
2030
+ };
2031
+ var createSlidingWindow = (overrides) => new SlidingWindowSummary({
2032
+ dataPoints: DEFAULT_AVG_WINDOW_SIZE,
2033
+ precision: 2,
2034
+ ...overrides
2035
+ });
2036
+ var createLocalCounters = () => ({
2037
+ storage: {
2038
+ loadedBytes: 0,
2039
+ storedBytes: 0,
2040
+ storedChunks: 0,
2041
+ loadedChunks: 0
2042
+ },
2043
+ replication: createMessageCounter(),
2044
+ byPeerId: {},
2045
+ byType: {}
2046
+ });
2047
+ var createLocalTimeSeries = () => ({
2048
+ storage: {
2049
+ loadedBytes: [],
2050
+ storedBytes: [],
2051
+ storedChunks: [],
2052
+ loadedChunks: []
2053
+ },
2054
+ replication: {
2055
+ sent: [],
2056
+ failed: [],
2057
+ received: []
2058
+ }
2059
+ });
2060
+ var createMessageCounter = () => ({
2061
+ sent: 0,
2062
+ received: 0,
2063
+ failed: 0
2064
+ });
2065
+ var createNetworkAverages = () => ({
2066
+ receivedMessageSize: createSlidingWindow(),
2067
+ sentMessageSize: createSlidingWindow(),
2068
+ sendDuration: createSlidingWindow(),
2069
+ receivedPerSecond: createSlidingWindow({
2070
+ dataPoints: PER_SECOND_RATE_AVG_WINDOW_SIZE
2071
+ }),
2072
+ sentPerSecond: createSlidingWindow({
2073
+ dataPoints: PER_SECOND_RATE_AVG_WINDOW_SIZE
2074
+ }),
2075
+ sendsFailedPerSecond: createSlidingWindow({
2076
+ dataPoints: PER_SECOND_RATE_AVG_WINDOW_SIZE
2077
+ })
2078
+ });
2079
+ var createStorageAverages = () => ({
2080
+ storedChunkSize: createSlidingWindow(),
2081
+ loadedChunkSize: createSlidingWindow(),
2082
+ loadDuration: createSlidingWindow(),
2083
+ storeDuration: createSlidingWindow(),
2084
+ loadsPerSecond: createSlidingWindow({
2085
+ dataPoints: PER_SECOND_RATE_AVG_WINDOW_SIZE
2086
+ }),
2087
+ storesPerSecond: createSlidingWindow({
2088
+ dataPoints: PER_SECOND_RATE_AVG_WINDOW_SIZE
2089
+ })
2090
+ });
2091
+ var getByteCount = (message) => {
2092
+ return message.type.length + message.senderId.length + message.targetId.length + (message.data?.byteLength ?? 0) + (message.documentId?.length ?? 0);
2093
+ };
2094
+
2095
+ // packages/core/echo/echo-pipeline/src/db-host/data-service.ts
2096
+ var __dxlog_file8 = "/home/runner/work/dxos/dxos/packages/core/echo/echo-pipeline/src/db-host/data-service.ts";
2097
+ var DataServiceImpl = class {
2098
+ constructor(params) {
2099
+ /**
2100
+ * Map of subscriptions.
2101
+ * subscriptionId -> DocumentsSynchronizer
2102
+ */
2103
+ this._subscriptions = /* @__PURE__ */ new Map();
2104
+ this._automergeHost = params.automergeHost;
2105
+ this._updateIndexes = params.updateIndexes;
2106
+ }
2107
+ subscribe(request) {
2108
+ return new Stream(({ next, ready }) => {
2109
+ const synchronizer = new DocumentsSynchronizer({
2110
+ repo: this._automergeHost.repo,
2111
+ sendUpdates: (updates) => next(updates)
2112
+ });
2113
+ synchronizer.open().then(() => {
2114
+ this._subscriptions.set(request.subscriptionId, synchronizer);
2115
+ ready();
2116
+ }).catch((err) => log7.catch(err, void 0, {
2117
+ F: __dxlog_file8,
2118
+ L: 66,
2119
+ S: this,
2120
+ C: (f, a) => f(...a)
2121
+ }));
2122
+ return () => synchronizer.close();
2123
+ });
2124
+ }
2125
+ async updateSubscription(request) {
2126
+ const synchronizer = this._subscriptions.get(request.subscriptionId);
2127
+ invariant7(synchronizer, "Subscription not found", {
2128
+ F: __dxlog_file8,
2129
+ L: 73,
2130
+ S: this,
2131
+ A: [
2132
+ "synchronizer",
2133
+ "'Subscription not found'"
2134
+ ]
2135
+ });
2136
+ if (request.addIds?.length) {
2137
+ await synchronizer.addDocuments(request.addIds);
2138
+ }
2139
+ if (request.removeIds?.length) {
2140
+ await synchronizer.removeDocuments(request.removeIds);
2141
+ }
2142
+ }
2143
+ async update(request) {
2144
+ if (!request.updates) {
2145
+ return;
2146
+ }
2147
+ const synchronizer = this._subscriptions.get(request.subscriptionId);
2148
+ invariant7(synchronizer, "Subscription not found", {
2149
+ F: __dxlog_file8,
2150
+ L: 88,
2151
+ S: this,
2152
+ A: [
2153
+ "synchronizer",
2154
+ "'Subscription not found'"
2155
+ ]
2156
+ });
2157
+ synchronizer.update(request.updates);
2158
+ }
2159
+ async flush(request) {
2160
+ await this._automergeHost.flush(request);
2161
+ }
2162
+ async getDocumentHeads(request) {
2163
+ const documentIds = request.documentIds;
2164
+ if (!documentIds) {
2165
+ return {
2166
+ heads: {
2167
+ entries: []
2168
+ }
2169
+ };
2170
+ }
2171
+ const heads = await this._automergeHost.getHeads(documentIds);
2172
+ return {
2173
+ heads: {
2174
+ entries: heads.map((heads2, idx) => ({
2175
+ documentId: documentIds[idx],
2176
+ heads: heads2
2177
+ }))
2178
+ }
2179
+ };
2180
+ }
2181
+ async waitUntilHeadsReplicated(request, options) {
2182
+ await this._automergeHost.waitUntilHeadsReplicated(request.heads);
2183
+ }
2184
+ async reIndexHeads(request, options) {
2185
+ await this._automergeHost.reIndexHeads(request.documentIds ?? []);
2186
+ }
2187
+ async updateIndexes() {
2188
+ await this._updateIndexes();
2189
+ }
2190
+ subscribeSpaceSyncState(request) {
2191
+ return new Stream(({ ctx, next, ready }) => {
2192
+ invariant7(SpaceId2.isValid(request.spaceId), void 0, {
2193
+ F: __dxlog_file8,
2194
+ L: 127,
2195
+ S: this,
2196
+ A: [
2197
+ "SpaceId.isValid(request.spaceId)",
2198
+ ""
2199
+ ]
2200
+ });
2201
+ const collectionId = deriveCollectionIdFromSpaceId(request.spaceId);
2202
+ const scheduler = new UpdateScheduler2(ctx, async () => {
2203
+ const state = await this._automergeHost.getCollectionSyncState(collectionId);
2204
+ next({
2205
+ peers: state.peers.map((peer) => ({
2206
+ peerId: peer.peerId,
2207
+ missingOnRemote: peer.missingOnRemote,
2208
+ missingOnLocal: peer.missingOnLocal,
2209
+ differentDocuments: peer.differentDocuments,
2210
+ localDocumentCount: peer.localDocumentCount,
2211
+ remoteDocumentCount: peer.remoteDocumentCount
2212
+ }))
2213
+ });
2214
+ });
2215
+ this._automergeHost.collectionStateUpdated.on(ctx, (e) => {
2216
+ if (e.collectionId === collectionId) {
2217
+ scheduler.trigger();
2218
+ }
2219
+ });
2220
+ scheduler.trigger();
2221
+ });
2222
+ }
2223
+ };
2224
+
2225
+ // packages/core/echo/echo-pipeline/src/db-host/echo-host.ts
2226
+ import { LifecycleState as LifecycleState4, Resource as Resource9 } from "@dxos/context";
2227
+ import { todo } from "@dxos/debug";
2228
+ import { createIdFromSpaceKey as createIdFromSpaceKey3, SpaceDocVersion as SpaceDocVersion3 } from "@dxos/echo-protocol";
2229
+ import { IndexMetadataStore, IndexStore, Indexer } from "@dxos/indexing";
2230
+ import { invariant as invariant11 } from "@dxos/invariant";
2231
+ import { IndexKind } from "@dxos/protocols/proto/dxos/echo/indexing";
2232
+ import { trace as trace5 } from "@dxos/tracing";
2233
+
2234
+ // packages/core/echo/echo-pipeline/src/db-host/documents-iterator.ts
2235
+ import * as A3 from "@dxos/automerge/automerge";
2236
+ import { Context as Context2 } from "@dxos/context";
2237
+ import { SpaceDocVersion } from "@dxos/echo-protocol";
2238
+ import { invariant as invariant8 } from "@dxos/invariant";
2239
+ import { log as log8 } from "@dxos/log";
2240
+ import { ObjectPointerVersion, objectPointerCodec as objectPointerCodec2 } from "@dxos/protocols";
2241
+ var __dxlog_file9 = "/home/runner/work/dxos/dxos/packages/core/echo/echo-pipeline/src/db-host/documents-iterator.ts";
2242
+ var LOG_VIEW_OPERATION_THRESHOLD = 300;
2243
+ var createSelectedDocumentsIterator = (automergeHost) => (
2244
+ /**
2245
+ * Get object data blobs from Automerge Repo by ids.
2246
+ */
2247
+ // TODO(mykola): Unload automerge handles after usage.
2248
+ async function* loadDocuments(objects) {
2249
+ for (const [id, heads] of objects.entries()) {
2250
+ try {
2251
+ const { documentId, objectId } = objectPointerCodec2.decode(id);
2252
+ const handle = await automergeHost.loadDoc(Context2.default(void 0, {
2253
+ F: __dxlog_file9,
2254
+ L: 30
2255
+ }), documentId);
2256
+ let doc = handle.docSync();
2257
+ invariant8(doc, void 0, {
2258
+ F: __dxlog_file9,
2259
+ L: 33,
2260
+ S: this,
2261
+ A: [
2262
+ "doc",
2263
+ ""
2264
+ ]
2265
+ });
2266
+ const currentHeads = A3.getHeads(doc);
2267
+ if (!A3.equals(currentHeads, heads)) {
2268
+ const begin = Date.now();
2269
+ doc = A3.view(doc, heads);
2270
+ const end = Date.now();
2271
+ if (end - begin > LOG_VIEW_OPERATION_THRESHOLD) {
2272
+ log8.info("Checking out document version is taking too long", {
2273
+ duration: end - begin,
2274
+ requestedHeads: heads,
2275
+ originalHeads: currentHeads
2276
+ }, {
2277
+ F: __dxlog_file9,
2278
+ L: 44,
2279
+ S: this,
2280
+ C: (f, a) => f(...a)
2281
+ });
2282
+ }
2283
+ }
2284
+ if (doc.version !== SpaceDocVersion.CURRENT) {
2285
+ continue;
2286
+ }
2287
+ if (!doc.objects?.[objectId]) {
2288
+ continue;
2289
+ }
2290
+ let newId = id;
2291
+ if (objectPointerCodec2.getVersion(id) === ObjectPointerVersion.V0) {
2292
+ const spaceKey = getSpaceKeyFromDoc(doc) ?? void 0;
2293
+ newId = objectPointerCodec2.encode({
2294
+ documentId,
2295
+ objectId,
2296
+ spaceKey
2297
+ });
2298
+ }
2299
+ yield [
2300
+ {
2301
+ id: newId,
2302
+ object: doc.objects[objectId],
2303
+ heads
2304
+ }
2305
+ ];
2306
+ } catch (error) {
2307
+ log8.error("Error loading document", {
2308
+ heads,
2309
+ id,
2310
+ error
2311
+ }, {
2312
+ F: __dxlog_file9,
2313
+ L: 70,
2314
+ S: this,
2315
+ C: (f, a) => f(...a)
2316
+ });
2317
+ }
2318
+ }
2319
+ }
2320
+ );
2321
+
2322
+ // packages/core/echo/echo-pipeline/src/db-host/query-service.ts
2323
+ import { DeferredTask } from "@dxos/async";
2324
+ import { getHeads as getHeads3 } from "@dxos/automerge/automerge";
2325
+ import { Stream as Stream2 } from "@dxos/codec-protobuf";
2326
+ import { Context as Context4, Resource as Resource7 } from "@dxos/context";
2327
+ import { log as log9 } from "@dxos/log";
2328
+ import { objectPointerCodec as objectPointerCodec4 } from "@dxos/protocols";
2329
+ import { trace as trace4 } from "@dxos/tracing";
2330
+
2331
+ // packages/core/echo/echo-pipeline/src/db-host/query-state.ts
2332
+ import { Context as Context3, LifecycleState as LifecycleState3, Resource as Resource6 } from "@dxos/context";
2333
+ import { createIdFromSpaceKey as createIdFromSpaceKey2 } from "@dxos/echo-protocol";
2334
+ import { invariant as invariant9 } from "@dxos/invariant";
2335
+ import { DXN, PublicKey as PublicKey3 } from "@dxos/keys";
2336
+ import { objectPointerCodec as objectPointerCodec3 } from "@dxos/protocols";
2337
+ import { trace as trace3 } from "@dxos/tracing";
2338
+ import { nonNullable as nonNullable2 } from "@dxos/util";
2339
+ function _ts_decorate4(decorators, target, key, desc) {
2340
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
2341
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
2342
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
2343
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
2344
+ }
2345
+ var __dxlog_file10 = "/home/runner/work/dxos/dxos/packages/core/echo/echo-pipeline/src/db-host/query-state.ts";
2346
+ var QueryState = class extends Resource6 {
2347
+ get active() {
2348
+ return this._lifecycleState === LifecycleState3.OPEN;
2349
+ }
2350
+ constructor(_params) {
2351
+ super();
2352
+ this._params = _params;
2353
+ this._results = [];
2354
+ this._firstRun = true;
2355
+ this.metrics = {
2356
+ objectsReturned: 0,
2357
+ objectsReturnedFromIndex: 0,
2358
+ documentsLoaded: 0,
2359
+ executionTime: 0,
2360
+ indexQueryTime: 0,
2361
+ documentLoadTime: 0
2362
+ };
2363
+ this.filter = _params.request.filter;
2364
+ }
2365
+ getResults() {
2366
+ return this._results;
2367
+ }
2368
+ // https://github.com/open-telemetry/semantic-conventions/blob/main/docs/attributes-registry/db.md#generic-database-attributes
2369
+ async execQuery() {
2370
+ const filter = this._params.request.filter;
2371
+ const beginQuery = performance.now();
2372
+ const hits = filter.objectIds && filter.objectIds?.length > 0 ? [] : await this._params.indexer.execQuery(filterToIndexQuery(filter));
2373
+ if (this._firstRun) {
2374
+ this.metrics.indexQueryTime = performance.now() - beginQuery;
2375
+ }
2376
+ const beginFilter = performance.now();
2377
+ const results = (await Promise.all(hits.map(async (result) => {
2378
+ if (this._firstRun) {
2379
+ this.metrics.objectsReturnedFromIndex++;
2380
+ }
2381
+ const { objectId, documentId, spaceKey: spaceKeyInIndex } = objectPointerCodec3.decode(result.id);
2382
+ let spaceKey;
2383
+ if (spaceKeyInIndex !== void 0) {
2384
+ spaceKey = spaceKeyInIndex;
2385
+ } else {
2386
+ if (this._firstRun) {
2387
+ this.metrics.documentsLoaded++;
2388
+ }
2389
+ const handle = await this._params.automergeHost.loadDoc(Context3.default(void 0, {
2390
+ F: __dxlog_file10,
2391
+ L: 116
2392
+ }), documentId);
2393
+ if (this._ctx.disposed) {
2394
+ return;
2395
+ }
2396
+ spaceKey = getSpaceKeyFromDoc(handle.docSync());
2397
+ }
2398
+ if (!spaceKey) {
2399
+ return;
2400
+ }
2401
+ if (this._params.request.filter.options?.spaces?.length && !this._params.request.filter.options.spaces.some((key) => key.equals(spaceKey))) {
2402
+ return;
2403
+ }
2404
+ if (this._firstRun) {
2405
+ this.metrics.objectsReturned++;
2406
+ }
2407
+ return {
2408
+ id: objectId,
2409
+ documentId,
2410
+ spaceId: await createIdFromSpaceKey2(PublicKey3.from(spaceKey)),
2411
+ spaceKey: PublicKey3.from(spaceKey),
2412
+ rank: result.rank
2413
+ };
2414
+ }))).filter(nonNullable2);
2415
+ if (this._firstRun) {
2416
+ this.metrics.documentLoadTime = performance.now() - beginFilter;
2417
+ }
2418
+ if (this._ctx.disposed) {
2419
+ return {
2420
+ changed: false
2421
+ };
2422
+ }
2423
+ const areResultsUnchanged = !this._firstRun && this._results.length === results.length && this._results.every((oldResult) => results.some((result) => result.id === oldResult.id)) && results.every((result) => this._results.some((oldResult) => oldResult.id === result.id));
2424
+ if (this._firstRun) {
2425
+ this.metrics.executionTime = performance.now() - beginQuery;
2426
+ }
2427
+ this._firstRun = false;
2428
+ if (areResultsUnchanged) {
2429
+ return {
2430
+ changed: false
2431
+ };
2432
+ }
2433
+ this._results = results;
2434
+ return {
2435
+ changed: true
2436
+ };
2437
+ }
2438
+ };
2439
+ _ts_decorate4([
2440
+ trace3.info({
2441
+ depth: null
2442
+ })
2443
+ ], QueryState.prototype, "filter", void 0);
2444
+ _ts_decorate4([
2445
+ trace3.info()
2446
+ ], QueryState.prototype, "metrics", void 0);
2447
+ _ts_decorate4([
2448
+ trace3.info()
2449
+ ], QueryState.prototype, "active", null);
2450
+ _ts_decorate4([
2451
+ trace3.span({
2452
+ showInBrowserTimeline: true,
2453
+ op: "db.query",
2454
+ attributes: {
2455
+ "db.system": "echo"
2456
+ }
2457
+ })
2458
+ ], QueryState.prototype, "execQuery", null);
2459
+ QueryState = _ts_decorate4([
2460
+ trace3.resource()
2461
+ ], QueryState);
2462
+ var filterToIndexQuery = (filter) => {
2463
+ invariant9(!(filter.type && (filter.or ?? []).length > 0), "Cannot mix type and or filters.", {
2464
+ F: __dxlog_file10,
2465
+ L: 181,
2466
+ S: void 0,
2467
+ A: [
2468
+ "!(filter.type && (filter.or ?? []).length > 0)",
2469
+ "'Cannot mix type and or filters.'"
2470
+ ]
2471
+ });
2472
+ invariant9((filter.or ?? []).every((subFilter) => !(subFilter.type && (subFilter.or ?? []).length > 0)), "Cannot mix type and or filters.", {
2473
+ F: __dxlog_file10,
2474
+ L: 182,
2475
+ S: void 0,
2476
+ A: [
2477
+ "(filter.or ?? []).every((subFilter) => !(subFilter.type && (subFilter.or ?? []).length > 0))",
2478
+ "'Cannot mix type and or filters.'"
2479
+ ]
2480
+ });
2481
+ if (filter.type || (filter.or ?? []).length > 0 && (filter.or ?? []).every((subFilter) => !subFilter.not && subFilter.type)) {
2482
+ return {
2483
+ typenames: filter.type && filter.type.length > 0 ? filter.type.map((type) => dxnToIndexerTypename(DXN.parse(type))) : (filter.or ?? []).flatMap((f) => f.type?.map((type) => dxnToIndexerTypename(DXN.parse(type))) ?? []).filter(nonNullable2),
2484
+ inverted: filter.not
2485
+ };
2486
+ } else {
2487
+ return {
2488
+ typenames: []
2489
+ };
2490
+ }
2491
+ };
2492
+ var dxnToIndexerTypename = (dxn) => {
2493
+ switch (dxn.kind) {
2494
+ case DXN.kind.TYPE:
2495
+ return dxn.parts[0];
2496
+ case DXN.kind.ECHO:
2497
+ return dxn.parts[1];
2498
+ default:
2499
+ throw new Error(`Invalid DXN kind: ${dxn.kind}`);
2500
+ }
2501
+ };
2502
+
2503
+ // packages/core/echo/echo-pipeline/src/db-host/query-service.ts
2504
+ var __dxlog_file11 = "/home/runner/work/dxos/dxos/packages/core/echo/echo-pipeline/src/db-host/query-service.ts";
2505
+ var QueryServiceImpl = class extends Resource7 {
2506
+ // TODO(burdon): OK for options, but not params. Pass separately and type readonly here.
2507
+ constructor(_params) {
2508
+ super();
2509
+ this._params = _params;
2510
+ this._queries = /* @__PURE__ */ new Set();
2511
+ this._updateQueries = new DeferredTask(this._ctx, async () => {
2512
+ await Promise.all(Array.from(this._queries).map(async (query) => {
2513
+ try {
2514
+ const { changed } = await query.state.execQuery();
2515
+ if (changed) {
2516
+ query.sendResults(query.state.getResults());
2517
+ }
2518
+ } catch (err) {
2519
+ log9.catch(err, void 0, {
2520
+ F: __dxlog_file11,
2521
+ L: 52,
2522
+ S: this,
2523
+ C: (f, a) => f(...a)
2524
+ });
2525
+ }
2526
+ }));
2527
+ });
2528
+ trace4.diagnostic({
2529
+ id: "active-queries",
2530
+ name: "Active Queries",
2531
+ fetch: () => {
2532
+ return Array.from(this._queries).map((query) => {
2533
+ return {
2534
+ filter: JSON.stringify(query.state.filter),
2535
+ metrics: query.state.metrics
2536
+ };
2537
+ });
2538
+ }
2539
+ });
2540
+ }
2541
+ async _open() {
2542
+ this._params.indexer.updated.on(this._ctx, () => this._updateQueries.schedule());
2543
+ }
2544
+ async _close() {
2545
+ await Promise.all(Array.from(this._queries).map((query) => query.close()));
2546
+ }
2547
+ async setConfig(config) {
2548
+ if (this._params.indexer.initialized) {
2549
+ log9.warn("Indexer already initialized.", void 0, {
2550
+ F: __dxlog_file11,
2551
+ L: 86,
2552
+ S: this,
2553
+ C: (f, a) => f(...a)
2554
+ });
2555
+ return;
2556
+ }
2557
+ this._params.indexer.setConfig(config);
2558
+ }
2559
+ execQuery(request) {
2560
+ return new Stream2(({ next, close, ctx }) => {
2561
+ const query = {
2562
+ state: new QueryState({
2563
+ indexer: this._params.indexer,
2564
+ automergeHost: this._params.automergeHost,
2565
+ request
2566
+ }),
2567
+ sendResults: (results) => {
2568
+ if (ctx.disposed) {
2569
+ return;
2570
+ }
2571
+ next({
2572
+ queryId: request.queryId,
2573
+ results
2574
+ });
2575
+ },
2576
+ close: async () => {
2577
+ close();
2578
+ await query.state.close();
2579
+ this._queries.delete(query);
2580
+ }
2581
+ };
2582
+ this._queries.add(query);
2583
+ queueMicrotask(async () => {
2584
+ await query.state.open();
2585
+ try {
2586
+ const { changed } = await query.state.execQuery();
2587
+ if (changed) {
2588
+ query.sendResults(query.state.getResults());
2589
+ }
2590
+ } catch (error) {
2591
+ log9.catch(error, void 0, {
2592
+ F: __dxlog_file11,
2593
+ L: 123,
2594
+ S: this,
2595
+ C: (f, a) => f(...a)
2596
+ });
2597
+ }
2598
+ });
2599
+ return query.close;
2600
+ });
2601
+ }
2602
+ /**
2603
+ * Re-index all loaded documents.
2604
+ */
2605
+ async reindex() {
2606
+ log9.info("Reindexing all documents...", void 0, {
2607
+ F: __dxlog_file11,
2608
+ L: 135,
2609
+ S: this,
2610
+ C: (f, a) => f(...a)
2611
+ });
2612
+ const iterator = createDocumentsIterator(this._params.automergeHost);
2613
+ const ids = /* @__PURE__ */ new Map();
2614
+ for await (const documents of iterator()) {
2615
+ for (const { id, heads } of documents) {
2616
+ ids.set(id, heads);
2617
+ }
2618
+ if (ids.size % 100 === 0) {
2619
+ log9.info("Collected documents...", {
2620
+ count: ids.size
2621
+ }, {
2622
+ F: __dxlog_file11,
2623
+ L: 143,
2624
+ S: this,
2625
+ C: (f, a) => f(...a)
2626
+ });
2627
+ }
2628
+ }
2629
+ log9.info("Marking all documents as dirty...", {
2630
+ count: ids.size
2631
+ }, {
2632
+ F: __dxlog_file11,
2633
+ L: 147,
2634
+ S: this,
2635
+ C: (f, a) => f(...a)
2636
+ });
2637
+ await this._params.indexer.reindex(ids);
2638
+ }
2639
+ };
2640
+ var createDocumentsIterator = (automergeHost) => (
2641
+ /**
2642
+ * Recursively get all object data blobs from loaded documents from Automerge Repo.
2643
+ */
2644
+ // TODO(mykola): Unload automerge handles after usage.
2645
+ async function* getAllDocuments() {
2646
+ const visited = /* @__PURE__ */ new Set();
2647
+ async function* getObjectsFromHandle(handle) {
2648
+ if (visited.has(handle.documentId)) {
2649
+ return;
2650
+ }
2651
+ const doc = handle.docSync();
2652
+ const spaceKey = getSpaceKeyFromDoc(doc) ?? void 0;
2653
+ if (doc.objects) {
2654
+ yield Object.entries(doc.objects).map(([objectId, object]) => {
2655
+ return {
2656
+ id: objectPointerCodec4.encode({
2657
+ documentId: handle.documentId,
2658
+ objectId,
2659
+ spaceKey
2660
+ }),
2661
+ object,
2662
+ heads: getHeads3(doc)
2663
+ };
2664
+ });
2665
+ }
2666
+ if (doc.links) {
2667
+ for (const id of Object.values(doc.links)) {
2668
+ const urlString = id.toString();
2669
+ if (visited.has(urlString)) {
2670
+ continue;
2671
+ }
2672
+ const linkHandle = await automergeHost.loadDoc(Context4.default(void 0, {
2673
+ F: __dxlog_file11,
2674
+ L: 189
2675
+ }), urlString);
2676
+ for await (const result of getObjectsFromHandle(linkHandle)) {
2677
+ yield result;
2678
+ }
2679
+ }
2680
+ }
2681
+ visited.add(handle.documentId);
2682
+ }
2683
+ for (const handle of Object.values(automergeHost.repo.handles)) {
2684
+ if (visited.has(handle.documentId)) {
2685
+ continue;
2686
+ }
2687
+ for await (const result of getObjectsFromHandle(handle)) {
2688
+ yield result;
2689
+ }
2690
+ visited.add(handle.documentId);
2691
+ }
2692
+ }
2693
+ );
2694
+
2695
+ // packages/core/echo/echo-pipeline/src/db-host/space-state-manager.ts
2696
+ import isEqual from "lodash.isequal";
2697
+ import { Event as Event3, UpdateScheduler as UpdateScheduler3 } from "@dxos/async";
2698
+ import { interpretAsDocumentId } from "@dxos/automerge/automerge-repo";
2699
+ import { Resource as Resource8, Context as Context5 } from "@dxos/context";
2700
+
2701
+ // packages/core/echo/echo-pipeline/src/db-host/database-root.ts
2702
+ import { SpaceDocVersion as SpaceDocVersion2 } from "@dxos/echo-protocol";
2703
+ import { invariant as invariant10 } from "@dxos/invariant";
2704
+
2705
+ // packages/core/echo/echo-pipeline/src/db-host/automerge-metrics.ts
2706
+ import * as A4 from "@dxos/automerge/automerge";
2707
+ import { log as log10 } from "@dxos/log";
2708
+ var __dxlog_file12 = "/home/runner/work/dxos/dxos/packages/core/echo/echo-pipeline/src/db-host/automerge-metrics.ts";
2709
+ var measureDocMetrics = (doc) => {
2710
+ const snapshot = A4.save(doc);
2711
+ const start = Date.now();
2712
+ const temp = A4.load(snapshot);
2713
+ const end = Date.now();
2714
+ A4.free(temp);
2715
+ const getAllChangesStart = Date.now();
2716
+ const mutationCount = A4.getAllChanges(doc).length;
2717
+ const getAllChangesEnd = Date.now();
2718
+ if (getAllChangesEnd - getAllChangesStart > 300) {
2719
+ log10.warn("getAllChanges took too long", {
2720
+ elapsed: getAllChangesEnd - getAllChangesStart
2721
+ }, {
2722
+ F: __dxlog_file12,
2723
+ L: 30,
2724
+ S: void 0,
2725
+ C: (f, a) => f(...a)
2726
+ });
2727
+ }
2728
+ return {
2729
+ compressedByteSize: snapshot.byteLength,
2730
+ loadTime: end - start,
2731
+ mutationCount
2732
+ };
2733
+ };
2734
+
2735
+ // packages/core/echo/echo-pipeline/src/db-host/database-root.ts
2736
+ var __dxlog_file13 = "/home/runner/work/dxos/dxos/packages/core/echo/echo-pipeline/src/db-host/database-root.ts";
2737
+ var DatabaseRoot = class {
2738
+ constructor(_rootHandle) {
2739
+ this._rootHandle = _rootHandle;
2740
+ }
2741
+ get documentId() {
2742
+ return this._rootHandle.documentId;
2743
+ }
2744
+ get url() {
2745
+ return this._rootHandle.url;
2746
+ }
2747
+ get isLoaded() {
2748
+ return !!this._rootHandle.docSync();
2749
+ }
2750
+ get handle() {
2751
+ return this._rootHandle;
2752
+ }
2753
+ docSync() {
2754
+ return this._rootHandle.docSync();
2755
+ }
2756
+ getVersion() {
2757
+ const doc = this.docSync();
2758
+ if (!doc) {
2759
+ return null;
2760
+ }
2761
+ return doc.version ?? SpaceDocVersion2.LEGACY;
2762
+ }
2763
+ getSpaceKey() {
2764
+ const doc = this.docSync();
2765
+ if (!doc) {
2766
+ return null;
2767
+ }
2768
+ return getSpaceKeyFromDoc(doc);
2769
+ }
2770
+ getInlineObjectCount() {
2771
+ const doc = this.docSync();
2772
+ if (!doc) {
2773
+ return null;
2774
+ }
2775
+ return Object.keys(doc.objects ?? {}).length;
2776
+ }
2777
+ getLinkedObjectCount() {
2778
+ const doc = this.docSync();
2779
+ if (!doc) {
2780
+ return null;
2781
+ }
2782
+ return Object.keys(doc.links ?? {}).length;
2783
+ }
2784
+ getAllLinkedDocuments() {
2785
+ const doc = this.docSync();
2786
+ invariant10(doc, void 0, {
2787
+ F: __dxlog_file13,
2788
+ L: 74,
2789
+ S: this,
2790
+ A: [
2791
+ "doc",
2792
+ ""
2793
+ ]
2794
+ });
2795
+ return Object.values(doc.links ?? {}).map((s) => s.toString());
2796
+ }
2797
+ measureMetrics() {
2798
+ const doc = this.docSync();
2799
+ if (!doc) {
2800
+ return null;
2801
+ }
2802
+ return measureDocMetrics(doc);
2803
+ }
2804
+ };
2805
+
2806
+ // packages/core/echo/echo-pipeline/src/db-host/space-state-manager.ts
2807
+ var __dxlog_file14 = "/home/runner/work/dxos/dxos/packages/core/echo/echo-pipeline/src/db-host/space-state-manager.ts";
2808
+ var SpaceStateManager = class extends Resource8 {
2809
+ constructor() {
2810
+ super(...arguments);
2811
+ this._roots = /* @__PURE__ */ new Map();
2812
+ this._rootBySpace = /* @__PURE__ */ new Map();
2813
+ this._perRootContext = /* @__PURE__ */ new Map();
2814
+ this._lastSpaceDocumentList = /* @__PURE__ */ new Map();
2815
+ this.spaceDocumentListUpdated = new Event3();
2816
+ }
2817
+ async _close(ctx) {
2818
+ for (const [_, rootCtx] of this._perRootContext) {
2819
+ await rootCtx.dispose();
2820
+ }
2821
+ this._roots.clear();
2822
+ }
2823
+ get roots() {
2824
+ return this._roots;
2825
+ }
2826
+ getRootByDocumentId(documentId) {
2827
+ return this._roots.get(documentId);
2828
+ }
2829
+ async assignRootToSpace(spaceId, handle) {
2830
+ let root;
2831
+ if (this._roots.has(handle.documentId)) {
2832
+ root = this._roots.get(handle.documentId);
2833
+ } else {
2834
+ root = new DatabaseRoot(handle);
2835
+ this._roots.set(handle.documentId, root);
2836
+ }
2837
+ if (this._rootBySpace.get(spaceId) === root.handle.documentId) {
2838
+ return root;
2839
+ }
2840
+ const prevRootId = this._rootBySpace.get(spaceId);
2841
+ if (prevRootId) {
2842
+ void this._perRootContext.get(prevRootId)?.dispose();
2843
+ this._perRootContext.delete(prevRootId);
2844
+ }
2845
+ this._rootBySpace.set(spaceId, root.handle.documentId);
2846
+ const ctx = new Context5(void 0, {
2847
+ F: __dxlog_file14,
2848
+ L: 58
2849
+ });
2850
+ this._perRootContext.set(root.handle.documentId, ctx);
2851
+ await root.handle.whenReady();
2852
+ const documentListCheckScheduler = new UpdateScheduler3(ctx, async () => {
2853
+ const documentIds = [
2854
+ root.documentId,
2855
+ ...root.getAllLinkedDocuments().map((url) => interpretAsDocumentId(url))
2856
+ ];
2857
+ if (!isEqual(documentIds, this._lastSpaceDocumentList.get(spaceId))) {
2858
+ this._lastSpaceDocumentList.set(spaceId, documentIds);
2859
+ this.spaceDocumentListUpdated.emit(new SpaceDocumentListUpdatedEvent(spaceId, documentIds));
2860
+ }
2861
+ }, {
2862
+ maxFrequency: 50
2863
+ });
2864
+ const triggerCheckOnChange = () => documentListCheckScheduler.trigger();
2865
+ root.handle.addListener("change", triggerCheckOnChange);
2866
+ ctx.onDispose(() => root.handle.removeListener("change", triggerCheckOnChange));
2867
+ documentListCheckScheduler.trigger();
2868
+ return root;
2869
+ }
2870
+ };
2871
+ var SpaceDocumentListUpdatedEvent = class {
2872
+ constructor(spaceId, documentIds) {
2873
+ this.spaceId = spaceId;
2874
+ this.documentIds = documentIds;
2875
+ }
2876
+ };
2877
+
2878
+ // packages/core/echo/echo-pipeline/src/db-host/echo-host.ts
2879
+ var __dxlog_file15 = "/home/runner/work/dxos/dxos/packages/core/echo/echo-pipeline/src/db-host/echo-host.ts";
2880
+ var INDEXER_CONFIG = {
2881
+ enabled: true,
2882
+ indexes: [
2883
+ {
2884
+ kind: IndexKind.Kind.SCHEMA_MATCH
2885
+ }
2886
+ ]
2887
+ };
2888
+ var EchoHost = class extends Resource9 {
2889
+ constructor({ kv }) {
2890
+ super();
2891
+ this._spaceStateManager = new SpaceStateManager();
2892
+ this._indexMetadataStore = new IndexMetadataStore({
2893
+ db: kv.sublevel("index-metadata")
2894
+ });
2895
+ this._echoDataMonitor = new EchoDataMonitor();
2896
+ this._automergeHost = new AutomergeHost({
2897
+ db: kv,
2898
+ dataMonitor: this._echoDataMonitor,
2899
+ indexMetadataStore: this._indexMetadataStore
2900
+ });
2901
+ this._indexer = new Indexer({
2902
+ db: kv,
2903
+ indexStore: new IndexStore({
2904
+ db: kv.sublevel("index-storage")
2905
+ }),
2906
+ metadataStore: this._indexMetadataStore,
2907
+ loadDocuments: createSelectedDocumentsIterator(this._automergeHost),
2908
+ indexCooldownTime: false ? 0 : void 0
2909
+ });
2910
+ this._indexer.setConfig(INDEXER_CONFIG);
2911
+ this._queryService = new QueryServiceImpl({
2912
+ automergeHost: this._automergeHost,
2913
+ indexer: this._indexer
2914
+ });
2915
+ this._dataService = new DataServiceImpl({
2916
+ automergeHost: this._automergeHost,
2917
+ updateIndexes: async () => {
2918
+ await this._indexer.updateIndexes();
2919
+ }
2920
+ });
2921
+ trace5.diagnostic({
2922
+ id: "echo-stats",
2923
+ name: "Echo Stats",
2924
+ fetch: async () => {
2925
+ return {
2926
+ dataStats: this._echoDataMonitor.computeStats(),
2927
+ loadedDocsCount: this._automergeHost.loadedDocsCount
2928
+ };
2929
+ }
2930
+ });
2931
+ trace5.diagnostic({
2932
+ id: "database-roots",
2933
+ name: "Database Roots",
2934
+ fetch: async () => {
2935
+ return Array.from(this._spaceStateManager.roots.values()).map((root) => ({
2936
+ url: root.url,
2937
+ isLoaded: root.isLoaded,
2938
+ spaceKey: root.getSpaceKey(),
2939
+ inlineObjects: root.getInlineObjectCount(),
2940
+ linkedObjects: root.getLinkedObjectCount()
2941
+ }));
2942
+ }
2943
+ });
2944
+ trace5.diagnostic({
2945
+ id: "database-root-metrics",
2946
+ name: "Database Roots (with metrics)",
2947
+ fetch: async () => {
2948
+ return Array.from(this._spaceStateManager.roots.values()).map((root) => ({
2949
+ url: root.url,
2950
+ isLoaded: root.isLoaded,
2951
+ spaceKey: root.getSpaceKey(),
2952
+ inlineObjects: root.getInlineObjectCount(),
2953
+ linkedObjects: root.getLinkedObjectCount(),
2954
+ ...root.measureMetrics() ?? {}
2955
+ }));
2956
+ }
2957
+ });
2958
+ }
2959
+ get queryService() {
2960
+ return this._queryService;
2961
+ }
2962
+ get dataService() {
2963
+ return this._dataService;
2964
+ }
2965
+ /**
2966
+ * @deprecated To be abstracted away.
2967
+ */
2968
+ get automergeRepo() {
2969
+ return this._automergeHost.repo;
2970
+ }
2971
+ get roots() {
2972
+ return this._spaceStateManager.roots;
2973
+ }
2974
+ async _open(ctx) {
2975
+ await this._automergeHost.open();
2976
+ await this._indexer.open(ctx);
2977
+ await this._queryService.open(ctx);
2978
+ await this._spaceStateManager.open(ctx);
2979
+ this._spaceStateManager.spaceDocumentListUpdated.on(this._ctx, (e) => {
2980
+ void this._automergeHost.updateLocalCollectionState(deriveCollectionIdFromSpaceId(e.spaceId), e.documentIds);
2981
+ });
2982
+ }
2983
+ async _close(ctx) {
2984
+ await this._spaceStateManager.close();
2985
+ await this._queryService.close(ctx);
2986
+ await this._indexer.close(ctx);
2987
+ await this._automergeHost.close();
2988
+ }
2989
+ /**
2990
+ * Flush all pending writes to the underlying storage.
2991
+ */
2992
+ async flush() {
2993
+ await this._automergeHost.repo.flush();
2994
+ }
2995
+ /**
2996
+ * Perform any pending index updates.
2997
+ */
2998
+ async updateIndexes() {
2999
+ await this._indexer.updateIndexes();
3000
+ }
3001
+ /**
3002
+ * Loads the document handle from the repo and waits for it to be ready.
3003
+ */
3004
+ async loadDoc(ctx, documentId, opts) {
3005
+ return await this._automergeHost.loadDoc(ctx, documentId, opts);
3006
+ }
3007
+ /**
3008
+ * Create new persisted document.
3009
+ */
3010
+ createDoc(initialValue, opts) {
3011
+ return this._automergeHost.createDoc(initialValue, opts);
3012
+ }
3013
+ /**
3014
+ * Create new space root.
3015
+ */
3016
+ async createSpaceRoot(spaceKey) {
3017
+ invariant11(this._lifecycleState === LifecycleState4.OPEN, void 0, {
3018
+ F: __dxlog_file15,
3019
+ L: 206,
3020
+ S: this,
3021
+ A: [
3022
+ "this._lifecycleState === LifecycleState.OPEN",
3023
+ ""
3024
+ ]
3025
+ });
3026
+ const spaceId = await createIdFromSpaceKey3(spaceKey);
3027
+ const automergeRoot = this._automergeHost.createDoc({
3028
+ version: SpaceDocVersion3.CURRENT,
3029
+ access: {
3030
+ spaceKey: spaceKey.toHex()
3031
+ }
3032
+ });
3033
+ await this._automergeHost.flush({
3034
+ documentIds: [
3035
+ automergeRoot.documentId
3036
+ ]
3037
+ });
3038
+ return await this.openSpaceRoot(spaceId, automergeRoot.url);
3039
+ }
3040
+ // TODO(dmaretskyi): Change to document id.
3041
+ async openSpaceRoot(spaceId, automergeUrl) {
3042
+ invariant11(this._lifecycleState === LifecycleState4.OPEN, void 0, {
3043
+ F: __dxlog_file15,
3044
+ L: 221,
3045
+ S: this,
3046
+ A: [
3047
+ "this._lifecycleState === LifecycleState.OPEN",
3048
+ ""
3049
+ ]
3050
+ });
3051
+ const handle = this._automergeHost.repo.find(automergeUrl);
3052
+ return this._spaceStateManager.assignRootToSpace(spaceId, handle);
3053
+ }
3054
+ // TODO(dmaretskyi): Change to document id.
3055
+ async closeSpaceRoot(automergeUrl) {
3056
+ todo();
3057
+ }
3058
+ /**
3059
+ * Install data replicator.
3060
+ */
3061
+ async addReplicator(replicator) {
3062
+ await this._automergeHost.addReplicator(replicator);
3063
+ }
3064
+ /**
3065
+ * Remove data replicator.
3066
+ */
3067
+ async removeReplicator(replicator) {
3068
+ await this._automergeHost.removeReplicator(replicator);
3069
+ }
3070
+ async getSpaceSyncState(spaceId) {
3071
+ const collectionId = deriveCollectionIdFromSpaceId(spaceId);
3072
+ return this._automergeHost.getCollectionSyncState(collectionId);
3073
+ }
3074
+ };
3075
+
3076
+ // packages/core/echo/echo-pipeline/src/db-host/migration.ts
3077
+ import { convertLegacyReference } from "@dxos/echo-protocol";
3078
+ import { decodeReference, encodeReference, isLegacyReference, LEGACY_TYPE_PROPERTIES, Reference, SpaceDocVersion as SpaceDocVersion4 } from "@dxos/echo-protocol";
3079
+ import { TYPE_PROPERTIES } from "@dxos/echo-schema";
3080
+ import { deepMapValuesAsync } from "@dxos/util";
3081
+ var convertLegacyReferences = async (doc) => {
3082
+ const newDoc = await deepMapValuesAsync(doc, async (value, recurse) => {
3083
+ if (isLegacyReference(value)) {
3084
+ return convertLegacyReference(value);
3085
+ }
3086
+ return recurse(value);
3087
+ });
3088
+ newDoc.version = SpaceDocVersion4.CURRENT;
3089
+ return newDoc;
3090
+ };
3091
+ var convertLegacySpaceRootDoc = async (root) => {
3092
+ const newDoc = await convertLegacyReferences(root);
3093
+ const properties = findInlineObjectOfType(newDoc, LEGACY_TYPE_PROPERTIES);
3094
+ if (properties) {
3095
+ const [_, obj] = properties;
3096
+ obj.system.type = encodeReference(Reference.fromLegacyTypename(TYPE_PROPERTIES));
3097
+ }
3098
+ return newDoc;
3099
+ };
3100
+ var findInlineObjectOfType = (spaceDoc, typename) => {
3101
+ for (const id in spaceDoc.objects ?? {}) {
3102
+ const obj = spaceDoc.objects[id];
3103
+ if (obj.system.type && decodeReference(obj.system.type).objectId === typename) {
3104
+ return [
3105
+ id,
3106
+ obj
3107
+ ];
3108
+ }
3109
+ }
3110
+ return void 0;
3111
+ };
3112
+
3113
+ // packages/core/echo/echo-pipeline/src/edge/echo-edge-replicator.ts
3114
+ import { Mutex, scheduleTask as scheduleTask2, scheduleMicroTask } from "@dxos/async";
3115
+ import * as A5 from "@dxos/automerge/automerge";
3116
+ import { cbor as cbor2 } from "@dxos/automerge/automerge-repo";
3117
+ import { Context as Context6, Resource as Resource10 } from "@dxos/context";
3118
+ import { randomUUID } from "@dxos/crypto";
3119
+ import { invariant as invariant12 } from "@dxos/invariant";
3120
+ import { log as log11 } from "@dxos/log";
3121
+ import { EdgeService } from "@dxos/protocols";
3122
+ import { buf } from "@dxos/protocols/buf";
3123
+ import { MessageSchema as RouterMessageSchema } from "@dxos/protocols/buf/dxos/edge/messenger_pb";
3124
+ import { bufferToArray } from "@dxos/util";
3125
+ function _using_ctx() {
3126
+ var _disposeSuppressedError = typeof SuppressedError === "function" ? SuppressedError : function(error, suppressed) {
3127
+ var err = new Error();
3128
+ err.name = "SuppressedError";
3129
+ err.suppressed = suppressed;
3130
+ err.error = error;
3131
+ return err;
3132
+ }, empty = {}, stack = [];
3133
+ function using(isAwait, value) {
3134
+ if (value != null) {
3135
+ if (Object(value) !== value) {
3136
+ throw new TypeError("using declarations can only be used with objects, functions, null, or undefined.");
3137
+ }
3138
+ if (isAwait) {
3139
+ var dispose = value[Symbol.asyncDispose || Symbol.for("Symbol.asyncDispose")];
3140
+ }
3141
+ if (dispose == null) {
3142
+ dispose = value[Symbol.dispose || Symbol.for("Symbol.dispose")];
3143
+ }
3144
+ if (typeof dispose !== "function") {
3145
+ throw new TypeError(`Property [Symbol.dispose] is not a function.`);
3146
+ }
3147
+ stack.push({
3148
+ v: value,
3149
+ d: dispose,
3150
+ a: isAwait
3151
+ });
3152
+ } else if (isAwait) {
3153
+ stack.push({
3154
+ d: value,
3155
+ a: isAwait
3156
+ });
3157
+ }
3158
+ return value;
3159
+ }
3160
+ return {
3161
+ e: empty,
3162
+ u: using.bind(null, false),
3163
+ a: using.bind(null, true),
3164
+ d: function() {
3165
+ var error = this.e;
3166
+ function next() {
3167
+ while (resource = stack.pop()) {
3168
+ try {
3169
+ var resource, disposalResult = resource.d && resource.d.call(resource.v);
3170
+ if (resource.a) {
3171
+ return Promise.resolve(disposalResult).then(next, err);
3172
+ }
3173
+ } catch (e) {
3174
+ return err(e);
3175
+ }
3176
+ }
3177
+ if (error !== empty) throw error;
3178
+ }
3179
+ function err(e) {
3180
+ error = error !== empty ? new _disposeSuppressedError(error, e) : e;
3181
+ return next();
3182
+ }
3183
+ return next();
3184
+ }
3185
+ };
3186
+ }
3187
+ var __dxlog_file16 = "/home/runner/work/dxos/dxos/packages/core/echo/echo-pipeline/src/edge/echo-edge-replicator.ts";
3188
+ var INITIAL_RESTART_DELAY = 500;
3189
+ var RESTART_DELAY_JITTER = 250;
3190
+ var MAX_RESTART_DELAY = 5e3;
3191
+ var EchoEdgeReplicator = class {
3192
+ constructor({ edgeConnection, disableSharePolicy }) {
3193
+ this._mutex = new Mutex();
3194
+ this._ctx = void 0;
3195
+ this._context = null;
3196
+ this._connectedSpaces = /* @__PURE__ */ new Set();
3197
+ this._connections = /* @__PURE__ */ new Map();
3198
+ this._sharePolicyEnabled = true;
3199
+ this._edgeConnection = edgeConnection;
3200
+ this._sharePolicyEnabled = !disableSharePolicy;
3201
+ }
3202
+ async connect(context) {
3203
+ log11.info("connect", {
3204
+ peerId: context.peerId,
3205
+ connectedSpaces: this._connectedSpaces.size
3206
+ }, {
3207
+ F: __dxlog_file16,
3208
+ L: 60,
3209
+ S: this,
3210
+ C: (f, a) => f(...a)
3211
+ });
3212
+ this._context = context;
3213
+ this._ctx = Context6.default(void 0, {
3214
+ F: __dxlog_file16,
3215
+ L: 63
3216
+ });
3217
+ this._ctx.onDispose(this._edgeConnection.onReconnected(() => {
3218
+ this._ctx && scheduleMicroTask(this._ctx, () => this._handleReconnect());
3219
+ }));
3220
+ }
3221
+ async _handleReconnect() {
3222
+ try {
3223
+ var _usingCtx = _using_ctx();
3224
+ const _guard = _usingCtx.u(await this._mutex.acquire());
3225
+ const spaces = [
3226
+ ...this._connectedSpaces
3227
+ ];
3228
+ for (const connection of this._connections.values()) {
3229
+ await connection.close();
3230
+ }
3231
+ this._connections.clear();
3232
+ if (this._context !== null) {
3233
+ for (const spaceId of spaces) {
3234
+ await this._openConnection(spaceId);
3235
+ }
3236
+ }
3237
+ } catch (_) {
3238
+ _usingCtx.e = _;
3239
+ } finally {
3240
+ _usingCtx.d();
3241
+ }
3242
+ }
3243
+ async disconnect() {
3244
+ try {
3245
+ var _usingCtx = _using_ctx();
3246
+ const _guard = _usingCtx.u(await this._mutex.acquire());
3247
+ await this._ctx?.dispose();
3248
+ for (const connection of this._connections.values()) {
3249
+ await connection.close();
3250
+ }
3251
+ this._connections.clear();
3252
+ } catch (_) {
3253
+ _usingCtx.e = _;
3254
+ } finally {
3255
+ _usingCtx.d();
3256
+ }
3257
+ }
3258
+ async connectToSpace(spaceId) {
3259
+ try {
3260
+ var _usingCtx = _using_ctx();
3261
+ const _guard = _usingCtx.u(await this._mutex.acquire());
3262
+ this._connectedSpaces.add(spaceId);
3263
+ if (this._context !== null) {
3264
+ await this._openConnection(spaceId);
3265
+ }
3266
+ } catch (_) {
3267
+ _usingCtx.e = _;
3268
+ } finally {
3269
+ _usingCtx.d();
3270
+ }
3271
+ }
3272
+ async disconnectFromSpace(spaceId) {
3273
+ try {
3274
+ var _usingCtx = _using_ctx();
3275
+ const _guard = _usingCtx.u(await this._mutex.acquire());
3276
+ this._connectedSpaces.delete(spaceId);
3277
+ const connection = this._connections.get(spaceId);
3278
+ if (connection) {
3279
+ await connection.close();
3280
+ this._connections.delete(spaceId);
3281
+ }
3282
+ } catch (_) {
3283
+ _usingCtx.e = _;
3284
+ } finally {
3285
+ _usingCtx.d();
3286
+ }
3287
+ }
3288
+ async _openConnection(spaceId, reconnects = 0) {
3289
+ invariant12(this._context, void 0, {
3290
+ F: __dxlog_file16,
3291
+ L: 121,
3292
+ S: this,
3293
+ A: [
3294
+ "this._context",
3295
+ ""
3296
+ ]
3297
+ });
3298
+ invariant12(!this._connections.has(spaceId), void 0, {
3299
+ F: __dxlog_file16,
3300
+ L: 122,
3301
+ S: this,
3302
+ A: [
3303
+ "!this._connections.has(spaceId)",
3304
+ ""
3305
+ ]
3306
+ });
3307
+ let restartScheduled = false;
3308
+ const connection = new EdgeReplicatorConnection({
3309
+ edgeConnection: this._edgeConnection,
3310
+ spaceId,
3311
+ context: this._context,
3312
+ sharedPolicyEnabled: this._sharePolicyEnabled,
3313
+ onRemoteConnected: async () => {
3314
+ this._context?.onConnectionOpen(connection);
3315
+ },
3316
+ onRemoteDisconnected: async () => {
3317
+ this._context?.onConnectionClosed(connection);
3318
+ },
3319
+ onRestartRequested: async () => {
3320
+ if (!this._ctx || restartScheduled) {
3321
+ return;
3322
+ }
3323
+ const restartDelay = Math.min(MAX_RESTART_DELAY, INITIAL_RESTART_DELAY * reconnects) + Math.random() * RESTART_DELAY_JITTER;
3324
+ log11.info("connection restart scheduled", {
3325
+ spaceId,
3326
+ reconnects,
3327
+ restartDelay
3328
+ }, {
3329
+ F: __dxlog_file16,
3330
+ L: 145,
3331
+ S: this,
3332
+ C: (f, a) => f(...a)
3333
+ });
3334
+ restartScheduled = true;
3335
+ scheduleTask2(this._ctx, async () => {
3336
+ try {
3337
+ var _usingCtx = _using_ctx();
3338
+ const _guard = _usingCtx.u(await this._mutex.acquire());
3339
+ if (this._connections.get(spaceId) !== connection) {
3340
+ return;
3341
+ }
3342
+ const ctx = this._ctx;
3343
+ await connection.close();
3344
+ this._connections.delete(spaceId);
3345
+ if (ctx?.disposed) {
3346
+ return;
3347
+ }
3348
+ await this._openConnection(spaceId, reconnects + 1);
3349
+ } catch (_) {
3350
+ _usingCtx.e = _;
3351
+ } finally {
3352
+ _usingCtx.d();
3353
+ }
3354
+ }, restartDelay);
3355
+ }
3356
+ });
3357
+ this._connections.set(spaceId, connection);
3358
+ await connection.open();
3359
+ }
3360
+ };
3361
+ var EdgeReplicatorConnection = class extends Resource10 {
3362
+ constructor({ edgeConnection, spaceId, context, sharedPolicyEnabled, onRemoteConnected, onRemoteDisconnected, onRestartRequested }) {
3363
+ super();
3364
+ this._remotePeerId = null;
3365
+ this._edgeConnection = edgeConnection;
3366
+ this._spaceId = spaceId;
3367
+ this._context = context;
3368
+ this._remotePeerId = `${EdgeService.AUTOMERGE_REPLICATOR}:${spaceId}-${randomUUID()}`;
3369
+ this._targetServiceId = `${EdgeService.AUTOMERGE_REPLICATOR}:${spaceId}`;
3370
+ this._sharedPolicyEnabled = sharedPolicyEnabled;
3371
+ this._onRemoteConnected = onRemoteConnected;
3372
+ this._onRemoteDisconnected = onRemoteDisconnected;
3373
+ this._onRestartRequested = onRestartRequested;
3374
+ this.readable = new ReadableStream({
3375
+ start: (controller) => {
3376
+ this._readableStreamController = controller;
3377
+ }
3378
+ });
3379
+ this.writable = new WritableStream({
3380
+ write: async (message, controller) => {
3381
+ await this._sendMessage(message);
3382
+ }
3383
+ });
3384
+ }
3385
+ async _open(ctx) {
3386
+ log11("open", void 0, {
3387
+ F: __dxlog_file16,
3388
+ L: 239,
3389
+ S: this,
3390
+ C: (f, a) => f(...a)
3391
+ });
3392
+ this._ctx.onDispose(this._edgeConnection.onMessage((msg) => {
3393
+ this._onMessage(msg);
3394
+ }));
3395
+ await this._onRemoteConnected();
3396
+ }
3397
+ async _close() {
3398
+ log11("close", void 0, {
3399
+ F: __dxlog_file16,
3400
+ L: 251,
3401
+ S: this,
3402
+ C: (f, a) => f(...a)
3403
+ });
3404
+ this._readableStreamController.close();
3405
+ await this._onRemoteDisconnected();
3406
+ }
3407
+ get peerId() {
3408
+ invariant12(this._remotePeerId, "Not connected", {
3409
+ F: __dxlog_file16,
3410
+ L: 257,
3411
+ S: this,
3412
+ A: [
3413
+ "this._remotePeerId",
3414
+ "'Not connected'"
3415
+ ]
3416
+ });
3417
+ return this._remotePeerId;
3418
+ }
3419
+ async shouldAdvertise(params) {
3420
+ if (!this._sharedPolicyEnabled) {
3421
+ return true;
3422
+ }
3423
+ const spaceId = await this._context.getContainingSpaceIdForDocument(params.documentId);
3424
+ if (!spaceId) {
3425
+ return true;
3426
+ }
3427
+ return spaceId === this._spaceId;
3428
+ }
3429
+ shouldSyncCollection(params) {
3430
+ if (!this._sharedPolicyEnabled) {
3431
+ return true;
3432
+ }
3433
+ const spaceId = getSpaceIdFromCollectionId(params.collectionId);
3434
+ return spaceId === this._spaceId;
3435
+ }
3436
+ _onMessage(message) {
3437
+ if (message.serviceId !== this._targetServiceId) {
3438
+ return;
3439
+ }
3440
+ const payload = cbor2.decode(message.payload.value);
3441
+ log11("recv", () => {
3442
+ const decodedData = payload.type === "sync" && payload.data ? A5.decodeSyncMessage(payload.data) : payload.type === "collection-state" ? payload.state : payload;
3443
+ return {
3444
+ from: message.serviceId,
3445
+ type: payload.type,
3446
+ decodedData
3447
+ };
3448
+ }, {
3449
+ F: __dxlog_file16,
3450
+ L: 288,
3451
+ S: this,
3452
+ C: (f, a) => f(...a)
3453
+ });
3454
+ payload.senderId = this._remotePeerId;
3455
+ this._processMessage(payload);
3456
+ }
3457
+ _processMessage(message) {
3458
+ if (isForbiddenErrorMessage(message)) {
3459
+ this._onRestartRequested();
3460
+ return;
3461
+ }
3462
+ this._readableStreamController.enqueue(message);
3463
+ }
3464
+ async _sendMessage(message) {
3465
+ message.targetId = this._targetServiceId;
3466
+ log11("send", {
3467
+ type: message.type,
3468
+ senderId: message.senderId,
3469
+ targetId: message.targetId,
3470
+ documentId: message.documentId
3471
+ }, {
3472
+ F: __dxlog_file16,
3473
+ L: 318,
3474
+ S: this,
3475
+ C: (f, a) => f(...a)
3476
+ });
3477
+ const encoded = cbor2.encode(message);
3478
+ await this._edgeConnection.send(buf.create(RouterMessageSchema, {
3479
+ serviceId: this._targetServiceId,
3480
+ source: {
3481
+ identityKey: this._edgeConnection.identityKey,
3482
+ peerKey: this._edgeConnection.peerKey
3483
+ },
3484
+ payload: {
3485
+ value: bufferToArray(encoded)
3486
+ }
3487
+ }));
3488
+ }
3489
+ };
3490
+ var isForbiddenErrorMessage = (message) => message.type === "error" && message.message === "Forbidden";
40
3491
  export {
41
3492
  AuthExtension,
42
3493
  AuthStatus,
@@ -44,25 +3495,33 @@ export {
44
3495
  CredentialRetrieverExtension,
45
3496
  CredentialServerExtension,
46
3497
  DataServiceImpl,
3498
+ DatabaseRoot,
47
3499
  DocumentsSynchronizer,
48
3500
  EchoDataMonitor,
3501
+ EchoEdgeReplicator,
3502
+ EchoHost,
49
3503
  LevelDBStorageAdapter,
50
3504
  MOCK_AUTH_PROVIDER,
51
3505
  MOCK_AUTH_VERIFIER,
52
3506
  MeshEchoReplicator,
53
3507
  MetadataStore,
54
3508
  Pipeline,
3509
+ QueryServiceImpl,
3510
+ QueryState,
55
3511
  Space,
56
3512
  SpaceManager,
57
3513
  SpaceProtocol,
58
3514
  SpaceProtocolSession,
59
3515
  TimeframeClock,
60
3516
  codec,
3517
+ convertLegacyReferences,
3518
+ convertLegacySpaceRootDoc,
61
3519
  createIdFromSpaceKey,
62
3520
  createMappedFeedWriter,
63
3521
  deriveCollectionIdFromSpaceId,
64
3522
  diffCollectionState,
65
3523
  encodingOptions,
3524
+ findInlineObjectOfType,
66
3525
  getSpaceIdFromCollectionId,
67
3526
  getSpaceKeyFromDoc,
68
3527
  hasInvitationExpired,