@dxos/echo-pipeline 0.8.4-main.84f28bd → 0.8.4-main.a4bbb77

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 (135) hide show
  1. package/dist/lib/browser/chunk-BVR7OOZK.mjs +251 -0
  2. package/dist/lib/browser/chunk-BVR7OOZK.mjs.map +7 -0
  3. package/dist/lib/browser/{chunk-GBFX5J5B.mjs → chunk-XGG76KKU.mjs} +487 -324
  4. package/dist/lib/browser/chunk-XGG76KKU.mjs.map +7 -0
  5. package/dist/lib/browser/filter/index.mjs +3 -1
  6. package/dist/lib/browser/index.mjs +1379 -562
  7. package/dist/lib/browser/index.mjs.map +4 -4
  8. package/dist/lib/browser/meta.json +1 -1
  9. package/dist/lib/browser/testing/index.mjs +107 -44
  10. package/dist/lib/browser/testing/index.mjs.map +3 -3
  11. package/dist/lib/node-esm/{chunk-FQFKWA3X.mjs → chunk-CHMJJ4DG.mjs} +487 -324
  12. package/dist/lib/node-esm/chunk-CHMJJ4DG.mjs.map +7 -0
  13. package/dist/lib/node-esm/chunk-L3ZEKVDR.mjs +251 -0
  14. package/dist/lib/node-esm/chunk-L3ZEKVDR.mjs.map +7 -0
  15. package/dist/lib/node-esm/filter/index.mjs +3 -1
  16. package/dist/lib/node-esm/index.mjs +1379 -562
  17. package/dist/lib/node-esm/index.mjs.map +4 -4
  18. package/dist/lib/node-esm/meta.json +1 -1
  19. package/dist/lib/node-esm/testing/index.mjs +107 -44
  20. package/dist/lib/node-esm/testing/index.mjs.map +3 -3
  21. package/dist/types/src/automerge/automerge-host.d.ts +15 -28
  22. package/dist/types/src/automerge/automerge-host.d.ts.map +1 -1
  23. package/dist/types/src/automerge/collection-synchronizer.d.ts +1 -1
  24. package/dist/types/src/automerge/collection-synchronizer.d.ts.map +1 -1
  25. package/dist/types/src/automerge/echo-network-adapter.d.ts +8 -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 +21 -2
  28. package/dist/types/src/automerge/echo-replicator.d.ts.map +1 -1
  29. package/dist/types/src/automerge/index.d.ts +1 -1
  30. package/dist/types/src/automerge/index.d.ts.map +1 -1
  31. package/dist/types/src/automerge/leveldb-storage-adapter.d.ts +1 -1
  32. package/dist/types/src/automerge/leveldb-storage-adapter.d.ts.map +1 -1
  33. package/dist/types/src/automerge/mesh-echo-replicator-connection.d.ts +1 -0
  34. package/dist/types/src/automerge/mesh-echo-replicator-connection.d.ts.map +1 -1
  35. package/dist/types/src/automerge/mesh-echo-replicator.d.ts.map +1 -1
  36. package/dist/types/src/common/codec.d.ts +1 -1
  37. package/dist/types/src/common/codec.d.ts.map +1 -1
  38. package/dist/types/src/db-host/data-service.d.ts +2 -2
  39. package/dist/types/src/db-host/data-service.d.ts.map +1 -1
  40. package/dist/types/src/db-host/database-root.d.ts.map +1 -1
  41. package/dist/types/src/db-host/documents-synchronizer.d.ts +2 -2
  42. package/dist/types/src/db-host/documents-synchronizer.d.ts.map +1 -1
  43. package/dist/types/src/db-host/echo-host.d.ts +2 -2
  44. package/dist/types/src/db-host/echo-host.d.ts.map +1 -1
  45. package/dist/types/src/db-host/query-service.d.ts +1 -1
  46. package/dist/types/src/db-host/query-service.d.ts.map +1 -1
  47. package/dist/types/src/db-host/space-state-manager.d.ts +1 -1
  48. package/dist/types/src/db-host/space-state-manager.d.ts.map +1 -1
  49. package/dist/types/src/edge/echo-edge-replicator.d.ts +4 -2
  50. package/dist/types/src/edge/echo-edge-replicator.d.ts.map +1 -1
  51. package/dist/types/src/filter/filter-match.d.ts +4 -1
  52. package/dist/types/src/filter/filter-match.d.ts.map +1 -1
  53. package/dist/types/src/metadata/metadata-store.d.ts +1 -1
  54. package/dist/types/src/metadata/metadata-store.d.ts.map +1 -1
  55. package/dist/types/src/pipeline/pipeline.d.ts +1 -1
  56. package/dist/types/src/pipeline/pipeline.d.ts.map +1 -1
  57. package/dist/types/src/query/errors.d.ts +24 -8
  58. package/dist/types/src/query/errors.d.ts.map +1 -1
  59. package/dist/types/src/query/plan.d.ts +8 -1
  60. package/dist/types/src/query/plan.d.ts.map +1 -1
  61. package/dist/types/src/query/query-executor.d.ts +4 -1
  62. package/dist/types/src/query/query-executor.d.ts.map +1 -1
  63. package/dist/types/src/query/query-planner.d.ts +2 -0
  64. package/dist/types/src/query/query-planner.d.ts.map +1 -1
  65. package/dist/types/src/space/admission-discovery-extension.d.ts.map +1 -1
  66. package/dist/types/src/space/control-pipeline.d.ts +1 -1
  67. package/dist/types/src/space/control-pipeline.d.ts.map +1 -1
  68. package/dist/types/src/space/space-manager.d.ts +1 -1
  69. package/dist/types/src/space/space-manager.d.ts.map +1 -1
  70. package/dist/types/src/space/space-protocol.d.ts +1 -1
  71. package/dist/types/src/space/space-protocol.d.ts.map +1 -1
  72. package/dist/types/src/space/space.d.ts +1 -1
  73. package/dist/types/src/space/space.d.ts.map +1 -1
  74. package/dist/types/src/testing/test-agent-builder.d.ts +2 -2
  75. package/dist/types/src/testing/test-agent-builder.d.ts.map +1 -1
  76. package/dist/types/src/testing/test-replicator.d.ts +1 -0
  77. package/dist/types/src/testing/test-replicator.d.ts.map +1 -1
  78. package/dist/types/src/util.d.ts +1 -1
  79. package/dist/types/src/util.d.ts.map +1 -1
  80. package/dist/types/tsconfig.tsbuildinfo +1 -1
  81. package/package.json +41 -38
  82. package/src/automerge/automerge-host.test.ts +18 -8
  83. package/src/automerge/automerge-host.ts +266 -70
  84. package/src/automerge/automerge-repo.test.ts +67 -16
  85. package/src/automerge/collection-synchronizer.test.ts +2 -2
  86. package/src/automerge/collection-synchronizer.ts +4 -4
  87. package/src/automerge/echo-data-monitor.ts +1 -1
  88. package/src/automerge/echo-network-adapter.test.ts +3 -3
  89. package/src/automerge/echo-network-adapter.ts +38 -6
  90. package/src/automerge/echo-replicator.ts +23 -2
  91. package/src/automerge/index.ts +1 -1
  92. package/src/automerge/leveldb-storage-adapter.ts +7 -7
  93. package/src/automerge/mesh-echo-replicator-connection.ts +4 -0
  94. package/src/automerge/mesh-echo-replicator.ts +2 -1
  95. package/src/automerge/storage-adapter.test.ts +1 -1
  96. package/src/common/space-id.ts +1 -1
  97. package/src/db-host/data-service.ts +9 -17
  98. package/src/db-host/database-root.ts +2 -2
  99. package/src/db-host/documents-synchronizer.test.ts +1 -1
  100. package/src/db-host/documents-synchronizer.ts +39 -26
  101. package/src/db-host/echo-host.ts +14 -15
  102. package/src/db-host/query-service.ts +8 -1
  103. package/src/db-host/space-state-manager.ts +2 -2
  104. package/src/edge/echo-edge-replicator.test.ts +5 -3
  105. package/src/edge/echo-edge-replicator.ts +75 -18
  106. package/src/filter/filter-match.test.ts +33 -13
  107. package/src/filter/filter-match.ts +177 -10
  108. package/src/metadata/metadata-store.ts +3 -3
  109. package/src/pipeline/pipeline-stress.test.ts +4 -2
  110. package/src/pipeline/pipeline.test.ts +3 -2
  111. package/src/pipeline/pipeline.ts +8 -5
  112. package/src/query/errors.ts +2 -0
  113. package/src/query/plan.ts +12 -1
  114. package/src/query/query-executor.ts +71 -11
  115. package/src/query/query-planner.test.ts +146 -2
  116. package/src/query/query-planner.ts +81 -11
  117. package/src/space/admission-discovery-extension.ts +2 -2
  118. package/src/space/control-pipeline.test.ts +4 -3
  119. package/src/space/control-pipeline.ts +9 -6
  120. package/src/space/space-manager.browser.test.ts +1 -1
  121. package/src/space/space-manager.ts +5 -4
  122. package/src/space/space-protocol.browser.test.ts +2 -2
  123. package/src/space/space-protocol.test.ts +3 -2
  124. package/src/space/space-protocol.ts +6 -3
  125. package/src/space/space.test.ts +1 -1
  126. package/src/space/space.ts +3 -2
  127. package/src/testing/test-agent-builder.ts +4 -3
  128. package/src/testing/test-replicator.ts +4 -0
  129. package/src/util.ts +1 -1
  130. package/dist/lib/browser/chunk-ANZAS5CC.mjs +0 -126
  131. package/dist/lib/browser/chunk-ANZAS5CC.mjs.map +0 -7
  132. package/dist/lib/browser/chunk-GBFX5J5B.mjs.map +0 -7
  133. package/dist/lib/node-esm/chunk-2SAZ7CCF.mjs +0 -126
  134. package/dist/lib/node-esm/chunk-2SAZ7CCF.mjs.map +0 -7
  135. package/dist/lib/node-esm/chunk-FQFKWA3X.mjs.map +0 -7
@@ -1,8 +1,9 @@
1
1
  import "@dxos/node-std/globals";
2
2
  import {
3
3
  filterMatchObject,
4
+ filterMatchObjectJSON,
4
5
  filterMatchValue
5
- } from "./chunk-ANZAS5CC.mjs";
6
+ } from "./chunk-BVR7OOZK.mjs";
6
7
  import {
7
8
  AuthExtension,
8
9
  AuthStatus,
@@ -25,7 +26,7 @@ import {
25
26
  mapTimeframeToFeedIndexes,
26
27
  startAfter,
27
28
  valueEncoding
28
- } from "./chunk-GBFX5J5B.mjs";
29
+ } from "./chunk-XGG76KKU.mjs";
29
30
  import "./chunk-CGS2ULMK.mjs";
30
31
 
31
32
  // src/db-host/data-service.ts
@@ -35,17 +36,10 @@ import { invariant as invariant7 } from "@dxos/invariant";
35
36
  import { SpaceId as SpaceId2 } from "@dxos/keys";
36
37
  import { log as log7 } from "@dxos/log";
37
38
 
38
- // src/db-host/documents-synchronizer.ts
39
- import { next as A2 } from "@automerge/automerge";
40
- import { UpdateScheduler } from "@dxos/async";
41
- import { Resource as Resource5 } from "@dxos/context";
42
- import { invariant as invariant6 } from "@dxos/invariant";
43
- import { log as log6 } from "@dxos/log";
44
-
45
39
  // src/automerge/automerge-host.ts
46
- import { getBackend, getHeads, isAutomerge, equals as headsEquals, save } from "@automerge/automerge";
40
+ import { getBackend, getHeads, equals as headsEquals, isAutomerge, save } from "@automerge/automerge";
47
41
  import { Repo, interpretAsDocumentId } from "@automerge/automerge-repo";
48
- import { Event as Event2, asyncTimeout } from "@dxos/async";
42
+ import { DeferredTask, Event as Event2, asyncTimeout } from "@dxos/async";
49
43
  import { Context, Resource as Resource3, cancelWithContext } from "@dxos/context";
50
44
  import { DatabaseDirectory } from "@dxos/echo-protocol";
51
45
  import { invariant as invariant2 } from "@dxos/invariant";
@@ -53,15 +47,28 @@ import { PublicKey } from "@dxos/keys";
53
47
  import { log as log3 } from "@dxos/log";
54
48
  import { objectPointerCodec } from "@dxos/protocols";
55
49
  import { trace as trace2 } from "@dxos/tracing";
56
- import { bufferToArray } from "@dxos/util";
50
+ import { ComplexSet, bufferToArray, isNonNullable as isNonNullable2, range } from "@dxos/util";
57
51
 
58
52
  // src/automerge/collection-synchronizer.ts
59
53
  import { next as am } from "@automerge/automerge";
60
- import { asyncReturn, Event, scheduleTask, scheduleTaskInterval } from "@dxos/async";
54
+ import { Event, asyncReturn, scheduleTask, scheduleTaskInterval } from "@dxos/async";
61
55
  import { Resource } from "@dxos/context";
62
56
  import { log } from "@dxos/log";
63
57
  import { trace } from "@dxos/tracing";
64
58
  import { defaultMap } from "@dxos/util";
59
+ function _define_property(obj, key, value) {
60
+ if (key in obj) {
61
+ Object.defineProperty(obj, key, {
62
+ value,
63
+ enumerable: true,
64
+ configurable: true,
65
+ writable: true
66
+ });
67
+ } else {
68
+ obj[key] = value;
69
+ }
70
+ return obj;
71
+ }
65
72
  function _ts_decorate(decorators, target, key, desc) {
66
73
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
67
74
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
@@ -72,19 +79,6 @@ var __dxlog_file = "/__w/dxos/dxos/packages/core/echo/echo-pipeline/src/automerg
72
79
  var MIN_QUERY_INTERVAL = 5e3;
73
80
  var POLL_INTERVAL = 3e4;
74
81
  var CollectionSynchronizer = class extends Resource {
75
- constructor(params) {
76
- super();
77
- /**
78
- * CollectionId -> State.
79
- */
80
- this._perCollectionStates = /* @__PURE__ */ new Map();
81
- this._activeCollections = /* @__PURE__ */ new Set();
82
- this._connectedPeers = /* @__PURE__ */ new Set();
83
- this.remoteStateUpdated = new Event();
84
- this._sendCollectionState = params.sendCollectionState;
85
- this._queryCollectionState = params.queryCollectionState;
86
- this._shouldSyncCollection = params.shouldSyncCollection;
87
- }
88
82
  async _open(ctx) {
89
83
  scheduleTaskInterval(this._ctx, async () => {
90
84
  for (const collectionId of this._perCollectionStates.keys()) {
@@ -263,6 +257,15 @@ var CollectionSynchronizer = class extends Resource {
263
257
  }
264
258
  }
265
259
  }
260
+ constructor(params) {
261
+ super(), _define_property(this, "_sendCollectionState", void 0), _define_property(this, "_queryCollectionState", void 0), _define_property(this, "_shouldSyncCollection", void 0), /**
262
+ * CollectionId -> State.
263
+ */
264
+ _define_property(this, "_perCollectionStates", /* @__PURE__ */ new Map()), _define_property(this, "_activeCollections", /* @__PURE__ */ new Set()), _define_property(this, "_connectedPeers", /* @__PURE__ */ new Set()), _define_property(this, "remoteStateUpdated", new Event());
265
+ this._sendCollectionState = params.sendCollectionState;
266
+ this._queryCollectionState = params.queryCollectionState;
267
+ this._shouldSyncCollection = params.shouldSyncCollection;
268
+ }
266
269
  };
267
270
  CollectionSynchronizer = _ts_decorate([
268
271
  trace.resource()
@@ -276,9 +279,9 @@ var diffCollectionState = (local, remote) => {
276
279
  const missingOnLocal = [];
277
280
  const different = [];
278
281
  for (const documentId of allDocuments) {
279
- if (!local.documents[documentId]) {
282
+ if (!local.documents[documentId] || local.documents[documentId].length === 0) {
280
283
  missingOnLocal.push(documentId);
281
- } else if (!remote.documents[documentId]) {
284
+ } else if (!remote.documents[documentId] || remote.documents[documentId].length === 0) {
282
285
  missingOnRemote.push(documentId);
283
286
  } else if (!am.equals(local.documents[documentId], remote.documents[documentId])) {
284
287
  different.push(documentId);
@@ -309,7 +312,7 @@ var getSpanName = (peerId) => {
309
312
 
310
313
  // src/automerge/echo-network-adapter.ts
311
314
  import { NetworkAdapter } from "@automerge/automerge-repo";
312
- import { synchronized, Trigger } from "@dxos/async";
315
+ import { Trigger, synchronized } from "@dxos/async";
313
316
  import { LifecycleState } from "@dxos/context";
314
317
  import { invariant } from "@dxos/invariant";
315
318
  import { log as log2 } from "@dxos/log";
@@ -321,6 +324,19 @@ var isCollectionQueryMessage = (message) => message.type === MESSAGE_TYPE_COLLEC
321
324
  var isCollectionStateMessage = (message) => message.type === MESSAGE_TYPE_COLLECTION_STATE;
322
325
 
323
326
  // src/automerge/echo-network-adapter.ts
327
+ function _define_property2(obj, key, value) {
328
+ if (key in obj) {
329
+ Object.defineProperty(obj, key, {
330
+ value,
331
+ enumerable: true,
332
+ configurable: true,
333
+ writable: true
334
+ });
335
+ } else {
336
+ obj[key] = value;
337
+ }
338
+ return obj;
339
+ }
324
340
  function _ts_decorate2(decorators, target, key, desc) {
325
341
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
326
342
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
@@ -329,9 +345,6 @@ function _ts_decorate2(decorators, target, key, desc) {
329
345
  }
330
346
  var __dxlog_file2 = "/__w/dxos/dxos/packages/core/echo/echo-pipeline/src/automerge/echo-network-adapter.ts";
331
347
  var EchoNetworkAdapter = class extends NetworkAdapter {
332
- constructor(_params) {
333
- super(), this._params = _params, this._replicators = /* @__PURE__ */ new Set(), this._connections = /* @__PURE__ */ new Map(), this._lifecycleState = LifecycleState.CLOSED, this._connected = new Trigger(), this._ready = new Trigger();
334
- }
335
348
  isReady() {
336
349
  return this._lifecycleState === LifecycleState.OPEN;
337
350
  }
@@ -380,7 +393,7 @@ var EchoNetworkAdapter = class extends NetworkAdapter {
380
393
  async addReplicator(replicator) {
381
394
  invariant(this._lifecycleState === LifecycleState.OPEN, void 0, {
382
395
  F: __dxlog_file2,
383
- L: 129,
396
+ L: 137,
384
397
  S: this,
385
398
  A: [
386
399
  "this._lifecycleState === LifecycleState.OPEN",
@@ -389,7 +402,7 @@ var EchoNetworkAdapter = class extends NetworkAdapter {
389
402
  });
390
403
  invariant(this.peerId, void 0, {
391
404
  F: __dxlog_file2,
392
- L: 130,
405
+ L: 138,
393
406
  S: this,
394
407
  A: [
395
408
  "this.peerId",
@@ -398,7 +411,7 @@ var EchoNetworkAdapter = class extends NetworkAdapter {
398
411
  });
399
412
  invariant(!this._replicators.has(replicator), void 0, {
400
413
  F: __dxlog_file2,
401
- L: 131,
414
+ L: 139,
402
415
  S: this,
403
416
  A: [
404
417
  "!this._replicators.has(replicator)",
@@ -422,7 +435,7 @@ var EchoNetworkAdapter = class extends NetworkAdapter {
422
435
  async removeReplicator(replicator) {
423
436
  invariant(this._lifecycleState === LifecycleState.OPEN, void 0, {
424
437
  F: __dxlog_file2,
425
- L: 150,
438
+ L: 158,
426
439
  S: this,
427
440
  A: [
428
441
  "this._lifecycleState === LifecycleState.OPEN",
@@ -431,7 +444,7 @@ var EchoNetworkAdapter = class extends NetworkAdapter {
431
444
  });
432
445
  invariant(this._replicators.has(replicator), void 0, {
433
446
  F: __dxlog_file2,
434
- L: 151,
447
+ L: 159,
435
448
  S: this,
436
449
  A: [
437
450
  "this._replicators.has(replicator)",
@@ -482,6 +495,27 @@ var EchoNetworkAdapter = class extends NetworkAdapter {
482
495
  }) ? connection.connection.peerId : null;
483
496
  }).filter(isNonNullable);
484
497
  }
498
+ bundleSyncEnabledForPeer(peerId) {
499
+ const connection = this._connections.get(peerId);
500
+ if (!connection) {
501
+ return false;
502
+ }
503
+ return connection.connection.bundleSyncEnabled;
504
+ }
505
+ async pushBundle(peerId, bundle) {
506
+ const connection = this._connections.get(peerId);
507
+ if (!connection) {
508
+ throw new Error("Connection not found.");
509
+ }
510
+ return connection.connection.pushBundle(bundle);
511
+ }
512
+ async pullBundle(peerId, docHeads) {
513
+ const connection = this._connections.get(peerId);
514
+ if (!connection) {
515
+ throw new Error("Connection not found.");
516
+ }
517
+ return connection.connection.pullBundle(docHeads);
518
+ }
485
519
  _send(message) {
486
520
  const connectionEntry = this._connections.get(message.targetId);
487
521
  if (!connectionEntry) {
@@ -494,7 +528,7 @@ var EchoNetworkAdapter = class extends NetworkAdapter {
494
528
  if (connectionEntry.isOpen) {
495
529
  log2.catch(err, void 0, {
496
530
  F: __dxlog_file2,
497
- L: 221,
531
+ L: 253,
498
532
  S: this,
499
533
  C: (f, a) => f(...a)
500
534
  });
@@ -507,13 +541,13 @@ var EchoNetworkAdapter = class extends NetworkAdapter {
507
541
  peerId: connection.peerId
508
542
  }, {
509
543
  F: __dxlog_file2,
510
- L: 229,
544
+ L: 261,
511
545
  S: this,
512
546
  C: (f, a) => f(...a)
513
547
  });
514
548
  invariant(!this._connections.has(connection.peerId), void 0, {
515
549
  F: __dxlog_file2,
516
- L: 230,
550
+ L: 262,
517
551
  S: this,
518
552
  A: [
519
553
  "!this._connections.has(connection.peerId as PeerId)",
@@ -540,7 +574,7 @@ var EchoNetworkAdapter = class extends NetworkAdapter {
540
574
  if (connectionEntry.isOpen) {
541
575
  log2.catch(err, void 0, {
542
576
  F: __dxlog_file2,
543
- L: 254,
577
+ L: 286,
544
578
  S: this,
545
579
  C: (f, a) => f(...a)
546
580
  });
@@ -551,7 +585,7 @@ var EchoNetworkAdapter = class extends NetworkAdapter {
551
585
  peerId: connection.peerId
552
586
  }, {
553
587
  F: __dxlog_file2,
554
- L: 259,
588
+ L: 291,
555
589
  S: this,
556
590
  C: (f, a) => f(...a)
557
591
  });
@@ -573,14 +607,14 @@ var EchoNetworkAdapter = class extends NetworkAdapter {
573
607
  peerId: connection.peerId
574
608
  }, {
575
609
  F: __dxlog_file2,
576
- L: 276,
610
+ L: 308,
577
611
  S: this,
578
612
  C: (f, a) => f(...a)
579
613
  });
580
614
  const entry = this._connections.get(connection.peerId);
581
615
  invariant(entry, void 0, {
582
616
  F: __dxlog_file2,
583
- L: 278,
617
+ L: 310,
584
618
  S: this,
585
619
  A: [
586
620
  "entry",
@@ -594,13 +628,13 @@ var EchoNetworkAdapter = class extends NetworkAdapter {
594
628
  this._params.monitor?.recordPeerDisconnected(connection.peerId);
595
629
  void entry.writer.abort().catch((err) => log2.catch(err, void 0, {
596
630
  F: __dxlog_file2,
597
- L: 284,
631
+ L: 316,
598
632
  S: this,
599
633
  C: (f, a) => f(...a)
600
634
  }));
601
635
  void entry.reader.cancel().catch((err) => log2.catch(err, void 0, {
602
636
  F: __dxlog_file2,
603
- L: 285,
637
+ L: 317,
604
638
  S: this,
605
639
  C: (f, a) => f(...a)
606
640
  }));
@@ -615,14 +649,14 @@ var EchoNetworkAdapter = class extends NetworkAdapter {
615
649
  peerId: connection.peerId
616
650
  }, {
617
651
  F: __dxlog_file2,
618
- L: 295,
652
+ L: 327,
619
653
  S: this,
620
654
  C: (f, a) => f(...a)
621
655
  });
622
656
  const entry = this._connections.get(connection.peerId);
623
657
  invariant(entry, void 0, {
624
658
  F: __dxlog_file2,
625
- L: 297,
659
+ L: 329,
626
660
  S: this,
627
661
  A: [
628
662
  "entry",
@@ -640,6 +674,12 @@ var EchoNetworkAdapter = class extends NetworkAdapter {
640
674
  peerMetadata: createEchoPeerMetadata()
641
675
  });
642
676
  }
677
+ constructor(_params) {
678
+ super(), _define_property2(this, "_params", void 0), _define_property2(this, "_replicators", void 0), /**
679
+ * Remote peer id -> connection.
680
+ */
681
+ _define_property2(this, "_connections", void 0), _define_property2(this, "_lifecycleState", void 0), _define_property2(this, "_connected", void 0), _define_property2(this, "_ready", void 0), this._params = _params, this._replicators = /* @__PURE__ */ new Set(), this._connections = /* @__PURE__ */ new Map(), this._lifecycleState = LifecycleState.CLOSED, this._connected = new Trigger(), this._ready = new Trigger();
682
+ }
643
683
  };
644
684
  _ts_decorate2([
645
685
  synchronized
@@ -661,10 +701,20 @@ var isEchoPeerMetadata = (metadata) => metadata?.dxos_peerSource === "EchoNetwor
661
701
 
662
702
  // src/automerge/heads-store.ts
663
703
  import { headsEncoding } from "@dxos/indexing";
664
- var HeadsStore = class {
665
- constructor({ db }) {
666
- this._db = db;
704
+ function _define_property3(obj, key, value) {
705
+ if (key in obj) {
706
+ Object.defineProperty(obj, key, {
707
+ value,
708
+ enumerable: true,
709
+ configurable: true,
710
+ writable: true
711
+ });
712
+ } else {
713
+ obj[key] = value;
667
714
  }
715
+ return obj;
716
+ }
717
+ var HeadsStore = class {
668
718
  setHeads(documentId, heads, batch) {
669
719
  batch.put(documentId, heads, {
670
720
  sublevel: this._db,
@@ -679,17 +729,31 @@ var HeadsStore = class {
679
729
  valueEncoding: headsEncoding
680
730
  });
681
731
  }
732
+ constructor({ db }) {
733
+ _define_property3(this, "_db", void 0);
734
+ this._db = db;
735
+ }
682
736
  };
683
737
 
684
738
  // src/automerge/leveldb-storage-adapter.ts
685
- import { LifecycleState as LifecycleState2, Resource as Resource2 } from "@dxos/context";
686
- var LevelDBStorageAdapter = class extends Resource2 {
687
- constructor(_params) {
688
- super(), this._params = _params;
739
+ import { Resource as Resource2 } from "@dxos/context";
740
+ function _define_property4(obj, key, value) {
741
+ if (key in obj) {
742
+ Object.defineProperty(obj, key, {
743
+ value,
744
+ enumerable: true,
745
+ configurable: true,
746
+ writable: true
747
+ });
748
+ } else {
749
+ obj[key] = value;
689
750
  }
751
+ return obj;
752
+ }
753
+ var LevelDBStorageAdapter = class extends Resource2 {
690
754
  async load(keyArray) {
691
755
  try {
692
- if (this._lifecycleState !== LifecycleState2.OPEN) {
756
+ if (!this.isOpen) {
693
757
  return void 0;
694
758
  }
695
759
  const startMs = Date.now();
@@ -707,7 +771,7 @@ var LevelDBStorageAdapter = class extends Resource2 {
707
771
  }
708
772
  }
709
773
  async save(keyArray, binary) {
710
- if (this._lifecycleState !== LifecycleState2.OPEN) {
774
+ if (!this.isOpen) {
711
775
  return void 0;
712
776
  }
713
777
  const startMs = Date.now();
@@ -725,7 +789,7 @@ var LevelDBStorageAdapter = class extends Resource2 {
725
789
  this._params.monitor?.recordStoreDuration(Date.now() - startMs);
726
790
  }
727
791
  async remove(keyArray) {
728
- if (this._lifecycleState !== LifecycleState2.OPEN) {
792
+ if (!this.isOpen) {
729
793
  return void 0;
730
794
  }
731
795
  await this._params.db.del(keyArray, {
@@ -733,7 +797,7 @@ var LevelDBStorageAdapter = class extends Resource2 {
733
797
  });
734
798
  }
735
799
  async loadRange(keyPrefix) {
736
- if (this._lifecycleState !== LifecycleState2.OPEN) {
800
+ if (!this.isOpen) {
737
801
  return [];
738
802
  }
739
803
  const startMs = Date.now();
@@ -756,7 +820,7 @@ var LevelDBStorageAdapter = class extends Resource2 {
756
820
  return result;
757
821
  }
758
822
  async removeRange(keyPrefix) {
759
- if (this._lifecycleState !== LifecycleState2.OPEN) {
823
+ if (!this.isOpen) {
760
824
  return void 0;
761
825
  }
762
826
  const batch = this._params.db.batch();
@@ -774,6 +838,9 @@ var LevelDBStorageAdapter = class extends Resource2 {
774
838
  }
775
839
  await batch.write();
776
840
  }
841
+ constructor(_params) {
842
+ super(), _define_property4(this, "_params", void 0), this._params = _params;
843
+ }
777
844
  };
778
845
  var keyEncoder = {
779
846
  encode: (key) => Buffer.from(key.map((k) => k.replaceAll("%", "%25").replaceAll("-", "%2D")).join("-")),
@@ -787,6 +854,19 @@ var encodingOptions = {
787
854
  var isLevelDbNotFoundError = (err) => err.code === "LEVEL_NOT_FOUND";
788
855
 
789
856
  // src/automerge/automerge-host.ts
857
+ function _define_property5(obj, key, value) {
858
+ if (key in obj) {
859
+ Object.defineProperty(obj, key, {
860
+ value,
861
+ enumerable: true,
862
+ configurable: true,
863
+ writable: true
864
+ });
865
+ } else {
866
+ obj[key] = value;
867
+ }
868
+ return obj;
869
+ }
790
870
  function _ts_decorate3(decorators, target, key, desc) {
791
871
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
792
872
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
@@ -800,44 +880,17 @@ var FIND_PARAMS = {
800
880
  "requesting"
801
881
  ]
802
882
  };
883
+ var BUNDLE_SIZE = 100;
884
+ var BUNDLE_SYNC_CONCURRENCY = 2;
885
+ var BUNDLE_SYNC_THRESHOLD = 50;
803
886
  var AutomergeHost = class extends Resource3 {
804
- constructor({ db, indexMetadataStore, dataMonitor, peerIdProvider, getSpaceKeyByRootDocumentId }) {
805
- super();
806
- this._collectionSynchronizer = new CollectionSynchronizer({
807
- queryCollectionState: this._queryCollectionState.bind(this),
808
- sendCollectionState: this._sendCollectionState.bind(this),
809
- shouldSyncCollection: this._shouldSyncCollection.bind(this)
810
- });
811
- this.collectionStateUpdated = new Event2();
812
- /**
813
- * Fired after a batch of documents was saved to disk.
814
- */
815
- this.documentsSaved = new Event2();
816
- this._db = db;
817
- this._storage = new LevelDBStorageAdapter({
818
- db: db.sublevel("automerge"),
819
- callbacks: {
820
- beforeSave: async (params) => this._beforeSave(params),
821
- afterSave: async (key) => this._afterSave(key)
822
- },
823
- monitor: dataMonitor
824
- });
825
- this._echoNetworkAdapter = new EchoNetworkAdapter({
826
- getContainingSpaceForDocument: this._getContainingSpaceForDocument.bind(this),
827
- isDocumentInRemoteCollection: this._isDocumentInRemoteCollection.bind(this),
828
- onCollectionStateQueried: this._onCollectionStateQueried.bind(this),
829
- onCollectionStateReceived: this._onCollectionStateReceived.bind(this),
830
- monitor: dataMonitor
831
- });
832
- this._headsStore = new HeadsStore({
833
- db: db.sublevel("heads")
834
- });
835
- this._indexMetadataStore = indexMetadataStore;
836
- this._peerIdProvider = peerIdProvider;
837
- this._getSpaceKeyByRootDocumentId = getSpaceKeyByRootDocumentId;
838
- }
839
887
  async _open() {
840
888
  this._peerId = `host-${this._peerIdProvider?.() ?? PublicKey.random().toHex()}`;
889
+ this._onHeadsChangedTask = new DeferredTask(this._ctx, async () => {
890
+ const docHeads = Array.from(this._headsUpdates.entries());
891
+ this._headsUpdates.clear();
892
+ this._onHeadsChanged(docHeads);
893
+ });
841
894
  await this._storage.open?.();
842
895
  this._repo = new Repo({
843
896
  peerId: this._peerId,
@@ -865,6 +918,28 @@ var AutomergeHost = class extends Resource3 {
865
918
  }
866
919
  }
867
920
  });
921
+ this._syncTask = new DeferredTask(this._ctx, async () => {
922
+ const collectionToSync = Array.from(this._collectionsToSync.values());
923
+ if (collectionToSync.length === 0) {
924
+ return;
925
+ }
926
+ await Promise.all(collectionToSync.map(async ({ collectionId, peerId }) => {
927
+ try {
928
+ await this._handleCollectionSync(collectionId, peerId);
929
+ } catch (err) {
930
+ log3.error("failed to sync collection", {
931
+ collectionId,
932
+ peerId,
933
+ err
934
+ }, {
935
+ F: __dxlog_file3,
936
+ L: 223,
937
+ S: this,
938
+ C: (f, a) => f(...a)
939
+ });
940
+ }
941
+ }));
942
+ });
868
943
  await this._echoNetworkAdapter.open();
869
944
  await this._collectionSynchronizer.open();
870
945
  await this._echoNetworkAdapter.open();
@@ -874,7 +949,8 @@ var AutomergeHost = class extends Resource3 {
874
949
  await this._collectionSynchronizer.close();
875
950
  await this._storage.close?.();
876
951
  await this._echoNetworkAdapter.close();
877
- await this._ctx.dispose();
952
+ this._syncTask = void 0;
953
+ this._onHeadsChangedTask = void 0;
878
954
  }
879
955
  /**
880
956
  * @deprecated To be abstracted away.
@@ -889,15 +965,42 @@ var AutomergeHost = class extends Resource3 {
889
965
  return Object.keys(this._repo.handles).length;
890
966
  }
891
967
  async addReplicator(replicator) {
968
+ invariant2(this.isOpen, "AutomergeHost is not open", {
969
+ F: __dxlog_file3,
970
+ L: 259,
971
+ S: this,
972
+ A: [
973
+ "this.isOpen",
974
+ "'AutomergeHost is not open'"
975
+ ]
976
+ });
892
977
  await this._echoNetworkAdapter.addReplicator(replicator);
893
978
  }
894
979
  async removeReplicator(replicator) {
980
+ invariant2(this.isOpen, "AutomergeHost is not open", {
981
+ F: __dxlog_file3,
982
+ L: 264,
983
+ S: this,
984
+ A: [
985
+ "this.isOpen",
986
+ "'AutomergeHost is not open'"
987
+ ]
988
+ });
895
989
  await this._echoNetworkAdapter.removeReplicator(replicator);
896
990
  }
897
991
  /**
898
992
  * Loads the document handle from the repo and waits for it to be ready.
899
993
  */
900
994
  async loadDoc(ctx, documentId, opts) {
995
+ invariant2(this.isOpen, "AutomergeHost is not open", {
996
+ F: __dxlog_file3,
997
+ L: 272,
998
+ S: this,
999
+ A: [
1000
+ "this.isOpen",
1001
+ "'AutomergeHost is not open'"
1002
+ ]
1003
+ });
901
1004
  let handle;
902
1005
  if (typeof documentId === "string") {
903
1006
  handle = this._repo.handles[documentId];
@@ -915,6 +1018,15 @@ var AutomergeHost = class extends Resource3 {
915
1018
  return handle;
916
1019
  }
917
1020
  async exportDoc(ctx, id) {
1021
+ invariant2(this.isOpen, "AutomergeHost is not open", {
1022
+ F: __dxlog_file3,
1023
+ L: 295,
1024
+ S: this,
1025
+ A: [
1026
+ "this.isOpen",
1027
+ "'AutomergeHost is not open'"
1028
+ ]
1029
+ });
918
1030
  const documentId = interpretAsDocumentId(id);
919
1031
  const chunks = await this._storage.loadRange([
920
1032
  documentId
@@ -925,6 +1037,15 @@ var AutomergeHost = class extends Resource3 {
925
1037
  * Create new persisted document.
926
1038
  */
927
1039
  createDoc(initialValue, opts) {
1040
+ invariant2(this.isOpen, "AutomergeHost is not open", {
1041
+ F: __dxlog_file3,
1042
+ L: 306,
1043
+ S: this,
1044
+ A: [
1045
+ "this.isOpen",
1046
+ "'AutomergeHost is not open'"
1047
+ ]
1048
+ });
928
1049
  if (opts?.preserveHistory) {
929
1050
  if (initialValue instanceof Uint8Array) {
930
1051
  return this._repo.import(initialValue);
@@ -941,6 +1062,15 @@ var AutomergeHost = class extends Resource3 {
941
1062
  }
942
1063
  }
943
1064
  async waitUntilHeadsReplicated(heads) {
1065
+ invariant2(this.isOpen, "AutomergeHost is not open", {
1066
+ F: __dxlog_file3,
1067
+ L: 328,
1068
+ S: this,
1069
+ A: [
1070
+ "this.isOpen",
1071
+ "'AutomergeHost is not open'"
1072
+ ]
1073
+ });
944
1074
  const entries = heads.entries;
945
1075
  if (!entries?.length) {
946
1076
  return;
@@ -956,10 +1086,10 @@ var AutomergeHost = class extends Resource3 {
956
1086
  return !(currentHeads !== null && headsEquals(currentHeads, targetHeads));
957
1087
  });
958
1088
  if (headsToWait.length > 0) {
959
- await Promise.all(headsToWait.map(async (entry, index) => {
1089
+ await Promise.all(headsToWait.map(async (entry) => {
960
1090
  const handle = await this.loadDoc(Context.default(void 0, {
961
1091
  F: __dxlog_file3,
962
- L: 293
1092
+ L: 346
963
1093
  }), entry.documentId);
964
1094
  await waitForHeads(handle, entry.heads);
965
1095
  }));
@@ -967,12 +1097,21 @@ var AutomergeHost = class extends Resource3 {
967
1097
  await this._repo.flush(documentIds.filter((documentId) => this._repo.handles[documentId] && this._repo.handles[documentId].isReady()));
968
1098
  }
969
1099
  async reIndexHeads(documentIds) {
1100
+ invariant2(this.isOpen, "AutomergeHost is not open", {
1101
+ F: __dxlog_file3,
1102
+ L: 359,
1103
+ S: this,
1104
+ A: [
1105
+ "this.isOpen",
1106
+ "'AutomergeHost is not open'"
1107
+ ]
1108
+ });
970
1109
  for (const documentId of documentIds) {
971
1110
  log3("re-indexing heads for document", {
972
1111
  documentId
973
1112
  }, {
974
1113
  F: __dxlog_file3,
975
- L: 307,
1114
+ L: 361,
976
1115
  S: this,
977
1116
  C: (f, a) => f(...a)
978
1117
  });
@@ -982,7 +1121,7 @@ var AutomergeHost = class extends Resource3 {
982
1121
  documentId
983
1122
  }, {
984
1123
  F: __dxlog_file3,
985
- L: 310,
1124
+ L: 364,
986
1125
  S: this,
987
1126
  C: (f, a) => f(...a)
988
1127
  });
@@ -995,7 +1134,7 @@ var AutomergeHost = class extends Resource3 {
995
1134
  }
996
1135
  log3("done re-indexing heads", void 0, {
997
1136
  F: __dxlog_file3,
998
- L: 319,
1137
+ L: 373,
999
1138
  S: this,
1000
1139
  C: (f, a) => f(...a)
1001
1140
  });
@@ -1056,13 +1195,31 @@ var AutomergeHost = class extends Resource3 {
1056
1195
  * Called by AutomergeStorageAdapter after levelDB batch commit.
1057
1196
  */
1058
1197
  async _afterSave(path) {
1198
+ if (!this.isOpen) {
1199
+ return void 0;
1200
+ }
1059
1201
  this._indexMetadataStore.notifyMarkedDirty();
1060
1202
  const documentId = path[0];
1061
- const document = this._repo.handles[documentId]?.doc();
1062
- if (document) {
1063
- const heads = getHeads(document);
1064
- this._onHeadsChanged(documentId, heads);
1203
+ const handle = this._repo.handles[documentId];
1204
+ if (!handle || !handle.isReady()) {
1205
+ return;
1206
+ }
1207
+ const document = handle.doc();
1208
+ if (!document) {
1209
+ return;
1065
1210
  }
1211
+ const heads = getHeads(document);
1212
+ this._headsUpdates.set(documentId, heads);
1213
+ invariant2(this._onHeadsChangedTask, "onHeadsChangedTask is not initialized", {
1214
+ F: __dxlog_file3,
1215
+ L: 449,
1216
+ S: this,
1217
+ A: [
1218
+ "this._onHeadsChangedTask",
1219
+ "'onHeadsChangedTask is not initialized'"
1220
+ ]
1221
+ });
1222
+ this._onHeadsChangedTask.schedule();
1066
1223
  this.documentsSaved.emit();
1067
1224
  }
1068
1225
  _automergePeers() {
@@ -1099,7 +1256,7 @@ var AutomergeHost = class extends Resource3 {
1099
1256
  * Flush documents to disk.
1100
1257
  */
1101
1258
  async flush({ documentIds } = {}) {
1102
- const loadedDocuments = documentIds?.filter((documentId) => {
1259
+ const loadedDocuments = (documentIds ?? Object.keys(this._repo.handles)).filter((documentId) => {
1103
1260
  const handle = this._repo.handles[documentId];
1104
1261
  return handle && handle.isReady();
1105
1262
  });
@@ -1155,8 +1312,17 @@ var AutomergeHost = class extends Resource3 {
1155
1312
  missingOnRemote: diff.missingOnRemote.length,
1156
1313
  missingOnLocal: diff.missingOnLocal.length,
1157
1314
  differentDocuments: diff.different.length,
1158
- localDocumentCount: Object.keys(localState.documents).length,
1159
- remoteDocumentCount: Object.keys(state.documents).length
1315
+ localDocumentCount: Object.entries(localState.documents).filter(([_, heads]) => heads.length > 0).length,
1316
+ remoteDocumentCount: Object.entries(state.documents).filter(([_, heads]) => heads.length > 0).length,
1317
+ totalDocumentCount: (/* @__PURE__ */ new Set([
1318
+ ...Object.keys(localState.documents),
1319
+ ...Object.keys(state.documents)
1320
+ ])).size,
1321
+ unsyncedDocumentCount: (/* @__PURE__ */ new Set([
1322
+ ...diff.missingOnLocal,
1323
+ ...diff.missingOnRemote,
1324
+ ...diff.different
1325
+ ])).size
1160
1326
  });
1161
1327
  }
1162
1328
  return result;
@@ -1173,6 +1339,14 @@ var AutomergeHost = class extends Resource3 {
1173
1339
  this._collectionSynchronizer.setLocalCollectionState(collectionId, {
1174
1340
  documents
1175
1341
  });
1342
+ const interestedPeers = this._echoNetworkAdapter.getPeersInterestedInCollection(collectionId);
1343
+ if (interestedPeers.length > 0) {
1344
+ for (const peerId of interestedPeers) {
1345
+ this._sendCollectionState(collectionId, peerId, {
1346
+ documents
1347
+ });
1348
+ }
1349
+ }
1176
1350
  }
1177
1351
  async clearLocalCollectionState(collectionId) {
1178
1352
  this._collectionSynchronizer.clearLocalCollectionState(collectionId);
@@ -1196,52 +1370,273 @@ var AutomergeHost = class extends Resource3 {
1196
1370
  this._collectionSynchronizer.onConnectionClosed(peerId);
1197
1371
  }
1198
1372
  _onRemoteCollectionStateUpdated(collectionId, peerId) {
1373
+ this._collectionsToSync.add({
1374
+ collectionId,
1375
+ peerId
1376
+ });
1377
+ this._syncTask?.schedule();
1378
+ }
1379
+ async _handleCollectionSync(collectionId, peerId) {
1199
1380
  const localState = this._collectionSynchronizer.getLocalCollectionState(collectionId);
1200
1381
  const remoteState = this._collectionSynchronizer.getRemoteCollectionStates(collectionId).get(peerId);
1201
1382
  if (!localState || !remoteState) {
1202
1383
  return;
1203
1384
  }
1204
1385
  const { different, missingOnLocal, missingOnRemote } = diffCollectionState(localState, remoteState);
1205
- const toReplicate = [
1206
- ...missingOnLocal,
1207
- ...missingOnRemote,
1386
+ if (different.length === 0 && missingOnLocal.length === 0 && missingOnRemote.length === 0) {
1387
+ return;
1388
+ }
1389
+ const toReplicateWithoutBatching = [
1208
1390
  ...different
1209
1391
  ];
1210
- if (toReplicate.length === 0) {
1392
+ const bundleSyncEnabled = this._echoNetworkAdapter.bundleSyncEnabledForPeer(peerId);
1393
+ if (bundleSyncEnabled && missingOnRemote.length >= BUNDLE_SYNC_THRESHOLD) {
1394
+ log3("pushing bundle", {
1395
+ amount: missingOnRemote.length
1396
+ }, {
1397
+ F: __dxlog_file3,
1398
+ L: 650,
1399
+ S: this,
1400
+ C: (f, a) => f(...a)
1401
+ });
1402
+ const { syncInteractively } = await this._pushInBundles(peerId, missingOnRemote);
1403
+ toReplicateWithoutBatching.push(...syncInteractively);
1404
+ } else {
1405
+ log3.verbose("failed to push bundle, replicating interactively", {
1406
+ collectionId,
1407
+ peerId,
1408
+ amount: missingOnRemote.length
1409
+ }, {
1410
+ F: __dxlog_file3,
1411
+ L: 654,
1412
+ S: this,
1413
+ C: (f, a) => f(...a)
1414
+ });
1415
+ toReplicateWithoutBatching.push(...missingOnRemote);
1416
+ }
1417
+ if (bundleSyncEnabled && missingOnLocal.length >= BUNDLE_SYNC_THRESHOLD) {
1418
+ log3("pulling bundle", {
1419
+ amount: missingOnLocal.length
1420
+ }, {
1421
+ F: __dxlog_file3,
1422
+ L: 662,
1423
+ S: this,
1424
+ C: (f, a) => f(...a)
1425
+ });
1426
+ const { syncInteractively } = await this._pullInBundles(peerId, missingOnLocal);
1427
+ toReplicateWithoutBatching.push(...syncInteractively);
1428
+ } else {
1429
+ log3.verbose("failed to pull bundle, replicating interactively", {
1430
+ collectionId,
1431
+ peerId,
1432
+ amount: missingOnLocal.length
1433
+ }, {
1434
+ F: __dxlog_file3,
1435
+ L: 666,
1436
+ S: this,
1437
+ C: (f, a) => f(...a)
1438
+ });
1439
+ toReplicateWithoutBatching.push(...missingOnLocal);
1440
+ }
1441
+ if (toReplicateWithoutBatching.length === 0) {
1211
1442
  return;
1212
1443
  }
1213
1444
  log3("replicating documents after collection sync", {
1214
1445
  collectionId,
1215
1446
  peerId,
1216
- toReplicate,
1217
- count: toReplicate.length
1447
+ toReplicateWithoutBatching,
1448
+ count: toReplicateWithoutBatching.length
1218
1449
  }, {
1219
1450
  F: __dxlog_file3,
1220
- L: 563,
1451
+ L: 678,
1221
1452
  S: this,
1222
1453
  C: (f, a) => f(...a)
1223
1454
  });
1224
- for (const documentId of toReplicate) {
1455
+ for (const documentId of toReplicateWithoutBatching) {
1225
1456
  this._repo.findWithProgress(documentId);
1226
1457
  }
1227
1458
  }
1228
- _onHeadsChanged(documentId, heads) {
1229
- const collectionsChanged = /* @__PURE__ */ new Set();
1230
- for (const collectionId of this._collectionSynchronizer.getRegisteredCollectionIds()) {
1231
- const state = this._collectionSynchronizer.getLocalCollectionState(collectionId);
1232
- if (state?.documents[documentId]) {
1233
- const newState = structuredClone(state);
1234
- newState.documents[documentId] = heads;
1235
- this._collectionSynchronizer.setLocalCollectionState(collectionId, newState);
1236
- collectionsChanged.add(collectionId);
1459
+ // TODO(mykola): Add retries of batches https://gist.github.com/mykola-vrmchk/fde270259e9209fcbf1331e5abbf12cf
1460
+ // TODO(mykola): Use effect to retry batches.
1461
+ async _pushInBundles(peerId, documentIds) {
1462
+ const documentsToPush = [
1463
+ ...documentIds
1464
+ ];
1465
+ const syncInteractively = [];
1466
+ while (documentsToPush.length > 0) {
1467
+ await Promise.all(range(BUNDLE_SYNC_CONCURRENCY).map(async () => {
1468
+ const bundle = documentsToPush.splice(0, BUNDLE_SIZE);
1469
+ if (bundle.length === 0) {
1470
+ return;
1471
+ }
1472
+ await this._pushBundle(peerId, bundle).catch((err) => {
1473
+ log3.warn("failed to push bundle, replicating interactively", {
1474
+ peerId,
1475
+ bundle,
1476
+ err
1477
+ }, {
1478
+ F: __dxlog_file3,
1479
+ L: 709,
1480
+ S: this,
1481
+ C: (f, a) => f(...a)
1482
+ });
1483
+ syncInteractively.push(...bundle);
1484
+ });
1485
+ }));
1486
+ }
1487
+ return {
1488
+ syncInteractively
1489
+ };
1490
+ }
1491
+ async _pushBundle(peerId, documentIds) {
1492
+ if (this._ctx.disposed) {
1493
+ return;
1494
+ }
1495
+ const docs = documentIds.map((documentId) => {
1496
+ const handle = this._repo.handles[documentId];
1497
+ if (!handle || !handle.isReady()) {
1498
+ log3.warn("document not ready, skipping", {
1499
+ documentId
1500
+ }, {
1501
+ F: __dxlog_file3,
1502
+ L: 727,
1503
+ S: this,
1504
+ C: (f, a) => f(...a)
1505
+ });
1506
+ return;
1507
+ }
1508
+ const doc = handle.doc();
1509
+ if (!doc) {
1510
+ log3.warn("document not available, skipping", {
1511
+ documentId
1512
+ }, {
1513
+ F: __dxlog_file3,
1514
+ L: 732,
1515
+ S: this,
1516
+ C: (f, a) => f(...a)
1517
+ });
1518
+ return;
1237
1519
  }
1520
+ return {
1521
+ documentId,
1522
+ data: save(doc),
1523
+ heads: getHeads(doc)
1524
+ };
1525
+ });
1526
+ await this._echoNetworkAdapter.pushBundle(peerId, docs.filter(isNonNullable2));
1527
+ }
1528
+ async _pullInBundles(peerId, documentIds) {
1529
+ const documentsToPull = [
1530
+ ...documentIds
1531
+ ];
1532
+ const syncInteractively = [];
1533
+ const docsToImport = {};
1534
+ while (documentsToPull.length > 0) {
1535
+ await Promise.all(range(BUNDLE_SYNC_CONCURRENCY).map(async () => {
1536
+ const bundle = documentsToPull.splice(0, BUNDLE_SIZE);
1537
+ if (bundle.length === 0) {
1538
+ return;
1539
+ }
1540
+ const result = await this._pullBundle(peerId, bundle).catch((err) => {
1541
+ log3.warn("failed to pull bundle, replicating interactively", {
1542
+ peerId,
1543
+ bundle,
1544
+ err
1545
+ }, {
1546
+ F: __dxlog_file3,
1547
+ L: 762,
1548
+ S: this,
1549
+ C: (f, a) => f(...a)
1550
+ });
1551
+ syncInteractively.push(...bundle);
1552
+ });
1553
+ if (result) {
1554
+ Object.assign(docsToImport, result.docsToImport);
1555
+ }
1556
+ }));
1238
1557
  }
1239
- for (const collectionId of collectionsChanged) {
1240
- this.collectionStateUpdated.emit({
1241
- collectionId
1558
+ for (const [documentId, data] of Object.entries(docsToImport)) {
1559
+ this._repo.import(data, {
1560
+ docId: documentId
1242
1561
  });
1243
1562
  }
1244
- }
1563
+ await this._repo.flush(Object.keys(docsToImport));
1564
+ return {
1565
+ syncInteractively
1566
+ };
1567
+ }
1568
+ async _pullBundle(peerId, documentIds) {
1569
+ if (this._ctx.disposed) {
1570
+ return;
1571
+ }
1572
+ const docHeads = Object.fromEntries(documentIds.map((documentId) => [
1573
+ documentId,
1574
+ []
1575
+ ]));
1576
+ const bundle = await this._echoNetworkAdapter.pullBundle(peerId, docHeads);
1577
+ return {
1578
+ docsToImport: bundle
1579
+ };
1580
+ }
1581
+ _onHeadsChanged(docHeads) {
1582
+ const collectionsChanged = /* @__PURE__ */ new Set();
1583
+ for (const collectionId of this._collectionSynchronizer.getRegisteredCollectionIds()) {
1584
+ const state = this._collectionSynchronizer.getLocalCollectionState(collectionId);
1585
+ let newState;
1586
+ for (const [documentId, heads] of docHeads) {
1587
+ if (state?.documents[documentId]) {
1588
+ if (!newState) {
1589
+ newState = structuredClone(state);
1590
+ }
1591
+ newState.documents[documentId] = heads;
1592
+ }
1593
+ }
1594
+ if (newState) {
1595
+ this._collectionSynchronizer.setLocalCollectionState(collectionId, newState);
1596
+ collectionsChanged.add(collectionId);
1597
+ }
1598
+ }
1599
+ for (const collectionId of collectionsChanged) {
1600
+ this.collectionStateUpdated.emit({
1601
+ collectionId
1602
+ });
1603
+ }
1604
+ }
1605
+ constructor({ db, indexMetadataStore, dataMonitor, peerIdProvider, getSpaceKeyByRootDocumentId }) {
1606
+ super(), _define_property5(this, "_db", void 0), _define_property5(this, "_indexMetadataStore", void 0), _define_property5(this, "_echoNetworkAdapter", void 0), _define_property5(this, "_collectionSynchronizer", new CollectionSynchronizer({
1607
+ queryCollectionState: this._queryCollectionState.bind(this),
1608
+ sendCollectionState: this._sendCollectionState.bind(this),
1609
+ shouldSyncCollection: this._shouldSyncCollection.bind(this)
1610
+ })), _define_property5(this, "_repo", void 0), _define_property5(this, "_storage", void 0), _define_property5(this, "_headsStore", void 0), _define_property5(this, "_syncTask", void 0), /**
1611
+ * Cache of collections that would be synced on next sync task run.
1612
+ */
1613
+ _define_property5(this, "_collectionsToSync", new ComplexSet(({ collectionId, peerId }) => `${collectionId}|${peerId}`)), _define_property5(this, "_peerId", void 0), _define_property5(this, "_peerIdProvider", void 0), _define_property5(this, "_getSpaceKeyByRootDocumentId", void 0), _define_property5(this, "collectionStateUpdated", new Event2()), /**
1614
+ * Fired after a batch of documents was saved to disk.
1615
+ */
1616
+ _define_property5(this, "documentsSaved", new Event2()), _define_property5(this, "_headsUpdates", /* @__PURE__ */ new Map()), _define_property5(this, "_onHeadsChangedTask", void 0);
1617
+ this._db = db;
1618
+ this._storage = new LevelDBStorageAdapter({
1619
+ db: db.sublevel("automerge"),
1620
+ callbacks: {
1621
+ beforeSave: async (params) => this._beforeSave(params),
1622
+ afterSave: async (key) => this._afterSave(key)
1623
+ },
1624
+ monitor: dataMonitor
1625
+ });
1626
+ this._echoNetworkAdapter = new EchoNetworkAdapter({
1627
+ getContainingSpaceForDocument: this._getContainingSpaceForDocument.bind(this),
1628
+ isDocumentInRemoteCollection: this._isDocumentInRemoteCollection.bind(this),
1629
+ onCollectionStateQueried: this._onCollectionStateQueried.bind(this),
1630
+ onCollectionStateReceived: this._onCollectionStateReceived.bind(this),
1631
+ monitor: dataMonitor
1632
+ });
1633
+ this._headsStore = new HeadsStore({
1634
+ db: db.sublevel("heads")
1635
+ });
1636
+ this._indexMetadataStore = indexMetadataStore;
1637
+ this._peerIdProvider = peerIdProvider;
1638
+ this._getSpaceKeyByRootDocumentId = getSpaceKeyByRootDocumentId;
1639
+ }
1245
1640
  };
1246
1641
  _ts_decorate3([
1247
1642
  trace2.info()
@@ -1277,7 +1672,7 @@ var changeIsPresentInDoc = (doc, changeHash) => {
1277
1672
  var decodeCollectionState = (state) => {
1278
1673
  invariant2(typeof state === "object" && state !== null, "Invalid state", {
1279
1674
  F: __dxlog_file3,
1280
- L: 614,
1675
+ L: 842,
1281
1676
  S: void 0,
1282
1677
  A: [
1283
1678
  "typeof state === 'object' && state !== null",
@@ -1294,7 +1689,7 @@ var encodeCollectionState = (state) => {
1294
1689
  import { invariant as invariant5 } from "@dxos/invariant";
1295
1690
  import { PublicKey as PublicKey2 } from "@dxos/keys";
1296
1691
  import { log as log5 } from "@dxos/log";
1297
- import { ComplexSet, defaultMap as defaultMap2 } from "@dxos/util";
1692
+ import { ComplexSet as ComplexSet2, defaultMap as defaultMap2 } from "@dxos/util";
1298
1693
 
1299
1694
  // src/automerge/mesh-echo-replicator-connection.ts
1300
1695
  import * as A from "@automerge/automerge";
@@ -1303,11 +1698,75 @@ import { Resource as Resource4 } from "@dxos/context";
1303
1698
  import { invariant as invariant3 } from "@dxos/invariant";
1304
1699
  import { log as log4 } from "@dxos/log";
1305
1700
  import { AutomergeReplicator } from "@dxos/teleport-extension-automerge-replicator";
1701
+ function _define_property6(obj, key, value) {
1702
+ if (key in obj) {
1703
+ Object.defineProperty(obj, key, {
1704
+ value,
1705
+ enumerable: true,
1706
+ configurable: true,
1707
+ writable: true
1708
+ });
1709
+ } else {
1710
+ obj[key] = value;
1711
+ }
1712
+ return obj;
1713
+ }
1306
1714
  var __dxlog_file4 = "/__w/dxos/dxos/packages/core/echo/echo-pipeline/src/automerge/mesh-echo-replicator-connection.ts";
1307
1715
  var DEFAULT_FACTORY = (params) => new AutomergeReplicator(...params);
1308
1716
  var MeshReplicatorConnection = class extends Resource4 {
1717
+ _disconnectIfEnabled() {
1718
+ if (this._isEnabled) {
1719
+ this._params.onRemoteDisconnected();
1720
+ }
1721
+ }
1722
+ get peerId() {
1723
+ invariant3(this._remotePeerId != null, "Remote peer has not connected yet.", {
1724
+ F: __dxlog_file4,
1725
+ L: 106,
1726
+ S: this,
1727
+ A: [
1728
+ "this._remotePeerId != null",
1729
+ "'Remote peer has not connected yet.'"
1730
+ ]
1731
+ });
1732
+ return this._remotePeerId;
1733
+ }
1734
+ get isEnabled() {
1735
+ return this._isEnabled;
1736
+ }
1737
+ get bundleSyncEnabled() {
1738
+ return false;
1739
+ }
1740
+ async shouldAdvertise(params) {
1741
+ return this._params.shouldAdvertise(params);
1742
+ }
1743
+ shouldSyncCollection(params) {
1744
+ return this._params.shouldSyncCollection(params);
1745
+ }
1746
+ /**
1747
+ * Start exchanging messages with the remote peer.
1748
+ * Call after the remote peer has connected.
1749
+ */
1750
+ enable() {
1751
+ invariant3(this._remotePeerId != null, "Remote peer has not connected yet.", {
1752
+ F: __dxlog_file4,
1753
+ L: 131,
1754
+ S: this,
1755
+ A: [
1756
+ "this._remotePeerId != null",
1757
+ "'Remote peer has not connected yet.'"
1758
+ ]
1759
+ });
1760
+ this._isEnabled = true;
1761
+ }
1762
+ /**
1763
+ * Stop exchanging messages with the remote peer.
1764
+ */
1765
+ disable() {
1766
+ this._isEnabled = false;
1767
+ }
1309
1768
  constructor(_params) {
1310
- super(), this._params = _params, this.remoteDeviceKey = null, this._remotePeerId = null, this._isEnabled = false;
1769
+ super(), _define_property6(this, "_params", void 0), _define_property6(this, "readable", void 0), _define_property6(this, "writable", void 0), _define_property6(this, "remoteDeviceKey", void 0), _define_property6(this, "replicatorExtension", void 0), _define_property6(this, "_remotePeerId", void 0), _define_property6(this, "_isEnabled", void 0), this._params = _params, this.remoteDeviceKey = null, this._remotePeerId = null, this._isEnabled = false;
1311
1770
  let readableStreamController;
1312
1771
  this.readable = new ReadableStream({
1313
1772
  start: (controller) => {
@@ -1371,54 +1830,6 @@ var MeshReplicatorConnection = class extends Resource4 {
1371
1830
  }
1372
1831
  ]);
1373
1832
  }
1374
- _disconnectIfEnabled() {
1375
- if (this._isEnabled) {
1376
- this._params.onRemoteDisconnected();
1377
- }
1378
- }
1379
- get peerId() {
1380
- invariant3(this._remotePeerId != null, "Remote peer has not connected yet.", {
1381
- F: __dxlog_file4,
1382
- L: 106,
1383
- S: this,
1384
- A: [
1385
- "this._remotePeerId != null",
1386
- "'Remote peer has not connected yet.'"
1387
- ]
1388
- });
1389
- return this._remotePeerId;
1390
- }
1391
- get isEnabled() {
1392
- return this._isEnabled;
1393
- }
1394
- async shouldAdvertise(params) {
1395
- return this._params.shouldAdvertise(params);
1396
- }
1397
- shouldSyncCollection(params) {
1398
- return this._params.shouldSyncCollection(params);
1399
- }
1400
- /**
1401
- * Start exchanging messages with the remote peer.
1402
- * Call after the remote peer has connected.
1403
- */
1404
- enable() {
1405
- invariant3(this._remotePeerId != null, "Remote peer has not connected yet.", {
1406
- F: __dxlog_file4,
1407
- L: 127,
1408
- S: this,
1409
- A: [
1410
- "this._remotePeerId != null",
1411
- "'Remote peer has not connected yet.'"
1412
- ]
1413
- });
1414
- this._isEnabled = true;
1415
- }
1416
- /**
1417
- * Stop exchanging messages with the remote peer.
1418
- */
1419
- disable() {
1420
- this._isEnabled = false;
1421
- }
1422
1833
  };
1423
1834
  var logSendSync = (message) => {
1424
1835
  log4("sendSyncMessage", () => {
@@ -1435,7 +1846,7 @@ var logSendSync = (message) => {
1435
1846
  };
1436
1847
  }, {
1437
1848
  F: __dxlog_file4,
1438
- L: 140,
1849
+ L: 144,
1439
1850
  S: void 0,
1440
1851
  C: (f, a) => f(...a)
1441
1852
  });
@@ -1461,26 +1872,21 @@ var getSpaceIdFromCollectionId = (collectionId) => {
1461
1872
  };
1462
1873
 
1463
1874
  // src/automerge/mesh-echo-replicator.ts
1875
+ function _define_property7(obj, key, value) {
1876
+ if (key in obj) {
1877
+ Object.defineProperty(obj, key, {
1878
+ value,
1879
+ enumerable: true,
1880
+ configurable: true,
1881
+ writable: true
1882
+ });
1883
+ } else {
1884
+ obj[key] = value;
1885
+ }
1886
+ return obj;
1887
+ }
1464
1888
  var __dxlog_file6 = "/__w/dxos/dxos/packages/core/echo/echo-pipeline/src/automerge/mesh-echo-replicator.ts";
1465
1889
  var MeshEchoReplicator = class {
1466
- constructor() {
1467
- /**
1468
- * We might have multiple connections open with a peer (one per space), but there'll be only one enabled
1469
- * connection at any given moment, because there's a single repo for all the spaces.
1470
- * When a connection closes (space was closed) it gets removed from the list and the next connection
1471
- * in the line gets enabled.
1472
- */
1473
- this._connectionsPerPeer = /* @__PURE__ */ new Map();
1474
- /**
1475
- * A set of all connections (enabled and disabled).
1476
- */
1477
- this._connections = /* @__PURE__ */ new Set();
1478
- /**
1479
- * spaceId -> deviceKey[]
1480
- */
1481
- this._authorizedDevices = /* @__PURE__ */ new Map();
1482
- this._context = null;
1483
- }
1484
1890
  async connect(context) {
1485
1891
  this._context = context;
1486
1892
  }
@@ -1500,7 +1906,7 @@ var MeshEchoReplicator = class {
1500
1906
  createExtension(extensionFactory) {
1501
1907
  invariant5(this._context, void 0, {
1502
1908
  F: __dxlog_file6,
1503
- L: 67,
1909
+ L: 68,
1504
1910
  S: this,
1505
1911
  A: [
1506
1912
  "this._context",
@@ -1515,13 +1921,13 @@ var MeshEchoReplicator = class {
1515
1921
  peerId: connection.peerId
1516
1922
  }, {
1517
1923
  F: __dxlog_file6,
1518
- L: 73,
1924
+ L: 74,
1519
1925
  S: this,
1520
1926
  C: (f, a) => f(...a)
1521
1927
  });
1522
1928
  invariant5(this._context, void 0, {
1523
1929
  F: __dxlog_file6,
1524
- L: 74,
1930
+ L: 75,
1525
1931
  S: this,
1526
1932
  A: [
1527
1933
  "this._context",
@@ -1546,7 +1952,7 @@ var MeshEchoReplicator = class {
1546
1952
  peerId: connection.peerId
1547
1953
  }, {
1548
1954
  F: __dxlog_file6,
1549
- L: 88,
1955
+ L: 89,
1550
1956
  S: this,
1551
1957
  C: (f, a) => f(...a)
1552
1958
  });
@@ -1558,7 +1964,7 @@ var MeshEchoReplicator = class {
1558
1964
  peerId: connection.peerId
1559
1965
  }, {
1560
1966
  F: __dxlog_file6,
1561
- L: 96,
1967
+ L: 97,
1562
1968
  S: this,
1563
1969
  C: (f, a) => f(...a)
1564
1970
  });
@@ -1580,13 +1986,13 @@ var MeshEchoReplicator = class {
1580
1986
  documentId: params.documentId
1581
1987
  }, {
1582
1988
  F: __dxlog_file6,
1583
- L: 114,
1989
+ L: 115,
1584
1990
  S: this,
1585
1991
  C: (f, a) => f(...a)
1586
1992
  });
1587
1993
  invariant5(this._context, void 0, {
1588
1994
  F: __dxlog_file6,
1589
- L: 115,
1995
+ L: 116,
1590
1996
  S: this,
1591
1997
  A: [
1592
1998
  "this._context",
@@ -1606,7 +2012,7 @@ var MeshEchoReplicator = class {
1606
2012
  acceptDocument: remoteDocumentExists
1607
2013
  }, {
1608
2014
  F: __dxlog_file6,
1609
- L: 123,
2015
+ L: 124,
1610
2016
  S: this,
1611
2017
  C: (f, a) => f(...a)
1612
2018
  });
@@ -1620,7 +2026,7 @@ var MeshEchoReplicator = class {
1620
2026
  documentId: params.documentId
1621
2027
  }, {
1622
2028
  F: __dxlog_file6,
1623
- L: 140,
2029
+ L: 141,
1624
2030
  S: this,
1625
2031
  C: (f, a) => f(...a)
1626
2032
  });
@@ -1636,7 +2042,7 @@ var MeshEchoReplicator = class {
1636
2042
  isAuthorized
1637
2043
  }, {
1638
2044
  F: __dxlog_file6,
1639
- L: 148,
2045
+ L: 149,
1640
2046
  S: this,
1641
2047
  C: (f, a) => f(...a)
1642
2048
  });
@@ -1644,7 +2050,7 @@ var MeshEchoReplicator = class {
1644
2050
  } catch (err) {
1645
2051
  log5.catch(err, void 0, {
1646
2052
  F: __dxlog_file6,
1647
- L: 158,
2053
+ L: 159,
1648
2054
  S: this,
1649
2055
  C: (f, a) => f(...a)
1650
2056
  });
@@ -1660,7 +2066,7 @@ var MeshEchoReplicator = class {
1660
2066
  collectionId
1661
2067
  }, {
1662
2068
  F: __dxlog_file6,
1663
- L: 168,
2069
+ L: 169,
1664
2070
  S: this,
1665
2071
  C: (f, a) => f(...a)
1666
2072
  });
@@ -1679,12 +2085,12 @@ var MeshEchoReplicator = class {
1679
2085
  deviceKey
1680
2086
  }, {
1681
2087
  F: __dxlog_file6,
1682
- L: 185,
2088
+ L: 186,
1683
2089
  S: this,
1684
2090
  C: (f, a) => f(...a)
1685
2091
  });
1686
2092
  const spaceId = await createIdFromSpaceKey(spaceKey);
1687
- defaultMap2(this._authorizedDevices, spaceId, () => new ComplexSet(PublicKey2.hash)).add(deviceKey);
2093
+ defaultMap2(this._authorizedDevices, spaceId, () => new ComplexSet2(PublicKey2.hash)).add(deviceKey);
1688
2094
  for (const connection of this._connections) {
1689
2095
  if (connection.isEnabled && connection.remoteDeviceKey && connection.remoteDeviceKey.equals(deviceKey)) {
1690
2096
  if (this._connectionsPerPeer.has(connection.peerId)) {
@@ -1693,11 +2099,30 @@ var MeshEchoReplicator = class {
1693
2099
  }
1694
2100
  }
1695
2101
  }
2102
+ constructor() {
2103
+ _define_property7(this, "_connectionsPerPeer", /* @__PURE__ */ new Map());
2104
+ _define_property7(this, "_connections", /* @__PURE__ */ new Set());
2105
+ _define_property7(this, "_authorizedDevices", /* @__PURE__ */ new Map());
2106
+ _define_property7(this, "_context", null);
2107
+ }
1696
2108
  };
1697
2109
 
1698
2110
  // src/automerge/echo-data-monitor.ts
1699
2111
  import { trace as trace3 } from "@dxos/tracing";
1700
- import { CircularBuffer, mapValues, SlidingWindowSummary } from "@dxos/util";
2112
+ import { CircularBuffer, SlidingWindowSummary, mapValues } from "@dxos/util";
2113
+ function _define_property8(obj, key, value) {
2114
+ if (key in obj) {
2115
+ Object.defineProperty(obj, key, {
2116
+ value,
2117
+ enumerable: true,
2118
+ configurable: true,
2119
+ writable: true
2120
+ });
2121
+ } else {
2122
+ obj[key] = value;
2123
+ }
2124
+ return obj;
2125
+ }
1701
2126
  function _ts_decorate4(decorators, target, key, desc) {
1702
2127
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1703
2128
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
@@ -1707,20 +2132,6 @@ function _ts_decorate4(decorators, target, key, desc) {
1707
2132
  var PER_SECOND_RATE_AVG_WINDOW_SIZE = 5;
1708
2133
  var DEFAULT_AVG_WINDOW_SIZE = 25;
1709
2134
  var EchoDataMonitor = class {
1710
- constructor(_params = {
1711
- timeSeriesLength: 30
1712
- }) {
1713
- this._params = _params;
1714
- this._lastTick = 0;
1715
- this._activeCounters = createLocalCounters();
1716
- this._localTimeSeries = createLocalTimeSeries();
1717
- this._storageAverages = createStorageAverages();
1718
- this._replicationAverages = createNetworkAverages();
1719
- this._sizeByMessage = {};
1720
- this._lastReceivedMessages = new CircularBuffer(100);
1721
- this._lastSentMessages = new CircularBuffer(100);
1722
- this._connectionsCount = 0;
1723
- }
1724
2135
  tick(timeMs) {
1725
2136
  this._advanceTimeWindow(timeMs - this._lastTick);
1726
2137
  this._lastTick = timeMs;
@@ -1960,8 +2371,9 @@ var EchoDataMonitor = class {
1960
2371
  messageCounts.failed++;
1961
2372
  }
1962
2373
  _getStatsForType(message) {
1963
- const messageSize = this._sizeByMessage[message.type] ??= createSlidingWindow();
1964
- const messageCounts = this._activeCounters.byType[message.type] ??= createMessageCounter();
2374
+ var _this__sizeByMessage, _message_type, _this__activeCounters_byType, _message_type1;
2375
+ const messageSize = (_this__sizeByMessage = this._sizeByMessage)[_message_type = message.type] ?? (_this__sizeByMessage[_message_type] = createSlidingWindow());
2376
+ const messageCounts = (_this__activeCounters_byType = this._activeCounters.byType)[_message_type1 = message.type] ?? (_this__activeCounters_byType[_message_type1] = createMessageCounter());
1965
2377
  return {
1966
2378
  messageCounts,
1967
2379
  messageSize
@@ -1970,21 +2382,48 @@ var EchoDataMonitor = class {
1970
2382
  _computeMessageHistogram(groupKey) {
1971
2383
  const result = {};
1972
2384
  for (const receivedMessage of this._lastReceivedMessages) {
1973
- const counters = result[receivedMessage[groupKey]] ??= {
2385
+ var _result, _receivedMessage_groupKey;
2386
+ const counters = (_result = result)[_receivedMessage_groupKey = receivedMessage[groupKey]] ?? (_result[_receivedMessage_groupKey] = {
1974
2387
  received: 0,
1975
2388
  sent: 0
1976
- };
2389
+ });
1977
2390
  counters.received++;
1978
2391
  }
1979
2392
  for (const receivedMessage of this._lastSentMessages) {
1980
- const counters = result[receivedMessage[groupKey]] ??= {
2393
+ var _result1, _receivedMessage_groupKey1;
2394
+ const counters = (_result1 = result)[_receivedMessage_groupKey1 = receivedMessage[groupKey]] ?? (_result1[_receivedMessage_groupKey1] = {
1981
2395
  received: 0,
1982
2396
  sent: 0
1983
- };
2397
+ });
1984
2398
  counters.sent++;
1985
2399
  }
1986
2400
  return result;
1987
2401
  }
2402
+ constructor(_params = {
2403
+ timeSeriesLength: 30
2404
+ }) {
2405
+ _define_property8(this, "_params", void 0);
2406
+ _define_property8(this, "_lastTick", void 0);
2407
+ _define_property8(this, "_activeCounters", void 0);
2408
+ _define_property8(this, "_lastCompleteCounters", void 0);
2409
+ _define_property8(this, "_localTimeSeries", void 0);
2410
+ _define_property8(this, "_storageAverages", void 0);
2411
+ _define_property8(this, "_replicationAverages", void 0);
2412
+ _define_property8(this, "_sizeByMessage", void 0);
2413
+ _define_property8(this, "_lastReceivedMessages", void 0);
2414
+ _define_property8(this, "_lastSentMessages", void 0);
2415
+ _define_property8(this, "_connectionsCount", void 0);
2416
+ this._params = _params;
2417
+ this._lastTick = 0;
2418
+ this._activeCounters = createLocalCounters();
2419
+ this._localTimeSeries = createLocalTimeSeries();
2420
+ this._storageAverages = createStorageAverages();
2421
+ this._replicationAverages = createNetworkAverages();
2422
+ this._sizeByMessage = {};
2423
+ this._lastReceivedMessages = new CircularBuffer(100);
2424
+ this._lastSentMessages = new CircularBuffer(100);
2425
+ this._connectionsCount = 0;
2426
+ }
1988
2427
  };
1989
2428
  EchoDataMonitor = _ts_decorate4([
1990
2429
  trace3.resource()
@@ -2057,19 +2496,36 @@ var getByteCount = (message) => {
2057
2496
  };
2058
2497
 
2059
2498
  // src/db-host/documents-synchronizer.ts
2499
+ import { next as A2 } from "@automerge/automerge";
2500
+ import { UpdateScheduler, sleep } from "@dxos/async";
2501
+ import { LifecycleState as LifecycleState2, Resource as Resource5, cancelWithContext as cancelWithContext2 } from "@dxos/context";
2502
+ import { invariant as invariant6 } from "@dxos/invariant";
2503
+ import { log as log6 } from "@dxos/log";
2504
+ function _define_property9(obj, key, value) {
2505
+ if (key in obj) {
2506
+ Object.defineProperty(obj, key, {
2507
+ value,
2508
+ enumerable: true,
2509
+ configurable: true,
2510
+ writable: true
2511
+ });
2512
+ } else {
2513
+ obj[key] = value;
2514
+ }
2515
+ return obj;
2516
+ }
2060
2517
  var __dxlog_file7 = "/__w/dxos/dxos/packages/core/echo/echo-pipeline/src/db-host/documents-synchronizer.ts";
2061
2518
  var MAX_UPDATE_FREQ = 10;
2519
+ var WRAP_AROUND_RETRY_LIMIT = 3;
2520
+ var WRAP_AROUND_RETRY_INITIAL_DELAY = 100;
2062
2521
  var DocumentsSynchronizer = class extends Resource5 {
2063
- constructor(_params) {
2064
- super(), this._params = _params, this._syncStates = /* @__PURE__ */ new Map(), this._pendingUpdates = /* @__PURE__ */ new Set(), this._sendUpdatesJob = void 0;
2065
- }
2066
- addDocuments(documentIds, retryCounter = 0) {
2067
- if (retryCounter > 3) {
2522
+ addDocuments(documentIds, retryCounter = 0, wrapAroundRetryDelay = WRAP_AROUND_RETRY_INITIAL_DELAY) {
2523
+ if (retryCounter > WRAP_AROUND_RETRY_LIMIT) {
2068
2524
  log6.warn("Failed to load document, retry limit reached", {
2069
2525
  documentIds
2070
2526
  }, {
2071
2527
  F: __dxlog_file7,
2072
- L: 52,
2528
+ L: 57,
2073
2529
  S: this,
2074
2530
  C: (f, a) => f(...a)
2075
2531
  });
@@ -2087,13 +2543,13 @@ var DocumentsSynchronizer = class extends Resource5 {
2087
2543
  error
2088
2544
  }, {
2089
2545
  F: __dxlog_file7,
2090
- L: 66,
2546
+ L: 71,
2091
2547
  S: this,
2092
2548
  C: (f, a) => f(...a)
2093
2549
  });
2094
- this.addDocuments([
2550
+ void cancelWithContext2(this._ctx, sleep(wrapAroundRetryDelay)).then(() => this.addDocuments([
2095
2551
  documentId
2096
- ], retryCounter + 1);
2552
+ ], retryCounter + 1, wrapAroundRetryDelay * 2));
2097
2553
  });
2098
2554
  }
2099
2555
  }
@@ -2115,14 +2571,9 @@ var DocumentsSynchronizer = class extends Resource5 {
2115
2571
  }
2116
2572
  async update(updates) {
2117
2573
  for (const { documentId, mutation, isNew } of updates) {
2118
- if (isNew) {
2119
- const doc = await this._params.repo.find(documentId, FIND_PARAMS);
2120
- doc.update((doc2) => A2.loadIncremental(doc2, mutation));
2121
- this._startSync(doc);
2122
- } else {
2123
- this._writeMutation(documentId, mutation);
2124
- }
2574
+ this._writeMutation(documentId, mutation, isNew);
2125
2575
  }
2576
+ await this._params.repo.flush(updates.map(({ documentId }) => documentId));
2126
2577
  }
2127
2578
  _startSync(doc) {
2128
2579
  if (this._syncStates.has(doc.documentId)) {
@@ -2130,7 +2581,7 @@ var DocumentsSynchronizer = class extends Resource5 {
2130
2581
  documentId: doc.documentId
2131
2582
  }, {
2132
2583
  F: __dxlog_file7,
2133
- L: 105,
2584
+ L: 108,
2134
2585
  S: this,
2135
2586
  C: (f, a) => f(...a)
2136
2587
  });
@@ -2141,6 +2592,7 @@ var DocumentsSynchronizer = class extends Resource5 {
2141
2592
  };
2142
2593
  this._subscribeForChanges(syncState);
2143
2594
  this._syncStates.set(doc.documentId, syncState);
2595
+ return syncState;
2144
2596
  }
2145
2597
  _subscribeForChanges(syncState) {
2146
2598
  const handler = () => {
@@ -2173,7 +2625,7 @@ var DocumentsSynchronizer = class extends Resource5 {
2173
2625
  const syncState = this._syncStates.get(documentId);
2174
2626
  invariant6(syncState, "Sync state for document not found", {
2175
2627
  F: __dxlog_file7,
2176
- L: 146,
2628
+ L: 150,
2177
2629
  S: this,
2178
2630
  A: [
2179
2631
  "syncState",
@@ -2192,41 +2644,64 @@ var DocumentsSynchronizer = class extends Resource5 {
2192
2644
  syncState.lastSentHead = A2.getHeads(doc);
2193
2645
  return mutation;
2194
2646
  }
2195
- _writeMutation(documentId, mutation) {
2196
- const syncState = this._syncStates.get(documentId);
2197
- invariant6(syncState, "Sync state for document not found", {
2198
- F: __dxlog_file7,
2199
- L: 162,
2200
- S: this,
2201
- A: [
2202
- "syncState",
2203
- "'Sync state for document not found'"
2204
- ]
2205
- });
2206
- syncState.handle.update((doc) => {
2207
- const headsBefore = A2.getHeads(doc);
2208
- const newDoc = A2.loadIncremental(doc, mutation);
2647
+ _writeMutation(documentId, mutation, isNew) {
2648
+ if (this._lifecycleState === LifecycleState2.CLOSED) {
2649
+ return;
2650
+ }
2651
+ if (isNew) {
2652
+ const newHandle = this._params.repo.import(mutation, {
2653
+ docId: documentId
2654
+ });
2655
+ const syncState = this._startSync(newHandle);
2656
+ syncState.lastSentHead = A2.getHeads(newHandle.doc());
2657
+ } else {
2658
+ const syncState = this._syncStates.get(documentId);
2659
+ invariant6(syncState, "Sync state for document not found", {
2660
+ F: __dxlog_file7,
2661
+ L: 174,
2662
+ S: this,
2663
+ A: [
2664
+ "syncState",
2665
+ "'Sync state for document not found'"
2666
+ ]
2667
+ });
2668
+ const headsBefore = A2.getHeads(syncState.handle.doc());
2669
+ this._params.repo.import(mutation, {
2670
+ docId: documentId
2671
+ });
2209
2672
  if (A2.equals(headsBefore, syncState.lastSentHead)) {
2210
- syncState.lastSentHead = A2.getHeads(newDoc);
2673
+ syncState.lastSentHead = A2.getHeads(syncState.handle.doc());
2211
2674
  }
2212
- return newDoc;
2213
- });
2675
+ }
2676
+ }
2677
+ constructor(_params) {
2678
+ super(), _define_property9(this, "_params", void 0), _define_property9(this, "_syncStates", void 0), /**
2679
+ * Documents that have pending updates.
2680
+ * Used to batch updates.
2681
+ */
2682
+ _define_property9(this, "_pendingUpdates", void 0), /**
2683
+ * Job that schedules if there are pending updates.
2684
+ */
2685
+ _define_property9(this, "_sendUpdatesJob", void 0), this._params = _params, this._syncStates = /* @__PURE__ */ new Map(), this._pendingUpdates = /* @__PURE__ */ new Set(), this._sendUpdatesJob = void 0;
2214
2686
  }
2215
2687
  };
2216
2688
 
2217
2689
  // src/db-host/data-service.ts
2690
+ function _define_property10(obj, key, value) {
2691
+ if (key in obj) {
2692
+ Object.defineProperty(obj, key, {
2693
+ value,
2694
+ enumerable: true,
2695
+ configurable: true,
2696
+ writable: true
2697
+ });
2698
+ } else {
2699
+ obj[key] = value;
2700
+ }
2701
+ return obj;
2702
+ }
2218
2703
  var __dxlog_file8 = "/__w/dxos/dxos/packages/core/echo/echo-pipeline/src/db-host/data-service.ts";
2219
2704
  var DataServiceImpl = class {
2220
- constructor(params) {
2221
- /**
2222
- * Map of subscriptions.
2223
- * subscriptionId -> DocumentsSynchronizer
2224
- */
2225
- this._subscriptions = /* @__PURE__ */ new Map();
2226
- this._automergeHost = params.automergeHost;
2227
- this._spaceStateManager = params.spaceStateManager;
2228
- this._updateIndexes = params.updateIndexes;
2229
- }
2230
2705
  subscribe(request) {
2231
2706
  return new Stream(({ next, ready }) => {
2232
2707
  const synchronizer = new DocumentsSynchronizer({
@@ -2238,7 +2713,7 @@ var DataServiceImpl = class {
2238
2713
  ready();
2239
2714
  }).catch((err) => log7.catch(err, void 0, {
2240
2715
  F: __dxlog_file8,
2241
- L: 71,
2716
+ L: 72,
2242
2717
  S: this,
2243
2718
  C: (f, a) => f(...a)
2244
2719
  }));
@@ -2249,7 +2724,7 @@ var DataServiceImpl = class {
2249
2724
  const synchronizer = this._subscriptions.get(request.subscriptionId);
2250
2725
  invariant7(synchronizer, "Subscription not found", {
2251
2726
  F: __dxlog_file8,
2252
- L: 78,
2727
+ L: 79,
2253
2728
  S: this,
2254
2729
  A: [
2255
2730
  "synchronizer",
@@ -2270,7 +2745,7 @@ var DataServiceImpl = class {
2270
2745
  const synchronizer = this._subscriptions.get(request.subscriptionId);
2271
2746
  invariant7(synchronizer, "Subscription not found", {
2272
2747
  F: __dxlog_file8,
2273
- L: 93,
2748
+ L: 94,
2274
2749
  S: this,
2275
2750
  A: [
2276
2751
  "synchronizer",
@@ -2315,7 +2790,7 @@ var DataServiceImpl = class {
2315
2790
  const spaceId = request.spaceId;
2316
2791
  invariant7(SpaceId2.isValid(spaceId), void 0, {
2317
2792
  F: __dxlog_file8,
2318
- L: 133,
2793
+ L: 134,
2319
2794
  S: this,
2320
2795
  A: [
2321
2796
  "SpaceId.isValid(spaceId)",
@@ -2336,14 +2811,7 @@ var DataServiceImpl = class {
2336
2811
  peers: []
2337
2812
  };
2338
2813
  next({
2339
- peers: state.peers.map((peer) => ({
2340
- peerId: peer.peerId,
2341
- missingOnRemote: peer.missingOnRemote,
2342
- missingOnLocal: peer.missingOnLocal,
2343
- differentDocuments: peer.differentDocuments,
2344
- localDocumentCount: peer.localDocumentCount,
2345
- remoteDocumentCount: peer.remoteDocumentCount
2346
- }))
2814
+ peers: state.peers
2347
2815
  });
2348
2816
  });
2349
2817
  this._automergeHost.collectionStateUpdated.on(ctx, (e) => {
@@ -2354,12 +2822,21 @@ var DataServiceImpl = class {
2354
2822
  scheduler.trigger();
2355
2823
  });
2356
2824
  }
2825
+ constructor(params) {
2826
+ _define_property10(this, "_subscriptions", /* @__PURE__ */ new Map());
2827
+ _define_property10(this, "_automergeHost", void 0);
2828
+ _define_property10(this, "_spaceStateManager", void 0);
2829
+ _define_property10(this, "_updateIndexes", void 0);
2830
+ this._automergeHost = params.automergeHost;
2831
+ this._spaceStateManager = params.spaceStateManager;
2832
+ this._updateIndexes = params.updateIndexes;
2833
+ }
2357
2834
  };
2358
2835
 
2359
2836
  // src/db-host/echo-host.ts
2360
2837
  import { LifecycleState as LifecycleState5, Resource as Resource9 } from "@dxos/context";
2361
2838
  import { todo } from "@dxos/debug";
2362
- import { createIdFromSpaceKey as createIdFromSpaceKey2, SpaceDocVersion as SpaceDocVersion3 } from "@dxos/echo-protocol";
2839
+ import { SpaceDocVersion as SpaceDocVersion3, createIdFromSpaceKey as createIdFromSpaceKey2 } from "@dxos/echo-protocol";
2363
2840
  import { IndexMetadataStore, IndexStore, Indexer } from "@dxos/indexing";
2364
2841
  import { invariant as invariant13 } from "@dxos/invariant";
2365
2842
  import { IndexKind } from "@dxos/protocols/proto/dxos/echo/indexing";
@@ -2456,7 +2933,7 @@ var createSelectedDocumentsIterator = (automergeHost) => (
2456
2933
  // src/db-host/query-service.ts
2457
2934
  import { getHeads as getHeads3 } from "@automerge/automerge";
2458
2935
  import { Schema } from "effect";
2459
- import { DeferredTask, scheduleMicroTask, synchronized as synchronized2 } from "@dxos/async";
2936
+ import { DeferredTask as DeferredTask2, scheduleMicroTask, synchronized as synchronized2 } from "@dxos/async";
2460
2937
  import { Stream as Stream2 } from "@dxos/codec-protobuf/stream";
2461
2938
  import { Context as Context4, Resource as Resource7 } from "@dxos/context";
2462
2939
  import { raise } from "@dxos/debug";
@@ -2466,23 +2943,26 @@ import { objectPointerCodec as objectPointerCodec4 } from "@dxos/protocols";
2466
2943
  import { trace as trace4 } from "@dxos/tracing";
2467
2944
 
2468
2945
  // src/query/query-executor.ts
2469
- import { Match } from "effect";
2946
+ import { Match, Predicate } from "effect";
2470
2947
  import { Context as Context3, ContextDisposedError, LifecycleState as LifecycleState3, Resource as Resource6 } from "@dxos/context";
2471
- import { DatabaseDirectory as DatabaseDirectory3, isEncodedReference, ObjectStructure } from "@dxos/echo-protocol";
2948
+ import { DatabaseDirectory as DatabaseDirectory3, ObjectStructure, isEncodedReference } from "@dxos/echo-protocol";
2472
2949
  import { EscapedPropPath } from "@dxos/indexing";
2473
2950
  import { invariant as invariant10 } from "@dxos/invariant";
2474
2951
  import { DXN, PublicKey as PublicKey3 } from "@dxos/keys";
2475
2952
  import { log as log9 } from "@dxos/log";
2476
2953
  import { objectPointerCodec as objectPointerCodec3 } from "@dxos/protocols";
2477
- import { getDeep, isNonNullable as isNonNullable2 } from "@dxos/util";
2954
+ import { getDeep, isNonNullable as isNonNullable3 } from "@dxos/util";
2478
2955
 
2479
2956
  // src/query/query-planner.ts
2957
+ import { Order } from "@dxos/echo";
2480
2958
  import { invariant as invariant9 } from "@dxos/invariant";
2481
2959
 
2482
2960
  // src/query/errors.ts
2483
2961
  import { BaseError } from "@dxos/errors";
2484
2962
  var QueryError = class extends BaseError.extend("QUERY_ERROR") {
2485
2963
  };
2964
+ var InvalidQueryError = class extends QueryError.extend("INVALID_QUERY") {
2965
+ };
2486
2966
 
2487
2967
  // src/query/plan.ts
2488
2968
  (function(QueryPlan2) {
@@ -2506,17 +2986,24 @@ var QueryError = class extends BaseError.extend("QUERY_ERROR") {
2506
2986
  var QueryPlan;
2507
2987
 
2508
2988
  // src/query/query-planner.ts
2989
+ function _define_property11(obj, key, value) {
2990
+ if (key in obj) {
2991
+ Object.defineProperty(obj, key, {
2992
+ value,
2993
+ enumerable: true,
2994
+ configurable: true,
2995
+ writable: true
2996
+ });
2997
+ } else {
2998
+ obj[key] = value;
2999
+ }
3000
+ return obj;
3001
+ }
2509
3002
  var __dxlog_file10 = "/__w/dxos/dxos/packages/core/echo/echo-pipeline/src/query/query-planner.ts";
2510
3003
  var DEFAULT_OPTIONS = {
2511
3004
  defaultTextSearchKind: "full-text"
2512
3005
  };
2513
3006
  var QueryPlanner = class {
2514
- constructor(options) {
2515
- this._options = {
2516
- ...DEFAULT_OPTIONS,
2517
- ...options
2518
- };
2519
- }
2520
3007
  createPlan(query) {
2521
3008
  let plan = this._generate(query, {
2522
3009
  ...DEFAULT_CONTEXT,
@@ -2524,6 +3011,7 @@ var QueryPlanner = class {
2524
3011
  });
2525
3012
  plan = this._optimizeEmptyFilters(plan);
2526
3013
  plan = this._optimizeSoloUnions(plan);
3014
+ plan = this._ensureOrderStep(plan);
2527
3015
  return plan;
2528
3016
  }
2529
3017
  _generate(query, context) {
@@ -2546,8 +3034,11 @@ var QueryPlanner = class {
2546
3034
  return this._generateUnionClause(query, context);
2547
3035
  case "set-difference":
2548
3036
  return this._generateSetDifferenceClause(query, context);
3037
+ case "order":
3038
+ return this._generateOrderClause(query, context);
2549
3039
  default:
2550
- throw new QueryError(`Unsupported query type: ${query.type}`, {
3040
+ throw new QueryError({
3041
+ message: `Unsupported query type: ${query.type}`,
2551
3042
  context: {
2552
3043
  query: context.originalQuery
2553
3044
  }
@@ -2573,6 +3064,7 @@ var QueryPlanner = class {
2573
3064
  // TODO(dmaretskyi): If the tip of the query ast is a [select, ...filter] shape we can reorder the filters so the query is most efficient.
2574
3065
  _generateSelectionFromFilter(filter, context) {
2575
3066
  switch (filter.type) {
3067
+ // Props
2576
3068
  case "object": {
2577
3069
  if (context.selectionInverted && filter.id === void 0 && filter.typename === null && Object.keys(filter.props).length === 0) {
2578
3070
  return QueryPlan.Plan.make([
@@ -2583,7 +3075,8 @@ var QueryPlanner = class {
2583
3075
  ]);
2584
3076
  }
2585
3077
  if (context.selectionInverted) {
2586
- throw new QueryError("Query too complex", {
3078
+ throw new QueryError({
3079
+ message: "Query too complex",
2587
3080
  context: {
2588
3081
  query: context.originalQuery
2589
3082
  }
@@ -2649,6 +3142,26 @@ var QueryPlanner = class {
2649
3142
  ]);
2650
3143
  }
2651
3144
  }
3145
+ // Tag
3146
+ case "tag": {
3147
+ return QueryPlan.Plan.make([
3148
+ {
3149
+ _tag: "SelectStep",
3150
+ spaces: context.selectionSpaces,
3151
+ selector: {
3152
+ _tag: "WildcardSelector"
3153
+ }
3154
+ },
3155
+ ...this._generateDeletedHandlingSteps(context),
3156
+ {
3157
+ _tag: "FilterStep",
3158
+ filter: {
3159
+ ...filter
3160
+ }
3161
+ }
3162
+ ]);
3163
+ }
3164
+ // Text
2652
3165
  case "text-search": {
2653
3166
  return QueryPlan.Plan.make([
2654
3167
  {
@@ -2663,48 +3176,54 @@ var QueryPlanner = class {
2663
3176
  ...this._generateDeletedHandlingSteps(context)
2664
3177
  ]);
2665
3178
  }
3179
+ // Compare
2666
3180
  case "compare":
2667
- throw new QueryError("Query too complex", {
3181
+ throw new QueryError({
3182
+ message: "Query too complex",
2668
3183
  context: {
2669
3184
  query: context.originalQuery
2670
3185
  }
2671
3186
  });
2672
3187
  case "in":
2673
- throw new QueryError("Query too complex", {
3188
+ throw new QueryError({
3189
+ message: "Query too complex",
2674
3190
  context: {
2675
3191
  query: context.originalQuery
2676
3192
  }
2677
3193
  });
2678
3194
  case "range":
2679
- throw new QueryError("Query too complex", {
3195
+ throw new QueryError({
3196
+ message: "Query too complex",
2680
3197
  context: {
2681
3198
  query: context.originalQuery
2682
3199
  }
2683
3200
  });
3201
+ // Boolean
2684
3202
  case "not":
2685
3203
  return this._generateSelectionFromFilter(filter.filter, {
2686
3204
  ...context,
2687
3205
  selectionInverted: !context.selectionInverted
2688
3206
  });
2689
3207
  case "and":
2690
- throw new QueryError("Query too complex", {
3208
+ throw new QueryError({
3209
+ message: "Query too complex",
2691
3210
  context: {
2692
3211
  query: context.originalQuery
2693
3212
  }
2694
3213
  });
2695
3214
  case "or":
2696
3215
  if (filter.filters.every(isTrivialTypenameFilter)) {
2697
- const typenames = filter.filters.map((f) => {
2698
- invariant9(f.type === "object" && f.typename !== null, void 0, {
3216
+ const typenames = filter.filters.map((filter2) => {
3217
+ invariant9(filter2.type === "object" && filter2.typename !== null, void 0, {
2699
3218
  F: __dxlog_file10,
2700
- L: 191,
3219
+ L: 224,
2701
3220
  S: this,
2702
3221
  A: [
2703
- "f.type === 'object' && f.typename !== null",
3222
+ "filter.type === 'object' && filter.typename !== null",
2704
3223
  ""
2705
3224
  ]
2706
3225
  });
2707
- return f.typename;
3226
+ return filter2.typename;
2708
3227
  });
2709
3228
  return QueryPlan.Plan.make([
2710
3229
  {
@@ -2719,14 +3238,16 @@ var QueryPlanner = class {
2719
3238
  ...this._generateDeletedHandlingSteps(context)
2720
3239
  ]);
2721
3240
  } else {
2722
- throw new QueryError("Query too complex", {
3241
+ throw new QueryError({
3242
+ message: "Query too complex",
2723
3243
  context: {
2724
3244
  query: context.originalQuery
2725
3245
  }
2726
3246
  });
2727
3247
  }
2728
3248
  default:
2729
- throw new QueryError(`Unsupported filter type: ${filter.type}`, {
3249
+ throw new QueryError({
3250
+ message: `Unsupported filter type: ${filter.type}`,
2730
3251
  context: {
2731
3252
  query: context.originalQuery
2732
3253
  }
@@ -2926,6 +3447,52 @@ var QueryPlanner = class {
2926
3447
  _optimizeSoloUnions(plan) {
2927
3448
  return plan;
2928
3449
  }
3450
+ _generateOrderClause(query, context) {
3451
+ return QueryPlan.Plan.make([
3452
+ ...this._generate(query.query, context).steps,
3453
+ {
3454
+ _tag: "OrderStep",
3455
+ order: query.order
3456
+ }
3457
+ ]);
3458
+ }
3459
+ // After complete plan is built, inspect it from the end:
3460
+ // - Walk backwards until hitting an object set changer.
3461
+ // - If an order step is found, skip.
3462
+ // - Otherwise append natural order to the end.
3463
+ _ensureOrderStep(plan) {
3464
+ const OBJECT_SET_CHANGERS = /* @__PURE__ */ new Set([
3465
+ "SelectStep",
3466
+ "TraverseStep",
3467
+ "UnionStep",
3468
+ "SetDifferenceStep"
3469
+ ]);
3470
+ for (let i = plan.steps.length - 1; i >= 0; i--) {
3471
+ const step = plan.steps[i];
3472
+ if (step._tag === "OrderStep") {
3473
+ return plan;
3474
+ }
3475
+ if (OBJECT_SET_CHANGERS.has(step._tag)) {
3476
+ break;
3477
+ }
3478
+ }
3479
+ return QueryPlan.Plan.make([
3480
+ ...plan.steps,
3481
+ {
3482
+ _tag: "OrderStep",
3483
+ order: [
3484
+ Order.natural.ast
3485
+ ]
3486
+ }
3487
+ ]);
3488
+ }
3489
+ constructor(options) {
3490
+ _define_property11(this, "_options", void 0);
3491
+ this._options = {
3492
+ ...DEFAULT_OPTIONS,
3493
+ ...options
3494
+ };
3495
+ }
2929
3496
  };
2930
3497
  var DEFAULT_CONTEXT = {
2931
3498
  originalQuery: null,
@@ -2951,7 +3518,21 @@ var isTrivialTypenameFilter = (filter) => {
2951
3518
  };
2952
3519
 
2953
3520
  // src/query/query-executor.ts
3521
+ function _define_property12(obj, key, value) {
3522
+ if (key in obj) {
3523
+ Object.defineProperty(obj, key, {
3524
+ value,
3525
+ enumerable: true,
3526
+ configurable: true,
3527
+ writable: true
3528
+ });
3529
+ } else {
3530
+ obj[key] = value;
3531
+ }
3532
+ return obj;
3533
+ }
2954
3534
  var __dxlog_file11 = "/__w/dxos/dxos/packages/core/echo/echo-pipeline/src/query/query-executor.ts";
3535
+ var isNullable = Predicate.isNullable;
2955
3536
  var ExecutionTrace = Object.freeze({
2956
3537
  makeEmpty: () => ({
2957
3538
  name: "Empty",
@@ -2978,19 +3559,6 @@ var ExecutionTrace = Object.freeze({
2978
3559
  });
2979
3560
  var TRACE_QUERY_EXECUTION = false;
2980
3561
  var QueryExecutor = class extends Resource6 {
2981
- constructor(options) {
2982
- super();
2983
- this._trace = ExecutionTrace.makeEmpty();
2984
- this._lastResultSet = [];
2985
- this._indexer = options.indexer;
2986
- this._automergeHost = options.automergeHost;
2987
- this._spaceStateManager = options.spaceStateManager;
2988
- this._id = options.queryId;
2989
- this._query = options.query;
2990
- this._reactivity = options.reactivity;
2991
- const queryPlanner = new QueryPlanner();
2992
- this._plan = queryPlanner.createPlan(this._query);
2993
- }
2994
3562
  get query() {
2995
3563
  return this._query;
2996
3564
  }
@@ -3016,7 +3584,7 @@ var QueryExecutor = class extends Resource6 {
3016
3584
  async execQuery() {
3017
3585
  invariant10(this._lifecycleState === LifecycleState3.OPEN, void 0, {
3018
3586
  F: __dxlog_file11,
3019
- L: 173,
3587
+ L: 176,
3020
3588
  S: this,
3021
3589
  A: [
3022
3590
  "this._lifecycleState === LifecycleState.OPEN",
@@ -3089,6 +3657,9 @@ var QueryExecutor = class extends Resource6 {
3089
3657
  case "TraverseStep":
3090
3658
  ({ workingSet: newWorkingSet, trace: trace6 } = await this._execTraverseStep(step, workingSet));
3091
3659
  break;
3660
+ case "OrderStep":
3661
+ ({ workingSet: newWorkingSet, trace: trace6 } = await this._execOrderStep(step, workingSet));
3662
+ break;
3092
3663
  default:
3093
3664
  throw new Error(`Unknown step type: ${step._tag}`);
3094
3665
  }
@@ -3126,7 +3697,7 @@ var QueryExecutor = class extends Resource6 {
3126
3697
  const results = await this._loadDocumentsAfterIndexQuery(indexHits);
3127
3698
  trace6.documentsLoaded += results.length;
3128
3699
  trace6.documentLoadTime += performance.now() - documentLoadStart;
3129
- workingSet.push(...results.filter(isNonNullable2).filter((item) => step.spaces.includes(item.spaceId)));
3700
+ workingSet.push(...results.filter(isNonNullable3).filter((item) => step.spaces.includes(item.spaceId)));
3130
3701
  trace6.objectCount = workingSet.length;
3131
3702
  break;
3132
3703
  }
@@ -3136,7 +3707,7 @@ var QueryExecutor = class extends Resource6 {
3136
3707
  sourceSpaceId: step.spaces[0]
3137
3708
  })));
3138
3709
  trace6.documentLoadTime += performance.now() - beginLoad;
3139
- workingSet.push(...items.filter(isNonNullable2));
3710
+ workingSet.push(...items.filter(isNonNullable3));
3140
3711
  trace6.objectCount = workingSet.length;
3141
3712
  break;
3142
3713
  }
@@ -3158,7 +3729,7 @@ var QueryExecutor = class extends Resource6 {
3158
3729
  const results = await this._loadDocumentsAfterIndexQuery(indexHits);
3159
3730
  trace6.documentsLoaded += results.length;
3160
3731
  trace6.documentLoadTime += performance.now() - documentLoadStart;
3161
- workingSet.push(...results.filter(isNonNullable2).filter((item) => step.spaces.includes(item.spaceId)));
3732
+ workingSet.push(...results.filter(isNonNullable3).filter((item) => step.spaces.includes(item.spaceId)));
3162
3733
  trace6.objectCount = workingSet.length;
3163
3734
  break;
3164
3735
  }
@@ -3183,7 +3754,7 @@ var QueryExecutor = class extends Resource6 {
3183
3754
  const results = await this._loadDocumentsAfterIndexQuery(indexHits);
3184
3755
  trace6.documentsLoaded += results.length;
3185
3756
  trace6.documentLoadTime += performance.now() - documentLoadStart;
3186
- workingSet.push(...results.filter(isNonNullable2).filter((item) => step.spaces.includes(item.spaceId)));
3757
+ workingSet.push(...results.filter(isNonNullable3).filter((item) => step.spaces.includes(item.spaceId)));
3187
3758
  trace6.objectCount = workingSet.length;
3188
3759
  break;
3189
3760
  }
@@ -3212,17 +3783,6 @@ var QueryExecutor = class extends Resource6 {
3212
3783
  };
3213
3784
  }
3214
3785
  async _execFilterDeletedStep(step, workingSet) {
3215
- if (workingSet.length === 6) {
3216
- log9.info("FilterDeletedStep", {
3217
- step,
3218
- workingSet
3219
- }, {
3220
- F: __dxlog_file11,
3221
- L: 386,
3222
- S: this,
3223
- C: (f, a) => f(...a)
3224
- });
3225
- }
3226
3786
  const expected = step.mode === "only-deleted";
3227
3787
  const result = workingSet.filter((item) => ObjectStructure.isDeleted(item.doc) === expected);
3228
3788
  return {
@@ -3260,24 +3820,24 @@ var QueryExecutor = class extends Resource6 {
3260
3820
  spaceId: item.spaceId
3261
3821
  } : null;
3262
3822
  } catch {
3263
- log9.warn("Invalid reference", {
3823
+ log9.warn("invalid reference", {
3264
3824
  ref: ref2["/"]
3265
3825
  }, {
3266
3826
  F: __dxlog_file11,
3267
- L: 431,
3827
+ L: 437,
3268
3828
  S: this,
3269
3829
  C: (f, a) => f(...a)
3270
3830
  });
3271
3831
  return null;
3272
3832
  }
3273
3833
  });
3274
- }).filter(isNonNullable2);
3834
+ }).filter(isNonNullable3);
3275
3835
  const beginLoad = performance.now();
3276
3836
  const items = await Promise.all(refs.map(({ ref, spaceId }) => this._loadFromDXN(ref, {
3277
3837
  sourceSpaceId: spaceId
3278
3838
  })));
3279
3839
  trace6.documentLoadTime += performance.now() - beginLoad;
3280
- newWorkingSet.push(...items.filter(isNonNullable2));
3840
+ newWorkingSet.push(...items.filter(isNonNullable3));
3281
3841
  trace6.objectCount = newWorkingSet.length;
3282
3842
  break;
3283
3843
  }
@@ -3296,7 +3856,7 @@ var QueryExecutor = class extends Resource6 {
3296
3856
  const results = await this._loadDocumentsAfterIndexQuery(indexHits);
3297
3857
  trace6.documentsLoaded += results.length;
3298
3858
  trace6.documentLoadTime += performance.now() - documentLoadStart;
3299
- newWorkingSet.push(...results.filter(isNonNullable2));
3859
+ newWorkingSet.push(...results.filter(isNonNullable3));
3300
3860
  trace6.objectCount = newWorkingSet.length;
3301
3861
  break;
3302
3862
  }
@@ -3318,23 +3878,23 @@ var QueryExecutor = class extends Resource6 {
3318
3878
  spaceId: item.spaceId
3319
3879
  };
3320
3880
  } catch {
3321
- log9.warn("Invalid reference", {
3881
+ log9.warn("invalid reference", {
3322
3882
  ref: ref["/"]
3323
3883
  }, {
3324
3884
  F: __dxlog_file11,
3325
- L: 494,
3885
+ L: 500,
3326
3886
  S: this,
3327
3887
  C: (f, a) => f(...a)
3328
3888
  });
3329
3889
  return null;
3330
3890
  }
3331
- }).filter(isNonNullable2);
3891
+ }).filter(isNonNullable3);
3332
3892
  const beginLoad = performance.now();
3333
3893
  const items = await Promise.all(refs.map(({ ref, spaceId }) => this._loadFromDXN(ref, {
3334
3894
  sourceSpaceId: spaceId
3335
3895
  })));
3336
3896
  trace6.documentLoadTime += performance.now() - beginLoad;
3337
- newWorkingSet.push(...items.filter(isNonNullable2));
3897
+ newWorkingSet.push(...items.filter(isNonNullable3));
3338
3898
  trace6.objectCount = newWorkingSet.length;
3339
3899
  break;
3340
3900
  }
@@ -3354,7 +3914,7 @@ var QueryExecutor = class extends Resource6 {
3354
3914
  const results = await this._loadDocumentsAfterIndexQuery(indexHits);
3355
3915
  trace6.documentsLoaded += results.length;
3356
3916
  trace6.documentLoadTime += performance.now() - documentLoadStart;
3357
- newWorkingSet.push(...results.filter(isNonNullable2));
3917
+ newWorkingSet.push(...results.filter(isNonNullable3));
3358
3918
  trace6.objectCount = newWorkingSet.length;
3359
3919
  break;
3360
3920
  }
@@ -3411,6 +3971,74 @@ var QueryExecutor = class extends Resource6 {
3411
3971
  trace: trace6
3412
3972
  };
3413
3973
  }
3974
+ async _execOrderStep(step, workingSet) {
3975
+ const compareItems = (a, b) => step.order.reduce((comparison, order) => {
3976
+ if (comparison !== 0) {
3977
+ return comparison;
3978
+ }
3979
+ return this._compareByOrder(a, b, order);
3980
+ }, 0);
3981
+ const sortedWorkingSet = [
3982
+ ...workingSet
3983
+ ].sort(compareItems);
3984
+ return {
3985
+ workingSet: sortedWorkingSet,
3986
+ trace: {
3987
+ ...ExecutionTrace.makeEmpty(),
3988
+ name: "Order",
3989
+ details: JSON.stringify(step.order),
3990
+ objectCount: sortedWorkingSet.length
3991
+ }
3992
+ };
3993
+ }
3994
+ _compareByOrder(a, b, order) {
3995
+ return Match.type().pipe(Match.withReturnType(), Match.when({
3996
+ kind: "natural"
3997
+ }, () => a.objectId.localeCompare(b.objectId)), Match.when({
3998
+ kind: "property"
3999
+ }, ({ property, direction }) => {
4000
+ const comparison = this._compareByProperty(a, b, property);
4001
+ return direction === "desc" ? -comparison : comparison;
4002
+ }), Match.exhaustive)(order);
4003
+ }
4004
+ _compareByProperty(a, b, property) {
4005
+ const aValue = a.doc.data[property];
4006
+ const bValue = b.doc.data[property];
4007
+ return Match.type().pipe(
4008
+ Match.withReturnType(),
4009
+ Match.when({
4010
+ a: isNullable,
4011
+ b: isNullable
4012
+ }, () => 0),
4013
+ Match.when({
4014
+ a: isNullable
4015
+ }, () => 1),
4016
+ Match.when({
4017
+ b: isNullable
4018
+ }, () => -1),
4019
+ Match.when({
4020
+ a: Match.string,
4021
+ b: Match.string
4022
+ }, ({ a: a2, b: b2 }) => a2.localeCompare(b2)),
4023
+ Match.when({
4024
+ a: Match.number,
4025
+ b: Match.number
4026
+ }, ({ a: a2, b: b2 }) => a2 - b2),
4027
+ Match.when({
4028
+ a: Match.boolean,
4029
+ b: Match.boolean
4030
+ }, ({ a: a2, b: b2 }) => a2 === b2 ? 0 : a2 ? 1 : -1),
4031
+ Match.when({
4032
+ a: Match.defined,
4033
+ b: Match.defined
4034
+ }, ({ a: a2, b: b2 }) => String(a2).localeCompare(String(b2))),
4035
+ // TODO(wittjosiah): Why does Match.exhaustive fail here?
4036
+ Match.orElse(() => 0)
4037
+ )({
4038
+ a: aValue,
4039
+ b: bValue
4040
+ });
4041
+ }
3414
4042
  async _loadDocumentsAfterIndexQuery(indexHits) {
3415
4043
  return Promise.all(indexHits.map(async (hit) => {
3416
4044
  return this._loadFromIndexHit(hit);
@@ -3420,7 +4048,7 @@ var QueryExecutor = class extends Resource6 {
3420
4048
  const { objectId, documentId, spaceKey: spaceKeyInIndex } = objectPointerCodec3.decode(hit.id);
3421
4049
  const handle = await this._automergeHost.loadDoc(Context3.default(void 0, {
3422
4050
  F: __dxlog_file11,
3423
- L: 604
4051
+ L: 664
3424
4052
  }), documentId);
3425
4053
  const doc = handle.doc();
3426
4054
  if (!doc) {
@@ -3448,7 +4076,7 @@ var QueryExecutor = class extends Resource6 {
3448
4076
  dxn
3449
4077
  }, {
3450
4078
  F: __dxlog_file11,
3451
- L: 631,
4079
+ L: 691,
3452
4080
  S: this,
3453
4081
  C: (f, a) => f(...a)
3454
4082
  });
@@ -3461,7 +4089,7 @@ var QueryExecutor = class extends Resource6 {
3461
4089
  spaceId
3462
4090
  }, {
3463
4091
  F: __dxlog_file11,
3464
- L: 639,
4092
+ L: 699,
3465
4093
  S: this,
3466
4094
  C: (f, a) => f(...a)
3467
4095
  });
@@ -3473,7 +4101,7 @@ var QueryExecutor = class extends Resource6 {
3473
4101
  spaceId
3474
4102
  }, {
3475
4103
  F: __dxlog_file11,
3476
- L: 644,
4104
+ L: 704,
3477
4105
  S: this,
3478
4106
  C: (f, a) => f(...a)
3479
4107
  });
@@ -3494,7 +4122,7 @@ var QueryExecutor = class extends Resource6 {
3494
4122
  }
3495
4123
  const handle = await this._automergeHost.loadDoc(Context3.default(void 0, {
3496
4124
  F: __dxlog_file11,
3497
- L: 663
4125
+ L: 723
3498
4126
  }), link);
3499
4127
  const doc = handle.doc();
3500
4128
  if (!doc) {
@@ -3511,9 +4139,37 @@ var QueryExecutor = class extends Resource6 {
3511
4139
  doc: object
3512
4140
  };
3513
4141
  }
4142
+ constructor(options) {
4143
+ super(), _define_property12(this, "_indexer", void 0), _define_property12(this, "_automergeHost", void 0), _define_property12(this, "_spaceStateManager", void 0), /**
4144
+ * Id of this query.
4145
+ */
4146
+ _define_property12(this, "_id", void 0), _define_property12(this, "_query", void 0), // TODO(dmaretskyi): Might be used in the future.
4147
+ _define_property12(this, "_reactivity", void 0), _define_property12(this, "_plan", void 0), _define_property12(this, "_trace", ExecutionTrace.makeEmpty()), _define_property12(this, "_lastResultSet", []);
4148
+ this._indexer = options.indexer;
4149
+ this._automergeHost = options.automergeHost;
4150
+ this._spaceStateManager = options.spaceStateManager;
4151
+ this._id = options.queryId;
4152
+ this._query = options.query;
4153
+ this._reactivity = options.reactivity;
4154
+ const queryPlanner = new QueryPlanner();
4155
+ this._plan = queryPlanner.createPlan(this._query);
4156
+ }
3514
4157
  };
3515
4158
 
3516
4159
  // src/db-host/query-service.ts
4160
+ function _define_property13(obj, key, value) {
4161
+ if (key in obj) {
4162
+ Object.defineProperty(obj, key, {
4163
+ value,
4164
+ enumerable: true,
4165
+ configurable: true,
4166
+ writable: true
4167
+ });
4168
+ } else {
4169
+ obj[key] = value;
4170
+ }
4171
+ return obj;
4172
+ }
3517
4173
  function _ts_decorate5(decorators, target, key, desc) {
3518
4174
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3519
4175
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
@@ -3522,26 +4178,9 @@ function _ts_decorate5(decorators, target, key, desc) {
3522
4178
  }
3523
4179
  var __dxlog_file12 = "/__w/dxos/dxos/packages/core/echo/echo-pipeline/src/db-host/query-service.ts";
3524
4180
  var QueryServiceImpl = class extends Resource7 {
3525
- // TODO(burdon): OK for options, but not params. Pass separately and type readonly here.
3526
- constructor(_params) {
3527
- super(), this._params = _params, this._queries = /* @__PURE__ */ new Set();
3528
- trace4.diagnostic({
3529
- id: "active-queries",
3530
- name: "Active Queries",
3531
- fetch: () => {
3532
- return Array.from(this._queries).map((query) => {
3533
- return {
3534
- query: JSON.stringify(query.executor.query),
3535
- plan: JSON.stringify(query.executor.plan),
3536
- trace: JSON.stringify(query.executor.trace)
3537
- };
3538
- });
3539
- }
3540
- });
3541
- }
3542
4181
  async _open() {
3543
4182
  this._params.indexer.updated.on(this._ctx, () => this.invalidateQueries());
3544
- this._updateQueries = new DeferredTask(this._ctx, this._executeQueries.bind(this));
4183
+ this._updateQueries = new DeferredTask2(this._ctx, this._executeQueries.bind(this));
3545
4184
  }
3546
4185
  async _close() {
3547
4186
  await this._updateQueries.join();
@@ -3552,6 +4191,18 @@ var QueryServiceImpl = class extends Resource7 {
3552
4191
  }
3553
4192
  execQuery(request) {
3554
4193
  return new Stream2(({ next, close, ctx }) => {
4194
+ if (this._params.indexer.config?.enabled !== true) {
4195
+ log10.error("indexer is disabled", {
4196
+ config: this._params.indexer.config
4197
+ }, {
4198
+ F: __dxlog_file12,
4199
+ L: 101,
4200
+ S: this,
4201
+ C: (f, a) => f(...a)
4202
+ });
4203
+ close();
4204
+ return;
4205
+ }
3555
4206
  const queryEntry = this._createQuery(ctx, request, next, close, close);
3556
4207
  scheduleMicroTask(ctx, async () => {
3557
4208
  await queryEntry.executor.open();
@@ -3567,7 +4218,7 @@ var QueryServiceImpl = class extends Resource7 {
3567
4218
  async reindex() {
3568
4219
  log10("Reindexing all documents...", void 0, {
3569
4220
  F: __dxlog_file12,
3570
- L: 113,
4221
+ L: 120,
3571
4222
  S: this,
3572
4223
  C: (f, a) => f(...a)
3573
4224
  });
@@ -3582,7 +4233,7 @@ var QueryServiceImpl = class extends Resource7 {
3582
4233
  count: ids.size
3583
4234
  }, {
3584
4235
  F: __dxlog_file12,
3585
- L: 121,
4236
+ L: 128,
3586
4237
  S: this,
3587
4238
  C: (f, a) => f(...a)
3588
4239
  });
@@ -3592,7 +4243,7 @@ var QueryServiceImpl = class extends Resource7 {
3592
4243
  count: ids.size
3593
4244
  }, {
3594
4245
  F: __dxlog_file12,
3595
- L: 125,
4246
+ L: 132,
3596
4247
  S: this,
3597
4248
  C: (f, a) => f(...a)
3598
4249
  });
@@ -3658,7 +4309,7 @@ var QueryServiceImpl = class extends Resource7 {
3658
4309
  } catch (err) {
3659
4310
  log10.catch(err, void 0, {
3660
4311
  F: __dxlog_file12,
3661
- L: 196,
4312
+ L: 203,
3662
4313
  S: this,
3663
4314
  C: (f, a) => f(...a)
3664
4315
  });
@@ -3669,11 +4320,28 @@ var QueryServiceImpl = class extends Resource7 {
3669
4320
  duration: performance.now() - begin
3670
4321
  }, {
3671
4322
  F: __dxlog_file12,
3672
- L: 200,
4323
+ L: 207,
3673
4324
  S: this,
3674
4325
  C: (f, a) => f(...a)
3675
4326
  });
3676
4327
  }
4328
+ // TODO(burdon): OK for options, but not params. Pass separately and type readonly here.
4329
+ constructor(_params) {
4330
+ super(), _define_property13(this, "_params", void 0), _define_property13(this, "_queries", void 0), _define_property13(this, "_updateQueries", void 0), this._params = _params, this._queries = /* @__PURE__ */ new Set();
4331
+ trace4.diagnostic({
4332
+ id: "active-queries",
4333
+ name: "Active Queries",
4334
+ fetch: () => {
4335
+ return Array.from(this._queries).map((query) => {
4336
+ return {
4337
+ query: JSON.stringify(query.executor.query),
4338
+ plan: JSON.stringify(query.executor.plan),
4339
+ trace: JSON.stringify(query.executor.trace)
4340
+ };
4341
+ });
4342
+ }
4343
+ });
4344
+ }
3677
4345
  };
3678
4346
  _ts_decorate5([
3679
4347
  synchronized2
@@ -3720,7 +4388,7 @@ var createDocumentsIterator = (automergeHost) => (
3720
4388
  }
3721
4389
  const linkHandle = await automergeHost.loadDoc(Context4.default(void 0, {
3722
4390
  F: __dxlog_file12,
3723
- L: 240
4391
+ L: 247
3724
4392
  }), urlString);
3725
4393
  for await (const result of getObjectsFromHandle(linkHandle)) {
3726
4394
  yield result;
@@ -3745,7 +4413,7 @@ var createDocumentsIterator = (automergeHost) => (
3745
4413
  import { interpretAsDocumentId as interpretAsDocumentId3 } from "@automerge/automerge-repo";
3746
4414
  import isEqual from "lodash.isequal";
3747
4415
  import { Event as Event3, UpdateScheduler as UpdateScheduler3 } from "@dxos/async";
3748
- import { Resource as Resource8, Context as Context5, LifecycleState as LifecycleState4 } from "@dxos/context";
4416
+ import { Context as Context5, LifecycleState as LifecycleState4, Resource as Resource8 } from "@dxos/context";
3749
4417
  import { invariant as invariant12 } from "@dxos/invariant";
3750
4418
 
3751
4419
  // src/db-host/database-root.ts
@@ -3784,6 +4452,19 @@ var measureDocMetrics = (doc) => {
3784
4452
  };
3785
4453
 
3786
4454
  // src/db-host/database-root.ts
4455
+ function _define_property14(obj, key, value) {
4456
+ if (key in obj) {
4457
+ Object.defineProperty(obj, key, {
4458
+ value,
4459
+ enumerable: true,
4460
+ configurable: true,
4461
+ writable: true
4462
+ });
4463
+ } else {
4464
+ obj[key] = value;
4465
+ }
4466
+ return obj;
4467
+ }
3787
4468
  var __dxlog_file14 = "/__w/dxos/dxos/packages/core/echo/echo-pipeline/src/db-host/database-root.ts";
3788
4469
  var DatabaseRoot = class {
3789
4470
  static mapLinks(doc, mapping) {
@@ -3799,9 +4480,6 @@ var DatabaseRoot = class {
3799
4480
  }
3800
4481
  });
3801
4482
  }
3802
- constructor(_rootHandle) {
3803
- this._rootHandle = _rootHandle;
3804
- }
3805
4483
  get documentId() {
3806
4484
  return this._rootHandle.documentId;
3807
4485
  }
@@ -3865,19 +4543,28 @@ var DatabaseRoot = class {
3865
4543
  }
3866
4544
  return measureDocMetrics(doc);
3867
4545
  }
4546
+ constructor(_rootHandle) {
4547
+ _define_property14(this, "_rootHandle", void 0);
4548
+ this._rootHandle = _rootHandle;
4549
+ }
3868
4550
  };
3869
4551
 
3870
4552
  // src/db-host/space-state-manager.ts
4553
+ function _define_property15(obj, key, value) {
4554
+ if (key in obj) {
4555
+ Object.defineProperty(obj, key, {
4556
+ value,
4557
+ enumerable: true,
4558
+ configurable: true,
4559
+ writable: true
4560
+ });
4561
+ } else {
4562
+ obj[key] = value;
4563
+ }
4564
+ return obj;
4565
+ }
3871
4566
  var __dxlog_file15 = "/__w/dxos/dxos/packages/core/echo/echo-pipeline/src/db-host/space-state-manager.ts";
3872
4567
  var SpaceStateManager = class extends Resource8 {
3873
- constructor() {
3874
- super(...arguments);
3875
- this._roots = /* @__PURE__ */ new Map();
3876
- this._rootBySpace = /* @__PURE__ */ new Map();
3877
- this._perRootContext = /* @__PURE__ */ new Map();
3878
- this._lastSpaceDocumentList = /* @__PURE__ */ new Map();
3879
- this.spaceDocumentListUpdated = new Event3();
3880
- }
3881
4568
  async _close(ctx) {
3882
4569
  for (const [_, rootCtx] of this._perRootContext) {
3883
4570
  await rootCtx.dispose();
@@ -3950,9 +4637,16 @@ var SpaceStateManager = class extends Resource8 {
3950
4637
  documentListCheckScheduler.trigger();
3951
4638
  return root;
3952
4639
  }
4640
+ constructor(...args) {
4641
+ super(...args), _define_property15(this, "_roots", /* @__PURE__ */ new Map()), _define_property15(this, "_rootBySpace", /* @__PURE__ */ new Map()), _define_property15(this, "_perRootContext", /* @__PURE__ */ new Map()), _define_property15(this, "_lastSpaceDocumentList", /* @__PURE__ */ new Map()), _define_property15(this, "spaceDocumentListUpdated", new Event3());
4642
+ }
3953
4643
  };
3954
4644
  var SpaceDocumentListUpdatedEvent = class {
3955
4645
  constructor(spaceId, spaceRootId, previousRootId, documentIds) {
4646
+ _define_property15(this, "spaceId", void 0);
4647
+ _define_property15(this, "spaceRootId", void 0);
4648
+ _define_property15(this, "previousRootId", void 0);
4649
+ _define_property15(this, "documentIds", void 0);
3956
4650
  this.spaceId = spaceId;
3957
4651
  this.spaceRootId = spaceRootId;
3958
4652
  this.previousRootId = previousRootId;
@@ -3961,6 +4655,19 @@ var SpaceDocumentListUpdatedEvent = class {
3961
4655
  };
3962
4656
 
3963
4657
  // src/db-host/echo-host.ts
4658
+ function _define_property16(obj, key, value) {
4659
+ if (key in obj) {
4660
+ Object.defineProperty(obj, key, {
4661
+ value,
4662
+ enumerable: true,
4663
+ configurable: true,
4664
+ writable: true
4665
+ });
4666
+ } else {
4667
+ obj[key] = value;
4668
+ }
4669
+ return obj;
4670
+ }
3964
4671
  var __dxlog_file16 = "/__w/dxos/dxos/packages/core/echo/echo-pipeline/src/db-host/echo-host.ts";
3965
4672
  var DEFAULT_INDEXING_CONFIG = {
3966
4673
  // TODO(dmaretskyi): Disabled by default since embedding generation is expensive.
@@ -3968,105 +4675,6 @@ var DEFAULT_INDEXING_CONFIG = {
3968
4675
  vector: false
3969
4676
  };
3970
4677
  var EchoHost = class extends Resource9 {
3971
- constructor({ kv, indexing = {}, peerIdProvider, getSpaceKeyByRootDocumentId }) {
3972
- super();
3973
- this._spaceStateManager = new SpaceStateManager();
3974
- const indexingConfig = {
3975
- ...DEFAULT_INDEXING_CONFIG,
3976
- ...indexing
3977
- };
3978
- this._indexMetadataStore = new IndexMetadataStore({
3979
- db: kv.sublevel("index-metadata")
3980
- });
3981
- this._echoDataMonitor = new EchoDataMonitor();
3982
- this._automergeHost = new AutomergeHost({
3983
- db: kv,
3984
- dataMonitor: this._echoDataMonitor,
3985
- indexMetadataStore: this._indexMetadataStore,
3986
- peerIdProvider,
3987
- getSpaceKeyByRootDocumentId
3988
- });
3989
- this._indexer = new Indexer({
3990
- db: kv,
3991
- indexStore: new IndexStore({
3992
- db: kv.sublevel("index-storage")
3993
- }),
3994
- metadataStore: this._indexMetadataStore,
3995
- loadDocuments: createSelectedDocumentsIterator(this._automergeHost),
3996
- indexCooldownTime: false ? 0 : void 0
3997
- });
3998
- void this._indexer.setConfig({
3999
- enabled: true,
4000
- indexes: [
4001
- //
4002
- {
4003
- kind: IndexKind.Kind.SCHEMA_MATCH
4004
- },
4005
- {
4006
- kind: IndexKind.Kind.GRAPH
4007
- },
4008
- ...indexingConfig.fullText ? [
4009
- {
4010
- kind: IndexKind.Kind.FULL_TEXT
4011
- }
4012
- ] : [],
4013
- ...indexingConfig.vector ? [
4014
- {
4015
- kind: IndexKind.Kind.VECTOR
4016
- }
4017
- ] : []
4018
- ]
4019
- });
4020
- this._queryService = new QueryServiceImpl({
4021
- automergeHost: this._automergeHost,
4022
- indexer: this._indexer,
4023
- spaceStateManager: this._spaceStateManager
4024
- });
4025
- this._dataService = new DataServiceImpl({
4026
- automergeHost: this._automergeHost,
4027
- spaceStateManager: this._spaceStateManager,
4028
- updateIndexes: async () => {
4029
- await this._indexer.updateIndexes();
4030
- }
4031
- });
4032
- trace5.diagnostic({
4033
- id: "echo-stats",
4034
- name: "Echo Stats",
4035
- fetch: async () => {
4036
- return {
4037
- dataStats: this._echoDataMonitor.computeStats(),
4038
- loadedDocsCount: this._automergeHost.loadedDocsCount
4039
- };
4040
- }
4041
- });
4042
- trace5.diagnostic({
4043
- id: "database-roots",
4044
- name: "Database Roots",
4045
- fetch: async () => {
4046
- return Array.from(this._spaceStateManager.roots.values()).map((root) => ({
4047
- url: root.url,
4048
- isLoaded: root.isLoaded,
4049
- spaceKey: root.getSpaceKey(),
4050
- inlineObjects: root.getInlineObjectCount(),
4051
- linkedObjects: root.getLinkedObjectCount()
4052
- }));
4053
- }
4054
- });
4055
- trace5.diagnostic({
4056
- id: "database-root-metrics",
4057
- name: "Database Roots (with metrics)",
4058
- fetch: async () => {
4059
- return Array.from(this._spaceStateManager.roots.values()).map((root) => ({
4060
- url: root.url,
4061
- isLoaded: root.isLoaded,
4062
- spaceKey: root.getSpaceKey(),
4063
- inlineObjects: root.getInlineObjectCount(),
4064
- linkedObjects: root.getLinkedObjectCount(),
4065
- ...root.measureMetrics() ?? {}
4066
- }));
4067
- }
4068
- });
4069
- }
4070
4678
  get queryService() {
4071
4679
  return this._queryService;
4072
4680
  }
@@ -4091,7 +4699,6 @@ var EchoHost = class extends Resource9 {
4091
4699
  if (e.previousRootId) {
4092
4700
  void this._automergeHost.clearLocalCollectionState(deriveCollectionIdFromSpaceId(e.spaceId, e.previousRootId));
4093
4701
  }
4094
- void this._automergeHost.updateLocalCollectionState(deriveCollectionIdFromSpaceId(e.spaceId), e.documentIds);
4095
4702
  void this._automergeHost.updateLocalCollectionState(deriveCollectionIdFromSpaceId(e.spaceId, e.spaceRootId), e.documentIds);
4096
4703
  });
4097
4704
  this._automergeHost.documentsSaved.on(this._ctx, () => {
@@ -4108,7 +4715,7 @@ var EchoHost = class extends Resource9 {
4108
4715
  * Flush all pending writes to the underlying storage.
4109
4716
  */
4110
4717
  async flush() {
4111
- await this._automergeHost.repo.flush();
4718
+ await this._automergeHost.flush();
4112
4719
  }
4113
4720
  /**
4114
4721
  * Perform any pending index updates.
@@ -4137,7 +4744,7 @@ var EchoHost = class extends Resource9 {
4137
4744
  async createSpaceRoot(spaceKey) {
4138
4745
  invariant13(this._lifecycleState === LifecycleState5.OPEN, void 0, {
4139
4746
  F: __dxlog_file16,
4140
- L: 255,
4747
+ L: 254,
4141
4748
  S: this,
4142
4749
  A: [
4143
4750
  "this._lifecycleState === LifecycleState.OPEN",
@@ -4165,7 +4772,7 @@ var EchoHost = class extends Resource9 {
4165
4772
  async openSpaceRoot(spaceId, automergeUrl) {
4166
4773
  invariant13(this._lifecycleState === LifecycleState5.OPEN, void 0, {
4167
4774
  F: __dxlog_file16,
4168
- L: 274,
4775
+ L: 273,
4169
4776
  S: this,
4170
4777
  A: [
4171
4778
  "this._lifecycleState === LifecycleState.OPEN",
@@ -4192,16 +4799,114 @@ var EchoHost = class extends Resource9 {
4192
4799
  async removeReplicator(replicator) {
4193
4800
  await this._automergeHost.removeReplicator(replicator);
4194
4801
  }
4802
+ constructor({ kv, indexing = {}, peerIdProvider, getSpaceKeyByRootDocumentId }) {
4803
+ super(), _define_property16(this, "_indexMetadataStore", void 0), _define_property16(this, "_indexer", void 0), _define_property16(this, "_automergeHost", void 0), _define_property16(this, "_queryService", void 0), _define_property16(this, "_dataService", void 0), _define_property16(this, "_spaceStateManager", new SpaceStateManager()), _define_property16(this, "_echoDataMonitor", void 0);
4804
+ const indexingConfig = {
4805
+ ...DEFAULT_INDEXING_CONFIG,
4806
+ ...indexing
4807
+ };
4808
+ this._indexMetadataStore = new IndexMetadataStore({
4809
+ db: kv.sublevel("index-metadata")
4810
+ });
4811
+ this._echoDataMonitor = new EchoDataMonitor();
4812
+ this._automergeHost = new AutomergeHost({
4813
+ db: kv,
4814
+ dataMonitor: this._echoDataMonitor,
4815
+ indexMetadataStore: this._indexMetadataStore,
4816
+ peerIdProvider,
4817
+ getSpaceKeyByRootDocumentId
4818
+ });
4819
+ this._indexer = new Indexer({
4820
+ db: kv,
4821
+ indexStore: new IndexStore({
4822
+ db: kv.sublevel("index-storage")
4823
+ }),
4824
+ metadataStore: this._indexMetadataStore,
4825
+ loadDocuments: createSelectedDocumentsIterator(this._automergeHost),
4826
+ indexCooldownTime: false ? 0 : void 0
4827
+ });
4828
+ void this._indexer.setConfig({
4829
+ enabled: true,
4830
+ indexes: [
4831
+ //
4832
+ {
4833
+ kind: IndexKind.Kind.SCHEMA_MATCH
4834
+ },
4835
+ {
4836
+ kind: IndexKind.Kind.GRAPH
4837
+ },
4838
+ ...indexingConfig.fullText ? [
4839
+ {
4840
+ kind: IndexKind.Kind.FULL_TEXT
4841
+ }
4842
+ ] : [],
4843
+ ...indexingConfig.vector ? [
4844
+ {
4845
+ kind: IndexKind.Kind.VECTOR
4846
+ }
4847
+ ] : []
4848
+ ]
4849
+ });
4850
+ this._queryService = new QueryServiceImpl({
4851
+ automergeHost: this._automergeHost,
4852
+ indexer: this._indexer,
4853
+ spaceStateManager: this._spaceStateManager
4854
+ });
4855
+ this._dataService = new DataServiceImpl({
4856
+ automergeHost: this._automergeHost,
4857
+ spaceStateManager: this._spaceStateManager,
4858
+ updateIndexes: async () => {
4859
+ await this._indexer.updateIndexes();
4860
+ }
4861
+ });
4862
+ trace5.diagnostic({
4863
+ id: "echo-stats",
4864
+ name: "Echo Stats",
4865
+ fetch: async () => {
4866
+ return {
4867
+ dataStats: this._echoDataMonitor.computeStats(),
4868
+ loadedDocsCount: this._automergeHost.loadedDocsCount
4869
+ };
4870
+ }
4871
+ });
4872
+ trace5.diagnostic({
4873
+ id: "database-roots",
4874
+ name: "Database Roots",
4875
+ fetch: async () => {
4876
+ return Array.from(this._spaceStateManager.roots.values()).map((root) => ({
4877
+ url: root.url,
4878
+ isLoaded: root.isLoaded,
4879
+ spaceKey: root.getSpaceKey(),
4880
+ inlineObjects: root.getInlineObjectCount(),
4881
+ linkedObjects: root.getLinkedObjectCount()
4882
+ }));
4883
+ }
4884
+ });
4885
+ trace5.diagnostic({
4886
+ id: "database-root-metrics",
4887
+ name: "Database Roots (with metrics)",
4888
+ fetch: async () => {
4889
+ return Array.from(this._spaceStateManager.roots.values()).map((root) => ({
4890
+ url: root.url,
4891
+ isLoaded: root.isLoaded,
4892
+ spaceKey: root.getSpaceKey(),
4893
+ inlineObjects: root.getInlineObjectCount(),
4894
+ linkedObjects: root.getLinkedObjectCount(),
4895
+ ...root.measureMetrics() ?? {}
4896
+ }));
4897
+ }
4898
+ });
4899
+ }
4195
4900
  };
4196
4901
 
4197
4902
  // src/edge/echo-edge-replicator.ts
4198
4903
  import { cbor as cbor2 } from "@automerge/automerge-repo";
4199
- import { Mutex, scheduleTask as scheduleTask2, scheduleMicroTask as scheduleMicroTask2 } from "@dxos/async";
4904
+ import { Mutex, scheduleMicroTask as scheduleMicroTask2, scheduleTask as scheduleTask2 } from "@dxos/async";
4200
4905
  import { Context as Context6, Resource as Resource11 } from "@dxos/context";
4201
4906
  import { randomUUID } from "@dxos/crypto";
4202
4907
  import { invariant as invariant14 } from "@dxos/invariant";
4203
4908
  import { log as log13 } from "@dxos/log";
4204
- import { EdgeService } from "@dxos/protocols";
4909
+ import { DocumentCodec, EdgeService } from "@dxos/protocols";
4205
4910
  import { buf } from "@dxos/protocols/buf";
4206
4911
  import { MessageSchema as RouterMessageSchema } from "@dxos/protocols/buf/dxos/edge/messenger_pb";
4207
4912
  import { bufferToArray as bufferToArray2 } from "@dxos/util";
@@ -4210,11 +4915,21 @@ import { bufferToArray as bufferToArray2 } from "@dxos/util";
4210
4915
  import { Trigger as Trigger2 } from "@dxos/async";
4211
4916
  import { Resource as Resource10 } from "@dxos/context";
4212
4917
  import { log as log12 } from "@dxos/log";
4918
+ function _define_property17(obj, key, value) {
4919
+ if (key in obj) {
4920
+ Object.defineProperty(obj, key, {
4921
+ value,
4922
+ enumerable: true,
4923
+ configurable: true,
4924
+ writable: true
4925
+ });
4926
+ } else {
4927
+ obj[key] = value;
4928
+ }
4929
+ return obj;
4930
+ }
4213
4931
  var __dxlog_file17 = "/__w/dxos/dxos/packages/core/echo/echo-pipeline/src/edge/inflight-request-limiter.ts";
4214
4932
  var InflightRequestLimiter = class extends Resource10 {
4215
- constructor(_config) {
4216
- super(), this._config = _config, this._inflightRequestBalance = 0, this._requestBarrier = new Trigger2();
4217
- }
4218
4933
  async _open() {
4219
4934
  this._inflightRequestBalance = 0;
4220
4935
  this._requestBarrier.reset();
@@ -4257,9 +4972,30 @@ var InflightRequestLimiter = class extends Resource10 {
4257
4972
  clearInterval(this._resetBalanceTimeout);
4258
4973
  }
4259
4974
  }
4975
+ constructor(_config) {
4976
+ super(), _define_property17(this, "_config", void 0), /**
4977
+ * Decrement when we receive a sync message, increment when we send one.
4978
+ * Can't exceed _config.maxInflightRequests.
4979
+ * Resets after timeout to avoid replicator being stuck.
4980
+ */
4981
+ _define_property17(this, "_inflightRequestBalance", void 0), _define_property17(this, "_requestBarrier", void 0), _define_property17(this, "_resetBalanceTimeout", void 0), this._config = _config, this._inflightRequestBalance = 0, this._requestBarrier = new Trigger2();
4982
+ }
4260
4983
  };
4261
4984
 
4262
4985
  // src/edge/echo-edge-replicator.ts
4986
+ function _define_property18(obj, key, value) {
4987
+ if (key in obj) {
4988
+ Object.defineProperty(obj, key, {
4989
+ value,
4990
+ enumerable: true,
4991
+ configurable: true,
4992
+ writable: true
4993
+ });
4994
+ } else {
4995
+ obj[key] = value;
4996
+ }
4997
+ return obj;
4998
+ }
4263
4999
  function _ts_add_disposable_resource(env, value, async) {
4264
5000
  if (value !== null && value !== void 0) {
4265
5001
  if (typeof value !== "object" && typeof value !== "function") throw new TypeError("Object expected.");
@@ -4330,30 +5066,20 @@ var INITIAL_RESTART_DELAY = 500;
4330
5066
  var RESTART_DELAY_JITTER = 250;
4331
5067
  var MAX_RESTART_DELAY = 5e3;
4332
5068
  var EchoEdgeReplicator = class {
4333
- constructor({ edgeConnection, disableSharePolicy }) {
4334
- this._mutex = new Mutex();
4335
- this._ctx = void 0;
4336
- this._context = null;
4337
- this._connectedSpaces = /* @__PURE__ */ new Set();
4338
- this._connections = /* @__PURE__ */ new Map();
4339
- this._sharePolicyEnabled = true;
4340
- this._edgeConnection = edgeConnection;
4341
- this._sharePolicyEnabled = !disableSharePolicy;
4342
- }
4343
5069
  async connect(context) {
4344
5070
  log13("connecting...", {
4345
5071
  peerId: context.peerId,
4346
5072
  connectedSpaces: this._connectedSpaces.size
4347
5073
  }, {
4348
5074
  F: __dxlog_file18,
4349
- L: 61,
5075
+ L: 72,
4350
5076
  S: this,
4351
5077
  C: (f, a) => f(...a)
4352
5078
  });
4353
5079
  this._context = context;
4354
5080
  this._ctx = Context6.default(void 0, {
4355
5081
  F: __dxlog_file18,
4356
- L: 63
5082
+ L: 74
4357
5083
  });
4358
5084
  this._ctx.onDispose(this._edgeConnection.onReconnected(() => {
4359
5085
  this._ctx && scheduleMicroTask2(this._ctx, () => this._handleReconnect());
@@ -4452,7 +5178,7 @@ var EchoEdgeReplicator = class {
4452
5178
  async _openConnection(spaceId, reconnects = 0) {
4453
5179
  invariant14(this._context, void 0, {
4454
5180
  F: __dxlog_file18,
4455
- L: 124,
5181
+ L: 135,
4456
5182
  S: this,
4457
5183
  A: [
4458
5184
  "this._context",
@@ -4461,7 +5187,7 @@ var EchoEdgeReplicator = class {
4461
5187
  });
4462
5188
  invariant14(!this._connections.has(spaceId), void 0, {
4463
5189
  F: __dxlog_file18,
4464
- L: 125,
5190
+ L: 136,
4465
5191
  S: this,
4466
5192
  A: [
4467
5193
  "!this._connections.has(spaceId)",
@@ -4470,14 +5196,31 @@ var EchoEdgeReplicator = class {
4470
5196
  });
4471
5197
  let restartScheduled = false;
4472
5198
  const connection = new EdgeReplicatorConnection({
5199
+ edgeHttpClient: this._edgeHttpClient,
4473
5200
  edgeConnection: this._edgeConnection,
4474
5201
  spaceId,
4475
5202
  context: this._context,
4476
5203
  sharedPolicyEnabled: this._sharePolicyEnabled,
4477
5204
  onRemoteConnected: async () => {
5205
+ log13.trace("dxos.echo.edge.replicator.onRemoteConnected", {
5206
+ spaceId
5207
+ }, {
5208
+ F: __dxlog_file18,
5209
+ L: 147,
5210
+ S: this,
5211
+ C: (f, a) => f(...a)
5212
+ });
4478
5213
  this._context?.onConnectionOpen(connection);
4479
5214
  },
4480
5215
  onRemoteDisconnected: async () => {
5216
+ log13.trace("dxos.echo.edge.replicator.onRemoteDisconnected", {
5217
+ spaceId
5218
+ }, {
5219
+ F: __dxlog_file18,
5220
+ L: 151,
5221
+ S: this,
5222
+ C: (f, a) => f(...a)
5223
+ });
4481
5224
  this._context?.onConnectionClosed(connection);
4482
5225
  },
4483
5226
  onRestartRequested: async () => {
@@ -4491,7 +5234,7 @@ var EchoEdgeReplicator = class {
4491
5234
  restartDelay
4492
5235
  }, {
4493
5236
  F: __dxlog_file18,
4494
- L: 148,
5237
+ L: 162,
4495
5238
  S: this,
4496
5239
  C: (f, a) => f(...a)
4497
5240
  });
@@ -4513,6 +5256,16 @@ var EchoEdgeReplicator = class {
4513
5256
  if (ctx?.disposed) {
4514
5257
  return;
4515
5258
  }
5259
+ log13.trace("dxos.echo.edge.replicator.restart", {
5260
+ spaceId,
5261
+ reconnects,
5262
+ restartDelay
5263
+ }, {
5264
+ F: __dxlog_file18,
5265
+ L: 179,
5266
+ S: this,
5267
+ C: (f, a) => f(...a)
5268
+ });
4516
5269
  await this._openConnection(spaceId, reconnects + 1);
4517
5270
  } catch (e) {
4518
5271
  env.error = e;
@@ -4526,42 +5279,27 @@ var EchoEdgeReplicator = class {
4526
5279
  this._connections.set(spaceId, connection);
4527
5280
  await connection.open();
4528
5281
  }
5282
+ constructor({ edgeConnection, edgeHttpClient, disableSharePolicy }) {
5283
+ _define_property18(this, "_edgeConnection", void 0);
5284
+ _define_property18(this, "_edgeHttpClient", void 0);
5285
+ _define_property18(this, "_mutex", new Mutex());
5286
+ _define_property18(this, "_ctx", void 0);
5287
+ _define_property18(this, "_context", null);
5288
+ _define_property18(this, "_connectedSpaces", /* @__PURE__ */ new Set());
5289
+ _define_property18(this, "_connections", /* @__PURE__ */ new Map());
5290
+ _define_property18(this, "_sharePolicyEnabled", true);
5291
+ this._edgeConnection = edgeConnection;
5292
+ this._edgeHttpClient = edgeHttpClient;
5293
+ this._sharePolicyEnabled = !disableSharePolicy;
5294
+ }
4529
5295
  };
4530
5296
  var MAX_INFLIGHT_REQUESTS = 5;
4531
5297
  var MAX_RATE_LIMIT_WAIT_TIME_MS = 3e3;
4532
5298
  var EdgeReplicatorConnection = class extends Resource11 {
4533
- constructor({ edgeConnection, spaceId, context, sharedPolicyEnabled, onRemoteConnected, onRemoteDisconnected, onRestartRequested }) {
4534
- super();
4535
- this._remotePeerId = null;
4536
- this._requestLimiter = new InflightRequestLimiter({
4537
- maxInflightRequests: MAX_INFLIGHT_REQUESTS,
4538
- resetBalanceTimeoutMs: MAX_RATE_LIMIT_WAIT_TIME_MS
4539
- });
4540
- this._edgeConnection = edgeConnection;
4541
- this._spaceId = spaceId;
4542
- this._context = context;
4543
- this._remotePeerId = `${EdgeService.AUTOMERGE_REPLICATOR}:${spaceId}-${randomUUID()}`;
4544
- this._targetServiceId = `${EdgeService.AUTOMERGE_REPLICATOR}:${spaceId}`;
4545
- this._sharedPolicyEnabled = sharedPolicyEnabled;
4546
- this._onRemoteConnected = onRemoteConnected;
4547
- this._onRemoteDisconnected = onRemoteDisconnected;
4548
- this._onRestartRequested = onRestartRequested;
4549
- this.readable = new ReadableStream({
4550
- start: (controller) => {
4551
- this._readableStreamController = controller;
4552
- }
4553
- });
4554
- this.writable = new WritableStream({
4555
- write: async (message, controller) => {
4556
- await this._requestLimiter.rateLimit(message);
4557
- await this._sendMessage(message);
4558
- }
4559
- });
4560
- }
4561
5299
  async _open(ctx) {
4562
5300
  log13("opening...", void 0, {
4563
5301
  F: __dxlog_file18,
4564
- L: 251,
5302
+ L: 270,
4565
5303
  S: this,
4566
5304
  C: (f, a) => f(...a)
4567
5305
  });
@@ -4569,12 +5307,29 @@ var EdgeReplicatorConnection = class extends Resource11 {
4569
5307
  this._ctx.onDispose(this._edgeConnection.onMessage((msg) => {
4570
5308
  this._onMessage(msg);
4571
5309
  }));
5310
+ let firstReconnect = true;
5311
+ this._ctx.onDispose(
5312
+ // NOTE: This will fire immediately if the connection is already open.
5313
+ this._edgeConnection.onReconnected(async () => {
5314
+ if (firstReconnect) {
5315
+ log13.verbose("first reconnect skipped", void 0, {
5316
+ F: __dxlog_file18,
5317
+ L: 285,
5318
+ S: this,
5319
+ C: (f, a) => f(...a)
5320
+ });
5321
+ firstReconnect = false;
5322
+ return;
5323
+ }
5324
+ this._onRestartRequested();
5325
+ })
5326
+ );
4572
5327
  await this._onRemoteConnected();
4573
5328
  }
4574
5329
  async _close() {
4575
5330
  log13("closing...", void 0, {
4576
5331
  F: __dxlog_file18,
4577
- L: 266,
5332
+ L: 298,
4578
5333
  S: this,
4579
5334
  C: (f, a) => f(...a)
4580
5335
  });
@@ -4585,7 +5340,7 @@ var EdgeReplicatorConnection = class extends Resource11 {
4585
5340
  get peerId() {
4586
5341
  invariant14(this._remotePeerId, "Not connected", {
4587
5342
  F: __dxlog_file18,
4588
- L: 275,
5343
+ L: 307,
4589
5344
  S: this,
4590
5345
  A: [
4591
5346
  "this._remotePeerId",
@@ -4610,7 +5365,7 @@ var EdgeReplicatorConnection = class extends Resource11 {
4610
5365
  remoteId: this._remotePeerId
4611
5366
  }, {
4612
5367
  F: __dxlog_file18,
4613
- L: 290,
5368
+ L: 322,
4614
5369
  S: this,
4615
5370
  C: (f, a) => f(...a)
4616
5371
  });
@@ -4636,13 +5391,36 @@ var EdgeReplicatorConnection = class extends Resource11 {
4636
5391
  remoteId: this._remotePeerId
4637
5392
  }, {
4638
5393
  F: __dxlog_file18,
4639
- L: 319,
5394
+ L: 351,
4640
5395
  S: this,
4641
5396
  C: (f, a) => f(...a)
4642
5397
  });
4643
5398
  payload.senderId = this._remotePeerId;
4644
5399
  this._processMessage(payload);
4645
5400
  }
5401
+ get bundleSyncEnabled() {
5402
+ return true;
5403
+ }
5404
+ async pushBundle(bundle) {
5405
+ const request = {
5406
+ bundle: bundle.map(({ documentId, data, heads }) => ({
5407
+ documentId,
5408
+ mutation: DocumentCodec.encode(data),
5409
+ heads
5410
+ }))
5411
+ };
5412
+ await this._edgeHttpClient.importBundle(this._spaceId, request);
5413
+ }
5414
+ async pullBundle(docHeads) {
5415
+ const request = {
5416
+ docHeads
5417
+ };
5418
+ const response = await this._edgeHttpClient.exportBundle(this._spaceId, request);
5419
+ return Object.fromEntries(response.bundle.map((doc) => [
5420
+ doc.documentId,
5421
+ DocumentCodec.decode(doc.mutation)
5422
+ ]));
5423
+ }
4646
5424
  _processMessage(message) {
4647
5425
  if (isForbiddenErrorMessage(message)) {
4648
5426
  this._onRestartRequested();
@@ -4659,27 +5437,65 @@ var EdgeReplicatorConnection = class extends Resource11 {
4659
5437
  remoteId: this._remotePeerId
4660
5438
  }, {
4661
5439
  F: __dxlog_file18,
4662
- L: 348,
5440
+ L: 401,
4663
5441
  S: this,
4664
5442
  C: (f, a) => f(...a)
4665
5443
  });
4666
5444
  const encoded = cbor2.encode(message);
4667
- await this._edgeConnection.send(buf.create(RouterMessageSchema, {
4668
- serviceId: this._targetServiceId,
4669
- source: {
4670
- identityKey: this._edgeConnection.identityKey,
4671
- peerKey: this._edgeConnection.peerKey
4672
- },
4673
- payload: {
4674
- value: bufferToArray2(encoded)
5445
+ try {
5446
+ await this._edgeConnection.send(buf.create(RouterMessageSchema, {
5447
+ serviceId: this._targetServiceId,
5448
+ source: {
5449
+ identityKey: this._edgeConnection.identityKey,
5450
+ peerKey: this._edgeConnection.peerKey
5451
+ },
5452
+ payload: {
5453
+ value: bufferToArray2(encoded)
5454
+ }
5455
+ }));
5456
+ } catch (err) {
5457
+ log13.error("failed to send message", {
5458
+ err
5459
+ }, {
5460
+ F: __dxlog_file18,
5461
+ L: 421,
5462
+ S: this,
5463
+ C: (f, a) => f(...a)
5464
+ });
5465
+ }
5466
+ }
5467
+ constructor({ edgeConnection, edgeHttpClient, spaceId, context, sharedPolicyEnabled, onRemoteConnected, onRemoteDisconnected, onRestartRequested }) {
5468
+ super(), _define_property18(this, "_edgeConnection", void 0), _define_property18(this, "_edgeHttpClient", void 0), _define_property18(this, "_remotePeerId", null), _define_property18(this, "_targetServiceId", void 0), _define_property18(this, "_spaceId", void 0), _define_property18(this, "_context", void 0), _define_property18(this, "_sharedPolicyEnabled", void 0), _define_property18(this, "_onRemoteConnected", void 0), _define_property18(this, "_onRemoteDisconnected", void 0), _define_property18(this, "_onRestartRequested", void 0), _define_property18(this, "_requestLimiter", new InflightRequestLimiter({
5469
+ maxInflightRequests: MAX_INFLIGHT_REQUESTS,
5470
+ resetBalanceTimeoutMs: MAX_RATE_LIMIT_WAIT_TIME_MS
5471
+ })), _define_property18(this, "_readableStreamController", void 0), _define_property18(this, "readable", void 0), _define_property18(this, "writable", void 0);
5472
+ this._edgeConnection = edgeConnection;
5473
+ this._edgeHttpClient = edgeHttpClient;
5474
+ this._spaceId = spaceId;
5475
+ this._context = context;
5476
+ this._remotePeerId = `${EdgeService.AUTOMERGE_REPLICATOR}:${spaceId}-${randomUUID()}`;
5477
+ this._targetServiceId = `${EdgeService.AUTOMERGE_REPLICATOR}:${spaceId}`;
5478
+ this._sharedPolicyEnabled = sharedPolicyEnabled;
5479
+ this._onRemoteConnected = onRemoteConnected;
5480
+ this._onRemoteDisconnected = onRemoteDisconnected;
5481
+ this._onRestartRequested = onRestartRequested;
5482
+ this.readable = new ReadableStream({
5483
+ start: (controller) => {
5484
+ this._readableStreamController = controller;
4675
5485
  }
4676
- }));
5486
+ });
5487
+ this.writable = new WritableStream({
5488
+ write: async (message, controller) => {
5489
+ await this._requestLimiter.rateLimit(message);
5490
+ await this._sendMessage(message);
5491
+ }
5492
+ });
4677
5493
  }
4678
5494
  };
4679
5495
  var isForbiddenErrorMessage = (message) => message.type === "error" && message.message === "Forbidden";
4680
5496
 
4681
5497
  // src/util.ts
4682
- import { decodeReference, ObjectStructure as ObjectStructure2 } from "@dxos/echo-protocol";
5498
+ import { ObjectStructure as ObjectStructure2, decodeReference } from "@dxos/echo-protocol";
4683
5499
  var findInlineObjectOfType = (spaceDoc, typename) => {
4684
5500
  for (const id in spaceDoc.objects ?? {}) {
4685
5501
  const obj = spaceDoc.objects[id];
@@ -4731,6 +5547,7 @@ export {
4731
5547
  diffCollectionState,
4732
5548
  encodingOptions,
4733
5549
  filterMatchObject,
5550
+ filterMatchObjectJSON,
4734
5551
  filterMatchValue,
4735
5552
  findInlineObjectOfType,
4736
5553
  getSpaceIdFromCollectionId,