@dxos/echo-pipeline 0.1.41 → 0.1.45-next.45667d4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/dist/lib/browser/{chunk-4YAT2XJW.mjs → chunk-2NGLSBL4.mjs} +1128 -1017
  2. package/dist/lib/browser/chunk-2NGLSBL4.mjs.map +7 -0
  3. package/dist/lib/browser/index.mjs +1 -3
  4. package/dist/lib/browser/index.mjs.map +1 -1
  5. package/dist/lib/browser/meta.json +1 -1
  6. package/dist/lib/browser/testing/index.mjs +72 -48
  7. package/dist/lib/browser/testing/index.mjs.map +3 -3
  8. package/dist/lib/node/index.cjs +1048 -938
  9. package/dist/lib/node/index.cjs.map +4 -4
  10. package/dist/lib/node/meta.json +1 -1
  11. package/dist/lib/node/testing/index.cjs +735 -540
  12. package/dist/lib/node/testing/index.cjs.map +4 -4
  13. package/dist/types/src/dbhost/database-host.d.ts +2 -4
  14. package/dist/types/src/dbhost/database-host.d.ts.map +1 -1
  15. package/dist/types/src/dbhost/snapshot-store.d.ts +2 -0
  16. package/dist/types/src/dbhost/snapshot-store.d.ts.map +1 -1
  17. package/dist/types/src/metadata/metadata-store.d.ts +3 -2
  18. package/dist/types/src/metadata/metadata-store.d.ts.map +1 -1
  19. package/dist/types/src/pipeline/pipeline.d.ts +27 -11
  20. package/dist/types/src/pipeline/pipeline.d.ts.map +1 -1
  21. package/dist/types/src/pipeline/timeframe-clock.d.ts +1 -0
  22. package/dist/types/src/pipeline/timeframe-clock.d.ts.map +1 -1
  23. package/dist/types/src/space/auth.d.ts +4 -4
  24. package/dist/types/src/space/auth.d.ts.map +1 -1
  25. package/dist/types/src/space/control-pipeline.d.ts +2 -4
  26. package/dist/types/src/space/control-pipeline.d.ts.map +1 -1
  27. package/dist/types/src/space/data-pipeline.d.ts +19 -10
  28. package/dist/types/src/space/data-pipeline.d.ts.map +1 -1
  29. package/dist/types/src/space/index.d.ts +0 -1
  30. package/dist/types/src/space/index.d.ts.map +1 -1
  31. package/dist/types/src/space/space-manager.d.ts +14 -14
  32. package/dist/types/src/space/space-manager.d.ts.map +1 -1
  33. package/dist/types/src/space/space-protocol.d.ts +2 -2
  34. package/dist/types/src/space/space-protocol.d.ts.map +1 -1
  35. package/dist/types/src/space/space.d.ts +20 -7
  36. package/dist/types/src/space/space.d.ts.map +1 -1
  37. package/dist/types/src/testing/database-test-rig.d.ts.map +1 -1
  38. package/dist/types/src/testing/test-agent-builder.d.ts +15 -5
  39. package/dist/types/src/testing/test-agent-builder.d.ts.map +1 -1
  40. package/package.json +30 -30
  41. package/src/common/feeds.ts +2 -2
  42. package/src/dbhost/data-service-host.ts +10 -10
  43. package/src/dbhost/data-service.ts +1 -1
  44. package/src/dbhost/database-host.ts +1 -9
  45. package/src/dbhost/snapshot-store.test.ts +2 -2
  46. package/src/dbhost/snapshot-store.ts +16 -1
  47. package/src/metadata/metadata-store.ts +14 -16
  48. package/src/pipeline/message-selector.ts +1 -1
  49. package/src/pipeline/pipeline.test.ts +68 -44
  50. package/src/pipeline/pipeline.ts +118 -35
  51. package/src/pipeline/timeframe-clock.ts +8 -2
  52. package/src/space/auth.ts +6 -6
  53. package/src/space/control-pipeline.test.ts +19 -20
  54. package/src/space/control-pipeline.ts +3 -5
  55. package/src/space/data-pipeline.ts +131 -69
  56. package/src/space/index.ts +0 -1
  57. package/src/space/replication.browser.test.ts +8 -8
  58. package/src/space/space-manager.browser.test.ts +3 -3
  59. package/src/space/space-manager.ts +25 -20
  60. package/src/space/space-protocol.browser.test.ts +1 -1
  61. package/src/space/space-protocol.test.ts +6 -6
  62. package/src/space/space-protocol.ts +7 -7
  63. package/src/space/space.test.ts +137 -48
  64. package/src/space/space.ts +82 -40
  65. package/src/testing/database-test-rig.ts +25 -24
  66. package/src/testing/test-agent-builder.ts +88 -49
  67. package/src/testing/test-feed-builder.ts +1 -1
  68. package/src/testing/util.ts +15 -15
  69. package/src/tests/database-unit.test.ts +4 -4
  70. package/src/tests/database.test.ts +1 -1
  71. package/dist/lib/browser/chunk-4YAT2XJW.mjs.map +0 -7
  72. package/dist/types/src/space/genesis.d.ts +0 -5
  73. package/dist/types/src/space/genesis.d.ts.map +0 -1
  74. package/src/space/genesis.ts +0 -69
@@ -60,7 +60,6 @@ __export(src_exports, {
60
60
  createMappedFeedWriter: () => createMappedFeedWriter,
61
61
  mapFeedIndexesToTimeframe: () => mapFeedIndexesToTimeframe,
62
62
  mapTimeframeToFeedIndexes: () => mapTimeframeToFeedIndexes,
63
- spaceGenesis: () => spaceGenesis,
64
63
  startAfter: () => startAfter,
65
64
  valueEncoding: () => valueEncoding
66
65
  });
@@ -231,13 +230,18 @@ var MetadataStore = class {
231
230
  await file.close();
232
231
  }
233
232
  }
233
+ _getSpace(spaceKey) {
234
+ const space = this.spaces.find((space2) => space2.key === spaceKey);
235
+ (0, import_node_assert.default)(space, "Space not found");
236
+ return space;
237
+ }
234
238
  /**
235
239
  * Clears storage - doesn't work for now.
236
240
  */
237
241
  async clear() {
238
242
  (0, import_log.log)("clearing all metadata", {}, {
239
243
  file: "metadata-store.ts",
240
- line: 126,
244
+ line: 132,
241
245
  scope: this,
242
246
  callSite: (f, a) => f(...a)
243
247
  });
@@ -258,20 +262,15 @@ var MetadataStore = class {
258
262
  await this._save();
259
263
  }
260
264
  async setSpaceLatestTimeframe(spaceKey, timeframe) {
261
- const space = this.spaces.find((space2) => space2.key === spaceKey);
262
- (0, import_node_assert.default)(space, "Space not found");
263
- space.dataTimeframe = timeframe;
265
+ this._getSpace(spaceKey).dataTimeframe = timeframe;
264
266
  await this._save();
265
267
  }
266
- async setSpaceSnapshot(spaceKey, snapshot) {
267
- const space = this.spaces.find((space2) => space2.key === spaceKey);
268
- (0, import_node_assert.default)(space, "Space not found");
269
- space.snapshot = snapshot;
268
+ async setCache(spaceKey, cache) {
269
+ this._getSpace(spaceKey).cache = cache;
270
270
  await this._save();
271
271
  }
272
272
  async setWritableFeedKeys(spaceKey, controlFeedKey, dataFeedKey) {
273
- const space = this.spaces.find((space2) => space2.key === spaceKey);
274
- (0, import_node_assert.default)(space, "Space not found");
273
+ const space = this._getSpace(spaceKey);
275
274
  space.controlFeedKey = controlFeedKey;
276
275
  space.dataFeedKey = dataFeedKey;
277
276
  await this._save();
@@ -289,10 +288,12 @@ var fromBytesInt32 = (buf) => buf.readInt32LE(0);
289
288
  var import_node_assert4 = __toESM(require("node:assert"));
290
289
  var import_async3 = require("@dxos/async");
291
290
  var import_context = require("@dxos/context");
291
+ var import_debug3 = require("@dxos/debug");
292
292
  var import_feed_store = require("@dxos/feed-store");
293
293
  var import_keys = require("@dxos/keys");
294
294
  var import_log3 = require("@dxos/log");
295
295
  var import_timeframe2 = require("@dxos/timeframe");
296
+ var import_util = require("@dxos/util");
296
297
 
297
298
  // packages/core/echo/echo-pipeline/src/common/codec.ts
298
299
  var import_hypercore = require("@dxos/hypercore");
@@ -374,6 +375,11 @@ var TimeframeClock = class {
374
375
  get pendingTimeframe() {
375
376
  return this._pendingTimeframe;
376
377
  }
378
+ setTimeframe(timeframe) {
379
+ this._timeframe = timeframe;
380
+ this._pendingTimeframe = timeframe;
381
+ this.update.emit(this._timeframe);
382
+ }
377
383
  updatePendingTimeframe(key, seq) {
378
384
  this._pendingTimeframe = import_timeframe.Timeframe.merge(this._pendingTimeframe, new import_timeframe.Timeframe([
379
385
  [
@@ -391,12 +397,12 @@ var TimeframeClock = class {
391
397
  return !gaps.isEmpty();
392
398
  }
393
399
  async waitUntilReached(target) {
394
- import_log2.log.info("waitUntilReached", {
400
+ (0, import_log2.log)("waitUntilReached", {
395
401
  target,
396
402
  current: this._timeframe
397
403
  }, {
398
404
  file: "timeframe-clock.ts",
399
- line: 67,
405
+ line: 73,
400
406
  scope: this,
401
407
  callSite: (f, a) => f(...a)
402
408
  });
@@ -407,7 +413,7 @@ var TimeframeClock = class {
407
413
  deps: import_timeframe.Timeframe.dependencies(target, this._timeframe)
408
414
  }, {
409
415
  file: "timeframe-clock.ts",
410
- line: 69,
416
+ line: 75,
411
417
  scope: this,
412
418
  callSite: (f, a) => f(...a)
413
419
  });
@@ -432,10 +438,11 @@ var __decorate3 = function(decorators, target, key, desc) {
432
438
  };
433
439
  var PipelineState = class {
434
440
  // prettier-ignore
435
- constructor(_iterator, _timeframeClock) {
436
- this._iterator = _iterator;
441
+ constructor(_feeds, _timeframeClock) {
442
+ this._feeds = _feeds;
437
443
  this._timeframeClock = _timeframeClock;
438
444
  this.timeframeUpdate = this._timeframeClock.update;
445
+ this.stalled = new import_async3.Event();
439
446
  }
440
447
  /**
441
448
  * Latest theoretical timeframe based on the last mutation in each feed.
@@ -443,7 +450,7 @@ var PipelineState = class {
443
450
  */
444
451
  // TODO(dmaretskyi): Rename `totalTimeframe`? or `lastTimeframe`.
445
452
  get endTimeframe() {
446
- return mapFeedIndexesToTimeframe(this._iterator.feeds.filter((feed) => feed.length > 0).map((feed) => ({
453
+ return mapFeedIndexesToTimeframe(Array.from(this._feeds.values()).filter((feed) => feed.length > 0).map((feed) => ({
447
454
  feedKey: feed.key,
448
455
  index: feed.length - 1
449
456
  })));
@@ -458,7 +465,7 @@ var PipelineState = class {
458
465
  return this._targetTimeframe ? this._targetTimeframe : new import_timeframe2.Timeframe();
459
466
  }
460
467
  get feeds() {
461
- return this._iterator.feeds;
468
+ return Array.from(this._feeds.values());
462
469
  }
463
470
  async waitUntilTimeframe(target) {
464
471
  await this._timeframeClock.waitUntilReached(target);
@@ -481,7 +488,7 @@ var PipelineState = class {
481
488
  target: this.targetTimeframe
482
489
  }, {
483
490
  file: "pipeline.ts",
484
- line: 105,
491
+ line: 109,
485
492
  scope: this,
486
493
  callSite: (f, a) => f(...a)
487
494
  });
@@ -490,7 +497,7 @@ var PipelineState = class {
490
497
  return import_timeframe2.Timeframe.dependencies(this.targetTimeframe, this.timeframe).isEmpty();
491
498
  }),
492
499
  ...breakOnStall ? [
493
- this._iterator.stalled.discardParameter().waitForCount(1)
500
+ this.stalled.discardParameter().waitForCount(1)
494
501
  ] : []
495
502
  ]);
496
503
  let done = false;
@@ -511,7 +518,7 @@ var PipelineState = class {
511
518
  dependencies: import_timeframe2.Timeframe.dependencies(this.targetTimeframe, this.timeframe)
512
519
  }, {
513
520
  file: "pipeline.ts",
514
- line: 131,
521
+ line: 135,
515
522
  scope: this,
516
523
  callSite: (f, a) => f(...a)
517
524
  });
@@ -523,25 +530,18 @@ var PipelineState = class {
523
530
  }
524
531
  };
525
532
  var Pipeline = class {
526
- // prettier-ignore
527
- constructor(_initialTimeframe = new import_timeframe2.Timeframe()) {
528
- this._initialTimeframe = _initialTimeframe;
529
- this._timeframeClock = new TimeframeClock(this._initialTimeframe);
530
- this._feedSetIterator = new import_feed_store.FeedSetIterator(createMessageSelector(this._timeframeClock), {
531
- start: startAfter(this._initialTimeframe),
532
- stallTimeout: 1e3
533
- });
534
- this._state = new PipelineState(this._feedSetIterator, this._timeframeClock);
533
+ constructor() {
534
+ this._timeframeClock = new TimeframeClock(new import_timeframe2.Timeframe());
535
+ this._feeds = new import_util.ComplexMap(import_keys.PublicKey.hash);
536
+ // External state accessor.
537
+ this._state = new PipelineState(this._feeds, this._timeframeClock);
538
+ // Waits for the message consumer to process the message and yield control back to the pipeline.
535
539
  this._processingTrigger = new import_async3.Trigger().wake();
540
+ this._pauseTrigger = new import_async3.Trigger().wake();
541
+ this._isStopping = false;
542
+ this._isStarted = false;
536
543
  this._isOpen = false;
537
- this._feedSetIterator.stalled.on((iterator) => {
538
- import_log3.log.warn(`Stalled after ${iterator.options.stallTimeout}ms with ${iterator.size} feeds.`, {}, {
539
- file: "pipeline.ts",
540
- line: 207,
541
- scope: this,
542
- callSite: (f, a) => f(...a)
543
- });
544
- });
544
+ this._isPaused = false;
545
545
  }
546
546
  get state() {
547
547
  return this._state;
@@ -556,10 +556,13 @@ var Pipeline = class {
556
556
  // NOTE: This cannot be synchronized with `stop` because stop waits for the mutation processing to complete,
557
557
  // which might be opening feeds during the mutation processing, which w
558
558
  async addFeed(feed) {
559
- await this._feedSetIterator.addFeed(feed);
559
+ this._feeds.set(feed.key, feed);
560
+ if (this._feedSetIterator) {
561
+ await this._feedSetIterator.addFeed(feed);
562
+ }
560
563
  }
561
564
  hasFeed(feedKey) {
562
- return this._feedSetIterator.hasFeed(feedKey);
565
+ return this._feeds.has(feedKey);
563
566
  }
564
567
  setWriteFeed(feed) {
565
568
  (0, import_node_assert4.default)(!this._writer, "Writer already set.");
@@ -572,33 +575,68 @@ var Pipeline = class {
572
575
  async start() {
573
576
  (0, import_log3.log)("starting...", {}, {
574
577
  file: "pipeline.ts",
575
- line: 249,
578
+ line: 254,
576
579
  scope: this,
577
580
  callSite: (f, a) => f(...a)
578
581
  });
582
+ await this._initIterator();
579
583
  await this._feedSetIterator.open();
580
584
  (0, import_log3.log)("started", {}, {
581
585
  file: "pipeline.ts",
582
- line: 251,
586
+ line: 257,
583
587
  scope: this,
584
588
  callSite: (f, a) => f(...a)
585
589
  });
590
+ this._isStarted = true;
586
591
  }
587
592
  async stop() {
593
+ var _a;
588
594
  (0, import_log3.log)("stopping...", {}, {
589
595
  file: "pipeline.ts",
590
- line: 256,
596
+ line: 263,
591
597
  scope: this,
592
598
  callSite: (f, a) => f(...a)
593
599
  });
594
- await this._feedSetIterator.close();
600
+ this._isStopping = true;
601
+ await ((_a = this._feedSetIterator) == null ? void 0 : _a.close());
595
602
  await this._processingTrigger.wait();
596
603
  (0, import_log3.log)("stopped", {}, {
597
604
  file: "pipeline.ts",
598
- line: 259,
605
+ line: 267,
599
606
  scope: this,
600
607
  callSite: (f, a) => f(...a)
601
608
  });
609
+ this._isStarted = false;
610
+ }
611
+ /**
612
+ * @param timeframe Timeframe of already processed messages. The pipeline will start processing messages AFTER this timeframe.
613
+ */
614
+ async setCursor(timeframe) {
615
+ (0, import_node_assert4.default)(!this._isStarted || this._isPaused, "Invalid state.");
616
+ this._timeframeClock.setTimeframe(timeframe);
617
+ if (this._feedSetIterator) {
618
+ await this._feedSetIterator.close();
619
+ await this._initIterator();
620
+ await this._feedSetIterator.open();
621
+ }
622
+ }
623
+ /**
624
+ * Calling pause while processing will cause a deadlock.
625
+ */
626
+ async pause() {
627
+ (0, import_node_assert4.default)(this._isStarted, "Pipeline is not open.");
628
+ if (this._isPaused) {
629
+ return;
630
+ }
631
+ this._pauseTrigger.reset();
632
+ await this._processingTrigger.wait();
633
+ this._isPaused = true;
634
+ }
635
+ async unpause() {
636
+ (0, import_node_assert4.default)(this._isStarted, "Pipeline is not open.");
637
+ (0, import_node_assert4.default)(this._isPaused, "Pipeline is not paused.");
638
+ this._pauseTrigger.wake();
639
+ this._isPaused = false;
602
640
  }
603
641
  /**
604
642
  * Starts to iterate over the ordered messages from the added feeds.
@@ -607,7 +645,21 @@ var Pipeline = class {
607
645
  async *consume() {
608
646
  (0, import_node_assert4.default)(!this._isOpen, "Pipeline is already being consumed.");
609
647
  this._isOpen = true;
610
- for await (const block of this._feedSetIterator) {
648
+ (0, import_node_assert4.default)(this._feedSetIterator, "Iterator not initialized.");
649
+ let lastFeedSetIterator = this._feedSetIterator;
650
+ let iterable = lastFeedSetIterator[Symbol.asyncIterator]();
651
+ while (!this._isStopping) {
652
+ await this._pauseTrigger.wait();
653
+ if (lastFeedSetIterator !== this._feedSetIterator) {
654
+ (0, import_node_assert4.default)(this._feedSetIterator, "Iterator not initialized.");
655
+ lastFeedSetIterator = this._feedSetIterator;
656
+ iterable = lastFeedSetIterator[Symbol.asyncIterator]();
657
+ }
658
+ const { done, value } = await iterable.next();
659
+ if (done) {
660
+ continue;
661
+ }
662
+ const block = value != null ? value : (0, import_debug3.failUndefined)();
611
663
  this._processingTrigger.reset();
612
664
  this._timeframeClock.updatePendingTimeframe(import_keys.PublicKey.from(block.feedKey), block.seq);
613
665
  yield block;
@@ -616,6 +668,24 @@ var Pipeline = class {
616
668
  }
617
669
  this._isOpen = false;
618
670
  }
671
+ async _initIterator() {
672
+ this._feedSetIterator = new import_feed_store.FeedSetIterator(createMessageSelector(this._timeframeClock), {
673
+ start: startAfter(this._timeframeClock.timeframe),
674
+ stallTimeout: 1e3
675
+ });
676
+ this._feedSetIterator.stalled.on((iterator) => {
677
+ import_log3.log.warn(`Stalled after ${iterator.options.stallTimeout}ms with ${iterator.size} feeds.`, {}, {
678
+ file: "pipeline.ts",
679
+ line: 357,
680
+ scope: this,
681
+ callSite: (f, a) => f(...a)
682
+ });
683
+ this._state.stalled.emit();
684
+ });
685
+ for (const feed of this._feeds.values()) {
686
+ await this._feedSetIterator.addFeed(feed);
687
+ }
688
+ }
619
689
  };
620
690
  __decorate3([
621
691
  import_async3.synchronized
@@ -623,6 +693,15 @@ __decorate3([
623
693
  __decorate3([
624
694
  import_async3.synchronized
625
695
  ], Pipeline.prototype, "stop", null);
696
+ __decorate3([
697
+ import_async3.synchronized
698
+ ], Pipeline.prototype, "setCursor", null);
699
+ __decorate3([
700
+ import_async3.synchronized
701
+ ], Pipeline.prototype, "pause", null);
702
+ __decorate3([
703
+ import_async3.synchronized
704
+ ], Pipeline.prototype, "unpause", null);
626
705
 
627
706
  // packages/core/echo/echo-pipeline/src/space/auth.ts
628
707
  var import_node_assert5 = __toESM(require("node:assert"));
@@ -642,7 +721,6 @@ var AuthExtension = class extends import_teleport.RpcExtension {
642
721
  AuthService: import_protocols3.schema.getService("dxos.mesh.teleport.auth.AuthService")
643
722
  },
644
723
  timeout: 60 * 1e3
645
- // Long timeout because auth can wait for sync in certain cases.
646
724
  });
647
725
  this._authParams = _authParams;
648
726
  this._ctx = new import_context2.Context({
@@ -712,22 +790,22 @@ var AuthExtension = class extends import_teleport.RpcExtension {
712
790
  };
713
791
 
714
792
  // packages/core/echo/echo-pipeline/src/space/space.ts
715
- var import_node_assert6 = __toESM(require("node:assert"));
716
- var import_async5 = require("@dxos/async");
717
- var import_log6 = require("@dxos/log");
718
- var import_credentials3 = require("@dxos/protocols/proto/dxos/halo/credentials");
719
- var import_util2 = require("@dxos/util");
793
+ var import_node_assert9 = __toESM(require("node:assert"));
794
+ var import_async7 = require("@dxos/async");
795
+ var import_log9 = require("@dxos/log");
796
+ var import_credentials4 = require("@dxos/protocols/proto/dxos/halo/credentials");
797
+ var import_util5 = require("@dxos/util");
720
798
 
721
799
  // packages/core/echo/echo-pipeline/src/space/control-pipeline.ts
722
800
  var import_credentials = require("@dxos/credentials");
723
801
  var import_keys2 = require("@dxos/keys");
724
802
  var import_log5 = require("@dxos/log");
725
803
  var import_credentials2 = require("@dxos/protocols/proto/dxos/halo/credentials");
726
- var import_util = require("@dxos/util");
804
+ var import_util2 = require("@dxos/util");
727
805
  var ControlPipeline = class {
728
- constructor({ spaceKey, genesisFeed, feedProvider, initialTimeframe }) {
729
- this.onFeedAdmitted = new import_util.Callback();
730
- this._pipeline = new Pipeline(initialTimeframe);
806
+ constructor({ spaceKey, genesisFeed, feedProvider }) {
807
+ this.onFeedAdmitted = new import_util2.Callback();
808
+ this._pipeline = new Pipeline();
731
809
  void this._pipeline.addFeed(genesisFeed);
732
810
  this._spaceStateMachine = new import_credentials.SpaceStateMachine(spaceKey);
733
811
  this._spaceStateMachine.onFeedAdmitted.set(async (info) => {
@@ -735,7 +813,7 @@ var ControlPipeline = class {
735
813
  key: info.key
736
814
  }, {
737
815
  file: "control-pipeline.ts",
738
- line: 41,
816
+ line: 39,
739
817
  scope: this,
740
818
  callSite: (f, a) => f(...a)
741
819
  });
@@ -746,7 +824,7 @@ var ControlPipeline = class {
746
824
  } catch (err) {
747
825
  import_log5.log.catch(err, {}, {
748
826
  file: "control-pipeline.ts",
749
- line: 49,
827
+ line: 47,
750
828
  scope: this,
751
829
  callSite: (f, a) => f(...a)
752
830
  });
@@ -769,7 +847,7 @@ var ControlPipeline = class {
769
847
  async start() {
770
848
  (0, import_log5.log)("starting...", {}, {
771
849
  file: "control-pipeline.ts",
772
- line: 73,
850
+ line: 71,
773
851
  scope: this,
774
852
  callSite: (f, a) => f(...a)
775
853
  });
@@ -781,7 +859,7 @@ var ControlPipeline = class {
781
859
  seq: msg.seq
782
860
  }, {
783
861
  file: "control-pipeline.ts",
784
- line: 78,
862
+ line: 76,
785
863
  scope: this,
786
864
  callSite: (f, a) => f(...a)
787
865
  });
@@ -792,7 +870,7 @@ var ControlPipeline = class {
792
870
  msg
793
871
  }, {
794
872
  file: "control-pipeline.ts",
795
- line: 85,
873
+ line: 83,
796
874
  scope: this,
797
875
  callSite: (f, a) => f(...a)
798
876
  });
@@ -801,7 +879,7 @@ var ControlPipeline = class {
801
879
  } catch (err) {
802
880
  import_log5.log.catch(err, {}, {
803
881
  file: "control-pipeline.ts",
804
- line: 89,
882
+ line: 87,
805
883
  scope: this,
806
884
  callSite: (f, a) => f(...a)
807
885
  });
@@ -811,7 +889,7 @@ var ControlPipeline = class {
811
889
  await this._pipeline.start();
812
890
  (0, import_log5.log)("started", {}, {
813
891
  file: "control-pipeline.ts",
814
- line: 95,
892
+ line: 93,
815
893
  scope: this,
816
894
  callSite: (f, a) => f(...a)
817
895
  });
@@ -819,216 +897,332 @@ var ControlPipeline = class {
819
897
  async stop() {
820
898
  (0, import_log5.log)("stopping...", {}, {
821
899
  file: "control-pipeline.ts",
822
- line: 99,
900
+ line: 97,
823
901
  scope: this,
824
902
  callSite: (f, a) => f(...a)
825
903
  });
826
904
  await this._pipeline.stop();
827
905
  (0, import_log5.log)("stopped", {}, {
828
906
  file: "control-pipeline.ts",
829
- line: 101,
907
+ line: 99,
830
908
  scope: this,
831
909
  callSite: (f, a) => f(...a)
832
910
  });
833
911
  }
834
912
  };
835
913
 
836
- // packages/core/echo/echo-pipeline/src/space/space.ts
837
- var __decorate4 = function(decorators, target, key, desc) {
838
- var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
839
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function")
840
- r = Reflect.decorate(decorators, target, key, desc);
841
- else
842
- for (var i = decorators.length - 1; i >= 0; i--)
843
- if (d = decorators[i])
844
- r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
845
- return c > 3 && r && Object.defineProperty(target, key, r), r;
846
- };
847
- var Space = class Space2 {
848
- constructor({ spaceKey, protocol, genesisFeed, feedProvider }) {
849
- this._addFeedLock = new import_async5.Lock();
850
- this.onCredentialProcessed = new import_util2.Callback();
851
- this.stateUpdate = new import_async5.Event();
852
- this._isOpen = false;
853
- (0, import_node_assert6.default)(spaceKey && feedProvider);
854
- this._key = spaceKey;
855
- this._genesisFeedKey = genesisFeed.key;
856
- this._feedProvider = feedProvider;
857
- this._controlPipeline = new ControlPipeline({
858
- spaceKey,
859
- genesisFeed,
860
- feedProvider
861
- });
862
- this._controlPipeline.onFeedAdmitted.set(async (info) => {
863
- if (info.assertion.designation === import_credentials3.AdmittedFeed.Designation.DATA) {
864
- await this._addFeedLock.executeSynchronized(async () => {
865
- if (this._dataPipeline) {
866
- if (!this._dataPipeline.hasFeed(info.key)) {
867
- return this._dataPipeline.addFeed(await this._feedProvider(info.key));
868
- }
869
- }
914
+ // packages/core/echo/echo-pipeline/src/space/data-pipeline.ts
915
+ var import_node_assert8 = __toESM(require("node:assert"));
916
+ var import_async6 = require("@dxos/async");
917
+ var import_context3 = require("@dxos/context");
918
+ var import_credentials3 = require("@dxos/credentials");
919
+ var import_echo_db3 = require("@dxos/echo-db");
920
+ var import_log8 = require("@dxos/log");
921
+ var import_timeframe3 = require("@dxos/timeframe");
922
+
923
+ // packages/core/echo/echo-pipeline/src/dbhost/data-service-host.ts
924
+ var import_node_assert6 = __toESM(require("node:assert"));
925
+ var import_codec_protobuf = require("@dxos/codec-protobuf");
926
+ var import_echo_db = require("@dxos/echo-db");
927
+ var import_log6 = require("@dxos/log");
928
+ var import_util3 = require("@dxos/util");
929
+ var DataServiceHost = class {
930
+ constructor(_itemManager, _itemDemuxer, _writeStream) {
931
+ this._itemManager = _itemManager;
932
+ this._itemDemuxer = _itemDemuxer;
933
+ this._writeStream = _writeStream;
934
+ this._clientTagMap = new import_util3.ComplexMap(([feedKey, seq]) => `${feedKey.toHex()}:${seq}`);
935
+ }
936
+ /**
937
+ * Real-time subscription to data objects in a space.
938
+ */
939
+ subscribe() {
940
+ return new import_codec_protobuf.Stream(({ next, ctx }) => {
941
+ const objects = Array.from(this._itemManager.entities.values()).map((entity) => entity.createSnapshot());
942
+ next({
943
+ batch: {
944
+ objects
945
+ }
946
+ });
947
+ this._itemDemuxer.mutation.on(ctx, (message) => {
948
+ var _a;
949
+ const { batch, meta } = message;
950
+ (0, import_log6.log)("message", {
951
+ batch,
952
+ meta
953
+ }, {
954
+ file: "data-service-host.ts",
955
+ line: 49,
956
+ scope: this,
957
+ callSite: (f, a) => f(...a)
958
+ });
959
+ const clientTag = this._clientTagMap.get([
960
+ message.meta.feedKey,
961
+ message.meta.seq
962
+ ]);
963
+ (_a = batch.objects) == null ? void 0 : _a.forEach((object) => {
964
+ (0, import_echo_db.setMetadataOnObject)(object, meta);
965
+ });
966
+ if (clientTag) {
967
+ (0, import_echo_db.tagMutationsInBatch)(batch, clientTag);
968
+ }
969
+ next({
970
+ clientTag,
971
+ feedKey: message.meta.feedKey,
972
+ seq: message.meta.seq,
973
+ batch
870
974
  });
871
- }
872
- if (!info.key.equals(genesisFeed.key)) {
873
- this.protocol.addFeed(await feedProvider(info.key));
874
- }
875
- });
876
- this._controlPipeline.onCredentialProcessed.set(async (credential) => {
877
- await this.onCredentialProcessed.callIfSet(credential);
878
- (0, import_log6.log)("onCredentialProcessed", {
879
- credential
880
- }, {
881
- file: "space.ts",
882
- line: 87,
883
- scope: this,
884
- callSite: (f, a) => f(...a)
885
975
  });
886
- this.stateUpdate.emit();
887
976
  });
888
- this.protocol = protocol;
889
- this.protocol.addFeed(genesisFeed);
890
977
  }
891
- get key() {
892
- return this._key;
978
+ async write(request) {
979
+ var _a, _b, _c;
980
+ (0, import_node_assert6.default)(this._writeStream, "Cannot write mutations in readonly mode");
981
+ (0, import_log6.log)("write", {
982
+ clientTag: request.clientTag,
983
+ objectCount: (_b = (_a = request.batch.objects) == null ? void 0 : _a.length) != null ? _b : 0
984
+ }, {
985
+ file: "data-service-host.ts",
986
+ line: 77,
987
+ scope: this,
988
+ callSite: (f, a) => f(...a)
989
+ });
990
+ const message = {
991
+ batch: {
992
+ objects: (_c = request.batch.objects) == null ? void 0 : _c.map((object) => {
993
+ var _a2;
994
+ return {
995
+ ...object,
996
+ mutations: (_a2 = object.mutations) == null ? void 0 : _a2.map((mutation) => ({
997
+ ...mutation,
998
+ meta: void 0
999
+ })),
1000
+ meta: void 0
1001
+ };
1002
+ })
1003
+ }
1004
+ };
1005
+ const receipt = await this._writeStream.write(message, {
1006
+ afterWrite: async (receipt2) => {
1007
+ if (request.clientTag) {
1008
+ (0, import_log6.log)("tag", {
1009
+ clientTag: request.clientTag,
1010
+ feedKey: receipt2.feedKey,
1011
+ seq: receipt2.seq
1012
+ }, {
1013
+ file: "data-service-host.ts",
1014
+ line: 96,
1015
+ scope: this,
1016
+ callSite: (f, a) => f(...a)
1017
+ });
1018
+ this._clientTagMap.set([
1019
+ receipt2.feedKey,
1020
+ receipt2.seq
1021
+ ], request.clientTag);
1022
+ }
1023
+ }
1024
+ });
1025
+ return receipt;
893
1026
  }
894
- get isOpen() {
895
- return this._isOpen;
1027
+ };
1028
+
1029
+ // packages/core/echo/echo-pipeline/src/dbhost/database-host.ts
1030
+ var import_echo_db2 = require("@dxos/echo-db");
1031
+ var DatabaseHost = class {
1032
+ constructor(_outboundStream) {
1033
+ this._outboundStream = _outboundStream;
896
1034
  }
897
- get genesisFeedKey() {
898
- return this._genesisFeedKey;
1035
+ get isReadOnly() {
1036
+ return !!this._outboundStream;
899
1037
  }
900
- get controlFeedKey() {
901
- var _a;
902
- return (_a = this._controlFeed) == null ? void 0 : _a.key;
1038
+ get echoProcessor() {
1039
+ return this._echoProcessor;
903
1040
  }
904
- get dataFeedKey() {
905
- var _a;
906
- return (_a = this._dataFeed) == null ? void 0 : _a.key;
1041
+ async open(itemManager, modelFactory) {
1042
+ this._itemManager = itemManager;
1043
+ this._itemManager._debugLabel = "host";
1044
+ this._itemDemuxer = new import_echo_db2.ItemDemuxer(itemManager, modelFactory);
1045
+ this._echoProcessor = this._itemDemuxer.open();
907
1046
  }
908
- get spaceState() {
909
- return this._controlPipeline.spaceState;
1047
+ async close() {
910
1048
  }
911
- /**
912
- * @test-only
913
- */
914
- get controlPipeline() {
915
- return this._controlPipeline.pipeline;
1049
+ getWriteStream() {
1050
+ return this._outboundStream;
916
1051
  }
917
- setControlFeed(feed) {
918
- (0, import_node_assert6.default)(!this._controlFeed, "Control feed already set.");
919
- this._controlFeed = feed;
920
- this._controlPipeline.setWriteFeed(feed);
921
- return this;
1052
+ createSnapshot() {
1053
+ return this._itemDemuxer.createSnapshot();
922
1054
  }
923
- setDataFeed(feed) {
1055
+ createDataServiceHost() {
924
1056
  var _a;
925
- (0, import_node_assert6.default)(!this._dataFeed, "Data feed already set.");
926
- this._dataFeed = feed;
927
- (_a = this._dataPipeline) == null ? void 0 : _a.setWriteFeed(feed);
928
- return this;
1057
+ return new DataServiceHost(this._itemManager, this._itemDemuxer, (_a = this._outboundStream) != null ? _a : void 0);
929
1058
  }
930
- /**
931
- * Use for diagnostics.
932
- */
933
- getControlFeeds() {
934
- return Array.from(this._controlPipeline.spaceState.feeds.values());
1059
+ };
1060
+
1061
+ // packages/core/echo/echo-pipeline/src/dbhost/snapshot-manager.ts
1062
+ var import_async5 = require("@dxos/async");
1063
+ var import_protocols4 = require("@dxos/protocols");
1064
+ var import_teleport_extension_object_sync = require("@dxos/teleport-extension-object-sync");
1065
+ var __decorate4 = function(decorators, target, key, desc) {
1066
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1067
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function")
1068
+ r = Reflect.decorate(decorators, target, key, desc);
1069
+ else
1070
+ for (var i = decorators.length - 1; i >= 0; i--)
1071
+ if (d = decorators[i])
1072
+ r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
1073
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
1074
+ };
1075
+ var SnapshotManager = class SnapshotManager2 {
1076
+ // prettier-ignore
1077
+ constructor(_snapshotStore) {
1078
+ this._snapshotStore = _snapshotStore;
1079
+ this._objectSync = new import_teleport_extension_object_sync.ObjectSync({
1080
+ getObject: async (id) => {
1081
+ const snapshot = await this._snapshotStore.loadSnapshot(id);
1082
+ if (!snapshot) {
1083
+ return void 0;
1084
+ }
1085
+ return {
1086
+ id,
1087
+ payload: import_protocols4.schema.getCodecForType("dxos.echo.snapshot.SpaceSnapshot").encodeAsAny(snapshot)
1088
+ };
1089
+ },
1090
+ setObject: async (data) => {
1091
+ const snapshot = import_protocols4.schema.getCodecForType("dxos.echo.snapshot.SpaceSnapshot").decode(data.payload);
1092
+ await this._snapshotStore.saveSnapshot(snapshot);
1093
+ }
1094
+ });
1095
+ }
1096
+ get objectSync() {
1097
+ return this._objectSync;
935
1098
  }
936
- /**
937
- * Use for diagnostics.
938
- */
939
- // getDataFeeds(): FeedInfo[] {
940
- // return this._dataPipeline?.getFeeds();
941
- // }
942
1099
  async open() {
943
- (0, import_log6.log)("opening...", {}, {
944
- file: "space.ts",
945
- line: 157,
946
- scope: this,
947
- callSite: (f, a) => f(...a)
948
- });
949
- if (this._isOpen) {
950
- return;
951
- }
952
- await this._controlPipeline.start();
953
- await this.protocol.start();
954
- this._isOpen = true;
955
- (0, import_log6.log)("opened", {}, {
956
- file: "space.ts",
957
- line: 167,
958
- scope: this,
959
- callSite: (f, a) => f(...a)
960
- });
1100
+ await this._objectSync.open();
961
1101
  }
962
1102
  async close() {
963
- (0, import_log6.log)("closing...", {
964
- key: this._key
1103
+ await this._objectSync.close();
1104
+ }
1105
+ async load(id) {
1106
+ const local = await this._snapshotStore.loadSnapshot(id);
1107
+ if (local) {
1108
+ return local;
1109
+ }
1110
+ const remote = await this._objectSync.download(id);
1111
+ return import_protocols4.schema.getCodecForType("dxos.echo.snapshot.SpaceSnapshot").decode(remote.payload);
1112
+ }
1113
+ async store(snapshot) {
1114
+ const id = await this._snapshotStore.saveSnapshot(snapshot);
1115
+ return id;
1116
+ }
1117
+ };
1118
+ SnapshotManager = __decorate4([
1119
+ (0, import_async5.trackLeaks)("open", "close")
1120
+ ], SnapshotManager);
1121
+
1122
+ // packages/core/echo/echo-pipeline/src/dbhost/snapshot-store.ts
1123
+ var import_crypto2 = require("@dxos/crypto");
1124
+ var import_protocols5 = require("@dxos/protocols");
1125
+ var SnapshotStore = class {
1126
+ // prettier-ignore
1127
+ constructor(_directory) {
1128
+ this._directory = _directory;
1129
+ }
1130
+ async saveSnapshot(snapshot) {
1131
+ const encoded = import_protocols5.schema.getCodecForType("dxos.echo.snapshot.SpaceSnapshot").encode(snapshot);
1132
+ const key = await import_crypto2.subtleCrypto.digest("SHA-256", encoded);
1133
+ const keyString = Buffer.from(key).toString("hex");
1134
+ const file = await this._directory.getOrCreateFile(keyString);
1135
+ try {
1136
+ await file.write(0, Buffer.from(encoded));
1137
+ } finally {
1138
+ await file.close();
1139
+ }
1140
+ return keyString;
1141
+ }
1142
+ async loadSnapshot(key) {
1143
+ const file = await this._directory.getOrCreateFile(key);
1144
+ try {
1145
+ const { size } = await file.stat();
1146
+ if (size === 0) {
1147
+ return void 0;
1148
+ }
1149
+ const buffer = await file.read(0, size);
1150
+ return import_protocols5.schema.getCodecForType("dxos.echo.snapshot.SpaceSnapshot").decode(buffer);
1151
+ } finally {
1152
+ await file.close();
1153
+ }
1154
+ }
1155
+ async listSnapshots() {
1156
+ const entries = await this._directory.list();
1157
+ return await Promise.all(entries.map(async (key) => {
1158
+ const { size } = await this._directory.getOrCreateFile(key).stat();
1159
+ return {
1160
+ key,
1161
+ size
1162
+ };
1163
+ }));
1164
+ }
1165
+ };
1166
+
1167
+ // packages/core/echo/echo-pipeline/src/dbhost/data-service.ts
1168
+ var import_node_assert7 = __toESM(require("node:assert"));
1169
+ var import_debug4 = require("@dxos/debug");
1170
+ var import_keys3 = require("@dxos/keys");
1171
+ var import_log7 = require("@dxos/log");
1172
+ var import_util4 = require("@dxos/util");
1173
+ var DataServiceSubscriptions = class {
1174
+ constructor() {
1175
+ this._spaces = new import_util4.ComplexMap(import_keys3.PublicKey.hash);
1176
+ }
1177
+ clear() {
1178
+ this._spaces.clear();
1179
+ }
1180
+ registerSpace(spaceKey, host) {
1181
+ (0, import_log7.log)("Registering space", {
1182
+ spaceKey
965
1183
  }, {
966
- file: "space.ts",
967
- line: 172,
1184
+ file: "data-service.ts",
1185
+ line: 31,
968
1186
  scope: this,
969
1187
  callSite: (f, a) => f(...a)
970
1188
  });
971
- if (!this._isOpen) {
972
- return;
973
- }
974
- await this.protocol.stop();
975
- await this._controlPipeline.stop();
976
- this._isOpen = false;
977
- (0, import_log6.log)("closed", {}, {
978
- file: "space.ts",
979
- line: 182,
1189
+ this._spaces.set(spaceKey, host);
1190
+ }
1191
+ unregisterSpace(spaceKey) {
1192
+ (0, import_log7.log)("Unregistering space", {
1193
+ spaceKey
1194
+ }, {
1195
+ file: "data-service.ts",
1196
+ line: 36,
980
1197
  scope: this,
981
1198
  callSite: (f, a) => f(...a)
982
1199
  });
1200
+ this._spaces.delete(spaceKey);
983
1201
  }
984
- // TODO(dmaretskyi): Make reusable.
985
- async createDataPipeline({ start }) {
986
- (0, import_node_assert6.default)(this._isOpen, "Space must be open to initialize data pipeline.");
987
- (0, import_node_assert6.default)(!this._dataPipeline, "Data pipeline already initialized.");
988
- const pipeline = new Pipeline(start);
989
- if (this._dataFeed) {
990
- pipeline.setWriteFeed(this._dataFeed);
991
- }
992
- this._dataPipeline = pipeline;
993
- await this._addFeedLock.executeSynchronized(async () => {
994
- for (const feed of this._controlPipeline.spaceState.feeds.values()) {
995
- if (feed.assertion.designation === import_credentials3.AdmittedFeed.Designation.DATA && !pipeline.hasFeed(feed.key)) {
996
- await pipeline.addFeed(await this._feedProvider(feed.key));
997
- }
998
- }
999
- });
1000
- return pipeline;
1202
+ getDataService(spaceKey) {
1203
+ return this._spaces.get(spaceKey);
1204
+ }
1205
+ };
1206
+ var DataServiceImpl = class {
1207
+ constructor(_subscriptions) {
1208
+ this._subscriptions = _subscriptions;
1209
+ }
1210
+ subscribe(request) {
1211
+ var _a;
1212
+ (0, import_node_assert7.default)(request.spaceKey);
1213
+ const host = (_a = this._subscriptions.getDataService(request.spaceKey)) != null ? _a : (0, import_debug4.raise)(new Error(`space not found: ${request.spaceKey}`));
1214
+ return host.subscribe();
1215
+ }
1216
+ write(request) {
1217
+ var _a;
1218
+ (0, import_node_assert7.default)(request.spaceKey);
1219
+ (0, import_node_assert7.default)(request.batch);
1220
+ const host = (_a = this._subscriptions.getDataService(request.spaceKey)) != null ? _a : (0, import_debug4.raise)(new Error(`space not found: ${request.spaceKey}`));
1221
+ return host.write(request);
1001
1222
  }
1002
1223
  };
1003
- __decorate4([
1004
- import_log6.logInfo
1005
- ], Space.prototype, "key", null);
1006
- __decorate4([
1007
- import_async5.synchronized
1008
- ], Space.prototype, "open", null);
1009
- __decorate4([
1010
- import_async5.synchronized
1011
- ], Space.prototype, "close", null);
1012
- Space = __decorate4([
1013
- (0, import_async5.trackLeaks)("open", "close")
1014
- ], Space);
1015
-
1016
- // packages/core/echo/echo-pipeline/src/space/space-manager.ts
1017
- var import_async6 = require("@dxos/async");
1018
- var import_debug3 = require("@dxos/debug");
1019
- var import_keys4 = require("@dxos/keys");
1020
- var import_log8 = require("@dxos/log");
1021
- var import_protocols4 = require("@dxos/protocols");
1022
- var import_util4 = require("@dxos/util");
1023
1224
 
1024
- // packages/core/echo/echo-pipeline/src/space/space-protocol.ts
1025
- var import_crypto2 = require("@dxos/crypto");
1026
- var import_keys3 = require("@dxos/keys");
1027
- var import_log7 = require("@dxos/log");
1028
- var import_network_manager = require("@dxos/network-manager");
1029
- var import_teleport2 = require("@dxos/teleport");
1030
- var import_teleport_extension_replicator = require("@dxos/teleport-extension-replicator");
1031
- var import_util3 = require("@dxos/util");
1225
+ // packages/core/echo/echo-pipeline/src/space/data-pipeline.ts
1032
1226
  var __decorate5 = function(decorators, target, key, desc) {
1033
1227
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1034
1228
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function")
@@ -1039,174 +1233,257 @@ var __decorate5 = function(decorators, target, key, desc) {
1039
1233
  r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
1040
1234
  return c > 3 && r && Object.defineProperty(target, key, r), r;
1041
1235
  };
1042
- var MOCK_AUTH_PROVIDER = async (nonce) => Buffer.from("mock");
1043
- var MOCK_AUTH_VERIFIER = async (nonce, credential) => true;
1044
- var SpaceProtocol = class {
1045
- constructor({ topic, swarmIdentity, networkManager, onSessionAuth }) {
1046
- this._feeds = /* @__PURE__ */ new Set();
1047
- this._sessions = new import_util3.ComplexMap(import_keys3.PublicKey.hash);
1048
- this._networkManager = networkManager;
1049
- this._swarmIdentity = swarmIdentity;
1050
- this._onSessionAuth = onSessionAuth;
1051
- this._topic = import_keys3.PublicKey.from((0, import_crypto2.discoveryKey)((0, import_crypto2.sha256)(topic.toHex())));
1236
+ var MESSAGES_PER_SNAPSHOT = 10;
1237
+ var AUTOMATIC_SNAPSHOT_DEBOUNCE_INTERVAL = 5e3;
1238
+ var TIMEFRAME_SAVE_DEBOUNCE_INTERVAL = 500;
1239
+ var DataPipeline = class DataPipeline2 {
1240
+ constructor(_params) {
1241
+ this._params = _params;
1242
+ this._ctx = new import_context3.Context();
1243
+ this._lastAutomaticSnapshotTimeframe = new import_timeframe3.Timeframe();
1244
+ this._isOpen = false;
1245
+ this._lastTimeframeSaveTime = 0;
1246
+ this._lastSnapshotSaveTime = 0;
1247
+ this.onNewEpoch = new import_async6.Event();
1052
1248
  }
1053
- get sessions() {
1054
- return this._sessions;
1249
+ get isOpen() {
1250
+ return this._isOpen;
1055
1251
  }
1056
- get feeds() {
1057
- return this._feeds;
1252
+ get pipeline() {
1253
+ return this._pipeline;
1058
1254
  }
1059
- get _ownPeerKey() {
1060
- return this._swarmIdentity.peerKey;
1255
+ get pipelineState() {
1256
+ var _a;
1257
+ return (_a = this._pipeline) == null ? void 0 : _a.state;
1061
1258
  }
1062
- // TODO(burdon): Create abstraction for Space (e.g., add keys and have provider).
1063
- addFeed(feed) {
1064
- (0, import_log7.log)("addFeed", {
1065
- key: feed.key
1066
- }, {
1067
- file: "space-protocol.ts",
1068
- line: 84,
1069
- scope: this,
1070
- callSite: (f, a) => f(...a)
1071
- });
1072
- this._feeds.add(feed);
1073
- for (const session of this._sessions.values()) {
1074
- session.replicator.addFeed(feed);
1075
- }
1259
+ setTargetTimeframe(timeframe) {
1260
+ var _a;
1261
+ this._targetTimeframe = timeframe;
1262
+ (_a = this._pipeline) == null ? void 0 : _a.state.setTargetTimeframe(timeframe);
1076
1263
  }
1077
- async start() {
1078
- if (this._connection) {
1264
+ createCredentialProcessor() {
1265
+ return {
1266
+ process: async (credential) => {
1267
+ const assertion = (0, import_credentials3.getCredentialAssertion)(credential);
1268
+ if (assertion["@type"] !== "dxos.halo.credentials.Epoch") {
1269
+ return;
1270
+ }
1271
+ (0, import_log8.log)("new epoch", {
1272
+ credential
1273
+ }, {
1274
+ file: "data-pipeline.ts",
1275
+ line: 110,
1276
+ scope: this,
1277
+ callSite: (f, a) => f(...a)
1278
+ });
1279
+ await this._processEpoch(assertion);
1280
+ this.currentEpoch = credential;
1281
+ this.onNewEpoch.emit(credential);
1282
+ }
1283
+ };
1284
+ }
1285
+ async open() {
1286
+ if (this._isOpen) {
1079
1287
  return;
1080
1288
  }
1081
- const credentials = await this._swarmIdentity.credentialProvider(Buffer.from(""));
1082
- const topologyConfig = {
1083
- originateConnections: 4,
1084
- maxPeers: 10,
1085
- sampleSize: 20
1289
+ this._pipeline = new Pipeline();
1290
+ await this._params.onPipelineCreated(this._pipeline);
1291
+ await this._pipeline.start();
1292
+ await this._pipeline.pause();
1293
+ if (this._targetTimeframe) {
1294
+ this._pipeline.state.setTargetTimeframe(this._targetTimeframe);
1295
+ }
1296
+ const feedWriter = {
1297
+ write: (data, options) => {
1298
+ (0, import_node_assert8.default)(this._pipeline, "Pipeline is not initialized.");
1299
+ (0, import_node_assert8.default)(this.currentEpoch, "Epoch is not initialized.");
1300
+ return this._pipeline.writer.write({
1301
+ data
1302
+ }, options);
1303
+ }
1086
1304
  };
1087
- (0, import_log7.log)("starting...", {}, {
1088
- file: "space-protocol.ts",
1089
- line: 107,
1090
- scope: this,
1091
- callSite: (f, a) => f(...a)
1092
- });
1093
- this._connection = await this._networkManager.joinSwarm({
1094
- protocolProvider: this._createProtocolProvider(credentials),
1095
- peerId: this._swarmIdentity.peerKey,
1096
- topic: this._topic,
1097
- topology: new import_network_manager.MMSTTopology(topologyConfig),
1098
- label: `Protocol swarm: ${this._topic}`
1305
+ this.databaseHost = new DatabaseHost(feedWriter);
1306
+ this.itemManager = new import_echo_db3.ItemManager(this._params.modelFactory);
1307
+ await this.databaseHost.open(this.itemManager, this._params.modelFactory);
1308
+ (0, import_async6.scheduleTask)(this._ctx, async () => {
1309
+ await this._consumePipeline();
1099
1310
  });
1100
- (0, import_log7.log)("started", {}, {
1101
- file: "space-protocol.ts",
1102
- line: 116,
1311
+ this._isOpen = true;
1312
+ }
1313
+ async close() {
1314
+ var _a, _b, _c;
1315
+ if (!this._isOpen) {
1316
+ return;
1317
+ }
1318
+ (0, import_log8.log)("close", {}, {
1319
+ file: "data-pipeline.ts",
1320
+ line: 162,
1103
1321
  scope: this,
1104
1322
  callSite: (f, a) => f(...a)
1105
1323
  });
1106
- }
1107
- async stop() {
1108
- if (this._connection) {
1109
- (0, import_log7.log)("stopping...", {}, {
1110
- file: "space-protocol.ts",
1111
- line: 121,
1324
+ this._isOpen = false;
1325
+ await this._ctx.dispose();
1326
+ await ((_a = this._pipeline) == null ? void 0 : _a.stop());
1327
+ try {
1328
+ await this._saveCache();
1329
+ if (this._pipeline) {
1330
+ await this._saveTargetTimeframe(this._pipeline.state.timeframe);
1331
+ }
1332
+ } catch (err) {
1333
+ import_log8.log.catch(err, {}, {
1334
+ file: "data-pipeline.ts",
1335
+ line: 175,
1112
1336
  scope: this,
1113
1337
  callSite: (f, a) => f(...a)
1114
1338
  });
1115
- await this._connection.close();
1116
- (0, import_log7.log)("stopped", {}, {
1117
- file: "space-protocol.ts",
1118
- line: 123,
1339
+ }
1340
+ await ((_b = this.databaseHost) == null ? void 0 : _b.close());
1341
+ await ((_c = this.itemManager) == null ? void 0 : _c.destroy());
1342
+ await this._params.snapshotManager.close();
1343
+ }
1344
+ async _consumePipeline() {
1345
+ (0, import_node_assert8.default)(this._pipeline, "Pipeline is not initialized.");
1346
+ for await (const msg of this._pipeline.consume()) {
1347
+ const { feedKey, seq, data } = msg;
1348
+ (0, import_log8.log)("processing message", {
1349
+ feedKey,
1350
+ seq
1351
+ }, {
1352
+ file: "data-pipeline.ts",
1353
+ line: 187,
1119
1354
  scope: this,
1120
1355
  callSite: (f, a) => f(...a)
1121
1356
  });
1357
+ try {
1358
+ if (data.payload.data) {
1359
+ const feedInfo = this._params.feedInfoProvider(feedKey);
1360
+ if (!feedInfo) {
1361
+ import_log8.log.error("Could not find feed.", {
1362
+ feedKey
1363
+ }, {
1364
+ file: "data-pipeline.ts",
1365
+ line: 193,
1366
+ scope: this,
1367
+ callSite: (f, a) => f(...a)
1368
+ });
1369
+ continue;
1370
+ }
1371
+ await this.databaseHost.echoProcessor({
1372
+ batch: data.payload.data.batch,
1373
+ meta: {
1374
+ feedKey,
1375
+ seq,
1376
+ timeframe: data.timeframe,
1377
+ memberKey: feedInfo.assertion.identityKey
1378
+ }
1379
+ });
1380
+ await this._noteTargetStateIfNeeded(this._pipeline.state.pendingTimeframe);
1381
+ }
1382
+ } catch (err) {
1383
+ import_log8.log.catch(err, {}, {
1384
+ file: "data-pipeline.ts",
1385
+ line: 211,
1386
+ scope: this,
1387
+ callSite: (f, a) => f(...a)
1388
+ });
1389
+ }
1122
1390
  }
1123
1391
  }
1124
- _createProtocolProvider(credentials) {
1125
- return (wireParams) => {
1126
- const session = new SpaceProtocolSession({
1127
- wireParams,
1128
- swarmIdentity: this._swarmIdentity,
1129
- onSessionAuth: this._onSessionAuth
1392
+ _createSnapshot() {
1393
+ (0, import_node_assert8.default)(this.databaseHost, "Database backend is not initialized.");
1394
+ return {
1395
+ spaceKey: this._params.spaceKey.asUint8Array(),
1396
+ timeframe: this._pipeline.state.timeframe,
1397
+ database: this.databaseHost.createSnapshot()
1398
+ };
1399
+ }
1400
+ async _saveTargetTimeframe(timeframe) {
1401
+ var _a;
1402
+ const newTimeframe = import_timeframe3.Timeframe.merge((_a = this._targetTimeframe) != null ? _a : new import_timeframe3.Timeframe(), timeframe);
1403
+ await this._params.metadataStore.setSpaceLatestTimeframe(this._params.spaceKey, newTimeframe);
1404
+ this._targetTimeframe = newTimeframe;
1405
+ }
1406
+ async _saveCache() {
1407
+ var _a;
1408
+ const cache = {};
1409
+ try {
1410
+ const propertiesItem = this.itemManager.items.find((item) => {
1411
+ var _a2, _b;
1412
+ return ((_a2 = item.modelMeta) == null ? void 0 : _a2.type) === "dxos:model/document" && ((_b = (0, import_echo_db3.getStateMachineFromItem)(item)) == null ? void 0 : _b.snapshot().type) === "dxos.sdk.client.Properties";
1130
1413
  });
1131
- this._sessions.set(wireParams.remotePeerId, session);
1132
- for (const feed of this._feeds) {
1133
- session.replicator.addFeed(feed);
1414
+ if (propertiesItem) {
1415
+ cache.properties = (_a = (0, import_echo_db3.getStateMachineFromItem)(propertiesItem)) == null ? void 0 : _a.snapshot();
1134
1416
  }
1135
- return session;
1136
- };
1417
+ } catch (err) {
1418
+ import_log8.log.warn("Failed to cache properties", err, {
1419
+ file: "data-pipeline.ts",
1420
+ line: 245,
1421
+ scope: this,
1422
+ callSite: (f, a) => f(...a)
1423
+ });
1424
+ }
1425
+ await this._params.metadataStore.setCache(this._params.spaceKey, cache);
1137
1426
  }
1138
- };
1139
- __decorate5([
1140
- import_log7.logInfo
1141
- ], SpaceProtocol.prototype, "_topic", void 0);
1142
- __decorate5([
1143
- import_log7.logInfo
1144
- ], SpaceProtocol.prototype, "_ownPeerKey", null);
1145
- var AuthStatus;
1146
- (function(AuthStatus2) {
1147
- AuthStatus2["INITIAL"] = "INITIAL";
1148
- AuthStatus2["SUCCESS"] = "SUCCESS";
1149
- AuthStatus2["FAILURE"] = "FAILURE";
1150
- })(AuthStatus || (AuthStatus = {}));
1151
- var SpaceProtocolSession = class {
1152
- // TODO(dmaretskyi): Allow to pass in extra extensions.
1153
- constructor({ wireParams, swarmIdentity, onSessionAuth }) {
1154
- // TODO(dmaretskyi): Start with upload=false when switching it on the fly works.
1155
- this.replicator = new import_teleport_extension_replicator.ReplicatorExtension().setOptions({
1156
- upload: true
1157
- });
1158
- this._authStatus = AuthStatus.INITIAL;
1159
- this._wireParams = wireParams;
1160
- this._swarmIdentity = swarmIdentity;
1161
- this._onSessionAuth = onSessionAuth;
1162
- this._teleport = new import_teleport2.Teleport(wireParams);
1427
+ async _noteTargetStateIfNeeded(timeframe) {
1428
+ if (Date.now() - this._lastTimeframeSaveTime > TIMEFRAME_SAVE_DEBOUNCE_INTERVAL) {
1429
+ this._lastTimeframeSaveTime = Date.now();
1430
+ await this._saveTargetTimeframe(timeframe);
1431
+ }
1432
+ if (Date.now() - this._lastSnapshotSaveTime > AUTOMATIC_SNAPSHOT_DEBOUNCE_INTERVAL && timeframe.totalMessages() - this._lastAutomaticSnapshotTimeframe.totalMessages() > MESSAGES_PER_SNAPSHOT) {
1433
+ await this._saveCache();
1434
+ }
1163
1435
  }
1164
- get authStatus() {
1165
- return this._authStatus;
1436
+ async _processEpoch(epoch) {
1437
+ (0, import_node_assert8.default)(this._isOpen);
1438
+ (0, import_node_assert8.default)(this._pipeline);
1439
+ if (epoch.snapshotCid) {
1440
+ const snapshot = await this._params.snapshotManager.load(epoch.snapshotCid);
1441
+ this.databaseHost._itemDemuxer.restoreFromSnapshot(snapshot.database);
1442
+ }
1443
+ await this._pipeline.pause();
1444
+ await this._pipeline.setCursor(epoch.timeframe);
1445
+ await this._pipeline.unpause();
1166
1446
  }
1167
- get stream() {
1168
- return this._teleport.stream;
1447
+ async waitUntilTimeframe(timeframe) {
1448
+ (0, import_node_assert8.default)(this._pipeline, "Pipeline is not initialized.");
1449
+ await this._pipeline.state.waitUntilTimeframe(timeframe);
1169
1450
  }
1170
- async initialize() {
1171
- await this._teleport.open();
1172
- this._teleport.addExtension("dxos.mesh.teleport.auth", new AuthExtension({
1173
- provider: this._swarmIdentity.credentialProvider,
1174
- verifier: this._swarmIdentity.credentialAuthenticator,
1175
- onAuthSuccess: () => {
1176
- var _a;
1177
- this._authStatus = AuthStatus.SUCCESS;
1178
- (0, import_log7.log)("Peer authenticated", {}, {
1179
- file: "space-protocol.ts",
1180
- line: 206,
1181
- scope: this,
1182
- callSite: (f, a) => f(...a)
1183
- });
1184
- (_a = this._onSessionAuth) == null ? void 0 : _a.call(this, this._teleport);
1185
- },
1186
- onAuthFailure: () => {
1187
- import_log7.log.warn("Auth failed", {}, {
1188
- file: "space-protocol.ts",
1189
- line: 211,
1190
- scope: this,
1191
- callSite: (f, a) => f(...a)
1192
- });
1193
- this._authStatus = AuthStatus.FAILURE;
1194
- }
1195
- }));
1196
- this._teleport.addExtension("dxos.mesh.teleport.replicator", this.replicator);
1451
+ async createEpoch() {
1452
+ (0, import_node_assert8.default)(this._pipeline);
1453
+ (0, import_node_assert8.default)(this.currentEpoch);
1454
+ await this._pipeline.pause();
1455
+ const snapshot = await this._createSnapshot();
1456
+ const snapshotCid = await this._params.snapshotManager.store(snapshot);
1457
+ const epoch = {
1458
+ previousId: this.currentEpoch.id,
1459
+ timeframe: this._pipeline.state.timeframe,
1460
+ number: this.currentEpoch.subject.assertion.number + 1,
1461
+ snapshotCid
1462
+ };
1463
+ await this._pipeline.unpause();
1464
+ return epoch;
1197
1465
  }
1198
- async destroy() {
1199
- await this._teleport.close();
1466
+ async ensureEpochInitialized() {
1467
+ await this.onNewEpoch.waitForCondition(() => !!this.currentEpoch);
1200
1468
  }
1201
1469
  };
1202
1470
  __decorate5([
1203
- import_log7.logInfo
1204
- ], SpaceProtocolSession.prototype, "_wireParams", void 0);
1471
+ import_async6.synchronized
1472
+ ], DataPipeline.prototype, "open", null);
1205
1473
  __decorate5([
1206
- import_log7.logInfo
1207
- ], SpaceProtocolSession.prototype, "authStatus", null);
1474
+ import_async6.synchronized
1475
+ ], DataPipeline.prototype, "close", null);
1476
+ __decorate5([
1477
+ import_async6.synchronized
1478
+ ], DataPipeline.prototype, "_processEpoch", null);
1479
+ __decorate5([
1480
+ import_async6.synchronized
1481
+ ], DataPipeline.prototype, "createEpoch", null);
1482
+ DataPipeline = __decorate5([
1483
+ (0, import_async6.trackLeaks)("open", "close")
1484
+ ], DataPipeline);
1208
1485
 
1209
- // packages/core/echo/echo-pipeline/src/space/space-manager.ts
1486
+ // packages/core/echo/echo-pipeline/src/space/space.ts
1210
1487
  var __decorate6 = function(decorators, target, key, desc) {
1211
1488
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1212
1489
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function")
@@ -1217,293 +1494,219 @@ var __decorate6 = function(decorators, target, key, desc) {
1217
1494
  r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
1218
1495
  return c > 3 && r && Object.defineProperty(target, key, r), r;
1219
1496
  };
1220
- var SpaceManager = class SpaceManager2 {
1221
- constructor({ feedStore, networkManager }) {
1222
- this._spaces = new import_util4.ComplexMap(import_keys4.PublicKey.hash);
1223
- this._instanceId = import_keys4.PublicKey.random().toHex();
1224
- this._feedStore = feedStore;
1225
- this._networkManager = networkManager;
1497
+ var Space = class Space2 {
1498
+ constructor(params) {
1499
+ this._addFeedLock = new import_async7.Lock();
1500
+ this.onCredentialProcessed = new import_util5.Callback();
1501
+ this.stateUpdate = new import_async7.Event();
1502
+ // Processes epoch credentials.
1503
+ this._dataPipelineCredentialConsumer = void 0;
1504
+ this._isOpen = false;
1505
+ (0, import_node_assert9.default)(params.spaceKey && params.feedProvider);
1506
+ this._key = params.spaceKey;
1507
+ this._genesisFeedKey = params.genesisFeed.key;
1508
+ this._feedProvider = params.feedProvider;
1509
+ this._snapshotManager = params.snapshotManager;
1510
+ this._controlPipeline = new ControlPipeline({
1511
+ spaceKey: params.spaceKey,
1512
+ genesisFeed: params.genesisFeed,
1513
+ feedProvider: params.feedProvider
1514
+ });
1515
+ this._controlPipeline.onFeedAdmitted.set(async (info) => {
1516
+ if (info.assertion.designation === import_credentials4.AdmittedFeed.Designation.DATA) {
1517
+ await this._addFeedLock.executeSynchronized(async () => {
1518
+ if (this._dataPipeline.pipeline) {
1519
+ if (!this._dataPipeline.pipeline.hasFeed(info.key)) {
1520
+ return this._dataPipeline.pipeline.addFeed(await this._feedProvider(info.key));
1521
+ }
1522
+ }
1523
+ });
1524
+ }
1525
+ if (!info.key.equals(params.genesisFeed.key)) {
1526
+ this.protocol.addFeed(await params.feedProvider(info.key));
1527
+ }
1528
+ });
1529
+ this._controlPipeline.onCredentialProcessed.set(async (credential) => {
1530
+ await this.onCredentialProcessed.callIfSet(credential);
1531
+ (0, import_log9.log)("onCredentialProcessed", {
1532
+ credential
1533
+ }, {
1534
+ file: "space.ts",
1535
+ line: 106,
1536
+ scope: this,
1537
+ callSite: (f, a) => f(...a)
1538
+ });
1539
+ this.stateUpdate.emit();
1540
+ });
1541
+ this.protocol = params.protocol;
1542
+ this.protocol.addFeed(params.genesisFeed);
1543
+ this._dataPipeline = new DataPipeline({
1544
+ modelFactory: params.modelFactory,
1545
+ metadataStore: params.metadataStore,
1546
+ snapshotManager: params.snapshotManager,
1547
+ memberKey: params.memberKey,
1548
+ spaceKey: this._key,
1549
+ feedInfoProvider: (feedKey) => this._controlPipeline.spaceState.feeds.get(feedKey),
1550
+ snapshotId: params.snapshotId,
1551
+ onPipelineCreated: async (pipeline) => {
1552
+ if (this._dataFeed) {
1553
+ pipeline.setWriteFeed(this._dataFeed);
1554
+ }
1555
+ await this._addFeedLock.executeSynchronized(async () => {
1556
+ for (const feed of this._controlPipeline.spaceState.feeds.values()) {
1557
+ if (feed.assertion.designation === import_credentials4.AdmittedFeed.Designation.DATA && !pipeline.hasFeed(feed.key)) {
1558
+ await pipeline.addFeed(await this._feedProvider(feed.key));
1559
+ }
1560
+ }
1561
+ });
1562
+ }
1563
+ });
1226
1564
  }
1227
- // TODO(burdon): Remove.
1228
- get spaces() {
1229
- return this._spaces;
1565
+ get key() {
1566
+ return this._key;
1230
1567
  }
1231
- async open() {
1232
- import_log8.log.trace("dxos.echo.space-manager", import_protocols4.trace.begin({
1233
- id: this._instanceId,
1234
- parentId: this._traceParent
1235
- }), {
1236
- file: "space-manager.ts",
1237
- line: 66,
1238
- scope: this,
1239
- callSite: (f, a) => f(...a)
1240
- });
1568
+ get isOpen() {
1569
+ return this._isOpen;
1241
1570
  }
1242
- async close() {
1243
- await Promise.all([
1244
- ...this._spaces.values()
1245
- ].map((space) => space.close()));
1246
- import_log8.log.trace("dxos.echo.space-manager", import_protocols4.trace.end({
1247
- id: this._instanceId
1248
- }), {
1249
- file: "space-manager.ts",
1250
- line: 72,
1251
- scope: this,
1252
- callSite: (f, a) => f(...a)
1253
- });
1571
+ get genesisFeedKey() {
1572
+ return this._genesisFeedKey;
1254
1573
  }
1255
- async constructSpace({ metadata, swarmIdentity, onNetworkConnection }) {
1574
+ get controlFeedKey() {
1256
1575
  var _a;
1257
- (0, import_log8.log)("constructing space...", {
1258
- spaceKey: metadata.genesisFeedKey
1259
- }, {
1260
- file: "space-manager.ts",
1261
- line: 76,
1262
- scope: this,
1263
- callSite: (f, a) => f(...a)
1264
- });
1265
- const genesisFeed = await this._feedStore.openFeed((_a = metadata.genesisFeedKey) != null ? _a : (0, import_debug3.failUndefined)());
1266
- const spaceKey = metadata.key;
1267
- const protocol = new SpaceProtocol({
1268
- topic: spaceKey,
1269
- swarmIdentity,
1270
- networkManager: this._networkManager,
1271
- onSessionAuth: onNetworkConnection
1272
- });
1273
- const space = new Space({
1274
- spaceKey,
1275
- protocol,
1276
- genesisFeed,
1277
- feedProvider: (feedKey) => this._feedStore.openFeed(feedKey)
1278
- });
1279
- this._spaces.set(space.key, space);
1280
- return space;
1576
+ return (_a = this._controlFeed) == null ? void 0 : _a.key;
1281
1577
  }
1282
- };
1283
- __decorate6([
1284
- import_async6.synchronized
1285
- ], SpaceManager.prototype, "open", null);
1286
- __decorate6([
1287
- import_async6.synchronized
1288
- ], SpaceManager.prototype, "close", null);
1289
- SpaceManager = __decorate6([
1290
- (0, import_async6.trackLeaks)("open", "close")
1291
- ], SpaceManager);
1292
-
1293
- // packages/core/echo/echo-pipeline/src/space/genesis.ts
1294
- var import_credentials4 = require("@dxos/credentials");
1295
- var import_debug4 = require("@dxos/debug");
1296
- var import_credentials5 = require("@dxos/protocols/proto/dxos/halo/credentials");
1297
- var spaceGenesis = async (keyring, signingContext, space) => {
1298
- var _a, _b, _c;
1299
- const credentials = [
1300
- await (0, import_credentials4.createCredential)({
1301
- signer: keyring,
1302
- issuer: space.key,
1303
- subject: space.key,
1304
- assertion: {
1305
- "@type": "dxos.halo.credentials.SpaceGenesis",
1306
- spaceKey: space.key
1307
- }
1308
- }),
1309
- await (0, import_credentials4.createCredential)({
1310
- signer: keyring,
1311
- issuer: space.key,
1312
- subject: signingContext.identityKey,
1313
- assertion: {
1314
- "@type": "dxos.halo.credentials.SpaceMember",
1315
- spaceKey: space.key,
1316
- role: import_credentials5.SpaceMember.Role.ADMIN,
1317
- profile: signingContext.profile,
1318
- genesisFeedKey: (_a = space.controlFeedKey) != null ? _a : (0, import_debug4.failUndefined)()
1319
- }
1320
- }),
1321
- await signingContext.credentialSigner.createCredential({
1322
- subject: (_b = space.controlFeedKey) != null ? _b : (0, import_debug4.failUndefined)(),
1323
- assertion: {
1324
- "@type": "dxos.halo.credentials.AdmittedFeed",
1325
- spaceKey: space.key,
1326
- identityKey: signingContext.identityKey,
1327
- deviceKey: signingContext.deviceKey,
1328
- designation: import_credentials5.AdmittedFeed.Designation.CONTROL
1329
- }
1330
- }),
1331
- await signingContext.credentialSigner.createCredential({
1332
- subject: (_c = space.dataFeedKey) != null ? _c : (0, import_debug4.failUndefined)(),
1333
- assertion: {
1334
- "@type": "dxos.halo.credentials.AdmittedFeed",
1335
- spaceKey: space.key,
1336
- identityKey: signingContext.identityKey,
1337
- deviceKey: signingContext.deviceKey,
1338
- designation: import_credentials5.AdmittedFeed.Designation.DATA
1339
- }
1340
- })
1341
- ];
1342
- for (const credential of credentials) {
1343
- await space.controlPipeline.writer.write({
1344
- credential: {
1345
- credential
1346
- }
1347
- });
1578
+ get dataFeedKey() {
1579
+ var _a;
1580
+ return (_a = this._dataFeed) == null ? void 0 : _a.key;
1348
1581
  }
1349
- return credentials;
1350
- };
1351
-
1352
- // packages/core/echo/echo-pipeline/src/space/data-pipeline.ts
1353
- var import_node_assert9 = __toESM(require("node:assert"));
1354
- var import_async8 = require("@dxos/async");
1355
- var import_context3 = require("@dxos/context");
1356
- var import_debug6 = require("@dxos/debug");
1357
- var import_echo_db3 = require("@dxos/echo-db");
1358
- var import_log11 = require("@dxos/log");
1359
- var import_timeframe3 = require("@dxos/timeframe");
1360
-
1361
- // packages/core/echo/echo-pipeline/src/dbhost/data-service-host.ts
1362
- var import_node_assert7 = __toESM(require("node:assert"));
1363
- var import_codec_protobuf = require("@dxos/codec-protobuf");
1364
- var import_echo_db = require("@dxos/echo-db");
1365
- var import_log9 = require("@dxos/log");
1366
- var import_util5 = require("@dxos/util");
1367
- var DataServiceHost = class {
1368
- constructor(_itemManager, _itemDemuxer, _writeStream) {
1369
- this._itemManager = _itemManager;
1370
- this._itemDemuxer = _itemDemuxer;
1371
- this._writeStream = _writeStream;
1372
- this._clientTagMap = new import_util5.ComplexMap(([feedKey, seq]) => `${feedKey.toHex()}:${seq}`);
1582
+ get spaceState() {
1583
+ return this._controlPipeline.spaceState;
1373
1584
  }
1374
1585
  /**
1375
- * Real-time subscription to data objects in a space.
1586
+ * @test-only
1376
1587
  */
1377
- subscribe() {
1378
- return new import_codec_protobuf.Stream(({ next, ctx }) => {
1379
- const objects = Array.from(this._itemManager.entities.values()).map((entity) => entity.createSnapshot());
1380
- next({
1381
- batch: {
1382
- objects
1383
- }
1384
- });
1385
- this._itemDemuxer.mutation.on(ctx, (message) => {
1386
- var _a;
1387
- const { batch, meta } = message;
1388
- (0, import_log9.log)("message", {
1389
- batch,
1390
- meta
1391
- }, {
1392
- file: "data-service-host.ts",
1393
- line: 49,
1394
- scope: this,
1395
- callSite: (f, a) => f(...a)
1396
- });
1397
- const clientTag = this._clientTagMap.get([
1398
- message.meta.feedKey,
1399
- message.meta.seq
1400
- ]);
1401
- (_a = batch.objects) == null ? void 0 : _a.forEach((object) => {
1402
- (0, import_echo_db.setMetadataOnObject)(object, meta);
1403
- });
1404
- if (clientTag) {
1405
- (0, import_echo_db.tagMutationsInBatch)(batch, clientTag);
1406
- }
1407
- next({
1408
- clientTag,
1409
- feedKey: message.meta.feedKey,
1410
- seq: message.meta.seq,
1411
- batch
1412
- });
1413
- });
1414
- });
1588
+ get controlPipeline() {
1589
+ return this._controlPipeline.pipeline;
1415
1590
  }
1416
- async write(request) {
1417
- var _a, _b, _c;
1418
- (0, import_node_assert7.default)(this._writeStream, "Cannot write mutations in readonly mode");
1419
- (0, import_log9.log)("write", {
1420
- clientTag: request.clientTag,
1421
- objectCount: (_b = (_a = request.batch.objects) == null ? void 0 : _a.length) != null ? _b : 0
1422
- }, {
1423
- file: "data-service-host.ts",
1424
- line: 77,
1425
- scope: this,
1426
- callSite: (f, a) => f(...a)
1427
- });
1428
- const message = {
1429
- batch: {
1430
- objects: (_c = request.batch.objects) == null ? void 0 : _c.map((object) => {
1431
- var _a2;
1432
- return {
1433
- ...object,
1434
- mutations: (_a2 = object.mutations) == null ? void 0 : _a2.map((mutation) => ({
1435
- ...mutation,
1436
- meta: void 0
1437
- })),
1438
- meta: void 0
1439
- };
1440
- })
1441
- }
1442
- };
1443
- const receipt = await this._writeStream.write(message, {
1444
- afterWrite: async (receipt2) => {
1445
- if (request.clientTag) {
1446
- (0, import_log9.log)("tag", {
1447
- clientTag: request.clientTag,
1448
- feedKey: receipt2.feedKey,
1449
- seq: receipt2.seq
1450
- }, {
1451
- file: "data-service-host.ts",
1452
- line: 96,
1453
- scope: this,
1454
- callSite: (f, a) => f(...a)
1455
- });
1456
- this._clientTagMap.set([
1457
- receipt2.feedKey,
1458
- receipt2.seq
1459
- ], request.clientTag);
1460
- }
1461
- }
1462
- });
1463
- return receipt;
1591
+ get dataPipeline() {
1592
+ return this._dataPipeline;
1464
1593
  }
1465
- };
1466
-
1467
- // packages/core/echo/echo-pipeline/src/dbhost/database-host.ts
1468
- var import_echo_db2 = require("@dxos/echo-db");
1469
- var DatabaseHost = class {
1470
- constructor(_outboundStream, _snapshot) {
1471
- this._outboundStream = _outboundStream;
1472
- this._snapshot = _snapshot;
1594
+ get snapshotManager() {
1595
+ return this._snapshotManager;
1473
1596
  }
1474
- get isReadOnly() {
1475
- return !!this._outboundStream;
1597
+ setControlFeed(feed) {
1598
+ (0, import_node_assert9.default)(!this._controlFeed, "Control feed already set.");
1599
+ this._controlFeed = feed;
1600
+ this._controlPipeline.setWriteFeed(feed);
1601
+ return this;
1476
1602
  }
1477
- get echoProcessor() {
1478
- return this._echoProcessor;
1603
+ setDataFeed(feed) {
1604
+ var _a;
1605
+ (0, import_node_assert9.default)(!this._dataFeed, "Data feed already set.");
1606
+ this._dataFeed = feed;
1607
+ (_a = this._dataPipeline.pipeline) == null ? void 0 : _a.setWriteFeed(feed);
1608
+ return this;
1479
1609
  }
1480
- async open(itemManager, modelFactory) {
1481
- this._itemManager = itemManager;
1482
- this._itemManager._debugLabel = "host";
1483
- this._itemDemuxer = new import_echo_db2.ItemDemuxer(itemManager, modelFactory);
1484
- this._echoProcessor = this._itemDemuxer.open();
1485
- if (this._snapshot) {
1486
- this._itemDemuxer.restoreFromSnapshot(this._snapshot);
1610
+ /**
1611
+ * Use for diagnostics.
1612
+ */
1613
+ getControlFeeds() {
1614
+ return Array.from(this._controlPipeline.spaceState.feeds.values());
1615
+ }
1616
+ /**
1617
+ * Use for diagnostics.
1618
+ */
1619
+ // getDataFeeds(): FeedInfo[] {
1620
+ // return this._dataPipeline?.getFeeds();
1621
+ // }
1622
+ async open() {
1623
+ (0, import_log9.log)("opening...", {}, {
1624
+ file: "space.ts",
1625
+ line: 208,
1626
+ scope: this,
1627
+ callSite: (f, a) => f(...a)
1628
+ });
1629
+ if (this._isOpen) {
1630
+ return;
1487
1631
  }
1632
+ await this._controlPipeline.start();
1633
+ await this.protocol.start();
1634
+ this._dataPipelineCredentialConsumer = this._controlPipeline.spaceState.registerProcessor(this._dataPipeline.createCredentialProcessor());
1635
+ this._isOpen = true;
1636
+ (0, import_log9.log)("opened", {}, {
1637
+ file: "space.ts",
1638
+ line: 222,
1639
+ scope: this,
1640
+ callSite: (f, a) => f(...a)
1641
+ });
1488
1642
  }
1489
1643
  async close() {
1490
- }
1491
- getWriteStream() {
1492
- return this._outboundStream;
1493
- }
1494
- createSnapshot() {
1495
- return this._itemDemuxer.createSnapshot();
1496
- }
1497
- createDataServiceHost() {
1498
1644
  var _a;
1499
- return new DataServiceHost(this._itemManager, this._itemDemuxer, (_a = this._outboundStream) != null ? _a : void 0);
1645
+ (0, import_log9.log)("closing...", {
1646
+ key: this._key
1647
+ }, {
1648
+ file: "space.ts",
1649
+ line: 227,
1650
+ scope: this,
1651
+ callSite: (f, a) => f(...a)
1652
+ });
1653
+ if (!this._isOpen) {
1654
+ return;
1655
+ }
1656
+ await ((_a = this._dataPipelineCredentialConsumer) == null ? void 0 : _a.close());
1657
+ this._dataPipelineCredentialConsumer = void 0;
1658
+ await this._dataPipeline.close();
1659
+ await this.protocol.stop();
1660
+ await this._controlPipeline.stop();
1661
+ this._isOpen = false;
1662
+ (0, import_log9.log)("closed", {}, {
1663
+ file: "space.ts",
1664
+ line: 241,
1665
+ scope: this,
1666
+ callSite: (f, a) => f(...a)
1667
+ });
1668
+ }
1669
+ async initializeDataPipeline() {
1670
+ import_log9.log.info("initializeDataPipeline", {}, {
1671
+ file: "space.ts",
1672
+ line: 245,
1673
+ scope: this,
1674
+ callSite: (f, a) => f(...a)
1675
+ });
1676
+ (0, import_node_assert9.default)(this._isOpen, "Space must be open to initialize data pipeline.");
1677
+ await this._dataPipeline.open();
1678
+ await this._dataPipelineCredentialConsumer.open();
1500
1679
  }
1501
1680
  };
1681
+ __decorate6([
1682
+ import_log9.logInfo
1683
+ ], Space.prototype, "key", null);
1684
+ __decorate6([
1685
+ import_async7.synchronized
1686
+ ], Space.prototype, "open", null);
1687
+ __decorate6([
1688
+ import_async7.synchronized
1689
+ ], Space.prototype, "close", null);
1690
+ Space = __decorate6([
1691
+ (0, import_async7.trackLeaks)("open", "close")
1692
+ ], Space);
1502
1693
 
1503
- // packages/core/echo/echo-pipeline/src/dbhost/snapshot-manager.ts
1504
- var import_async7 = require("@dxos/async");
1505
- var import_protocols5 = require("@dxos/protocols");
1506
- var import_teleport_extension_object_sync = require("@dxos/teleport-extension-object-sync");
1694
+ // packages/core/echo/echo-pipeline/src/space/space-manager.ts
1695
+ var import_async8 = require("@dxos/async");
1696
+ var import_debug5 = require("@dxos/debug");
1697
+ var import_keys5 = require("@dxos/keys");
1698
+ var import_log11 = require("@dxos/log");
1699
+ var import_protocols6 = require("@dxos/protocols");
1700
+ var import_util7 = require("@dxos/util");
1701
+
1702
+ // packages/core/echo/echo-pipeline/src/space/space-protocol.ts
1703
+ var import_crypto3 = require("@dxos/crypto");
1704
+ var import_keys4 = require("@dxos/keys");
1705
+ var import_log10 = require("@dxos/log");
1706
+ var import_network_manager = require("@dxos/network-manager");
1707
+ var import_teleport2 = require("@dxos/teleport");
1708
+ var import_teleport_extension_replicator = require("@dxos/teleport-extension-replicator");
1709
+ var import_util6 = require("@dxos/util");
1507
1710
  var __decorate7 = function(decorators, target, key, desc) {
1508
1711
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1509
1712
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function")
@@ -1514,147 +1717,174 @@ var __decorate7 = function(decorators, target, key, desc) {
1514
1717
  r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
1515
1718
  return c > 3 && r && Object.defineProperty(target, key, r), r;
1516
1719
  };
1517
- var SnapshotManager = class SnapshotManager2 {
1518
- // prettier-ignore
1519
- constructor(_snapshotStore) {
1520
- this._snapshotStore = _snapshotStore;
1521
- this._objectSync = new import_teleport_extension_object_sync.ObjectSync({
1522
- getObject: async (id) => {
1523
- const snapshot = await this._snapshotStore.loadSnapshot(id);
1524
- if (!snapshot) {
1525
- return void 0;
1526
- }
1527
- return {
1528
- id,
1529
- payload: import_protocols5.schema.getCodecForType("dxos.echo.snapshot.SpaceSnapshot").encodeAsAny(snapshot)
1530
- };
1531
- },
1532
- setObject: async (data) => {
1533
- const snapshot = import_protocols5.schema.getCodecForType("dxos.echo.snapshot.SpaceSnapshot").decode(data.payload);
1534
- await this._snapshotStore.saveSnapshot(snapshot);
1535
- }
1536
- });
1537
- }
1538
- get objectSync() {
1539
- return this._objectSync;
1540
- }
1541
- async open() {
1542
- await this._objectSync.open();
1543
- }
1544
- async close() {
1545
- await this._objectSync.close();
1720
+ var MOCK_AUTH_PROVIDER = async (nonce) => Buffer.from("mock");
1721
+ var MOCK_AUTH_VERIFIER = async (nonce, credential) => true;
1722
+ var SpaceProtocol = class {
1723
+ constructor({ topic, swarmIdentity, networkManager, onSessionAuth }) {
1724
+ this._feeds = /* @__PURE__ */ new Set();
1725
+ this._sessions = new import_util6.ComplexMap(import_keys4.PublicKey.hash);
1726
+ this._networkManager = networkManager;
1727
+ this._swarmIdentity = swarmIdentity;
1728
+ this._onSessionAuth = onSessionAuth;
1729
+ this._topic = import_keys4.PublicKey.from((0, import_crypto3.discoveryKey)((0, import_crypto3.sha256)(topic.toHex())));
1546
1730
  }
1547
- async load(id) {
1548
- const local = await this._snapshotStore.loadSnapshot(id);
1549
- if (local) {
1550
- return local;
1551
- }
1552
- const remote = await this._objectSync.download(id);
1553
- return import_protocols5.schema.getCodecForType("dxos.echo.snapshot.SpaceSnapshot").decode(remote.payload);
1731
+ get sessions() {
1732
+ return this._sessions;
1554
1733
  }
1555
- async store(snapshot) {
1556
- const id = await this._snapshotStore.saveSnapshot(snapshot);
1557
- return id;
1734
+ get feeds() {
1735
+ return this._feeds;
1558
1736
  }
1559
- };
1560
- SnapshotManager = __decorate7([
1561
- (0, import_async7.trackLeaks)("open", "close")
1562
- ], SnapshotManager);
1563
-
1564
- // packages/core/echo/echo-pipeline/src/dbhost/snapshot-store.ts
1565
- var import_crypto3 = require("@dxos/crypto");
1566
- var import_protocols6 = require("@dxos/protocols");
1567
- var SnapshotStore = class {
1568
- // prettier-ignore
1569
- constructor(_directory) {
1570
- this._directory = _directory;
1737
+ get _ownPeerKey() {
1738
+ return this._swarmIdentity.peerKey;
1571
1739
  }
1572
- async saveSnapshot(snapshot) {
1573
- const encoded = import_protocols6.schema.getCodecForType("dxos.echo.snapshot.SpaceSnapshot").encode(snapshot);
1574
- const key = await import_crypto3.subtleCrypto.digest("SHA-256", encoded);
1575
- const keyString = Buffer.from(key).toString("hex");
1576
- const file = await this._directory.getOrCreateFile(keyString);
1577
- try {
1578
- await file.write(0, Buffer.from(encoded));
1579
- } finally {
1580
- await file.close();
1740
+ // TODO(burdon): Create abstraction for Space (e.g., add keys and have provider).
1741
+ addFeed(feed) {
1742
+ (0, import_log10.log)("addFeed", {
1743
+ key: feed.key
1744
+ }, {
1745
+ file: "space-protocol.ts",
1746
+ line: 84,
1747
+ scope: this,
1748
+ callSite: (f, a) => f(...a)
1749
+ });
1750
+ this._feeds.add(feed);
1751
+ for (const session of this._sessions.values()) {
1752
+ session.replicator.addFeed(feed);
1581
1753
  }
1582
- return keyString;
1583
1754
  }
1584
- async loadSnapshot(key) {
1585
- const file = await this._directory.getOrCreateFile(key);
1586
- try {
1587
- const { size } = await file.stat();
1588
- if (size === 0) {
1589
- return void 0;
1590
- }
1591
- const buffer = await file.read(0, size);
1592
- return import_protocols6.schema.getCodecForType("dxos.echo.snapshot.SpaceSnapshot").decode(buffer);
1593
- } finally {
1594
- await file.close();
1755
+ async start() {
1756
+ if (this._connection) {
1757
+ return;
1595
1758
  }
1596
- }
1597
- };
1598
-
1599
- // packages/core/echo/echo-pipeline/src/dbhost/data-service.ts
1600
- var import_node_assert8 = __toESM(require("node:assert"));
1601
- var import_debug5 = require("@dxos/debug");
1602
- var import_keys5 = require("@dxos/keys");
1603
- var import_log10 = require("@dxos/log");
1604
- var import_util6 = require("@dxos/util");
1605
- var DataServiceSubscriptions = class {
1606
- constructor() {
1607
- this._spaces = new import_util6.ComplexMap(import_keys5.PublicKey.hash);
1608
- }
1609
- clear() {
1610
- this._spaces.clear();
1611
- }
1612
- registerSpace(spaceKey, host) {
1613
- (0, import_log10.log)("Registering space", {
1614
- spaceKey
1615
- }, {
1616
- file: "data-service.ts",
1617
- line: 31,
1759
+ const credentials = await this._swarmIdentity.credentialProvider(Buffer.from(""));
1760
+ const topologyConfig = {
1761
+ originateConnections: 4,
1762
+ maxPeers: 10,
1763
+ sampleSize: 20
1764
+ };
1765
+ (0, import_log10.log)("starting...", {}, {
1766
+ file: "space-protocol.ts",
1767
+ line: 107,
1618
1768
  scope: this,
1619
1769
  callSite: (f, a) => f(...a)
1620
1770
  });
1621
- this._spaces.set(spaceKey, host);
1771
+ this._connection = await this._networkManager.joinSwarm({
1772
+ protocolProvider: this._createProtocolProvider(credentials),
1773
+ peerId: this._swarmIdentity.peerKey,
1774
+ topic: this._topic,
1775
+ topology: new import_network_manager.MMSTTopology(topologyConfig),
1776
+ label: `Protocol swarm: ${this._topic}`
1777
+ });
1778
+ (0, import_log10.log)("started", {}, {
1779
+ file: "space-protocol.ts",
1780
+ line: 116,
1781
+ scope: this,
1782
+ callSite: (f, a) => f(...a)
1783
+ });
1784
+ }
1785
+ async stop() {
1786
+ if (this._connection) {
1787
+ (0, import_log10.log)("stopping...", {}, {
1788
+ file: "space-protocol.ts",
1789
+ line: 121,
1790
+ scope: this,
1791
+ callSite: (f, a) => f(...a)
1792
+ });
1793
+ await this._connection.close();
1794
+ (0, import_log10.log)("stopped", {}, {
1795
+ file: "space-protocol.ts",
1796
+ line: 123,
1797
+ scope: this,
1798
+ callSite: (f, a) => f(...a)
1799
+ });
1800
+ }
1622
1801
  }
1623
- unregisterSpace(spaceKey) {
1624
- (0, import_log10.log)("Unregistering space", {
1625
- spaceKey
1626
- }, {
1627
- file: "data-service.ts",
1628
- line: 36,
1629
- scope: this,
1630
- callSite: (f, a) => f(...a)
1802
+ _createProtocolProvider(credentials) {
1803
+ return (wireParams) => {
1804
+ const session = new SpaceProtocolSession({
1805
+ wireParams,
1806
+ swarmIdentity: this._swarmIdentity,
1807
+ onSessionAuth: this._onSessionAuth
1808
+ });
1809
+ this._sessions.set(wireParams.remotePeerId, session);
1810
+ for (const feed of this._feeds) {
1811
+ session.replicator.addFeed(feed);
1812
+ }
1813
+ return session;
1814
+ };
1815
+ }
1816
+ };
1817
+ __decorate7([
1818
+ import_log10.logInfo
1819
+ ], SpaceProtocol.prototype, "_topic", void 0);
1820
+ __decorate7([
1821
+ import_log10.logInfo
1822
+ ], SpaceProtocol.prototype, "_ownPeerKey", null);
1823
+ var AuthStatus;
1824
+ (function(AuthStatus2) {
1825
+ AuthStatus2["INITIAL"] = "INITIAL";
1826
+ AuthStatus2["SUCCESS"] = "SUCCESS";
1827
+ AuthStatus2["FAILURE"] = "FAILURE";
1828
+ })(AuthStatus || (AuthStatus = {}));
1829
+ var SpaceProtocolSession = class {
1830
+ // TODO(dmaretskyi): Allow to pass in extra extensions.
1831
+ constructor({ wireParams, swarmIdentity, onSessionAuth }) {
1832
+ // TODO(dmaretskyi): Start with upload=false when switching it on the fly works.
1833
+ this.replicator = new import_teleport_extension_replicator.ReplicatorExtension().setOptions({
1834
+ upload: true
1631
1835
  });
1632
- this._spaces.delete(spaceKey);
1836
+ this._authStatus = AuthStatus.INITIAL;
1837
+ this._wireParams = wireParams;
1838
+ this._swarmIdentity = swarmIdentity;
1839
+ this._onSessionAuth = onSessionAuth;
1840
+ this._teleport = new import_teleport2.Teleport(wireParams);
1633
1841
  }
1634
- getDataService(spaceKey) {
1635
- return this._spaces.get(spaceKey);
1842
+ get authStatus() {
1843
+ return this._authStatus;
1636
1844
  }
1637
- };
1638
- var DataServiceImpl = class {
1639
- constructor(_subscriptions) {
1640
- this._subscriptions = _subscriptions;
1845
+ get stream() {
1846
+ return this._teleport.stream;
1641
1847
  }
1642
- subscribe(request) {
1643
- var _a;
1644
- (0, import_node_assert8.default)(request.spaceKey);
1645
- const host = (_a = this._subscriptions.getDataService(request.spaceKey)) != null ? _a : (0, import_debug5.raise)(new Error(`space not found: ${request.spaceKey}`));
1646
- return host.subscribe();
1848
+ async initialize() {
1849
+ await this._teleport.open();
1850
+ this._teleport.addExtension("dxos.mesh.teleport.auth", new AuthExtension({
1851
+ provider: this._swarmIdentity.credentialProvider,
1852
+ verifier: this._swarmIdentity.credentialAuthenticator,
1853
+ onAuthSuccess: () => {
1854
+ var _a;
1855
+ this._authStatus = AuthStatus.SUCCESS;
1856
+ (0, import_log10.log)("Peer authenticated", {}, {
1857
+ file: "space-protocol.ts",
1858
+ line: 206,
1859
+ scope: this,
1860
+ callSite: (f, a) => f(...a)
1861
+ });
1862
+ (_a = this._onSessionAuth) == null ? void 0 : _a.call(this, this._teleport);
1863
+ },
1864
+ onAuthFailure: () => {
1865
+ import_log10.log.warn("Auth failed", {}, {
1866
+ file: "space-protocol.ts",
1867
+ line: 211,
1868
+ scope: this,
1869
+ callSite: (f, a) => f(...a)
1870
+ });
1871
+ this._authStatus = AuthStatus.FAILURE;
1872
+ }
1873
+ }));
1874
+ this._teleport.addExtension("dxos.mesh.teleport.replicator", this.replicator);
1647
1875
  }
1648
- write(request) {
1649
- var _a;
1650
- (0, import_node_assert8.default)(request.spaceKey);
1651
- (0, import_node_assert8.default)(request.batch);
1652
- const host = (_a = this._subscriptions.getDataService(request.spaceKey)) != null ? _a : (0, import_debug5.raise)(new Error(`space not found: ${request.spaceKey}`));
1653
- return host.write(request);
1876
+ async destroy() {
1877
+ await this._teleport.close();
1654
1878
  }
1655
1879
  };
1880
+ __decorate7([
1881
+ import_log10.logInfo
1882
+ ], SpaceProtocolSession.prototype, "_wireParams", void 0);
1883
+ __decorate7([
1884
+ import_log10.logInfo
1885
+ ], SpaceProtocolSession.prototype, "authStatus", null);
1656
1886
 
1657
- // packages/core/echo/echo-pipeline/src/space/data-pipeline.ts
1887
+ // packages/core/echo/echo-pipeline/src/space/space-manager.ts
1658
1888
  var __decorate8 = function(decorators, target, key, desc) {
1659
1889
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1660
1890
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function")
@@ -1665,204 +1895,85 @@ var __decorate8 = function(decorators, target, key, desc) {
1665
1895
  r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
1666
1896
  return c > 3 && r && Object.defineProperty(target, key, r), r;
1667
1897
  };
1668
- var MESSAGES_PER_SNAPSHOT = 10;
1669
- var AUTOMATIC_SNAPSHOT_DEBOUNCE_INTERVAL = 5e3;
1670
- var TIMEFRAME_SAVE_DEBOUNCE_INTERVAL = 500;
1671
- var DISABLE_SNAPSHOT_CACHE = true;
1672
- var DataPipeline = class DataPipeline2 {
1673
- constructor(_params) {
1674
- this._params = _params;
1675
- this._ctx = new import_context3.Context();
1676
- this._lastAutomaticSnapshotTimeframe = new import_timeframe3.Timeframe();
1677
- this._isOpen = false;
1678
- this._lastTimeframeSaveTime = 0;
1679
- this._lastSnapshotSaveTime = 0;
1680
- }
1681
- get isOpen() {
1682
- return this._isOpen;
1898
+ var SpaceManager = class SpaceManager2 {
1899
+ constructor({ feedStore, networkManager, modelFactory, metadataStore, snapshotStore }) {
1900
+ this._spaces = new import_util7.ComplexMap(import_keys5.PublicKey.hash);
1901
+ this._instanceId = import_keys5.PublicKey.random().toHex();
1902
+ this._feedStore = feedStore;
1903
+ this._networkManager = networkManager;
1904
+ this._modelFactory = modelFactory;
1905
+ this._metadataStore = metadataStore;
1906
+ this._snapshotStore = snapshotStore;
1683
1907
  }
1684
- get pipelineState() {
1685
- var _a;
1686
- return (_a = this._pipeline) == null ? void 0 : _a.state;
1908
+ // TODO(burdon): Remove.
1909
+ get spaces() {
1910
+ return this._spaces;
1687
1911
  }
1688
- get snapshotTimeframe() {
1689
- var _a;
1690
- return (_a = this._snapshot) == null ? void 0 : _a.timeframe;
1912
+ async open() {
1691
1913
  }
1692
- getStartTimeframe() {
1693
- var _a;
1694
- return snapshotTimeframeToStartingTimeframe((_a = this.snapshotTimeframe) != null ? _a : new import_timeframe3.Timeframe());
1914
+ async close() {
1915
+ await Promise.all([
1916
+ ...this._spaces.values()
1917
+ ].map((space) => space.close()));
1695
1918
  }
1696
- setTargetTimeframe(timeframe) {
1919
+ async constructSpace({ metadata, swarmIdentity, onNetworkConnection, memberKey }) {
1697
1920
  var _a;
1698
- this._targetTimeframe = timeframe;
1699
- (_a = this._pipeline) == null ? void 0 : _a.state.setTargetTimeframe(timeframe);
1700
- }
1701
- async open(spaceContext) {
1702
- var _a, _b, _c, _d;
1703
- if (this._isOpen) {
1704
- return;
1705
- }
1706
- this._spaceContext = spaceContext;
1707
- if (this._params.snapshotId && !DISABLE_SNAPSHOT_CACHE) {
1708
- this._snapshot = await this._params.snapshotManager.load(this._params.snapshotId);
1709
- this._lastAutomaticSnapshotTimeframe = (_b = (_a = this._snapshot) == null ? void 0 : _a.timeframe) != null ? _b : new import_timeframe3.Timeframe();
1710
- }
1711
- this._pipeline = await this._spaceContext.openPipeline(this.getStartTimeframe());
1712
- if (this._targetTimeframe) {
1713
- this._pipeline.state.setTargetTimeframe(this._targetTimeframe);
1714
- }
1715
- const feedWriter = createMappedFeedWriter((data) => ({
1716
- data
1717
- }), (_c = this._pipeline.writer) != null ? _c : (0, import_debug6.failUndefined)());
1718
- this.databaseBackend = new DatabaseHost(feedWriter, (_d = this._snapshot) == null ? void 0 : _d.database);
1719
- this._itemManager = new import_echo_db3.ItemManager(this._params.modelFactory);
1720
- await this.databaseBackend.open(this._itemManager, this._params.modelFactory);
1721
- (0, import_async8.scheduleTask)(this._ctx, async () => {
1722
- await this._consumePipeline();
1921
+ import_log11.log.trace("dxos.echo.space-manager.construct-space", import_protocols6.trace.begin({
1922
+ id: this._instanceId
1923
+ }), {
1924
+ file: "space-manager.ts",
1925
+ line: 74,
1926
+ scope: this,
1927
+ callSite: (f, a) => f(...a)
1723
1928
  });
1724
- this._isOpen = true;
1725
- }
1726
- async close() {
1727
- var _a, _b, _c;
1728
- if (!this._isOpen) {
1729
- return;
1730
- }
1731
- (0, import_log11.log)("close", {}, {
1732
- file: "data-pipeline.ts",
1733
- line: 145,
1929
+ (0, import_log11.log)("constructing space...", {
1930
+ spaceKey: metadata.genesisFeedKey
1931
+ }, {
1932
+ file: "space-manager.ts",
1933
+ line: 75,
1734
1934
  scope: this,
1735
1935
  callSite: (f, a) => f(...a)
1736
1936
  });
1737
- this._isOpen = false;
1738
- await this._ctx.dispose();
1739
- await ((_a = this._pipeline) == null ? void 0 : _a.stop());
1740
- try {
1741
- if (this._pipeline) {
1742
- await this._saveTargetTimeframe(this._pipeline.state.timeframe);
1743
- if (!DISABLE_SNAPSHOT_CACHE) {
1744
- await this._saveSnapshot(this._pipeline.state.timeframe);
1745
- }
1746
- }
1747
- } catch (err) {
1748
- import_log11.log.catch(err, {}, {
1749
- file: "data-pipeline.ts",
1750
- line: 160,
1751
- scope: this,
1752
- callSite: (f, a) => f(...a)
1753
- });
1754
- }
1755
- await ((_b = this.databaseBackend) == null ? void 0 : _b.close());
1756
- await ((_c = this._itemManager) == null ? void 0 : _c.destroy());
1757
- await this._params.snapshotManager.close();
1758
- }
1759
- async _consumePipeline() {
1760
- (0, import_node_assert9.default)(this._pipeline, "Pipeline is not initialized.");
1761
- for await (const msg of this._pipeline.consume()) {
1762
- const { feedKey, seq, data } = msg;
1763
- (0, import_log11.log)("processing message", {
1764
- msg
1765
- }, {
1766
- file: "data-pipeline.ts",
1767
- line: 172,
1768
- scope: this,
1769
- callSite: (f, a) => f(...a)
1770
- });
1771
- try {
1772
- if (data.payload.data) {
1773
- const feedInfo = this._params.feedInfoProvider(feedKey);
1774
- if (!feedInfo) {
1775
- import_log11.log.error("Could not find feed.", {
1776
- feedKey
1777
- }, {
1778
- file: "data-pipeline.ts",
1779
- line: 178,
1780
- scope: this,
1781
- callSite: (f, a) => f(...a)
1782
- });
1783
- continue;
1784
- }
1785
- await this.databaseBackend.echoProcessor({
1786
- batch: data.payload.data.batch,
1787
- meta: {
1788
- feedKey,
1789
- seq,
1790
- timeframe: data.timeframe,
1791
- memberKey: feedInfo.assertion.identityKey
1792
- }
1793
- });
1794
- await this._noteTargetStateIfNeeded(this._pipeline.state.pendingTimeframe);
1795
- }
1796
- } catch (err) {
1797
- import_log11.log.catch(err, {}, {
1798
- file: "data-pipeline.ts",
1799
- line: 196,
1800
- scope: this,
1801
- callSite: (f, a) => f(...a)
1802
- });
1803
- }
1804
- }
1805
- }
1806
- _createSnapshot(timeframe) {
1807
- (0, import_node_assert9.default)(this.databaseBackend, "Database backend is not initialized.");
1808
- return {
1809
- spaceKey: this._params.spaceKey.asUint8Array(),
1810
- timeframe,
1811
- database: this.databaseBackend.createSnapshot()
1812
- };
1813
- }
1814
- async _saveSnapshot(timeframe) {
1815
- const snapshot = await this._createSnapshot(timeframe);
1816
- const snapshotKey = await this._params.snapshotManager.store(snapshot);
1817
- await this._params.metadataStore.setSpaceSnapshot(this._params.spaceKey, snapshotKey);
1818
- return snapshot;
1819
- }
1820
- async _saveTargetTimeframe(timeframe) {
1821
- var _a;
1822
- const newTimeframe = import_timeframe3.Timeframe.merge((_a = this._targetTimeframe) != null ? _a : new import_timeframe3.Timeframe(), timeframe);
1823
- await this._params.metadataStore.setSpaceLatestTimeframe(this._params.spaceKey, newTimeframe);
1824
- this._targetTimeframe = newTimeframe;
1825
- }
1826
- async _noteTargetStateIfNeeded(timeframe) {
1827
- var _a;
1828
- if (Date.now() - this._lastTimeframeSaveTime > TIMEFRAME_SAVE_DEBOUNCE_INTERVAL) {
1829
- this._lastTimeframeSaveTime = Date.now();
1830
- await this._saveTargetTimeframe(timeframe);
1831
- }
1832
- if (!DISABLE_SNAPSHOT_CACHE && Date.now() - this._lastSnapshotSaveTime > AUTOMATIC_SNAPSHOT_DEBOUNCE_INTERVAL && timeframe.totalMessages() - this._lastAutomaticSnapshotTimeframe.totalMessages() > MESSAGES_PER_SNAPSHOT) {
1833
- this._lastSnapshotSaveTime = Date.now();
1834
- const snapshot = await this._saveSnapshot(timeframe);
1835
- this._lastAutomaticSnapshotTimeframe = (_a = snapshot.timeframe) != null ? _a : (0, import_debug6.failUndefined)();
1836
- (0, import_log11.log)("save", {
1837
- snapshot
1838
- }, {
1839
- file: "data-pipeline.ts",
1840
- line: 239,
1841
- scope: this,
1842
- callSite: (f, a) => f(...a)
1843
- });
1844
- }
1845
- }
1846
- async waitUntilTimeframe(timeframe) {
1847
- (0, import_node_assert9.default)(this._pipeline, "Pipeline is not initialized.");
1848
- await this._pipeline.state.waitUntilTimeframe(timeframe);
1937
+ const genesisFeed = await this._feedStore.openFeed((_a = metadata.genesisFeedKey) != null ? _a : (0, import_debug5.failUndefined)());
1938
+ const spaceKey = metadata.key;
1939
+ const protocol = new SpaceProtocol({
1940
+ topic: spaceKey,
1941
+ swarmIdentity,
1942
+ networkManager: this._networkManager,
1943
+ onSessionAuth: onNetworkConnection
1944
+ });
1945
+ const snapshotManager = new SnapshotManager(this._snapshotStore);
1946
+ const space = new Space({
1947
+ spaceKey,
1948
+ protocol,
1949
+ genesisFeed,
1950
+ feedProvider: (feedKey) => this._feedStore.openFeed(feedKey),
1951
+ modelFactory: this._modelFactory,
1952
+ metadataStore: this._metadataStore,
1953
+ snapshotManager,
1954
+ memberKey
1955
+ });
1956
+ this._spaces.set(space.key, space);
1957
+ import_log11.log.trace("dxos.echo.space-manager.construct-space", import_protocols6.trace.end({
1958
+ id: this._instanceId
1959
+ }), {
1960
+ file: "space-manager.ts",
1961
+ line: 100,
1962
+ scope: this,
1963
+ callSite: (f, a) => f(...a)
1964
+ });
1965
+ return space;
1849
1966
  }
1850
1967
  };
1851
1968
  __decorate8([
1852
1969
  import_async8.synchronized
1853
- ], DataPipeline.prototype, "open", null);
1970
+ ], SpaceManager.prototype, "open", null);
1854
1971
  __decorate8([
1855
1972
  import_async8.synchronized
1856
- ], DataPipeline.prototype, "close", null);
1857
- DataPipeline = __decorate8([
1973
+ ], SpaceManager.prototype, "close", null);
1974
+ SpaceManager = __decorate8([
1858
1975
  (0, import_async8.trackLeaks)("open", "close")
1859
- ], DataPipeline);
1860
- var snapshotTimeframeToStartingTimeframe = (snapshotTimeframe) => {
1861
- return snapshotTimeframe.map(([key, seq]) => [
1862
- key,
1863
- seq + 1
1864
- ]);
1865
- };
1976
+ ], SpaceManager);
1866
1977
  // Annotate the CommonJS export names for ESM import in node:
1867
1978
  0 && (module.exports = {
1868
1979
  AuthExtension,
@@ -1895,7 +2006,6 @@ var snapshotTimeframeToStartingTimeframe = (snapshotTimeframe) => {
1895
2006
  createMappedFeedWriter,
1896
2007
  mapFeedIndexesToTimeframe,
1897
2008
  mapTimeframeToFeedIndexes,
1898
- spaceGenesis,
1899
2009
  startAfter,
1900
2010
  valueEncoding
1901
2011
  });