@dxos/echo-pipeline 0.6.5 → 0.6.6-main.e1a6e1f

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 (30) hide show
  1. package/dist/lib/browser/{chunk-2MII6KJX.mjs → chunk-LNIQX6PQ.mjs} +2132 -2119
  2. package/dist/lib/browser/chunk-LNIQX6PQ.mjs.map +7 -0
  3. package/dist/lib/browser/index.mjs +1 -1
  4. package/dist/lib/browser/meta.json +1 -1
  5. package/dist/lib/browser/testing/index.mjs +1 -1
  6. package/dist/lib/node/{chunk-6MWU4MHX.cjs → chunk-LTR534RP.cjs} +2125 -2112
  7. package/dist/lib/node/chunk-LTR534RP.cjs.map +7 -0
  8. package/dist/lib/node/index.cjs +35 -35
  9. package/dist/lib/node/index.cjs.map +1 -1
  10. package/dist/lib/node/meta.json +1 -1
  11. package/dist/lib/node/testing/index.cjs +11 -11
  12. package/dist/types/src/automerge/echo-network-adapter.d.ts.map +1 -1
  13. package/dist/types/src/automerge/echo-replicator.d.ts +5 -1
  14. package/dist/types/src/automerge/echo-replicator.d.ts.map +1 -1
  15. package/dist/types/src/automerge/network-protocol.d.ts +3 -28
  16. package/dist/types/src/automerge/network-protocol.d.ts.map +1 -1
  17. package/dist/types/src/space/space-manager.d.ts +3 -1
  18. package/dist/types/src/space/space-manager.d.ts.map +1 -1
  19. package/dist/types/src/space/space-protocol.d.ts +10 -3
  20. package/dist/types/src/space/space-protocol.d.ts.map +1 -1
  21. package/dist/types/src/space/space.d.ts.map +1 -1
  22. package/package.json +33 -33
  23. package/src/automerge/echo-network-adapter.ts +5 -0
  24. package/src/automerge/echo-replicator.ts +5 -1
  25. package/src/automerge/network-protocol.ts +8 -34
  26. package/src/space/space-manager.ts +15 -2
  27. package/src/space/space-protocol.ts +42 -5
  28. package/src/space/space.ts +4 -4
  29. package/dist/lib/browser/chunk-2MII6KJX.mjs.map +0 -7
  30. package/dist/lib/node/chunk-6MWU4MHX.cjs.map +0 -7
@@ -434,138 +434,121 @@ var diffCollectionState = (local, remote) => {
434
434
  };
435
435
  };
436
436
 
437
- // packages/core/echo/echo-pipeline/src/automerge/leveldb-storage-adapter.ts
438
- import { LifecycleState, Resource as Resource3 } from "@dxos/context";
439
- var LevelDBStorageAdapter = class extends Resource3 {
440
- constructor(_params) {
441
- super();
442
- this._params = _params;
443
- }
444
- async load(keyArray) {
445
- try {
446
- if (this._lifecycleState !== LifecycleState.OPEN) {
447
- return void 0;
448
- }
449
- const startMs = Date.now();
450
- const chunk = await this._params.db.get(keyArray, {
451
- ...encodingOptions
452
- });
453
- this._params.monitor?.recordBytesLoaded(chunk.byteLength);
454
- this._params.monitor?.recordLoadDuration(Date.now() - startMs);
455
- return chunk;
456
- } catch (err) {
457
- if (isLevelDbNotFoundError(err)) {
458
- return void 0;
459
- }
460
- throw err;
461
- }
462
- }
463
- async save(keyArray, binary) {
464
- if (this._lifecycleState !== LifecycleState.OPEN) {
465
- return void 0;
466
- }
467
- const startMs = Date.now();
468
- const batch = this._params.db.batch();
469
- await this._params.callbacks?.beforeSave?.({
470
- path: keyArray,
471
- batch
437
+ // packages/core/echo/echo-pipeline/src/space/auth.ts
438
+ import { runInContext, scheduleTask as scheduleTask2 } from "@dxos/async";
439
+ import { Context } from "@dxos/context";
440
+ import { randomBytes } from "@dxos/crypto";
441
+ import { invariant as invariant3 } from "@dxos/invariant";
442
+ import { log as log2 } from "@dxos/log";
443
+ import { schema as schema4 } from "@dxos/protocols";
444
+ import { RpcExtension } from "@dxos/teleport";
445
+ var __dxlog_file3 = "/home/runner/work/dxos/dxos/packages/core/echo/echo-pipeline/src/space/auth.ts";
446
+ var AuthExtension = class extends RpcExtension {
447
+ constructor(_authParams) {
448
+ super({
449
+ requested: {
450
+ AuthService: schema4.getService("dxos.mesh.teleport.auth.AuthService")
451
+ },
452
+ exposed: {
453
+ AuthService: schema4.getService("dxos.mesh.teleport.auth.AuthService")
454
+ },
455
+ timeout: 60 * 1e3
472
456
  });
473
- batch.put(keyArray, Buffer.from(binary), {
474
- ...encodingOptions
457
+ this._authParams = _authParams;
458
+ this._ctx = new Context({
459
+ onError: (err) => {
460
+ log2.catch(err, void 0, {
461
+ F: __dxlog_file3,
462
+ L: 28,
463
+ S: this,
464
+ C: (f, a) => f(...a)
465
+ });
466
+ }
467
+ }, {
468
+ F: __dxlog_file3,
469
+ L: 26
475
470
  });
476
- await batch.write();
477
- this._params.monitor?.recordBytesStored(binary.byteLength);
478
- await this._params.callbacks?.afterSave?.(keyArray);
479
- this._params.monitor?.recordStoreDuration(Date.now() - startMs);
480
471
  }
481
- async remove(keyArray) {
482
- if (this._lifecycleState !== LifecycleState.OPEN) {
483
- return void 0;
484
- }
485
- await this._params.db.del(keyArray, {
486
- ...encodingOptions
472
+ async getHandlers() {
473
+ return {
474
+ AuthService: {
475
+ authenticate: async ({ challenge }) => {
476
+ try {
477
+ const credential = await this._authParams.provider(challenge);
478
+ if (!credential) {
479
+ throw new Error("auth rejected");
480
+ }
481
+ return {
482
+ credential
483
+ };
484
+ } catch (err) {
485
+ log2.error("failed to generate auth credentials", err, {
486
+ F: __dxlog_file3,
487
+ L: 55,
488
+ S: this,
489
+ C: (f, a) => f(...a)
490
+ });
491
+ throw new Error("auth rejected");
492
+ }
493
+ }
494
+ }
495
+ };
496
+ }
497
+ async onOpen(context) {
498
+ await super.onOpen(context);
499
+ scheduleTask2(this._ctx, async () => {
500
+ try {
501
+ const challenge = randomBytes(32);
502
+ const { credential } = await this.rpc.AuthService.authenticate({
503
+ challenge
504
+ });
505
+ invariant3(credential?.length > 0, "invalid credential", {
506
+ F: __dxlog_file3,
507
+ L: 69,
508
+ S: this,
509
+ A: [
510
+ "credential?.length > 0",
511
+ "'invalid credential'"
512
+ ]
513
+ });
514
+ const success = await this._authParams.verifier(challenge, credential);
515
+ invariant3(success, "credential not verified", {
516
+ F: __dxlog_file3,
517
+ L: 71,
518
+ S: this,
519
+ A: [
520
+ "success",
521
+ "'credential not verified'"
522
+ ]
523
+ });
524
+ runInContext(this._ctx, () => this._authParams.onAuthSuccess());
525
+ } catch (err) {
526
+ log2("auth failed", err, {
527
+ F: __dxlog_file3,
528
+ L: 74,
529
+ S: this,
530
+ C: (f, a) => f(...a)
531
+ });
532
+ this.close();
533
+ this._authParams.onAuthFailure();
534
+ }
487
535
  });
488
536
  }
489
- async loadRange(keyPrefix) {
490
- if (this._lifecycleState !== LifecycleState.OPEN) {
491
- return [];
492
- }
493
- const startMs = Date.now();
494
- const result = [];
495
- for await (const [key, value] of this._params.db.iterator({
496
- gte: keyPrefix,
497
- lte: [
498
- ...keyPrefix,
499
- "\uFFFF"
500
- ],
501
- ...encodingOptions
502
- })) {
503
- result.push({
504
- key,
505
- data: value
506
- });
507
- this._params.monitor?.recordBytesLoaded(value.byteLength);
508
- }
509
- this._params.monitor?.recordLoadDuration(Date.now() - startMs);
510
- return result;
537
+ async onClose() {
538
+ await this._ctx.dispose();
539
+ await super.onClose();
511
540
  }
512
- async removeRange(keyPrefix) {
513
- if (this._lifecycleState !== LifecycleState.OPEN) {
514
- return void 0;
515
- }
516
- const batch = this._params.db.batch();
517
- for await (const [key] of this._params.db.iterator({
518
- gte: keyPrefix,
519
- lte: [
520
- ...keyPrefix,
521
- "\uFFFF"
522
- ],
523
- ...encodingOptions
524
- })) {
525
- batch.del(key, {
526
- ...encodingOptions
527
- });
528
- }
529
- await batch.write();
541
+ async onAbort() {
542
+ await this._ctx.dispose();
543
+ await super.onAbort();
530
544
  }
531
545
  };
532
- var keyEncoder = {
533
- encode: (key) => Buffer.from(key.map((k) => k.replaceAll("%", "%25").replaceAll("-", "%2D")).join("-")),
534
- decode: (key) => Buffer.from(key).toString().split("-").map((k) => k.replaceAll("%2D", "-").replaceAll("%25", "%")),
535
- format: "buffer"
536
- };
537
- var encodingOptions = {
538
- keyEncoding: keyEncoder,
539
- valueEncoding: "buffer"
540
- };
541
- var isLevelDbNotFoundError = (err) => err.code === "LEVEL_NOT_FOUND";
542
546
 
543
- // packages/core/echo/echo-pipeline/src/automerge/automerge-host.ts
544
- import { Event as Event2, asyncTimeout } from "@dxos/async";
545
- import { getBackend, getHeads, isAutomerge, equals as headsEquals, save } from "@dxos/automerge/automerge";
546
- import { Repo } from "@dxos/automerge/automerge-repo";
547
- import { Context, Resource as Resource4, cancelWithContext as cancelWithContext2 } from "@dxos/context";
548
- import { invariant as invariant4 } from "@dxos/invariant";
549
- import { PublicKey as PublicKey2 } from "@dxos/keys";
547
+ // packages/core/echo/echo-pipeline/src/pipeline/timeframe-clock.ts
548
+ import { Event as Event2 } from "@dxos/async";
549
+ import { timed } from "@dxos/debug";
550
550
  import { log as log3 } from "@dxos/log";
551
- import { objectPointerCodec } from "@dxos/protocols";
552
- import { trace } from "@dxos/tracing";
553
-
554
- // packages/core/echo/echo-pipeline/src/automerge/echo-network-adapter.ts
555
- import { synchronized, Trigger } from "@dxos/async";
556
- import { NetworkAdapter } from "@dxos/automerge/automerge-repo";
557
- import { LifecycleState as LifecycleState2 } from "@dxos/context";
558
- import { invariant as invariant3 } from "@dxos/invariant";
559
- import { log as log2 } from "@dxos/log";
560
- import { nonNullable } from "@dxos/util";
561
-
562
- // packages/core/echo/echo-pipeline/src/automerge/network-protocol.ts
563
- var MESSAGE_TYPE_COLLECTION_QUERY = "collection-query";
564
- var isCollectionQueryMessage = (message) => message.type === MESSAGE_TYPE_COLLECTION_QUERY;
565
- var MESSAGE_TYPE_COLLECTION_STATE = "collection-state";
566
- var isCollectionStateMessage = (message) => message.type === MESSAGE_TYPE_COLLECTION_STATE;
567
-
568
- // packages/core/echo/echo-pipeline/src/automerge/echo-network-adapter.ts
551
+ import { Timeframe } from "@dxos/timeframe";
569
552
  function _ts_decorate(decorators, target, key, desc) {
570
553
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
571
554
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function")
@@ -576,360 +559,575 @@ function _ts_decorate(decorators, target, key, desc) {
576
559
  r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
577
560
  return c > 3 && r && Object.defineProperty(target, key, r), r;
578
561
  }
579
- var __dxlog_file3 = "/home/runner/work/dxos/dxos/packages/core/echo/echo-pipeline/src/automerge/echo-network-adapter.ts";
580
- var EchoNetworkAdapter = class extends NetworkAdapter {
581
- constructor(_params) {
582
- super();
583
- this._params = _params;
584
- this._replicators = /* @__PURE__ */ new Set();
585
- this._connections = /* @__PURE__ */ new Map();
586
- this._lifecycleState = LifecycleState2.CLOSED;
587
- this._connected = new Trigger();
562
+ var __dxlog_file4 = "/home/runner/work/dxos/dxos/packages/core/echo/echo-pipeline/src/pipeline/timeframe-clock.ts";
563
+ var mapTimeframeToFeedIndexes = (timeframe) => timeframe.frames().map(([feedKey, index]) => ({
564
+ feedKey,
565
+ index
566
+ }));
567
+ var mapFeedIndexesToTimeframe = (indexes) => new Timeframe(indexes.map(({ feedKey, index }) => [
568
+ feedKey,
569
+ index
570
+ ]));
571
+ var startAfter = (timeframe) => timeframe.frames().map(([feedKey, index]) => ({
572
+ feedKey,
573
+ index: index + 1
574
+ }));
575
+ var TimeframeClock = class {
576
+ constructor(_timeframe = new Timeframe()) {
577
+ this._timeframe = _timeframe;
578
+ this.update = new Event2();
579
+ this._pendingTimeframe = _timeframe;
588
580
  }
589
- connect(peerId, peerMetadata) {
590
- this.peerId = peerId;
591
- this.peerMetadata = peerMetadata;
592
- this._connected.wake();
581
+ /**
582
+ * Timeframe that was processed by ECHO.
583
+ */
584
+ get timeframe() {
585
+ return this._timeframe;
593
586
  }
594
- send(message) {
595
- this._send(message);
587
+ /**
588
+ * Timeframe that is currently being processed by ECHO.
589
+ * Will be equal to `timeframe` after the processing is complete.
590
+ */
591
+ get pendingTimeframe() {
592
+ return this._pendingTimeframe;
596
593
  }
597
- disconnect() {
594
+ setTimeframe(timeframe) {
595
+ this._timeframe = timeframe;
596
+ this._pendingTimeframe = timeframe;
597
+ this.update.emit(this._timeframe);
598
598
  }
599
- async open() {
600
- if (this._lifecycleState === LifecycleState2.OPEN) {
601
- return;
602
- }
603
- this._lifecycleState = LifecycleState2.OPEN;
604
- log2("emit ready", void 0, {
605
- F: __dxlog_file3,
606
- L: 78,
599
+ updatePendingTimeframe(key, seq) {
600
+ this._pendingTimeframe = Timeframe.merge(this._pendingTimeframe, new Timeframe([
601
+ [
602
+ key,
603
+ seq
604
+ ]
605
+ ]));
606
+ }
607
+ updateTimeframe() {
608
+ this._timeframe = this._pendingTimeframe;
609
+ this.update.emit(this._timeframe);
610
+ }
611
+ hasGaps(timeframe) {
612
+ const gaps = Timeframe.dependencies(timeframe, this._timeframe);
613
+ return !gaps.isEmpty();
614
+ }
615
+ async waitUntilReached(target) {
616
+ log3("waitUntilReached", {
617
+ target,
618
+ current: this._timeframe
619
+ }, {
620
+ F: __dxlog_file4,
621
+ L: 70,
607
622
  S: this,
608
623
  C: (f, a) => f(...a)
609
624
  });
610
- this.emit("ready", {
611
- network: this
625
+ await this.update.waitForCondition(() => {
626
+ log3("check if reached", {
627
+ target,
628
+ current: this._timeframe,
629
+ deps: Timeframe.dependencies(target, this._timeframe)
630
+ }, {
631
+ F: __dxlog_file4,
632
+ L: 72,
633
+ S: this,
634
+ C: (f, a) => f(...a)
635
+ });
636
+ return Timeframe.dependencies(target, this._timeframe).isEmpty();
612
637
  });
613
638
  }
614
- async close() {
615
- if (this._lifecycleState === LifecycleState2.CLOSED) {
616
- return this;
617
- }
618
- for (const replicator of this._replicators) {
619
- await replicator.disconnect();
639
+ };
640
+ _ts_decorate([
641
+ timed(5e3)
642
+ ], TimeframeClock.prototype, "waitUntilReached", null);
643
+
644
+ // packages/core/echo/echo-pipeline/src/pipeline/pipeline.ts
645
+ import { Event as Event3, sleepWithContext, synchronized, Trigger } from "@dxos/async";
646
+ import { Context as Context2, rejectOnDispose } from "@dxos/context";
647
+ import { failUndefined } from "@dxos/debug";
648
+ import { FeedSetIterator } from "@dxos/feed-store";
649
+ import { invariant as invariant5 } from "@dxos/invariant";
650
+ import { PublicKey as PublicKey2 } from "@dxos/keys";
651
+ import { log as log5 } from "@dxos/log";
652
+ import { Timeframe as Timeframe2 } from "@dxos/timeframe";
653
+ import { ComplexMap } from "@dxos/util";
654
+
655
+ // packages/core/echo/echo-pipeline/src/pipeline/message-selector.ts
656
+ import { invariant as invariant4 } from "@dxos/invariant";
657
+ import { log as log4 } from "@dxos/log";
658
+ var __dxlog_file5 = "/home/runner/work/dxos/dxos/packages/core/echo/echo-pipeline/src/pipeline/message-selector.ts";
659
+ var createMessageSelector = (timeframeClock) => {
660
+ return (messages) => {
661
+ for (let i = 0; i < messages.length; i++) {
662
+ const { data: { timeframe } } = messages[i];
663
+ invariant4(timeframe, void 0, {
664
+ F: __dxlog_file5,
665
+ L: 25,
666
+ S: void 0,
667
+ A: [
668
+ "timeframe",
669
+ ""
670
+ ]
671
+ });
672
+ if (!timeframeClock.hasGaps(timeframe)) {
673
+ return i;
674
+ }
620
675
  }
621
- this._replicators.clear();
622
- this._lifecycleState = LifecycleState2.CLOSED;
676
+ log4("Skipping...", void 0, {
677
+ F: __dxlog_file5,
678
+ L: 33,
679
+ S: void 0,
680
+ C: (f, a) => f(...a)
681
+ });
682
+ };
683
+ };
684
+
685
+ // packages/core/echo/echo-pipeline/src/pipeline/pipeline.ts
686
+ function _ts_decorate2(decorators, target, key, desc) {
687
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
688
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function")
689
+ r = Reflect.decorate(decorators, target, key, desc);
690
+ else
691
+ for (var i = decorators.length - 1; i >= 0; i--)
692
+ if (d = decorators[i])
693
+ r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
694
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
695
+ }
696
+ var __dxlog_file6 = "/home/runner/work/dxos/dxos/packages/core/echo/echo-pipeline/src/pipeline/pipeline.ts";
697
+ var PipelineState = class {
698
+ constructor(_feeds, _timeframeClock) {
699
+ this._feeds = _feeds;
700
+ this._timeframeClock = _timeframeClock;
701
+ this._ctx = new Context2(void 0, {
702
+ F: __dxlog_file6,
703
+ L: 41
704
+ });
705
+ this.timeframeUpdate = this._timeframeClock.update;
706
+ this.stalled = new Event3();
707
+ this._startTimeframe = new Timeframe2();
708
+ this._reachedTarget = false;
623
709
  }
624
- async whenConnected() {
625
- await this._connected.wait({
626
- timeout: 1e4
710
+ /**
711
+ * Latest theoretical timeframe based on the last mutation in each feed.
712
+ * NOTE: This might never be reached if the mutation dependencies
713
+ */
714
+ // TODO(dmaretskyi): Rename `totalTimeframe`? or `lastTimeframe`.
715
+ get endTimeframe() {
716
+ return mapFeedIndexesToTimeframe(Array.from(this._feeds.values()).filter((feed) => feed.length > 0).map((feed) => ({
717
+ feedKey: feed.key,
718
+ index: feed.length - 1
719
+ })));
720
+ }
721
+ get startTimeframe() {
722
+ return this._startTimeframe;
723
+ }
724
+ get timeframe() {
725
+ return this._timeframeClock.timeframe;
726
+ }
727
+ get pendingTimeframe() {
728
+ return this._timeframeClock.pendingTimeframe;
729
+ }
730
+ get targetTimeframe() {
731
+ return this._targetTimeframe ? this._targetTimeframe : new Timeframe2();
732
+ }
733
+ get reachedTarget() {
734
+ return this._reachedTarget;
735
+ }
736
+ get feeds() {
737
+ return Array.from(this._feeds.values());
738
+ }
739
+ async waitUntilTimeframe(target) {
740
+ await this._timeframeClock.waitUntilReached(target);
741
+ }
742
+ setTargetTimeframe(target) {
743
+ this._targetTimeframe = target;
744
+ }
745
+ /**
746
+ * Wait until the pipeline processes all messages in the feed and reaches the target timeframe if that is set.
747
+ *
748
+ * This function will resolve immediately if the pipeline is stalled.
749
+ *
750
+ * @param timeout Timeout in milliseconds to specify the maximum wait time.
751
+ */
752
+ async waitUntilReachedTargetTimeframe({ ctx = new Context2(void 0, {
753
+ F: __dxlog_file6,
754
+ L: 129
755
+ }), timeout, breakOnStall = true } = {}) {
756
+ log5("waitUntilReachedTargetTimeframe", {
757
+ timeout,
758
+ current: this.timeframe,
759
+ target: this.targetTimeframe
760
+ }, {
761
+ F: __dxlog_file6,
762
+ L: 133,
763
+ S: this,
764
+ C: (f, a) => f(...a)
627
765
  });
766
+ this._reachedTargetPromise ??= Promise.race([
767
+ this._timeframeClock.update.waitForCondition(() => {
768
+ return Timeframe2.dependencies(this.targetTimeframe, this.timeframe).isEmpty();
769
+ }),
770
+ ...breakOnStall ? [
771
+ this.stalled.discardParameter().waitForCount(1)
772
+ ] : []
773
+ ]);
774
+ let done = false;
775
+ if (timeout) {
776
+ return Promise.race([
777
+ rejectOnDispose(ctx),
778
+ rejectOnDispose(this._ctx),
779
+ this._reachedTargetPromise.then(() => {
780
+ done = true;
781
+ this._reachedTarget = true;
782
+ }),
783
+ sleepWithContext(this._ctx, timeout).then(() => {
784
+ if (done) {
785
+ return;
786
+ }
787
+ log5.warn("waitUntilReachedTargetTimeframe timed out", {
788
+ timeout,
789
+ current: this.timeframe,
790
+ target: this.targetTimeframe,
791
+ dependencies: Timeframe2.dependencies(this.targetTimeframe, this.timeframe)
792
+ }, {
793
+ F: __dxlog_file6,
794
+ L: 161,
795
+ S: this,
796
+ C: (f, a) => f(...a)
797
+ });
798
+ })
799
+ ]);
800
+ } else {
801
+ return this._reachedTargetPromise;
802
+ }
628
803
  }
629
- async addReplicator(replicator) {
630
- invariant3(this._lifecycleState === LifecycleState2.OPEN, void 0, {
631
- F: __dxlog_file3,
632
- L: 104,
804
+ };
805
+ var Pipeline = class {
806
+ constructor() {
807
+ this._timeframeClock = new TimeframeClock(new Timeframe2());
808
+ this._feeds = new ComplexMap(PublicKey2.hash);
809
+ // External state accessor.
810
+ this._state = new PipelineState(this._feeds, this._timeframeClock);
811
+ // Waits for the message consumer to process the message and yield control back to the pipeline.
812
+ this._processingTrigger = new Trigger().wake();
813
+ this._pauseTrigger = new Trigger().wake();
814
+ // Pending downloads.
815
+ this._downloads = new ComplexMap((value) => PublicKey2.hash(value.key));
816
+ this._isStopping = false;
817
+ this._isStarted = false;
818
+ this._isBeingConsumed = false;
819
+ this._isPaused = false;
820
+ }
821
+ get state() {
822
+ return this._state;
823
+ }
824
+ get writer() {
825
+ invariant5(this._writer, "Writer not set.", {
826
+ F: __dxlog_file6,
827
+ L: 243,
633
828
  S: this,
634
829
  A: [
635
- "this._lifecycleState === LifecycleState.OPEN",
636
- ""
830
+ "this._writer",
831
+ "'Writer not set.'"
637
832
  ]
638
833
  });
639
- invariant3(this.peerId, void 0, {
640
- F: __dxlog_file3,
641
- L: 105,
834
+ return this._writer;
835
+ }
836
+ hasFeed(feedKey) {
837
+ return this._feeds.has(feedKey);
838
+ }
839
+ getFeeds() {
840
+ return this._feedSetIterator.feeds;
841
+ }
842
+ // NOTE: This cannot be synchronized with `stop` because stop waits for the mutation processing to complete,
843
+ // which might be opening feeds during the mutation processing, which w
844
+ async addFeed(feed) {
845
+ this._feeds.set(feed.key, feed);
846
+ if (this._feedSetIterator) {
847
+ await this._feedSetIterator.addFeed(feed);
848
+ }
849
+ if (this._isStarted && !this._isPaused) {
850
+ this._setFeedDownloadState(feed);
851
+ }
852
+ }
853
+ setWriteFeed(feed) {
854
+ invariant5(!this._writer, "Writer already set.", {
855
+ F: __dxlog_file6,
856
+ L: 270,
642
857
  S: this,
643
858
  A: [
644
- "this.peerId",
645
- ""
859
+ "!this._writer",
860
+ "'Writer already set.'"
646
861
  ]
647
862
  });
648
- invariant3(!this._replicators.has(replicator), void 0, {
649
- F: __dxlog_file3,
650
- L: 106,
863
+ invariant5(feed.properties.writable, "Feed must be writable.", {
864
+ F: __dxlog_file6,
865
+ L: 271,
651
866
  S: this,
652
867
  A: [
653
- "!this._replicators.has(replicator)",
654
- ""
868
+ "feed.properties.writable",
869
+ "'Feed must be writable.'"
655
870
  ]
656
871
  });
657
- this._replicators.add(replicator);
658
- await replicator.connect({
659
- peerId: this.peerId,
660
- onConnectionOpen: this._onConnectionOpen.bind(this),
661
- onConnectionClosed: this._onConnectionClosed.bind(this),
662
- onConnectionAuthScopeChanged: this._onConnectionAuthScopeChanged.bind(this),
663
- getContainingSpaceForDocument: this._params.getContainingSpaceForDocument
664
- });
872
+ this._writer = createMappedFeedWriter((payload) => ({
873
+ timeframe: this._timeframeClock.timeframe,
874
+ payload
875
+ }), feed.createFeedWriter());
665
876
  }
666
- async removeReplicator(replicator) {
667
- invariant3(this._lifecycleState === LifecycleState2.OPEN, void 0, {
668
- F: __dxlog_file3,
669
- L: 120,
877
+ async start() {
878
+ invariant5(!this._isStarted, "Pipeline is already started.", {
879
+ F: __dxlog_file6,
880
+ L: 284,
670
881
  S: this,
671
882
  A: [
672
- "this._lifecycleState === LifecycleState.OPEN",
673
- ""
883
+ "!this._isStarted",
884
+ "'Pipeline is already started.'"
674
885
  ]
675
886
  });
676
- invariant3(this._replicators.has(replicator), void 0, {
677
- F: __dxlog_file3,
678
- L: 121,
887
+ log5("starting...", void 0, {
888
+ F: __dxlog_file6,
889
+ L: 285,
679
890
  S: this,
680
- A: [
681
- "this._replicators.has(replicator)",
682
- ""
683
- ]
891
+ C: (f, a) => f(...a)
684
892
  });
685
- await replicator.disconnect();
686
- this._replicators.delete(replicator);
687
- }
688
- async shouldAdvertise(peerId, params) {
689
- const connection = this._connections.get(peerId);
690
- if (!connection) {
691
- return false;
692
- }
693
- return connection.connection.shouldAdvertise(params);
694
- }
695
- shouldSyncCollection(peerId, params) {
696
- const connection = this._connections.get(peerId);
697
- if (!connection) {
698
- return false;
893
+ await this._initIterator();
894
+ await this._feedSetIterator.open();
895
+ this._isStarted = true;
896
+ log5("started", void 0, {
897
+ F: __dxlog_file6,
898
+ L: 289,
899
+ S: this,
900
+ C: (f, a) => f(...a)
901
+ });
902
+ if (!this._isPaused) {
903
+ for (const feed of this._feeds.values()) {
904
+ this._setFeedDownloadState(feed);
905
+ }
699
906
  }
700
- return connection.connection.shouldSyncCollection(params);
701
- }
702
- queryCollectionState(collectionId, targetId) {
703
- const message = {
704
- type: "collection-query",
705
- senderId: this.peerId,
706
- targetId,
707
- collectionId
708
- };
709
- this._send(message);
710
- }
711
- sendCollectionState(collectionId, targetId, state) {
712
- const message = {
713
- type: "collection-state",
714
- senderId: this.peerId,
715
- targetId,
716
- collectionId,
717
- state
718
- };
719
- this._send(message);
720
907
  }
721
- _send(message) {
722
- const connectionEntry = this._connections.get(message.targetId);
723
- if (!connectionEntry) {
724
- throw new Error("Connection not found.");
908
+ async stop() {
909
+ log5("stopping...", void 0, {
910
+ F: __dxlog_file6,
911
+ L: 300,
912
+ S: this,
913
+ C: (f, a) => f(...a)
914
+ });
915
+ this._isStopping = true;
916
+ for (const [feed, handle] of this._downloads.entries()) {
917
+ feed.undownload(handle);
725
918
  }
726
- const writeStart = Date.now();
727
- connectionEntry.writer.write(message).then(() => {
728
- const durationMs = Date.now() - writeStart;
729
- this._params.monitor?.recordMessageSent(message, durationMs);
730
- }).catch((err) => {
731
- if (connectionEntry.isOpen) {
732
- log2.catch(err, void 0, {
733
- F: __dxlog_file3,
734
- L: 181,
735
- S: this,
736
- C: (f, a) => f(...a)
737
- });
738
- }
739
- this._params.monitor?.recordMessageSendingFailed(message);
919
+ this._downloads.clear();
920
+ await this._feedSetIterator?.close();
921
+ await this._processingTrigger.wait();
922
+ await this._state._ctx.dispose();
923
+ this._state._ctx = new Context2(void 0, {
924
+ F: __dxlog_file6,
925
+ L: 309
740
926
  });
741
- }
742
- // TODO(dmaretskyi): Remove.
743
- getPeersInterestedInCollection(collectionId) {
744
- return Array.from(this._connections.values()).map((connection) => {
745
- return connection.connection.shouldSyncCollection({
746
- collectionId
747
- }) ? connection.connection.peerId : null;
748
- }).filter(nonNullable);
749
- }
750
- _onConnectionOpen(connection) {
751
- log2("Connection opened", {
752
- peerId: connection.peerId
753
- }, {
754
- F: __dxlog_file3,
755
- L: 199,
927
+ this._state._reachedTargetPromise = void 0;
928
+ this._state._reachedTarget = false;
929
+ this._isStarted = false;
930
+ log5("stopped", void 0, {
931
+ F: __dxlog_file6,
932
+ L: 313,
756
933
  S: this,
757
934
  C: (f, a) => f(...a)
758
935
  });
759
- invariant3(!this._connections.has(connection.peerId), void 0, {
760
- F: __dxlog_file3,
761
- L: 200,
936
+ }
937
+ /**
938
+ * @param timeframe Timeframe of already processed messages.
939
+ * The pipeline will start processing messages AFTER this timeframe.
940
+ */
941
+ async setCursor(timeframe) {
942
+ invariant5(!this._isStarted || this._isPaused, "Invalid state.", {
943
+ F: __dxlog_file6,
944
+ L: 322,
762
945
  S: this,
763
946
  A: [
764
- "!this._connections.has(connection.peerId as PeerId)",
765
- ""
947
+ "!this._isStarted || this._isPaused",
948
+ "'Invalid state.'"
766
949
  ]
767
950
  });
768
- const reader = connection.readable.getReader();
769
- const writer = connection.writable.getWriter();
770
- const connectionEntry = {
771
- connection,
772
- reader,
773
- writer,
774
- isOpen: true
775
- };
776
- this._connections.set(connection.peerId, connectionEntry);
777
- queueMicrotask(async () => {
778
- try {
779
- while (true) {
780
- const { done, value } = await reader.read();
781
- if (done) {
782
- break;
783
- }
784
- this._onMessage(value);
785
- }
786
- } catch (err) {
787
- if (connectionEntry.isOpen) {
788
- log2.catch(err, void 0, {
789
- F: __dxlog_file3,
790
- L: 219,
791
- S: this,
792
- C: (f, a) => f(...a)
793
- });
794
- }
795
- }
796
- });
797
- log2("emit peer-candidate", {
798
- peerId: connection.peerId
799
- }, {
800
- F: __dxlog_file3,
801
- L: 224,
802
- S: this,
803
- C: (f, a) => f(...a)
804
- });
805
- this._emitPeerCandidate(connection);
806
- this._params.monitor?.recordPeerConnected(connection.peerId);
807
- }
808
- _onMessage(message) {
809
- if (isCollectionQueryMessage(message)) {
810
- this._params.onCollectionStateQueried(message.collectionId, message.senderId);
811
- } else if (isCollectionStateMessage(message)) {
812
- this._params.onCollectionStateReceived(message.collectionId, message.senderId, message.state);
813
- } else {
814
- this.emit("message", message);
951
+ this._state._startTimeframe = timeframe;
952
+ this._timeframeClock.setTimeframe(timeframe);
953
+ if (this._feedSetIterator) {
954
+ await this._feedSetIterator.close();
955
+ await this._initIterator();
956
+ await this._feedSetIterator.open();
815
957
  }
816
- this._params.monitor?.recordMessageReceived(message);
817
958
  }
818
959
  /**
819
- * Trigger doc-synchronizer shared documents set recalculation. Happens on peer-candidate.
820
- * TODO(y): replace with a proper API call when sharePolicy update becomes supported by automerge-repo
960
+ * Calling pause while processing will cause a deadlock.
821
961
  */
822
- _onConnectionAuthScopeChanged(connection) {
823
- log2("Connection auth scope changed", {
824
- peerId: connection.peerId
825
- }, {
826
- F: __dxlog_file3,
827
- L: 245,
828
- S: this,
829
- C: (f, a) => f(...a)
830
- });
831
- const entry = this._connections.get(connection.peerId);
832
- invariant3(entry, void 0, {
833
- F: __dxlog_file3,
834
- L: 247,
962
+ async pause() {
963
+ if (this._isPaused) {
964
+ return;
965
+ }
966
+ this._pauseTrigger.reset();
967
+ await this._processingTrigger.wait();
968
+ this._isPaused = true;
969
+ }
970
+ async unpause() {
971
+ invariant5(this._isPaused, "Pipeline is not paused.", {
972
+ F: __dxlog_file6,
973
+ L: 351,
835
974
  S: this,
836
975
  A: [
837
- "entry",
838
- ""
976
+ "this._isPaused",
977
+ "'Pipeline is not paused.'"
839
978
  ]
840
979
  });
841
- this.emit("peer-disconnected", {
842
- peerId: connection.peerId
843
- });
844
- this._emitPeerCandidate(connection);
980
+ this._pauseTrigger.wake();
981
+ this._isPaused = false;
982
+ for (const feed of this._feeds.values()) {
983
+ this._setFeedDownloadState(feed);
984
+ }
845
985
  }
846
- _onConnectionClosed(connection) {
847
- log2("Connection closed", {
848
- peerId: connection.peerId
849
- }, {
850
- F: __dxlog_file3,
851
- L: 253,
986
+ /**
987
+ * Starts to iterate over the ordered messages from the added feeds.
988
+ * Updates the timeframe clock after the message has bee processed.
989
+ */
990
+ async *consume() {
991
+ invariant5(!this._isBeingConsumed, "Pipeline is already being consumed.", {
992
+ F: __dxlog_file6,
993
+ L: 366,
852
994
  S: this,
853
- C: (f, a) => f(...a)
995
+ A: [
996
+ "!this._isBeingConsumed",
997
+ "'Pipeline is already being consumed.'"
998
+ ]
854
999
  });
855
- const entry = this._connections.get(connection.peerId);
856
- invariant3(entry, void 0, {
857
- F: __dxlog_file3,
858
- L: 255,
1000
+ this._isBeingConsumed = true;
1001
+ invariant5(this._feedSetIterator, "Iterator not initialized.", {
1002
+ F: __dxlog_file6,
1003
+ L: 369,
859
1004
  S: this,
860
1005
  A: [
861
- "entry",
862
- ""
1006
+ "this._feedSetIterator",
1007
+ "'Iterator not initialized.'"
863
1008
  ]
864
1009
  });
865
- entry.isOpen = false;
866
- this.emit("peer-disconnected", {
867
- peerId: connection.peerId
868
- });
869
- this._params.monitor?.recordPeerDisconnected(connection.peerId);
870
- void entry.reader.cancel().catch((err) => log2.catch(err, void 0, {
871
- F: __dxlog_file3,
872
- L: 261,
873
- S: this,
874
- C: (f, a) => f(...a)
875
- }));
876
- void entry.writer.abort().catch((err) => log2.catch(err, void 0, {
877
- F: __dxlog_file3,
878
- L: 262,
1010
+ let lastFeedSetIterator = this._feedSetIterator;
1011
+ let iterable = lastFeedSetIterator[Symbol.asyncIterator]();
1012
+ while (!this._isStopping) {
1013
+ await this._pauseTrigger.wait();
1014
+ if (lastFeedSetIterator !== this._feedSetIterator) {
1015
+ invariant5(this._feedSetIterator, "Iterator not initialized.", {
1016
+ F: __dxlog_file6,
1017
+ L: 378,
1018
+ S: this,
1019
+ A: [
1020
+ "this._feedSetIterator",
1021
+ "'Iterator not initialized.'"
1022
+ ]
1023
+ });
1024
+ lastFeedSetIterator = this._feedSetIterator;
1025
+ iterable = lastFeedSetIterator[Symbol.asyncIterator]();
1026
+ }
1027
+ const { done, value } = await iterable.next();
1028
+ if (!done) {
1029
+ const block = value ?? failUndefined();
1030
+ this._processingTrigger.reset();
1031
+ this._timeframeClock.updatePendingTimeframe(PublicKey2.from(block.feedKey), block.seq);
1032
+ yield block;
1033
+ this._processingTrigger.wake();
1034
+ this._timeframeClock.updateTimeframe();
1035
+ }
1036
+ }
1037
+ this._isBeingConsumed = false;
1038
+ }
1039
+ _setFeedDownloadState(feed) {
1040
+ let handle = this._downloads.get(feed);
1041
+ if (handle) {
1042
+ feed.undownload(handle);
1043
+ }
1044
+ const timeframe = this._state._startTimeframe;
1045
+ const seq = timeframe.get(feed.key) ?? -1;
1046
+ log5("download", {
1047
+ feed: feed.key.truncate(),
1048
+ seq,
1049
+ length: feed.length
1050
+ }, {
1051
+ F: __dxlog_file6,
1052
+ L: 407,
879
1053
  S: this,
880
1054
  C: (f, a) => f(...a)
881
- }));
882
- this._connections.delete(connection.peerId);
1055
+ });
1056
+ handle = feed.download({
1057
+ start: seq + 1,
1058
+ linear: true
1059
+ }, (err, data) => {
1060
+ if (err) {
1061
+ } else {
1062
+ log5.info("downloaded", {
1063
+ data
1064
+ }, {
1065
+ F: __dxlog_file6,
1066
+ L: 412,
1067
+ S: this,
1068
+ C: (f, a) => f(...a)
1069
+ });
1070
+ }
1071
+ });
1072
+ this._downloads.set(feed, handle);
883
1073
  }
884
- _emitPeerCandidate(connection) {
885
- this.emit("peer-candidate", {
886
- peerId: connection.peerId,
887
- peerMetadata: createEchoPeerMetadata()
1074
+ async _initIterator() {
1075
+ this._feedSetIterator = new FeedSetIterator(createMessageSelector(this._timeframeClock), {
1076
+ start: startAfter(this._timeframeClock.timeframe),
1077
+ stallTimeout: 1e3
1078
+ });
1079
+ this._feedSetIterator.stalled.on((iterator) => {
1080
+ log5.warn(`Stalled after ${iterator.options.stallTimeout}ms with ${iterator.size} feeds.`, void 0, {
1081
+ F: __dxlog_file6,
1082
+ L: 426,
1083
+ S: this,
1084
+ C: (f, a) => f(...a)
1085
+ });
1086
+ this._state.stalled.emit();
888
1087
  });
1088
+ for (const feed of this._feeds.values()) {
1089
+ await this._feedSetIterator.addFeed(feed);
1090
+ }
889
1091
  }
890
1092
  };
891
- _ts_decorate([
1093
+ _ts_decorate2([
892
1094
  synchronized
893
- ], EchoNetworkAdapter.prototype, "open", null);
894
- _ts_decorate([
1095
+ ], Pipeline.prototype, "start", null);
1096
+ _ts_decorate2([
895
1097
  synchronized
896
- ], EchoNetworkAdapter.prototype, "close", null);
897
- _ts_decorate([
1098
+ ], Pipeline.prototype, "stop", null);
1099
+ _ts_decorate2([
898
1100
  synchronized
899
- ], EchoNetworkAdapter.prototype, "addReplicator", null);
900
- _ts_decorate([
1101
+ ], Pipeline.prototype, "setCursor", null);
1102
+ _ts_decorate2([
901
1103
  synchronized
902
- ], EchoNetworkAdapter.prototype, "removeReplicator", null);
903
- var createEchoPeerMetadata = () => ({
904
- // TODO(dmaretskyi): Refactor this.
905
- dxos_peerSource: "EchoNetworkAdapter"
906
- });
907
- var isEchoPeerMetadata = (metadata) => metadata?.dxos_peerSource === "EchoNetworkAdapter";
1104
+ ], Pipeline.prototype, "pause", null);
1105
+ _ts_decorate2([
1106
+ synchronized
1107
+ ], Pipeline.prototype, "unpause", null);
908
1108
 
909
- // packages/core/echo/echo-pipeline/src/automerge/heads-store.ts
910
- import { headsEncoding } from "@dxos/indexing";
911
- var HeadsStore = class {
912
- constructor({ db }) {
913
- this._db = db;
914
- }
915
- setHeads(documentId, heads, batch) {
916
- batch.put(documentId, heads, {
917
- sublevel: this._db,
918
- keyEncoding: "utf8",
919
- valueEncoding: headsEncoding
920
- });
921
- }
922
- // TODO(dmaretskyi): Make batched.
923
- async getHeads(documentIds) {
924
- return this._db.getMany(documentIds, {
925
- keyEncoding: "utf8",
926
- valueEncoding: headsEncoding
927
- });
928
- }
929
- };
1109
+ // packages/core/echo/echo-pipeline/src/space/space.ts
1110
+ import { Event as Event4, scheduleMicroTask, synchronized as synchronized2, trackLeaks as trackLeaks2 } from "@dxos/async";
1111
+ import { LifecycleState, Resource as Resource3 } from "@dxos/context";
1112
+ import { subtleCrypto as subtleCrypto2 } from "@dxos/crypto";
1113
+ import { invariant as invariant6 } from "@dxos/invariant";
1114
+ import { PublicKey as PublicKey4, SpaceId } from "@dxos/keys";
1115
+ import { log as log7, logInfo } from "@dxos/log";
1116
+ import { AdmittedFeed as AdmittedFeed2 } from "@dxos/protocols/proto/dxos/halo/credentials";
1117
+ import { trace as trace2 } from "@dxos/tracing";
1118
+ import { Callback as Callback2, ComplexMap as ComplexMap2 } from "@dxos/util";
930
1119
 
931
- // packages/core/echo/echo-pipeline/src/automerge/automerge-host.ts
932
- function _ts_decorate2(decorators, target, key, desc) {
1120
+ // packages/core/echo/echo-pipeline/src/space/control-pipeline.ts
1121
+ import { DeferredTask, sleepWithContext as sleepWithContext2, trackLeaks } from "@dxos/async";
1122
+ import { Context as Context3 } from "@dxos/context";
1123
+ import { SpaceStateMachine } from "@dxos/credentials";
1124
+ import { PublicKey as PublicKey3 } from "@dxos/keys";
1125
+ import { log as log6 } from "@dxos/log";
1126
+ import { AdmittedFeed } from "@dxos/protocols/proto/dxos/halo/credentials";
1127
+ import { Timeframe as Timeframe3 } from "@dxos/timeframe";
1128
+ import { TimeSeriesCounter, TimeUsageCounter, trace } from "@dxos/tracing";
1129
+ import { Callback, tracer } from "@dxos/util";
1130
+ function _ts_decorate3(decorators, target, key, desc) {
933
1131
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
934
1132
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function")
935
1133
  r = Reflect.decorate(decorators, target, key, desc);
@@ -939,579 +1137,566 @@ function _ts_decorate2(decorators, target, key, desc) {
939
1137
  r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
940
1138
  return c > 3 && r && Object.defineProperty(target, key, r), r;
941
1139
  }
942
- var __dxlog_file4 = "/home/runner/work/dxos/dxos/packages/core/echo/echo-pipeline/src/automerge/automerge-host.ts";
943
- var AutomergeHost = class extends Resource4 {
944
- constructor({ db, indexMetadataStore, dataMonitor }) {
945
- super();
946
- this._collectionSynchronizer = new CollectionSynchronizer({
947
- queryCollectionState: this._queryCollectionState.bind(this),
948
- sendCollectionState: this._sendCollectionState.bind(this),
949
- shouldSyncCollection: this._shouldSyncCollection.bind(this)
950
- });
951
- this._db = db;
952
- this._storage = new LevelDBStorageAdapter({
953
- db: db.sublevel("automerge"),
954
- callbacks: {
955
- beforeSave: async (params) => this._beforeSave(params),
956
- afterSave: async (key) => this._afterSave(key)
957
- },
958
- monitor: dataMonitor
959
- });
960
- this._echoNetworkAdapter = new EchoNetworkAdapter({
961
- getContainingSpaceForDocument: this._getContainingSpaceForDocument.bind(this),
962
- onCollectionStateQueried: this._onCollectionStateQueried.bind(this),
963
- onCollectionStateReceived: this._onCollectionStateReceived.bind(this),
964
- monitor: dataMonitor
965
- });
966
- this._headsStore = new HeadsStore({
967
- db: db.sublevel("heads")
1140
+ var __dxlog_file7 = "/home/runner/work/dxos/dxos/packages/core/echo/echo-pipeline/src/space/control-pipeline.ts";
1141
+ var TIMEFRAME_SAVE_DEBOUNCE_INTERVAL = 500;
1142
+ var CONTROL_PIPELINE_SNAPSHOT_DELAY = 1e4;
1143
+ var USE_SNAPSHOTS = true;
1144
+ var ControlPipeline = class {
1145
+ constructor({ spaceKey, genesisFeed, feedProvider, metadataStore }) {
1146
+ this._ctx = new Context3(void 0, {
1147
+ F: __dxlog_file7,
1148
+ L: 47
968
1149
  });
969
- this._indexMetadataStore = indexMetadataStore;
970
- }
971
- async _open() {
972
- this._peerId = `host-${PublicKey2.random().toHex()}`;
973
- await this._storage.open?.();
974
- this._repo = new Repo({
975
- peerId: this._peerId,
976
- sharePolicy: this._sharePolicy.bind(this),
977
- storage: this._storage,
978
- network: [
979
- // Upstream swarm.
980
- this._echoNetworkAdapter
981
- ]
1150
+ this._lastTimeframeSaveTime = Date.now();
1151
+ this.onFeedAdmitted = new Callback();
1152
+ this._usage = new TimeUsageCounter();
1153
+ this._mutations = new TimeSeriesCounter();
1154
+ this._snapshotTask = new DeferredTask(this._ctx, async () => {
1155
+ await sleepWithContext2(this._ctx, CONTROL_PIPELINE_SNAPSHOT_DELAY);
1156
+ await this._saveSnapshot();
982
1157
  });
983
- Event2.wrap(this._echoNetworkAdapter, "peer-candidate").on(this._ctx, (e) => this._onPeerConnected(e.peerId));
984
- Event2.wrap(this._echoNetworkAdapter, "peer-disconnected").on(this._ctx, (e) => this._onPeerDisconnected(e.peerId));
985
- this._collectionSynchronizer.remoteStateUpdated.on(this._ctx, ({ collectionId, peerId }) => {
986
- this._onRemoteCollectionStateUpdated(collectionId, peerId);
1158
+ this._spaceKey = spaceKey;
1159
+ this._metadata = metadataStore;
1160
+ this._pipeline = new Pipeline();
1161
+ void this._pipeline.addFeed(genesisFeed);
1162
+ this._spaceStateMachine = new SpaceStateMachine(spaceKey);
1163
+ this._spaceStateMachine.onFeedAdmitted.set(async (info) => {
1164
+ log6("feed admitted", {
1165
+ key: info.key
1166
+ }, {
1167
+ F: __dxlog_file7,
1168
+ L: 82,
1169
+ S: this,
1170
+ C: (f, a) => f(...a)
1171
+ });
1172
+ if (info.assertion.designation === AdmittedFeed.Designation.CONTROL && !info.key.equals(genesisFeed.key)) {
1173
+ queueMicrotask(async () => {
1174
+ try {
1175
+ const feed = await feedProvider(info.key);
1176
+ if (!this._pipeline.hasFeed(feed.key)) {
1177
+ await this._pipeline.addFeed(feed);
1178
+ }
1179
+ } catch (err) {
1180
+ log6.catch(err, void 0, {
1181
+ F: __dxlog_file7,
1182
+ L: 93,
1183
+ S: this,
1184
+ C: (f, a) => f(...a)
1185
+ });
1186
+ }
1187
+ });
1188
+ }
1189
+ await this.onFeedAdmitted.callIfSet(info);
987
1190
  });
988
- await this._echoNetworkAdapter.open();
989
- await this._collectionSynchronizer.open();
990
- await this._echoNetworkAdapter.open();
991
- await this._echoNetworkAdapter.whenConnected();
992
- }
993
- async _close() {
994
- await this._collectionSynchronizer.close();
995
- await this._storage.close?.();
996
- await this._echoNetworkAdapter.close();
997
- await this._ctx.dispose();
998
- }
999
- /**
1000
- * @deprecated To be abstracted away.
1001
- */
1002
- get repo() {
1003
- return this._repo;
1191
+ this.onMemberRoleChanged = this._spaceStateMachine.onMemberRoleChanged;
1192
+ this.onCredentialProcessed = this._spaceStateMachine.onCredentialProcessed;
1193
+ this.onDelegatedInvitation = this._spaceStateMachine.onDelegatedInvitation;
1194
+ this.onDelegatedInvitationRemoved = this._spaceStateMachine.onDelegatedInvitationRemoved;
1004
1195
  }
1005
- get peerId() {
1006
- return this._peerId;
1196
+ get spaceState() {
1197
+ return this._spaceStateMachine;
1007
1198
  }
1008
- get loadedDocsCount() {
1009
- return Object.keys(this._repo.handles).length;
1199
+ get pipeline() {
1200
+ return this._pipeline;
1010
1201
  }
1011
- async addReplicator(replicator) {
1012
- await this._echoNetworkAdapter.addReplicator(replicator);
1202
+ async setWriteFeed(feed) {
1203
+ await this._pipeline.addFeed(feed);
1204
+ this._pipeline.setWriteFeed(feed);
1013
1205
  }
1014
- async removeReplicator(replicator) {
1015
- await this._echoNetworkAdapter.removeReplicator(replicator);
1016
- }
1017
- /**
1018
- * Loads the document handle from the repo and waits for it to be ready.
1019
- */
1020
- async loadDoc(ctx, documentId, opts) {
1021
- let handle;
1022
- if (typeof documentId === "string") {
1023
- handle = this._repo.handles[documentId];
1024
- }
1025
- if (!handle) {
1026
- handle = this._repo.find(documentId);
1027
- }
1028
- if (!handle.isReady()) {
1029
- if (!opts?.timeout) {
1030
- await cancelWithContext2(ctx, handle.whenReady());
1031
- } else {
1032
- await cancelWithContext2(ctx, asyncTimeout(handle.whenReady(), opts.timeout));
1033
- }
1034
- }
1035
- return handle;
1036
- }
1037
- /**
1038
- * Create new persisted document.
1039
- */
1040
- createDoc(initialValue, opts) {
1041
- if (opts?.preserveHistory) {
1042
- if (!isAutomerge(initialValue)) {
1043
- throw new TypeError("Initial value must be an Automerge document");
1044
- }
1045
- return this._repo.import(save(initialValue));
1046
- } else {
1047
- return this._repo.create(initialValue);
1048
- }
1049
- }
1050
- async waitUntilHeadsReplicated(heads) {
1051
- const entries = heads.entries;
1052
- if (!entries?.length) {
1053
- return;
1206
+ async start() {
1207
+ const snapshot = this._metadata.getSpaceControlPipelineSnapshot(this._spaceKey);
1208
+ log6("load snapshot", {
1209
+ key: this._spaceKey,
1210
+ present: !!snapshot,
1211
+ tf: snapshot?.timeframe
1212
+ }, {
1213
+ F: __dxlog_file7,
1214
+ L: 123,
1215
+ S: this,
1216
+ C: (f, a) => f(...a)
1217
+ });
1218
+ if (USE_SNAPSHOTS && snapshot) {
1219
+ await this._processSnapshot(snapshot);
1054
1220
  }
1055
- const documentIds = entries.map((entry) => entry.documentId);
1056
- const documentHeads = await this.getHeads(documentIds);
1057
- const headsToWait = entries.filter((entry, index) => {
1058
- const targetHeads = entry.heads;
1059
- if (!targetHeads || targetHeads.length === 0) {
1060
- return false;
1061
- }
1062
- const currentHeads = documentHeads[index];
1063
- return !(currentHeads !== null && headsEquals(currentHeads, targetHeads));
1221
+ log6("starting...", void 0, {
1222
+ F: __dxlog_file7,
1223
+ L: 128,
1224
+ S: this,
1225
+ C: (f, a) => f(...a)
1064
1226
  });
1065
- if (headsToWait.length > 0) {
1066
- await Promise.all(headsToWait.map(async (entry, index) => {
1067
- const handle = await this.loadDoc(Context.default(void 0, {
1068
- F: __dxlog_file4,
1069
- L: 226
1070
- }), entry.documentId);
1071
- await waitForHeads(handle, entry.heads);
1227
+ setTimeout(async () => {
1228
+ void this._consumePipeline(new Context3(void 0, {
1229
+ F: __dxlog_file7,
1230
+ L: 130
1072
1231
  }));
1073
- }
1074
- await this._repo.flush(documentIds.filter((documentId) => !!this._repo.handles[documentId]));
1232
+ });
1233
+ await this._pipeline.start();
1234
+ log6("started", void 0, {
1235
+ F: __dxlog_file7,
1236
+ L: 134,
1237
+ S: this,
1238
+ C: (f, a) => f(...a)
1239
+ });
1075
1240
  }
1076
- async reIndexHeads(documentIds) {
1077
- for (const documentId of documentIds) {
1078
- log3.info("re-indexing heads for document", {
1079
- documentId
1080
- }, {
1081
- F: __dxlog_file4,
1082
- L: 238,
1083
- S: this,
1084
- C: (f, a) => f(...a)
1241
+ async _processSnapshot(snapshot) {
1242
+ await this._pipeline.setCursor(snapshot.timeframe);
1243
+ for (const message of snapshot.messages ?? []) {
1244
+ const result = await this._spaceStateMachine.process(message.credential, {
1245
+ sourceFeed: message.feedKey,
1246
+ skipVerification: true
1085
1247
  });
1086
- const handle = this._repo.find(documentId);
1087
- await handle.whenReady([
1088
- "ready",
1089
- "requesting"
1090
- ]);
1091
- if (handle.inState([
1092
- "requesting"
1093
- ])) {
1094
- log3.warn("document is not available locally, skipping", {
1095
- documentId
1248
+ if (!result) {
1249
+ log6.warn("credential processing failed from snapshot", {
1250
+ message
1096
1251
  }, {
1097
- F: __dxlog_file4,
1098
- L: 242,
1252
+ F: __dxlog_file7,
1253
+ L: 147,
1099
1254
  S: this,
1100
1255
  C: (f, a) => f(...a)
1101
1256
  });
1102
- continue;
1103
1257
  }
1104
- const doc = handle.docSync();
1105
- invariant4(doc, void 0, {
1106
- F: __dxlog_file4,
1107
- L: 247,
1108
- S: this,
1109
- A: [
1110
- "doc",
1111
- ""
1112
- ]
1113
- });
1114
- const heads = getHeads(doc);
1115
- const batch = this._db.batch();
1116
- this._headsStore.setHeads(documentId, heads, batch);
1117
- await batch.write();
1118
1258
  }
1119
- log3.info("done re-indexing heads", void 0, {
1120
- F: __dxlog_file4,
1121
- L: 254,
1259
+ }
1260
+ async _saveSnapshot() {
1261
+ await this._pipeline.pause();
1262
+ const snapshot = {
1263
+ timeframe: this._pipeline.state.timeframe,
1264
+ messages: this._spaceStateMachine.credentialEntries.map((entry) => ({
1265
+ feedKey: entry.sourceFeed,
1266
+ credential: entry.credential
1267
+ }))
1268
+ };
1269
+ await this._pipeline.unpause();
1270
+ log6("save snapshot", {
1271
+ key: this._spaceKey,
1272
+ snapshot
1273
+ }, {
1274
+ F: __dxlog_file7,
1275
+ L: 163,
1122
1276
  S: this,
1123
1277
  C: (f, a) => f(...a)
1124
1278
  });
1279
+ await this._metadata.setSpaceControlPipelineSnapshot(this._spaceKey, snapshot);
1125
1280
  }
1126
- // TODO(dmaretskyi): Share based on HALO permissions and space affinity.
1127
- // Hosts, running in the worker, don't share documents unless requested by other peers.
1128
- // NOTE: If both peers return sharePolicy=false the replication will not happen
1129
- // https://github.com/automerge/automerge-repo/pull/292
1130
- async _sharePolicy(peerId, documentId) {
1131
- if (peerId.startsWith("client-")) {
1132
- return false;
1133
- }
1134
- if (!documentId) {
1135
- return false;
1281
+ async _consumePipeline(ctx) {
1282
+ for await (const msg of this._pipeline.consume()) {
1283
+ const span = this._usage.beginRecording();
1284
+ this._mutations.inc();
1285
+ try {
1286
+ await this._processMessage(ctx, msg);
1287
+ } catch (err) {
1288
+ log6.catch(err, void 0, {
1289
+ F: __dxlog_file7,
1290
+ L: 176,
1291
+ S: this,
1292
+ C: (f, a) => f(...a)
1293
+ });
1294
+ }
1295
+ span.end();
1136
1296
  }
1137
- const peerMetadata = this.repo.peerMetadataByPeerId[peerId];
1138
- if (isEchoPeerMetadata(peerMetadata)) {
1139
- return this._echoNetworkAdapter.shouldAdvertise(peerId, {
1140
- documentId
1297
+ }
1298
+ async _processMessage(ctx, msg) {
1299
+ log6("processing", {
1300
+ key: msg.feedKey,
1301
+ seq: msg.seq
1302
+ }, {
1303
+ F: __dxlog_file7,
1304
+ L: 186,
1305
+ S: this,
1306
+ C: (f, a) => f(...a)
1307
+ });
1308
+ if (msg.data.payload.credential) {
1309
+ const timer = tracer.mark("dxos.echo.pipeline.control");
1310
+ const result = await this._spaceStateMachine.process(msg.data.payload.credential.credential, {
1311
+ sourceFeed: PublicKey3.from(msg.feedKey)
1141
1312
  });
1313
+ timer.end();
1314
+ if (!result) {
1315
+ log6.warn("processing failed", {
1316
+ msg
1317
+ }, {
1318
+ F: __dxlog_file7,
1319
+ L: 195,
1320
+ S: this,
1321
+ C: (f, a) => f(...a)
1322
+ });
1323
+ } else {
1324
+ await this._noteTargetStateIfNeeded(this._pipeline.state.pendingTimeframe);
1325
+ }
1326
+ this._snapshotTask.schedule();
1142
1327
  }
1143
- return false;
1144
1328
  }
1145
- async _beforeSave({ path, batch }) {
1146
- const handle = this._repo.handles[path[0]];
1147
- if (!handle) {
1148
- return;
1149
- }
1150
- const doc = handle.docSync();
1151
- if (!doc) {
1152
- return;
1329
+ async _noteTargetStateIfNeeded(timeframe) {
1330
+ if (Date.now() - this._lastTimeframeSaveTime > TIMEFRAME_SAVE_DEBOUNCE_INTERVAL) {
1331
+ this._lastTimeframeSaveTime = Date.now();
1332
+ await this._saveTargetTimeframe(timeframe);
1153
1333
  }
1154
- const heads = getHeads(doc);
1155
- this._headsStore.setHeads(handle.documentId, heads, batch);
1156
- const spaceKey = getSpaceKeyFromDoc(doc) ?? void 0;
1157
- const objectIds = Object.keys(doc.objects ?? {});
1158
- const encodedIds = objectIds.map((objectId) => objectPointerCodec.encode({
1159
- documentId: handle.documentId,
1160
- objectId,
1161
- spaceKey
1162
- }));
1163
- const idToLastHash = new Map(encodedIds.map((id) => [
1164
- id,
1165
- heads
1166
- ]));
1167
- this._indexMetadataStore.markDirty(idToLastHash, batch);
1168
1334
  }
1169
- _shouldSyncCollection(collectionId, peerId) {
1170
- const peerMetadata = this._repo.peerMetadataByPeerId[peerId];
1171
- if (isEchoPeerMetadata(peerMetadata)) {
1172
- return this._echoNetworkAdapter.shouldSyncCollection(peerId, {
1173
- collectionId
1335
+ async stop() {
1336
+ log6("stopping...", void 0, {
1337
+ F: __dxlog_file7,
1338
+ L: 215,
1339
+ S: this,
1340
+ C: (f, a) => f(...a)
1341
+ });
1342
+ await this._ctx.dispose();
1343
+ await this._pipeline.stop();
1344
+ await this._saveTargetTimeframe(this._pipeline.state.timeframe);
1345
+ log6("stopped", void 0, {
1346
+ F: __dxlog_file7,
1347
+ L: 219,
1348
+ S: this,
1349
+ C: (f, a) => f(...a)
1350
+ });
1351
+ }
1352
+ async _saveTargetTimeframe(timeframe) {
1353
+ try {
1354
+ const newTimeframe = Timeframe3.merge(this._targetTimeframe ?? new Timeframe3(), timeframe);
1355
+ await this._metadata.setSpaceControlLatestTimeframe(this._spaceKey, newTimeframe);
1356
+ this._targetTimeframe = newTimeframe;
1357
+ } catch (err) {
1358
+ log6(err, void 0, {
1359
+ F: __dxlog_file7,
1360
+ L: 228,
1361
+ S: this,
1362
+ C: (f, a) => f(...a)
1174
1363
  });
1175
1364
  }
1176
- return false;
1177
1365
  }
1178
- /**
1179
- * Called by AutomergeStorageAdapter after levelDB batch commit.
1180
- */
1181
- async _afterSave(path) {
1182
- this._indexMetadataStore.notifyMarkedDirty();
1183
- const documentId = path[0];
1184
- const document = this._repo.handles[documentId]?.docSync();
1185
- if (document) {
1186
- const heads = getHeads(document);
1187
- this._onHeadsChanged(documentId, heads);
1188
- }
1189
- }
1190
- _automergePeers() {
1191
- return this._repo.peers;
1366
+ };
1367
+ _ts_decorate3([
1368
+ trace.metricsCounter()
1369
+ ], ControlPipeline.prototype, "_usage", void 0);
1370
+ _ts_decorate3([
1371
+ trace.metricsCounter()
1372
+ ], ControlPipeline.prototype, "_mutations", void 0);
1373
+ _ts_decorate3([
1374
+ trace.span({
1375
+ showInBrowserTimeline: true
1376
+ })
1377
+ ], ControlPipeline.prototype, "start", null);
1378
+ _ts_decorate3([
1379
+ trace.span()
1380
+ ], ControlPipeline.prototype, "_consumePipeline", null);
1381
+ _ts_decorate3([
1382
+ trace.span()
1383
+ ], ControlPipeline.prototype, "_processMessage", null);
1384
+ ControlPipeline = _ts_decorate3([
1385
+ trace.resource(),
1386
+ trackLeaks("start", "stop")
1387
+ ], ControlPipeline);
1388
+
1389
+ // packages/core/echo/echo-pipeline/src/space/space.ts
1390
+ function _ts_decorate4(decorators, target, key, desc) {
1391
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1392
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function")
1393
+ r = Reflect.decorate(decorators, target, key, desc);
1394
+ else
1395
+ for (var i = decorators.length - 1; i >= 0; i--)
1396
+ if (d = decorators[i])
1397
+ r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
1398
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
1399
+ }
1400
+ var __dxlog_file8 = "/home/runner/work/dxos/dxos/packages/core/echo/echo-pipeline/src/space/space.ts";
1401
+ var Space = class extends Resource3 {
1402
+ constructor(params) {
1403
+ super();
1404
+ this.onCredentialProcessed = new Callback2();
1405
+ this.stateUpdate = new Event4();
1406
+ invariant6(params.spaceKey && params.feedProvider, void 0, {
1407
+ F: __dxlog_file8,
1408
+ L: 76,
1409
+ S: this,
1410
+ A: [
1411
+ "params.spaceKey && params.feedProvider",
1412
+ ""
1413
+ ]
1414
+ });
1415
+ this._id = params.id;
1416
+ this._key = params.spaceKey;
1417
+ this._genesisFeedKey = params.genesisFeed.key;
1418
+ this._feedProvider = params.feedProvider;
1419
+ this._snapshotManager = params.snapshotManager;
1420
+ this._controlPipeline = new ControlPipeline({
1421
+ spaceKey: params.spaceKey,
1422
+ genesisFeed: params.genesisFeed,
1423
+ feedProvider: params.feedProvider,
1424
+ metadataStore: params.metadataStore
1425
+ });
1426
+ this._controlPipeline.onFeedAdmitted.set(async (info) => {
1427
+ const sparse = info.assertion.designation === AdmittedFeed2.Designation.DATA;
1428
+ if (!info.key.equals(params.genesisFeed.key)) {
1429
+ scheduleMicroTask(this._ctx, async () => {
1430
+ await this.protocol.addFeed(await params.feedProvider(info.key, {
1431
+ sparse
1432
+ }));
1433
+ });
1434
+ }
1435
+ });
1436
+ this._controlPipeline.onCredentialProcessed.set(async (credential) => {
1437
+ await this.onCredentialProcessed.callIfSet(credential);
1438
+ log7("onCredentialProcessed", {
1439
+ credential
1440
+ }, {
1441
+ F: __dxlog_file8,
1442
+ L: 104,
1443
+ S: this,
1444
+ C: (f, a) => f(...a)
1445
+ });
1446
+ this.stateUpdate.emit();
1447
+ });
1448
+ this._controlPipeline.onDelegatedInvitation.set(async (invitation) => {
1449
+ log7("onDelegatedInvitation", {
1450
+ invitation
1451
+ }, {
1452
+ F: __dxlog_file8,
1453
+ L: 108,
1454
+ S: this,
1455
+ C: (f, a) => f(...a)
1456
+ });
1457
+ await params.onDelegatedInvitationStatusChange(invitation, true);
1458
+ });
1459
+ this._controlPipeline.onDelegatedInvitationRemoved.set(async (invitation) => {
1460
+ log7("onDelegatedInvitationRemoved", {
1461
+ invitation
1462
+ }, {
1463
+ F: __dxlog_file8,
1464
+ L: 112,
1465
+ S: this,
1466
+ C: (f, a) => f(...a)
1467
+ });
1468
+ await params.onDelegatedInvitationStatusChange(invitation, false);
1469
+ });
1470
+ this._controlPipeline.onMemberRoleChanged.set(async (changedMembers) => {
1471
+ log7("onMemberRoleChanged", () => ({
1472
+ changedMembers: changedMembers.map((m) => [
1473
+ m.key,
1474
+ m.role
1475
+ ])
1476
+ }), {
1477
+ F: __dxlog_file8,
1478
+ L: 116,
1479
+ S: this,
1480
+ C: (f, a) => f(...a)
1481
+ });
1482
+ await params.onMemberRolesChanged(changedMembers);
1483
+ });
1484
+ this.protocol = params.protocol;
1192
1485
  }
1193
- async _getContainingSpaceForDocument(documentId) {
1194
- const doc = this._repo.handles[documentId]?.docSync();
1195
- if (!doc) {
1196
- return null;
1197
- }
1198
- const spaceKeyHex = getSpaceKeyFromDoc(doc);
1199
- if (!spaceKeyHex) {
1200
- return null;
1201
- }
1202
- return PublicKey2.from(spaceKeyHex);
1486
+ get id() {
1487
+ return this._id;
1203
1488
  }
1204
- /**
1205
- * Flush documents to disk.
1206
- */
1207
- async flush({ documentIds } = {}) {
1208
- await this._repo.flush(documentIds);
1489
+ get key() {
1490
+ return this._key;
1209
1491
  }
1210
- async getHeads(documentIds) {
1211
- const result = [];
1212
- const storeRequestIds = [];
1213
- const storeResultIndices = [];
1214
- for (const documentId of documentIds) {
1215
- const doc = this._repo.handles[documentId]?.docSync();
1216
- if (doc) {
1217
- result.push(getHeads(doc));
1218
- } else {
1219
- storeRequestIds.push(documentId);
1220
- storeResultIndices.push(result.length);
1221
- result.push(void 0);
1222
- }
1223
- }
1224
- if (storeRequestIds.length > 0) {
1225
- const storedHeads = await this._headsStore.getHeads(storeRequestIds);
1226
- for (let i = 0; i < storedHeads.length; i++) {
1227
- result[storeResultIndices[i]] = storedHeads[i];
1228
- }
1229
- }
1230
- return result;
1492
+ get isOpen() {
1493
+ return this._lifecycleState === LifecycleState.OPEN;
1231
1494
  }
1232
- //
1233
- // Collection sync.
1234
- //
1235
- getLocalCollectionState(collectionId) {
1236
- return this._collectionSynchronizer.getLocalCollectionState(collectionId);
1495
+ get genesisFeedKey() {
1496
+ return this._genesisFeedKey;
1237
1497
  }
1238
- getRemoteCollectionStates(collectionId) {
1239
- return this._collectionSynchronizer.getRemoteCollectionStates(collectionId);
1498
+ get controlFeedKey() {
1499
+ return this._controlFeed?.key;
1240
1500
  }
1241
- refreshCollection(collectionId) {
1242
- this._collectionSynchronizer.refreshCollection(collectionId);
1501
+ get dataFeedKey() {
1502
+ return this._dataFeed?.key;
1243
1503
  }
1244
- async getCollectionSyncState(collectionId) {
1245
- const result = {
1246
- peers: []
1247
- };
1248
- const localState = this.getLocalCollectionState(collectionId);
1249
- const remoteState = this.getRemoteCollectionStates(collectionId);
1250
- if (!localState) {
1251
- return result;
1252
- }
1253
- for (const [peerId, state] of remoteState) {
1254
- const diff = diffCollectionState(localState, state);
1255
- result.peers.push({
1256
- peerId,
1257
- differentDocuments: diff.different.length
1258
- });
1259
- }
1260
- return result;
1504
+ get spaceState() {
1505
+ return this._controlPipeline.spaceState;
1261
1506
  }
1262
1507
  /**
1263
- * Update the local collection state based on the locally stored document heads.
1508
+ * @test-only
1264
1509
  */
1265
- async updateLocalCollectionState(collectionId, documentIds) {
1266
- const heads = await this.getHeads(documentIds);
1267
- const documents = Object.fromEntries(heads.map((heads2, index) => [
1268
- documentIds[index],
1269
- heads2 ?? []
1270
- ]));
1271
- this._collectionSynchronizer.setLocalCollectionState(collectionId, {
1272
- documents
1273
- });
1274
- }
1275
- _onCollectionStateQueried(collectionId, peerId) {
1276
- this._collectionSynchronizer.onCollectionStateQueried(collectionId, peerId);
1510
+ get controlPipeline() {
1511
+ return this._controlPipeline.pipeline;
1277
1512
  }
1278
- _onCollectionStateReceived(collectionId, peerId, state) {
1279
- this._collectionSynchronizer.onRemoteStateReceived(collectionId, peerId, decodeCollectionState(state));
1513
+ get snapshotManager() {
1514
+ return this._snapshotManager;
1280
1515
  }
1281
- _queryCollectionState(collectionId, peerId) {
1282
- this._echoNetworkAdapter.queryCollectionState(collectionId, peerId);
1516
+ async setControlFeed(feed) {
1517
+ invariant6(!this._controlFeed, "Control feed already set.", {
1518
+ F: __dxlog_file8,
1519
+ L: 168,
1520
+ S: this,
1521
+ A: [
1522
+ "!this._controlFeed",
1523
+ "'Control feed already set.'"
1524
+ ]
1525
+ });
1526
+ this._controlFeed = feed;
1527
+ await this._controlPipeline.setWriteFeed(feed);
1528
+ return this;
1283
1529
  }
1284
- _sendCollectionState(collectionId, peerId, state) {
1285
- this._echoNetworkAdapter.sendCollectionState(collectionId, peerId, encodeCollectionState(state));
1530
+ async setDataFeed(feed) {
1531
+ invariant6(!this._dataFeed, "Data feed already set.", {
1532
+ F: __dxlog_file8,
1533
+ L: 175,
1534
+ S: this,
1535
+ A: [
1536
+ "!this._dataFeed",
1537
+ "'Data feed already set.'"
1538
+ ]
1539
+ });
1540
+ this._dataFeed = feed;
1541
+ return this;
1286
1542
  }
1287
- _onPeerConnected(peerId) {
1288
- this._collectionSynchronizer.onConnectionOpen(peerId);
1543
+ /**
1544
+ * Use for diagnostics.
1545
+ */
1546
+ getControlFeeds() {
1547
+ return Array.from(this._controlPipeline.spaceState.feeds.values());
1289
1548
  }
1290
- _onPeerDisconnected(peerId) {
1291
- this._collectionSynchronizer.onConnectionClosed(peerId);
1549
+ async _open(ctx) {
1550
+ log7("opening...", void 0, {
1551
+ F: __dxlog_file8,
1552
+ L: 189,
1553
+ S: this,
1554
+ C: (f, a) => f(...a)
1555
+ });
1556
+ await this._controlPipeline.start();
1557
+ await this.protocol.start();
1558
+ await this.protocol.addFeed(await this._feedProvider(this._genesisFeedKey));
1559
+ log7("opened", void 0, {
1560
+ F: __dxlog_file8,
1561
+ L: 196,
1562
+ S: this,
1563
+ C: (f, a) => f(...a)
1564
+ });
1292
1565
  }
1293
- _onRemoteCollectionStateUpdated(collectionId, peerId) {
1294
- const localState = this._collectionSynchronizer.getLocalCollectionState(collectionId);
1295
- const remoteState = this._collectionSynchronizer.getRemoteCollectionStates(collectionId).get(peerId);
1296
- if (!localState || !remoteState) {
1297
- return;
1298
- }
1299
- const { different } = diffCollectionState(localState, remoteState);
1300
- if (different.length === 0) {
1301
- return;
1302
- }
1303
- log3.info("replication documents after collection sync", {
1304
- count: different.length
1566
+ async _close() {
1567
+ log7("closing...", {
1568
+ key: this._key
1305
1569
  }, {
1306
- F: __dxlog_file4,
1307
- L: 463,
1570
+ F: __dxlog_file8,
1571
+ L: 201,
1572
+ S: this,
1573
+ C: (f, a) => f(...a)
1574
+ });
1575
+ await this.protocol.stop();
1576
+ await this._controlPipeline.stop();
1577
+ log7("closed", void 0, {
1578
+ F: __dxlog_file8,
1579
+ L: 207,
1308
1580
  S: this,
1309
1581
  C: (f, a) => f(...a)
1310
1582
  });
1311
- for (const documentId of different) {
1312
- this._repo.find(documentId);
1313
- }
1314
- }
1315
- _onHeadsChanged(documentId, heads) {
1316
- for (const collectionId of this._collectionSynchronizer.getRegisteredCollectionIds()) {
1317
- const state = this._collectionSynchronizer.getLocalCollectionState(collectionId);
1318
- if (state?.documents[documentId]) {
1319
- const newState = structuredClone(state);
1320
- newState.documents[documentId] = heads;
1321
- this._collectionSynchronizer.setLocalCollectionState(collectionId, newState);
1322
- }
1323
- }
1324
1583
  }
1325
1584
  };
1326
- _ts_decorate2([
1327
- trace.info()
1328
- ], AutomergeHost.prototype, "_peerId", void 0);
1329
- _ts_decorate2([
1330
- trace.info({
1331
- depth: null
1332
- })
1333
- ], AutomergeHost.prototype, "_automergePeers", null);
1334
- _ts_decorate2([
1335
- trace.span({
1336
- showInBrowserTimeline: true
1337
- })
1338
- ], AutomergeHost.prototype, "flush", null);
1339
- AutomergeHost = _ts_decorate2([
1340
- trace.resource()
1341
- ], AutomergeHost);
1342
- var getSpaceKeyFromDoc = (doc) => {
1343
- const rawSpaceKey = doc.access?.spaceKey ?? doc.experimental_spaceKey;
1344
- if (rawSpaceKey == null) {
1345
- return null;
1585
+ _ts_decorate4([
1586
+ trace2.info()
1587
+ ], Space.prototype, "protocol", void 0);
1588
+ _ts_decorate4([
1589
+ trace2.info()
1590
+ ], Space.prototype, "_controlPipeline", void 0);
1591
+ _ts_decorate4([
1592
+ logInfo,
1593
+ trace2.info()
1594
+ ], Space.prototype, "id", null);
1595
+ _ts_decorate4([
1596
+ logInfo,
1597
+ trace2.info()
1598
+ ], Space.prototype, "key", null);
1599
+ _ts_decorate4([
1600
+ trace2.span()
1601
+ ], Space.prototype, "_open", null);
1602
+ _ts_decorate4([
1603
+ synchronized2
1604
+ ], Space.prototype, "_close", null);
1605
+ Space = _ts_decorate4([
1606
+ trackLeaks2("open", "close"),
1607
+ trace2.resource()
1608
+ ], Space);
1609
+ var SPACE_IDS_CACHE = new ComplexMap2(PublicKey4.hash);
1610
+ var createIdFromSpaceKey = async (spaceKey) => {
1611
+ const cachedValue = SPACE_IDS_CACHE.get(spaceKey);
1612
+ if (cachedValue !== void 0) {
1613
+ return cachedValue;
1346
1614
  }
1347
- return String(rawSpaceKey);
1348
- };
1349
- var waitForHeads = async (handle, heads) => {
1350
- const unavailableHeads = new Set(heads);
1351
- await handle.whenReady();
1352
- await Event2.wrap(handle, "change").waitForCondition(() => {
1353
- for (const changeHash of unavailableHeads.values()) {
1354
- if (changeIsPresentInDoc(handle.docSync(), changeHash)) {
1355
- unavailableHeads.delete(changeHash);
1356
- }
1357
- }
1358
- return unavailableHeads.size === 0;
1359
- });
1360
- };
1361
- var changeIsPresentInDoc = (doc, changeHash) => {
1362
- return !!getBackend(doc).getChangeByHash(changeHash);
1363
- };
1364
- var decodeCollectionState = (state) => {
1365
- invariant4(typeof state === "object" && state !== null, "Invalid state", {
1366
- F: __dxlog_file4,
1367
- L: 516,
1368
- S: void 0,
1369
- A: [
1370
- "typeof state === 'object' && state !== null",
1371
- "'Invalid state'"
1372
- ]
1373
- });
1374
- return state;
1375
- };
1376
- var encodeCollectionState = (state) => {
1377
- return state;
1378
- };
1379
-
1380
- // packages/core/echo/echo-pipeline/src/automerge/space-collection.ts
1381
- import { invariant as invariant5 } from "@dxos/invariant";
1382
- import { SpaceId } from "@dxos/keys";
1383
- var __dxlog_file5 = "/home/runner/work/dxos/dxos/packages/core/echo/echo-pipeline/src/automerge/space-collection.ts";
1384
- var deriveCollectionIdFromSpaceId = (spaceId) => `space:${spaceId}`;
1385
- var getSpaceIdFromCollectionId = (collectionId) => {
1386
- const spaceId = collectionId.replace(/^space:/, "");
1387
- invariant5(SpaceId.isValid(spaceId), void 0, {
1388
- F: __dxlog_file5,
1389
- L: 12,
1390
- S: void 0,
1391
- A: [
1392
- "SpaceId.isValid(spaceId)",
1393
- ""
1394
- ]
1395
- });
1615
+ const digest = await subtleCrypto2.digest("SHA-256", spaceKey.asUint8Array());
1616
+ const bytes = new Uint8Array(digest).slice(0, SpaceId.byteLength);
1617
+ const spaceId = SpaceId.encode(bytes);
1618
+ SPACE_IDS_CACHE.set(spaceKey, spaceId);
1396
1619
  return spaceId;
1397
1620
  };
1398
1621
 
1399
- // packages/core/echo/echo-pipeline/src/space/auth.ts
1400
- import { runInContext, scheduleTask as scheduleTask2 } from "@dxos/async";
1401
- import { Context as Context2 } from "@dxos/context";
1402
- import { randomBytes } from "@dxos/crypto";
1403
- import { invariant as invariant6 } from "@dxos/invariant";
1404
- import { log as log4 } from "@dxos/log";
1405
- import { schema as schema4 } from "@dxos/protocols";
1406
- import { RpcExtension } from "@dxos/teleport";
1407
- var __dxlog_file6 = "/home/runner/work/dxos/dxos/packages/core/echo/echo-pipeline/src/space/auth.ts";
1408
- var AuthExtension = class extends RpcExtension {
1409
- constructor(_authParams) {
1410
- super({
1411
- requested: {
1412
- AuthService: schema4.getService("dxos.mesh.teleport.auth.AuthService")
1413
- },
1414
- exposed: {
1415
- AuthService: schema4.getService("dxos.mesh.teleport.auth.AuthService")
1416
- },
1417
- timeout: 60 * 1e3
1418
- });
1419
- this._authParams = _authParams;
1420
- this._ctx = new Context2({
1421
- onError: (err) => {
1422
- log4.catch(err, void 0, {
1423
- F: __dxlog_file6,
1424
- L: 28,
1425
- S: this,
1426
- C: (f, a) => f(...a)
1427
- });
1428
- }
1429
- }, {
1430
- F: __dxlog_file6,
1431
- L: 26
1432
- });
1433
- }
1434
- async getHandlers() {
1435
- return {
1436
- AuthService: {
1437
- authenticate: async ({ challenge }) => {
1438
- try {
1439
- const credential = await this._authParams.provider(challenge);
1440
- if (!credential) {
1441
- throw new Error("auth rejected");
1442
- }
1443
- return {
1444
- credential
1445
- };
1446
- } catch (err) {
1447
- log4.error("failed to generate auth credentials", err, {
1448
- F: __dxlog_file6,
1449
- L: 55,
1450
- S: this,
1451
- C: (f, a) => f(...a)
1452
- });
1453
- throw new Error("auth rejected");
1454
- }
1455
- }
1456
- }
1457
- };
1458
- }
1459
- async onOpen(context) {
1460
- await super.onOpen(context);
1461
- scheduleTask2(this._ctx, async () => {
1462
- try {
1463
- const challenge = randomBytes(32);
1464
- const { credential } = await this.rpc.AuthService.authenticate({
1465
- challenge
1466
- });
1467
- invariant6(credential?.length > 0, "invalid credential", {
1468
- F: __dxlog_file6,
1469
- L: 69,
1470
- S: this,
1471
- A: [
1472
- "credential?.length > 0",
1473
- "'invalid credential'"
1474
- ]
1475
- });
1476
- const success = await this._authParams.verifier(challenge, credential);
1477
- invariant6(success, "credential not verified", {
1478
- F: __dxlog_file6,
1479
- L: 71,
1480
- S: this,
1481
- A: [
1482
- "success",
1483
- "'credential not verified'"
1484
- ]
1485
- });
1486
- runInContext(this._ctx, () => this._authParams.onAuthSuccess());
1622
+ // packages/core/echo/echo-pipeline/src/space/admission-discovery-extension.ts
1623
+ import { scheduleTask as scheduleTask3 } from "@dxos/async";
1624
+ import { Context as Context4 } from "@dxos/context";
1625
+ import { ProtocolError, schema as schema5 } from "@dxos/protocols";
1626
+ import { RpcExtension as RpcExtension2 } from "@dxos/teleport";
1627
+ var __dxlog_file9 = "/home/runner/work/dxos/dxos/packages/core/echo/echo-pipeline/src/space/admission-discovery-extension.ts";
1628
+ var CredentialRetrieverExtension = class extends RpcExtension2 {
1629
+ constructor(_request, _onResult) {
1630
+ super({
1631
+ requested: {
1632
+ AdmissionDiscoveryService: schema5.getService("dxos.mesh.teleport.AdmissionDiscoveryService")
1633
+ }
1634
+ });
1635
+ this._request = _request;
1636
+ this._onResult = _onResult;
1637
+ this._ctx = new Context4(void 0, {
1638
+ F: __dxlog_file9,
1639
+ L: 25
1640
+ });
1641
+ }
1642
+ async getHandlers() {
1643
+ return {};
1644
+ }
1645
+ async onOpen(context) {
1646
+ await super.onOpen(context);
1647
+ scheduleTask3(this._ctx, async () => {
1648
+ try {
1649
+ const result = await this.rpc.AdmissionDiscoveryService.getAdmissionCredential(this._request);
1650
+ this._onResult.wake(result.admissionCredential);
1487
1651
  } catch (err) {
1488
- log4("auth failed", err, {
1489
- F: __dxlog_file6,
1490
- L: 74,
1491
- S: this,
1492
- C: (f, a) => f(...a)
1493
- });
1494
- this.close();
1495
- this._authParams.onAuthFailure();
1652
+ context.close(err);
1496
1653
  }
1497
1654
  });
1498
1655
  }
1499
1656
  async onClose() {
1500
1657
  await this._ctx.dispose();
1501
- await super.onClose();
1502
1658
  }
1503
1659
  async onAbort() {
1504
1660
  await this._ctx.dispose();
1505
- await super.onAbort();
1661
+ }
1662
+ };
1663
+ var CredentialServerExtension = class extends RpcExtension2 {
1664
+ constructor(_space) {
1665
+ super({
1666
+ exposed: {
1667
+ AdmissionDiscoveryService: schema5.getService("dxos.mesh.teleport.AdmissionDiscoveryService")
1668
+ }
1669
+ });
1670
+ this._space = _space;
1671
+ }
1672
+ async getHandlers() {
1673
+ return {
1674
+ AdmissionDiscoveryService: {
1675
+ getAdmissionCredential: async (request) => {
1676
+ const memberInfo = this._space.spaceState.members.get(request.memberKey);
1677
+ if (!memberInfo?.credential) {
1678
+ throw new ProtocolError("Space member not found.", request);
1679
+ }
1680
+ return {
1681
+ admissionCredential: memberInfo.credential
1682
+ };
1683
+ }
1684
+ }
1685
+ };
1506
1686
  }
1507
1687
  };
1508
1688
 
1509
- // packages/core/echo/echo-pipeline/src/pipeline/timeframe-clock.ts
1510
- import { Event as Event3 } from "@dxos/async";
1511
- import { timed } from "@dxos/debug";
1512
- import { log as log5 } from "@dxos/log";
1513
- import { Timeframe } from "@dxos/timeframe";
1514
- function _ts_decorate3(decorators, target, key, desc) {
1689
+ // packages/core/echo/echo-pipeline/src/space/space-protocol.ts
1690
+ import { discoveryKey, subtleCrypto as subtleCrypto3 } from "@dxos/crypto";
1691
+ import { PublicKey as PublicKey5 } from "@dxos/keys";
1692
+ import { log as log8, logInfo as logInfo2 } from "@dxos/log";
1693
+ import { MMSTTopology } from "@dxos/network-manager";
1694
+ import { Teleport } from "@dxos/teleport";
1695
+ import { BlobSync } from "@dxos/teleport-extension-object-sync";
1696
+ import { ReplicatorExtension } from "@dxos/teleport-extension-replicator";
1697
+ import { trace as trace3 } from "@dxos/tracing";
1698
+ import { CallbackCollection, ComplexMap as ComplexMap3 } from "@dxos/util";
1699
+ function _ts_decorate5(decorators, target, key, desc) {
1515
1700
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1516
1701
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function")
1517
1702
  r = Reflect.decorate(decorators, target, key, desc);
@@ -1521,131 +1706,252 @@ function _ts_decorate3(decorators, target, key, desc) {
1521
1706
  r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
1522
1707
  return c > 3 && r && Object.defineProperty(target, key, r), r;
1523
1708
  }
1524
- var __dxlog_file7 = "/home/runner/work/dxos/dxos/packages/core/echo/echo-pipeline/src/pipeline/timeframe-clock.ts";
1525
- var mapTimeframeToFeedIndexes = (timeframe) => timeframe.frames().map(([feedKey, index]) => ({
1526
- feedKey,
1527
- index
1528
- }));
1529
- var mapFeedIndexesToTimeframe = (indexes) => new Timeframe(indexes.map(({ feedKey, index }) => [
1530
- feedKey,
1531
- index
1532
- ]));
1533
- var startAfter = (timeframe) => timeframe.frames().map(([feedKey, index]) => ({
1534
- feedKey,
1535
- index: index + 1
1536
- }));
1537
- var TimeframeClock = class {
1538
- constructor(_timeframe = new Timeframe()) {
1539
- this._timeframe = _timeframe;
1540
- this.update = new Event3();
1541
- this._pendingTimeframe = _timeframe;
1709
+ var __dxlog_file10 = "/home/runner/work/dxos/dxos/packages/core/echo/echo-pipeline/src/space/space-protocol.ts";
1710
+ var MOCK_AUTH_PROVIDER = async (nonce) => Buffer.from("mock");
1711
+ var MOCK_AUTH_VERIFIER = async (nonce, credential) => true;
1712
+ var SpaceProtocol = class {
1713
+ constructor({ topic, swarmIdentity, networkManager, onSessionAuth, onAuthFailure, blobStore, disableP2pReplication }) {
1714
+ this._feeds = /* @__PURE__ */ new Set();
1715
+ this._sessions = new ComplexMap3(PublicKey5.hash);
1716
+ // TODO(burdon): Move to config (with sensible defaults).
1717
+ this._topology = new MMSTTopology({
1718
+ originateConnections: 4,
1719
+ maxPeers: 10,
1720
+ sampleSize: 20
1721
+ });
1722
+ this.feedAdded = new CallbackCollection();
1723
+ this._spaceKey = topic;
1724
+ this._networkManager = networkManager;
1725
+ this._swarmIdentity = swarmIdentity;
1726
+ this._onSessionAuth = onSessionAuth;
1727
+ this._onAuthFailure = onAuthFailure;
1728
+ this.blobSync = new BlobSync({
1729
+ blobStore
1730
+ });
1731
+ this._topic = subtleCrypto3.digest("SHA-256", topic.asBuffer()).then(discoveryKey).then(PublicKey5.from);
1732
+ this._disableP2pReplication = disableP2pReplication ?? false;
1542
1733
  }
1543
- /**
1544
- * Timeframe that was processed by ECHO.
1545
- */
1546
- get timeframe() {
1547
- return this._timeframe;
1734
+ get sessions() {
1735
+ return this._sessions;
1548
1736
  }
1549
- /**
1550
- * Timeframe that is currently being processed by ECHO.
1551
- * Will be equal to `timeframe` after the processing is complete.
1552
- */
1553
- get pendingTimeframe() {
1554
- return this._pendingTimeframe;
1737
+ get feeds() {
1738
+ return this._feeds;
1555
1739
  }
1556
- setTimeframe(timeframe) {
1557
- this._timeframe = timeframe;
1558
- this._pendingTimeframe = timeframe;
1559
- this.update.emit(this._timeframe);
1740
+ get _ownPeerKey() {
1741
+ return this._swarmIdentity.peerKey;
1560
1742
  }
1561
- updatePendingTimeframe(key, seq) {
1562
- this._pendingTimeframe = Timeframe.merge(this._pendingTimeframe, new Timeframe([
1563
- [
1564
- key,
1565
- seq
1566
- ]
1567
- ]));
1743
+ // TODO(burdon): Create abstraction for Space (e.g., add keys and have provider).
1744
+ async addFeed(feed) {
1745
+ log8("addFeed", {
1746
+ key: feed.key
1747
+ }, {
1748
+ F: __dxlog_file10,
1749
+ L: 127,
1750
+ S: this,
1751
+ C: (f, a) => f(...a)
1752
+ });
1753
+ this._feeds.add(feed);
1754
+ for (const session of this._sessions.values()) {
1755
+ session.replicator.addFeed(feed);
1756
+ }
1757
+ await this.feedAdded.callSerial(feed);
1568
1758
  }
1569
- updateTimeframe() {
1570
- this._timeframe = this._pendingTimeframe;
1571
- this.update.emit(this._timeframe);
1759
+ // TODO(burdon): Rename open? Common open/close interfaces for all services?
1760
+ async start() {
1761
+ if (this._connection) {
1762
+ return;
1763
+ }
1764
+ const credentials = await this._swarmIdentity.credentialProvider(Buffer.from(""));
1765
+ await this.blobSync.open();
1766
+ log8("starting...", void 0, {
1767
+ F: __dxlog_file10,
1768
+ L: 148,
1769
+ S: this,
1770
+ C: (f, a) => f(...a)
1771
+ });
1772
+ const topic = await this._topic;
1773
+ this._connection = await this._networkManager.joinSwarm({
1774
+ protocolProvider: this._createProtocolProvider(credentials),
1775
+ peerId: this._swarmIdentity.peerKey,
1776
+ topic,
1777
+ topology: this._topology,
1778
+ label: `swarm ${topic.truncate()} for space ${this._spaceKey.truncate()}`
1779
+ });
1780
+ log8("started", void 0, {
1781
+ F: __dxlog_file10,
1782
+ L: 158,
1783
+ S: this,
1784
+ C: (f, a) => f(...a)
1785
+ });
1572
1786
  }
1573
- hasGaps(timeframe) {
1574
- const gaps = Timeframe.dependencies(timeframe, this._timeframe);
1575
- return !gaps.isEmpty();
1787
+ updateTopology() {
1788
+ this._topology.forceUpdate();
1789
+ }
1790
+ async stop() {
1791
+ await this.blobSync.close();
1792
+ if (this._connection) {
1793
+ log8("stopping...", void 0, {
1794
+ F: __dxlog_file10,
1795
+ L: 169,
1796
+ S: this,
1797
+ C: (f, a) => f(...a)
1798
+ });
1799
+ await this._connection.close();
1800
+ log8("stopped", void 0, {
1801
+ F: __dxlog_file10,
1802
+ L: 171,
1803
+ S: this,
1804
+ C: (f, a) => f(...a)
1805
+ });
1806
+ }
1807
+ }
1808
+ _createProtocolProvider(credentials) {
1809
+ return (wireParams) => {
1810
+ const session = new SpaceProtocolSession({
1811
+ wireParams,
1812
+ swarmIdentity: this._swarmIdentity,
1813
+ onSessionAuth: this._onSessionAuth,
1814
+ onAuthFailure: this._onAuthFailure,
1815
+ blobSync: this.blobSync,
1816
+ disableP2pReplication: this._disableP2pReplication
1817
+ });
1818
+ this._sessions.set(wireParams.remotePeerId, session);
1819
+ for (const feed of this._feeds) {
1820
+ session.replicator.addFeed(feed);
1821
+ }
1822
+ return session;
1823
+ };
1824
+ }
1825
+ };
1826
+ _ts_decorate5([
1827
+ logInfo2,
1828
+ trace3.info()
1829
+ ], SpaceProtocol.prototype, "_topic", void 0);
1830
+ _ts_decorate5([
1831
+ trace3.info()
1832
+ ], SpaceProtocol.prototype, "_spaceKey", void 0);
1833
+ _ts_decorate5([
1834
+ logInfo2
1835
+ ], SpaceProtocol.prototype, "_ownPeerKey", null);
1836
+ SpaceProtocol = _ts_decorate5([
1837
+ trace3.resource()
1838
+ ], SpaceProtocol);
1839
+ var AuthStatus;
1840
+ (function(AuthStatus2) {
1841
+ AuthStatus2["INITIAL"] = "INITIAL";
1842
+ AuthStatus2["SUCCESS"] = "SUCCESS";
1843
+ AuthStatus2["FAILURE"] = "FAILURE";
1844
+ })(AuthStatus || (AuthStatus = {}));
1845
+ var SpaceProtocolSession = class {
1846
+ // TODO(dmaretskyi): Allow to pass in extra extensions.
1847
+ constructor({ wireParams, swarmIdentity, onSessionAuth, onAuthFailure, blobSync, disableP2pReplication }) {
1848
+ // TODO(dmaretskyi): Start with upload=false when switching it on the fly works.
1849
+ this.replicator = new ReplicatorExtension().setOptions({
1850
+ upload: true
1851
+ });
1852
+ this._authStatus = "INITIAL";
1853
+ this._wireParams = wireParams;
1854
+ this._swarmIdentity = swarmIdentity;
1855
+ this._onSessionAuth = onSessionAuth;
1856
+ this._onAuthFailure = onAuthFailure;
1857
+ this._blobSync = blobSync;
1858
+ this._teleport = new Teleport(wireParams);
1859
+ this._disableP2pReplication = disableP2pReplication ?? false;
1860
+ }
1861
+ get authStatus() {
1862
+ return this._authStatus;
1863
+ }
1864
+ get stats() {
1865
+ return this._teleport.stats;
1866
+ }
1867
+ get stream() {
1868
+ return this._teleport.stream;
1869
+ }
1870
+ async open(sessionId) {
1871
+ await this._teleport.open(sessionId);
1872
+ this._teleport.addExtension("dxos.mesh.teleport.auth", new AuthExtension({
1873
+ provider: this._swarmIdentity.credentialProvider,
1874
+ verifier: this._swarmIdentity.credentialAuthenticator,
1875
+ onAuthSuccess: () => {
1876
+ log8("Peer authenticated", void 0, {
1877
+ F: __dxlog_file10,
1878
+ L: 282,
1879
+ S: this,
1880
+ C: (f, a) => f(...a)
1881
+ });
1882
+ this._authStatus = "SUCCESS";
1883
+ this._onSessionAuth?.(this._teleport);
1884
+ },
1885
+ onAuthFailure: () => {
1886
+ this._authStatus = "FAILURE";
1887
+ this._onAuthFailure?.(this._teleport);
1888
+ }
1889
+ }));
1890
+ if (!this._disableP2pReplication) {
1891
+ this._teleport.addExtension("dxos.mesh.teleport.replicator", this.replicator);
1892
+ }
1893
+ this._teleport.addExtension("dxos.mesh.teleport.blobsync", this._blobSync.createExtension());
1576
1894
  }
1577
- async waitUntilReached(target) {
1578
- log5("waitUntilReached", {
1579
- target,
1580
- current: this._timeframe
1581
- }, {
1582
- F: __dxlog_file7,
1583
- L: 70,
1895
+ async close() {
1896
+ log8("close", void 0, {
1897
+ F: __dxlog_file10,
1898
+ L: 301,
1584
1899
  S: this,
1585
1900
  C: (f, a) => f(...a)
1586
1901
  });
1587
- await this.update.waitForCondition(() => {
1588
- log5("check if reached", {
1589
- target,
1590
- current: this._timeframe,
1591
- deps: Timeframe.dependencies(target, this._timeframe)
1592
- }, {
1593
- F: __dxlog_file7,
1594
- L: 72,
1595
- S: this,
1596
- C: (f, a) => f(...a)
1597
- });
1598
- return Timeframe.dependencies(target, this._timeframe).isEmpty();
1599
- });
1902
+ await this._teleport.close();
1903
+ }
1904
+ async abort() {
1905
+ await this._teleport.abort();
1600
1906
  }
1601
1907
  };
1602
- _ts_decorate3([
1603
- timed(5e3)
1604
- ], TimeframeClock.prototype, "waitUntilReached", null);
1908
+ _ts_decorate5([
1909
+ logInfo2
1910
+ ], SpaceProtocolSession.prototype, "_wireParams", void 0);
1911
+ _ts_decorate5([
1912
+ logInfo2
1913
+ ], SpaceProtocolSession.prototype, "authStatus", null);
1605
1914
 
1606
- // packages/core/echo/echo-pipeline/src/pipeline/pipeline.ts
1607
- import { Event as Event4, sleepWithContext, synchronized as synchronized2, Trigger as Trigger2 } from "@dxos/async";
1608
- import { Context as Context3, rejectOnDispose } from "@dxos/context";
1609
- import { failUndefined } from "@dxos/debug";
1610
- import { FeedSetIterator } from "@dxos/feed-store";
1915
+ // packages/core/echo/echo-pipeline/src/space/space-manager.ts
1916
+ import { synchronized as synchronized4, trackLeaks as trackLeaks3, Trigger as Trigger3 } from "@dxos/async";
1917
+ import { failUndefined as failUndefined2 } from "@dxos/debug";
1918
+ import { PublicKey as PublicKey8 } from "@dxos/keys";
1919
+ import { log as log14 } from "@dxos/log";
1920
+ import { trace as trace6 } from "@dxos/protocols";
1921
+ import { ComplexMap as ComplexMap4 } from "@dxos/util";
1922
+
1923
+ // packages/core/echo/echo-pipeline/src/db-host/data-service.ts
1924
+ import { Stream } from "@dxos/codec-protobuf";
1925
+ import { invariant as invariant12 } from "@dxos/invariant";
1926
+ import { SpaceId as SpaceId3 } from "@dxos/keys";
1927
+ import { log as log13 } from "@dxos/log";
1928
+
1929
+ // packages/core/echo/echo-pipeline/src/automerge/automerge-host.ts
1930
+ import { Event as Event5, asyncTimeout } from "@dxos/async";
1931
+ import { getBackend, getHeads, isAutomerge, equals as headsEquals, save } from "@dxos/automerge/automerge";
1932
+ import { Repo } from "@dxos/automerge/automerge-repo";
1933
+ import { Context as Context5, Resource as Resource5, cancelWithContext as cancelWithContext2 } from "@dxos/context";
1611
1934
  import { invariant as invariant8 } from "@dxos/invariant";
1612
- import { PublicKey as PublicKey3 } from "@dxos/keys";
1613
- import { log as log7 } from "@dxos/log";
1614
- import { Timeframe as Timeframe2 } from "@dxos/timeframe";
1615
- import { ComplexMap } from "@dxos/util";
1935
+ import { PublicKey as PublicKey6 } from "@dxos/keys";
1936
+ import { log as log10 } from "@dxos/log";
1937
+ import { objectPointerCodec } from "@dxos/protocols";
1938
+ import { trace as trace4 } from "@dxos/tracing";
1616
1939
 
1617
- // packages/core/echo/echo-pipeline/src/pipeline/message-selector.ts
1940
+ // packages/core/echo/echo-pipeline/src/automerge/echo-network-adapter.ts
1941
+ import { synchronized as synchronized3, Trigger as Trigger2 } from "@dxos/async";
1942
+ import { NetworkAdapter } from "@dxos/automerge/automerge-repo";
1943
+ import { LifecycleState as LifecycleState2 } from "@dxos/context";
1618
1944
  import { invariant as invariant7 } from "@dxos/invariant";
1619
- import { log as log6 } from "@dxos/log";
1620
- var __dxlog_file8 = "/home/runner/work/dxos/dxos/packages/core/echo/echo-pipeline/src/pipeline/message-selector.ts";
1621
- var createMessageSelector = (timeframeClock) => {
1622
- return (messages) => {
1623
- for (let i = 0; i < messages.length; i++) {
1624
- const { data: { timeframe } } = messages[i];
1625
- invariant7(timeframe, void 0, {
1626
- F: __dxlog_file8,
1627
- L: 25,
1628
- S: void 0,
1629
- A: [
1630
- "timeframe",
1631
- ""
1632
- ]
1633
- });
1634
- if (!timeframeClock.hasGaps(timeframe)) {
1635
- return i;
1636
- }
1637
- }
1638
- log6("Skipping...", void 0, {
1639
- F: __dxlog_file8,
1640
- L: 33,
1641
- S: void 0,
1642
- C: (f, a) => f(...a)
1643
- });
1644
- };
1645
- };
1945
+ import { log as log9 } from "@dxos/log";
1946
+ import { nonNullable } from "@dxos/util";
1646
1947
 
1647
- // packages/core/echo/echo-pipeline/src/pipeline/pipeline.ts
1648
- function _ts_decorate4(decorators, target, key, desc) {
1948
+ // packages/core/echo/echo-pipeline/src/automerge/network-protocol.ts
1949
+ import { MESSAGE_TYPE_COLLECTION_QUERY, MESSAGE_TYPE_COLLECTION_STATE } from "@dxos/protocols";
1950
+ var isCollectionQueryMessage = (message) => message.type === MESSAGE_TYPE_COLLECTION_QUERY;
1951
+ var isCollectionStateMessage = (message) => message.type === MESSAGE_TYPE_COLLECTION_STATE;
1952
+
1953
+ // packages/core/echo/echo-pipeline/src/automerge/echo-network-adapter.ts
1954
+ function _ts_decorate6(decorators, target, key, desc) {
1649
1955
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1650
1956
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function")
1651
1957
  r = Reflect.decorate(decorators, target, key, desc);
@@ -1655,701 +1961,470 @@ function _ts_decorate4(decorators, target, key, desc) {
1655
1961
  r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
1656
1962
  return c > 3 && r && Object.defineProperty(target, key, r), r;
1657
1963
  }
1658
- var __dxlog_file9 = "/home/runner/work/dxos/dxos/packages/core/echo/echo-pipeline/src/pipeline/pipeline.ts";
1659
- var PipelineState = class {
1660
- constructor(_feeds, _timeframeClock) {
1661
- this._feeds = _feeds;
1662
- this._timeframeClock = _timeframeClock;
1663
- this._ctx = new Context3(void 0, {
1664
- F: __dxlog_file9,
1665
- L: 41
1666
- });
1667
- this.timeframeUpdate = this._timeframeClock.update;
1668
- this.stalled = new Event4();
1669
- this._startTimeframe = new Timeframe2();
1670
- this._reachedTarget = false;
1671
- }
1672
- /**
1673
- * Latest theoretical timeframe based on the last mutation in each feed.
1674
- * NOTE: This might never be reached if the mutation dependencies
1675
- */
1676
- // TODO(dmaretskyi): Rename `totalTimeframe`? or `lastTimeframe`.
1677
- get endTimeframe() {
1678
- return mapFeedIndexesToTimeframe(Array.from(this._feeds.values()).filter((feed) => feed.length > 0).map((feed) => ({
1679
- feedKey: feed.key,
1680
- index: feed.length - 1
1681
- })));
1682
- }
1683
- get startTimeframe() {
1684
- return this._startTimeframe;
1685
- }
1686
- get timeframe() {
1687
- return this._timeframeClock.timeframe;
1688
- }
1689
- get pendingTimeframe() {
1690
- return this._timeframeClock.pendingTimeframe;
1691
- }
1692
- get targetTimeframe() {
1693
- return this._targetTimeframe ? this._targetTimeframe : new Timeframe2();
1694
- }
1695
- get reachedTarget() {
1696
- return this._reachedTarget;
1964
+ var __dxlog_file11 = "/home/runner/work/dxos/dxos/packages/core/echo/echo-pipeline/src/automerge/echo-network-adapter.ts";
1965
+ var EchoNetworkAdapter = class extends NetworkAdapter {
1966
+ constructor(_params) {
1967
+ super();
1968
+ this._params = _params;
1969
+ this._replicators = /* @__PURE__ */ new Set();
1970
+ this._connections = /* @__PURE__ */ new Map();
1971
+ this._lifecycleState = LifecycleState2.CLOSED;
1972
+ this._connected = new Trigger2();
1697
1973
  }
1698
- get feeds() {
1699
- return Array.from(this._feeds.values());
1974
+ connect(peerId, peerMetadata) {
1975
+ this.peerId = peerId;
1976
+ this.peerMetadata = peerMetadata;
1977
+ this._connected.wake();
1700
1978
  }
1701
- async waitUntilTimeframe(target) {
1702
- await this._timeframeClock.waitUntilReached(target);
1979
+ send(message) {
1980
+ this._send(message);
1703
1981
  }
1704
- setTargetTimeframe(target) {
1705
- this._targetTimeframe = target;
1982
+ disconnect() {
1706
1983
  }
1707
- /**
1708
- * Wait until the pipeline processes all messages in the feed and reaches the target timeframe if that is set.
1709
- *
1710
- * This function will resolve immediately if the pipeline is stalled.
1711
- *
1712
- * @param timeout Timeout in milliseconds to specify the maximum wait time.
1713
- */
1714
- async waitUntilReachedTargetTimeframe({ ctx = new Context3(void 0, {
1715
- F: __dxlog_file9,
1716
- L: 129
1717
- }), timeout, breakOnStall = true } = {}) {
1718
- log7("waitUntilReachedTargetTimeframe", {
1719
- timeout,
1720
- current: this.timeframe,
1721
- target: this.targetTimeframe
1722
- }, {
1723
- F: __dxlog_file9,
1724
- L: 133,
1725
- S: this,
1726
- C: (f, a) => f(...a)
1727
- });
1728
- this._reachedTargetPromise ??= Promise.race([
1729
- this._timeframeClock.update.waitForCondition(() => {
1730
- return Timeframe2.dependencies(this.targetTimeframe, this.timeframe).isEmpty();
1731
- }),
1732
- ...breakOnStall ? [
1733
- this.stalled.discardParameter().waitForCount(1)
1734
- ] : []
1735
- ]);
1736
- let done = false;
1737
- if (timeout) {
1738
- return Promise.race([
1739
- rejectOnDispose(ctx),
1740
- rejectOnDispose(this._ctx),
1741
- this._reachedTargetPromise.then(() => {
1742
- done = true;
1743
- this._reachedTarget = true;
1744
- }),
1745
- sleepWithContext(this._ctx, timeout).then(() => {
1746
- if (done) {
1747
- return;
1748
- }
1749
- log7.warn("waitUntilReachedTargetTimeframe timed out", {
1750
- timeout,
1751
- current: this.timeframe,
1752
- target: this.targetTimeframe,
1753
- dependencies: Timeframe2.dependencies(this.targetTimeframe, this.timeframe)
1754
- }, {
1755
- F: __dxlog_file9,
1756
- L: 161,
1757
- S: this,
1758
- C: (f, a) => f(...a)
1759
- });
1760
- })
1761
- ]);
1762
- } else {
1763
- return this._reachedTargetPromise;
1984
+ async open() {
1985
+ if (this._lifecycleState === LifecycleState2.OPEN) {
1986
+ return;
1764
1987
  }
1765
- }
1766
- };
1767
- var Pipeline = class {
1768
- constructor() {
1769
- this._timeframeClock = new TimeframeClock(new Timeframe2());
1770
- this._feeds = new ComplexMap(PublicKey3.hash);
1771
- // External state accessor.
1772
- this._state = new PipelineState(this._feeds, this._timeframeClock);
1773
- // Waits for the message consumer to process the message and yield control back to the pipeline.
1774
- this._processingTrigger = new Trigger2().wake();
1775
- this._pauseTrigger = new Trigger2().wake();
1776
- // Pending downloads.
1777
- this._downloads = new ComplexMap((value) => PublicKey3.hash(value.key));
1778
- this._isStopping = false;
1779
- this._isStarted = false;
1780
- this._isBeingConsumed = false;
1781
- this._isPaused = false;
1782
- }
1783
- get state() {
1784
- return this._state;
1785
- }
1786
- get writer() {
1787
- invariant8(this._writer, "Writer not set.", {
1788
- F: __dxlog_file9,
1789
- L: 243,
1988
+ this._lifecycleState = LifecycleState2.OPEN;
1989
+ log9("emit ready", void 0, {
1990
+ F: __dxlog_file11,
1991
+ L: 79,
1790
1992
  S: this,
1791
- A: [
1792
- "this._writer",
1793
- "'Writer not set.'"
1794
- ]
1993
+ C: (f, a) => f(...a)
1994
+ });
1995
+ this.emit("ready", {
1996
+ network: this
1795
1997
  });
1796
- return this._writer;
1797
- }
1798
- hasFeed(feedKey) {
1799
- return this._feeds.has(feedKey);
1800
- }
1801
- getFeeds() {
1802
- return this._feedSetIterator.feeds;
1803
1998
  }
1804
- // NOTE: This cannot be synchronized with `stop` because stop waits for the mutation processing to complete,
1805
- // which might be opening feeds during the mutation processing, which w
1806
- async addFeed(feed) {
1807
- this._feeds.set(feed.key, feed);
1808
- if (this._feedSetIterator) {
1809
- await this._feedSetIterator.addFeed(feed);
1999
+ async close() {
2000
+ if (this._lifecycleState === LifecycleState2.CLOSED) {
2001
+ return this;
1810
2002
  }
1811
- if (this._isStarted && !this._isPaused) {
1812
- this._setFeedDownloadState(feed);
2003
+ for (const replicator of this._replicators) {
2004
+ await replicator.disconnect();
1813
2005
  }
2006
+ this._replicators.clear();
2007
+ this._lifecycleState = LifecycleState2.CLOSED;
1814
2008
  }
1815
- setWriteFeed(feed) {
1816
- invariant8(!this._writer, "Writer already set.", {
1817
- F: __dxlog_file9,
1818
- L: 270,
2009
+ async whenConnected() {
2010
+ await this._connected.wait({
2011
+ timeout: 1e4
2012
+ });
2013
+ }
2014
+ async addReplicator(replicator) {
2015
+ invariant7(this._lifecycleState === LifecycleState2.OPEN, void 0, {
2016
+ F: __dxlog_file11,
2017
+ L: 105,
1819
2018
  S: this,
1820
2019
  A: [
1821
- "!this._writer",
1822
- "'Writer already set.'"
2020
+ "this._lifecycleState === LifecycleState.OPEN",
2021
+ ""
1823
2022
  ]
1824
2023
  });
1825
- invariant8(feed.properties.writable, "Feed must be writable.", {
1826
- F: __dxlog_file9,
1827
- L: 271,
2024
+ invariant7(this.peerId, void 0, {
2025
+ F: __dxlog_file11,
2026
+ L: 106,
1828
2027
  S: this,
1829
2028
  A: [
1830
- "feed.properties.writable",
1831
- "'Feed must be writable.'"
2029
+ "this.peerId",
2030
+ ""
1832
2031
  ]
1833
2032
  });
1834
- this._writer = createMappedFeedWriter((payload) => ({
1835
- timeframe: this._timeframeClock.timeframe,
1836
- payload
1837
- }), feed.createFeedWriter());
1838
- }
1839
- async start() {
1840
- invariant8(!this._isStarted, "Pipeline is already started.", {
1841
- F: __dxlog_file9,
1842
- L: 284,
2033
+ invariant7(!this._replicators.has(replicator), void 0, {
2034
+ F: __dxlog_file11,
2035
+ L: 107,
1843
2036
  S: this,
1844
2037
  A: [
1845
- "!this._isStarted",
1846
- "'Pipeline is already started.'"
2038
+ "!this._replicators.has(replicator)",
2039
+ ""
1847
2040
  ]
1848
2041
  });
1849
- log7("starting...", void 0, {
1850
- F: __dxlog_file9,
1851
- L: 285,
1852
- S: this,
1853
- C: (f, a) => f(...a)
1854
- });
1855
- await this._initIterator();
1856
- await this._feedSetIterator.open();
1857
- this._isStarted = true;
1858
- log7("started", void 0, {
1859
- F: __dxlog_file9,
1860
- L: 289,
1861
- S: this,
1862
- C: (f, a) => f(...a)
1863
- });
1864
- if (!this._isPaused) {
1865
- for (const feed of this._feeds.values()) {
1866
- this._setFeedDownloadState(feed);
2042
+ this._replicators.add(replicator);
2043
+ await replicator.connect({
2044
+ peerId: this.peerId,
2045
+ onConnectionOpen: this._onConnectionOpen.bind(this),
2046
+ onConnectionClosed: this._onConnectionClosed.bind(this),
2047
+ onConnectionAuthScopeChanged: this._onConnectionAuthScopeChanged.bind(this),
2048
+ getContainingSpaceForDocument: this._params.getContainingSpaceForDocument,
2049
+ getContainingSpaceIdForDocument: async (documentId) => {
2050
+ const key = await this._params.getContainingSpaceForDocument(documentId);
2051
+ return key ? createIdFromSpaceKey(key) : null;
1867
2052
  }
1868
- }
1869
- }
1870
- async stop() {
1871
- log7("stopping...", void 0, {
1872
- F: __dxlog_file9,
1873
- L: 300,
1874
- S: this,
1875
- C: (f, a) => f(...a)
1876
- });
1877
- this._isStopping = true;
1878
- for (const [feed, handle] of this._downloads.entries()) {
1879
- feed.undownload(handle);
1880
- }
1881
- this._downloads.clear();
1882
- await this._feedSetIterator?.close();
1883
- await this._processingTrigger.wait();
1884
- await this._state._ctx.dispose();
1885
- this._state._ctx = new Context3(void 0, {
1886
- F: __dxlog_file9,
1887
- L: 309
1888
- });
1889
- this._state._reachedTargetPromise = void 0;
1890
- this._state._reachedTarget = false;
1891
- this._isStarted = false;
1892
- log7("stopped", void 0, {
1893
- F: __dxlog_file9,
1894
- L: 313,
1895
- S: this,
1896
- C: (f, a) => f(...a)
1897
2053
  });
1898
2054
  }
1899
- /**
1900
- * @param timeframe Timeframe of already processed messages.
1901
- * The pipeline will start processing messages AFTER this timeframe.
1902
- */
1903
- async setCursor(timeframe) {
1904
- invariant8(!this._isStarted || this._isPaused, "Invalid state.", {
1905
- F: __dxlog_file9,
1906
- L: 322,
2055
+ async removeReplicator(replicator) {
2056
+ invariant7(this._lifecycleState === LifecycleState2.OPEN, void 0, {
2057
+ F: __dxlog_file11,
2058
+ L: 125,
1907
2059
  S: this,
1908
2060
  A: [
1909
- "!this._isStarted || this._isPaused",
1910
- "'Invalid state.'"
2061
+ "this._lifecycleState === LifecycleState.OPEN",
2062
+ ""
1911
2063
  ]
1912
2064
  });
1913
- this._state._startTimeframe = timeframe;
1914
- this._timeframeClock.setTimeframe(timeframe);
1915
- if (this._feedSetIterator) {
1916
- await this._feedSetIterator.close();
1917
- await this._initIterator();
1918
- await this._feedSetIterator.open();
1919
- }
1920
- }
1921
- /**
1922
- * Calling pause while processing will cause a deadlock.
1923
- */
1924
- async pause() {
1925
- if (this._isPaused) {
1926
- return;
1927
- }
1928
- this._pauseTrigger.reset();
1929
- await this._processingTrigger.wait();
1930
- this._isPaused = true;
1931
- }
1932
- async unpause() {
1933
- invariant8(this._isPaused, "Pipeline is not paused.", {
1934
- F: __dxlog_file9,
1935
- L: 351,
2065
+ invariant7(this._replicators.has(replicator), void 0, {
2066
+ F: __dxlog_file11,
2067
+ L: 126,
1936
2068
  S: this,
1937
2069
  A: [
1938
- "this._isPaused",
1939
- "'Pipeline is not paused.'"
2070
+ "this._replicators.has(replicator)",
2071
+ ""
1940
2072
  ]
1941
2073
  });
1942
- this._pauseTrigger.wake();
1943
- this._isPaused = false;
1944
- for (const feed of this._feeds.values()) {
1945
- this._setFeedDownloadState(feed);
2074
+ await replicator.disconnect();
2075
+ this._replicators.delete(replicator);
2076
+ }
2077
+ async shouldAdvertise(peerId, params) {
2078
+ const connection = this._connections.get(peerId);
2079
+ if (!connection) {
2080
+ return false;
1946
2081
  }
2082
+ return connection.connection.shouldAdvertise(params);
1947
2083
  }
1948
- /**
1949
- * Starts to iterate over the ordered messages from the added feeds.
1950
- * Updates the timeframe clock after the message has bee processed.
1951
- */
1952
- async *consume() {
1953
- invariant8(!this._isBeingConsumed, "Pipeline is already being consumed.", {
1954
- F: __dxlog_file9,
1955
- L: 366,
1956
- S: this,
1957
- A: [
1958
- "!this._isBeingConsumed",
1959
- "'Pipeline is already being consumed.'"
1960
- ]
1961
- });
1962
- this._isBeingConsumed = true;
1963
- invariant8(this._feedSetIterator, "Iterator not initialized.", {
1964
- F: __dxlog_file9,
1965
- L: 369,
1966
- S: this,
1967
- A: [
1968
- "this._feedSetIterator",
1969
- "'Iterator not initialized.'"
1970
- ]
1971
- });
1972
- let lastFeedSetIterator = this._feedSetIterator;
1973
- let iterable = lastFeedSetIterator[Symbol.asyncIterator]();
1974
- while (!this._isStopping) {
1975
- await this._pauseTrigger.wait();
1976
- if (lastFeedSetIterator !== this._feedSetIterator) {
1977
- invariant8(this._feedSetIterator, "Iterator not initialized.", {
1978
- F: __dxlog_file9,
1979
- L: 378,
1980
- S: this,
1981
- A: [
1982
- "this._feedSetIterator",
1983
- "'Iterator not initialized.'"
1984
- ]
1985
- });
1986
- lastFeedSetIterator = this._feedSetIterator;
1987
- iterable = lastFeedSetIterator[Symbol.asyncIterator]();
1988
- }
1989
- const { done, value } = await iterable.next();
1990
- if (!done) {
1991
- const block = value ?? failUndefined();
1992
- this._processingTrigger.reset();
1993
- this._timeframeClock.updatePendingTimeframe(PublicKey3.from(block.feedKey), block.seq);
1994
- yield block;
1995
- this._processingTrigger.wake();
1996
- this._timeframeClock.updateTimeframe();
1997
- }
2084
+ shouldSyncCollection(peerId, params) {
2085
+ const connection = this._connections.get(peerId);
2086
+ if (!connection) {
2087
+ return false;
1998
2088
  }
1999
- this._isBeingConsumed = false;
2089
+ return connection.connection.shouldSyncCollection(params);
2000
2090
  }
2001
- _setFeedDownloadState(feed) {
2002
- let handle = this._downloads.get(feed);
2003
- if (handle) {
2004
- feed.undownload(handle);
2091
+ queryCollectionState(collectionId, targetId) {
2092
+ const message = {
2093
+ type: "collection-query",
2094
+ senderId: this.peerId,
2095
+ targetId,
2096
+ collectionId
2097
+ };
2098
+ this._send(message);
2099
+ }
2100
+ sendCollectionState(collectionId, targetId, state) {
2101
+ const message = {
2102
+ type: "collection-state",
2103
+ senderId: this.peerId,
2104
+ targetId,
2105
+ collectionId,
2106
+ state
2107
+ };
2108
+ this._send(message);
2109
+ }
2110
+ _send(message) {
2111
+ const connectionEntry = this._connections.get(message.targetId);
2112
+ if (!connectionEntry) {
2113
+ throw new Error("Connection not found.");
2005
2114
  }
2006
- const timeframe = this._state._startTimeframe;
2007
- const seq = timeframe.get(feed.key) ?? -1;
2008
- log7("download", {
2009
- feed: feed.key.truncate(),
2010
- seq,
2011
- length: feed.length
2012
- }, {
2013
- F: __dxlog_file9,
2014
- L: 407,
2015
- S: this,
2016
- C: (f, a) => f(...a)
2017
- });
2018
- handle = feed.download({
2019
- start: seq + 1,
2020
- linear: true
2021
- }, (err, data) => {
2022
- if (err) {
2023
- } else {
2024
- log7.info("downloaded", {
2025
- data
2026
- }, {
2027
- F: __dxlog_file9,
2028
- L: 412,
2115
+ const writeStart = Date.now();
2116
+ connectionEntry.writer.write(message).then(() => {
2117
+ const durationMs = Date.now() - writeStart;
2118
+ this._params.monitor?.recordMessageSent(message, durationMs);
2119
+ }).catch((err) => {
2120
+ if (connectionEntry.isOpen) {
2121
+ log9.catch(err, void 0, {
2122
+ F: __dxlog_file11,
2123
+ L: 186,
2029
2124
  S: this,
2030
2125
  C: (f, a) => f(...a)
2031
2126
  });
2032
2127
  }
2128
+ this._params.monitor?.recordMessageSendingFailed(message);
2033
2129
  });
2034
- this._downloads.set(feed, handle);
2035
2130
  }
2036
- async _initIterator() {
2037
- this._feedSetIterator = new FeedSetIterator(createMessageSelector(this._timeframeClock), {
2038
- start: startAfter(this._timeframeClock.timeframe),
2039
- stallTimeout: 1e3
2040
- });
2041
- this._feedSetIterator.stalled.on((iterator) => {
2042
- log7.warn(`Stalled after ${iterator.options.stallTimeout}ms with ${iterator.size} feeds.`, void 0, {
2043
- F: __dxlog_file9,
2044
- L: 426,
2045
- S: this,
2046
- C: (f, a) => f(...a)
2047
- });
2048
- this._state.stalled.emit();
2049
- });
2050
- for (const feed of this._feeds.values()) {
2051
- await this._feedSetIterator.addFeed(feed);
2052
- }
2131
+ // TODO(dmaretskyi): Remove.
2132
+ getPeersInterestedInCollection(collectionId) {
2133
+ return Array.from(this._connections.values()).map((connection) => {
2134
+ return connection.connection.shouldSyncCollection({
2135
+ collectionId
2136
+ }) ? connection.connection.peerId : null;
2137
+ }).filter(nonNullable);
2053
2138
  }
2054
- };
2055
- _ts_decorate4([
2056
- synchronized2
2057
- ], Pipeline.prototype, "start", null);
2058
- _ts_decorate4([
2059
- synchronized2
2060
- ], Pipeline.prototype, "stop", null);
2061
- _ts_decorate4([
2062
- synchronized2
2063
- ], Pipeline.prototype, "setCursor", null);
2064
- _ts_decorate4([
2065
- synchronized2
2066
- ], Pipeline.prototype, "pause", null);
2067
- _ts_decorate4([
2068
- synchronized2
2069
- ], Pipeline.prototype, "unpause", null);
2070
-
2071
- // packages/core/echo/echo-pipeline/src/space/space.ts
2072
- import { Event as Event5, synchronized as synchronized3, trackLeaks as trackLeaks2 } from "@dxos/async";
2073
- import { LifecycleState as LifecycleState3, Resource as Resource5 } from "@dxos/context";
2074
- import { subtleCrypto as subtleCrypto2 } from "@dxos/crypto";
2075
- import { invariant as invariant9 } from "@dxos/invariant";
2076
- import { PublicKey as PublicKey5, SpaceId as SpaceId2 } from "@dxos/keys";
2077
- import { log as log9, logInfo } from "@dxos/log";
2078
- import { AdmittedFeed as AdmittedFeed2 } from "@dxos/protocols/proto/dxos/halo/credentials";
2079
- import { trace as trace3 } from "@dxos/tracing";
2080
- import { Callback as Callback2, ComplexMap as ComplexMap2 } from "@dxos/util";
2081
-
2082
- // packages/core/echo/echo-pipeline/src/space/control-pipeline.ts
2083
- import { DeferredTask, sleepWithContext as sleepWithContext2, trackLeaks } from "@dxos/async";
2084
- import { Context as Context4 } from "@dxos/context";
2085
- import { SpaceStateMachine } from "@dxos/credentials";
2086
- import { PublicKey as PublicKey4 } from "@dxos/keys";
2087
- import { log as log8 } from "@dxos/log";
2088
- import { AdmittedFeed } from "@dxos/protocols/proto/dxos/halo/credentials";
2089
- import { Timeframe as Timeframe3 } from "@dxos/timeframe";
2090
- import { TimeSeriesCounter, TimeUsageCounter, trace as trace2 } from "@dxos/tracing";
2091
- import { Callback, tracer } from "@dxos/util";
2092
- function _ts_decorate5(decorators, target, key, desc) {
2093
- var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
2094
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function")
2095
- r = Reflect.decorate(decorators, target, key, desc);
2096
- else
2097
- for (var i = decorators.length - 1; i >= 0; i--)
2098
- if (d = decorators[i])
2099
- r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
2100
- return c > 3 && r && Object.defineProperty(target, key, r), r;
2101
- }
2102
- var __dxlog_file10 = "/home/runner/work/dxos/dxos/packages/core/echo/echo-pipeline/src/space/control-pipeline.ts";
2103
- var TIMEFRAME_SAVE_DEBOUNCE_INTERVAL = 500;
2104
- var CONTROL_PIPELINE_SNAPSHOT_DELAY = 1e4;
2105
- var USE_SNAPSHOTS = true;
2106
- var ControlPipeline = class {
2107
- constructor({ spaceKey, genesisFeed, feedProvider, metadataStore }) {
2108
- this._ctx = new Context4(void 0, {
2109
- F: __dxlog_file10,
2110
- L: 47
2139
+ _onConnectionOpen(connection) {
2140
+ log9("Connection opened", {
2141
+ peerId: connection.peerId
2142
+ }, {
2143
+ F: __dxlog_file11,
2144
+ L: 204,
2145
+ S: this,
2146
+ C: (f, a) => f(...a)
2111
2147
  });
2112
- this._lastTimeframeSaveTime = Date.now();
2113
- this.onFeedAdmitted = new Callback();
2114
- this._usage = new TimeUsageCounter();
2115
- this._mutations = new TimeSeriesCounter();
2116
- this._snapshotTask = new DeferredTask(this._ctx, async () => {
2117
- await sleepWithContext2(this._ctx, CONTROL_PIPELINE_SNAPSHOT_DELAY);
2118
- await this._saveSnapshot();
2148
+ invariant7(!this._connections.has(connection.peerId), void 0, {
2149
+ F: __dxlog_file11,
2150
+ L: 205,
2151
+ S: this,
2152
+ A: [
2153
+ "!this._connections.has(connection.peerId as PeerId)",
2154
+ ""
2155
+ ]
2119
2156
  });
2120
- this._spaceKey = spaceKey;
2121
- this._metadata = metadataStore;
2122
- this._pipeline = new Pipeline();
2123
- void this._pipeline.addFeed(genesisFeed);
2124
- this._spaceStateMachine = new SpaceStateMachine(spaceKey);
2125
- this._spaceStateMachine.onFeedAdmitted.set(async (info) => {
2126
- log8("feed admitted", {
2127
- key: info.key
2128
- }, {
2129
- F: __dxlog_file10,
2130
- L: 82,
2131
- S: this,
2132
- C: (f, a) => f(...a)
2133
- });
2134
- if (info.assertion.designation === AdmittedFeed.Designation.CONTROL && !info.key.equals(genesisFeed.key)) {
2135
- queueMicrotask(async () => {
2136
- try {
2137
- const feed = await feedProvider(info.key);
2138
- if (!this._pipeline.hasFeed(feed.key)) {
2139
- await this._pipeline.addFeed(feed);
2140
- }
2141
- } catch (err) {
2142
- log8.catch(err, void 0, {
2143
- F: __dxlog_file10,
2144
- L: 93,
2145
- S: this,
2146
- C: (f, a) => f(...a)
2147
- });
2157
+ const reader = connection.readable.getReader();
2158
+ const writer = connection.writable.getWriter();
2159
+ const connectionEntry = {
2160
+ connection,
2161
+ reader,
2162
+ writer,
2163
+ isOpen: true
2164
+ };
2165
+ this._connections.set(connection.peerId, connectionEntry);
2166
+ queueMicrotask(async () => {
2167
+ try {
2168
+ while (true) {
2169
+ const { done, value } = await reader.read();
2170
+ if (done) {
2171
+ break;
2148
2172
  }
2149
- });
2173
+ this._onMessage(value);
2174
+ }
2175
+ } catch (err) {
2176
+ if (connectionEntry.isOpen) {
2177
+ log9.catch(err, void 0, {
2178
+ F: __dxlog_file11,
2179
+ L: 224,
2180
+ S: this,
2181
+ C: (f, a) => f(...a)
2182
+ });
2183
+ }
2150
2184
  }
2151
- await this.onFeedAdmitted.callIfSet(info);
2152
2185
  });
2153
- this.onMemberRoleChanged = this._spaceStateMachine.onMemberRoleChanged;
2154
- this.onCredentialProcessed = this._spaceStateMachine.onCredentialProcessed;
2155
- this.onDelegatedInvitation = this._spaceStateMachine.onDelegatedInvitation;
2156
- this.onDelegatedInvitationRemoved = this._spaceStateMachine.onDelegatedInvitationRemoved;
2157
- }
2158
- get spaceState() {
2159
- return this._spaceStateMachine;
2186
+ log9("emit peer-candidate", {
2187
+ peerId: connection.peerId
2188
+ }, {
2189
+ F: __dxlog_file11,
2190
+ L: 229,
2191
+ S: this,
2192
+ C: (f, a) => f(...a)
2193
+ });
2194
+ this._emitPeerCandidate(connection);
2195
+ this._params.monitor?.recordPeerConnected(connection.peerId);
2160
2196
  }
2161
- get pipeline() {
2162
- return this._pipeline;
2197
+ _onMessage(message) {
2198
+ if (isCollectionQueryMessage(message)) {
2199
+ this._params.onCollectionStateQueried(message.collectionId, message.senderId);
2200
+ } else if (isCollectionStateMessage(message)) {
2201
+ this._params.onCollectionStateReceived(message.collectionId, message.senderId, message.state);
2202
+ } else {
2203
+ this.emit("message", message);
2204
+ }
2205
+ this._params.monitor?.recordMessageReceived(message);
2163
2206
  }
2164
- async setWriteFeed(feed) {
2165
- await this._pipeline.addFeed(feed);
2166
- this._pipeline.setWriteFeed(feed);
2207
+ /**
2208
+ * Trigger doc-synchronizer shared documents set recalculation. Happens on peer-candidate.
2209
+ * TODO(y): replace with a proper API call when sharePolicy update becomes supported by automerge-repo
2210
+ */
2211
+ _onConnectionAuthScopeChanged(connection) {
2212
+ log9("Connection auth scope changed", {
2213
+ peerId: connection.peerId
2214
+ }, {
2215
+ F: __dxlog_file11,
2216
+ L: 250,
2217
+ S: this,
2218
+ C: (f, a) => f(...a)
2219
+ });
2220
+ const entry = this._connections.get(connection.peerId);
2221
+ invariant7(entry, void 0, {
2222
+ F: __dxlog_file11,
2223
+ L: 252,
2224
+ S: this,
2225
+ A: [
2226
+ "entry",
2227
+ ""
2228
+ ]
2229
+ });
2230
+ this.emit("peer-disconnected", {
2231
+ peerId: connection.peerId
2232
+ });
2233
+ this._emitPeerCandidate(connection);
2167
2234
  }
2168
- async start() {
2169
- const snapshot = this._metadata.getSpaceControlPipelineSnapshot(this._spaceKey);
2170
- log8("load snapshot", {
2171
- key: this._spaceKey,
2172
- present: !!snapshot,
2173
- tf: snapshot?.timeframe
2235
+ _onConnectionClosed(connection) {
2236
+ log9("Connection closed", {
2237
+ peerId: connection.peerId
2174
2238
  }, {
2175
- F: __dxlog_file10,
2176
- L: 123,
2239
+ F: __dxlog_file11,
2240
+ L: 258,
2177
2241
  S: this,
2178
2242
  C: (f, a) => f(...a)
2179
2243
  });
2180
- if (USE_SNAPSHOTS && snapshot) {
2181
- await this._processSnapshot(snapshot);
2182
- }
2183
- log8("starting...", void 0, {
2184
- F: __dxlog_file10,
2185
- L: 128,
2244
+ const entry = this._connections.get(connection.peerId);
2245
+ invariant7(entry, void 0, {
2246
+ F: __dxlog_file11,
2247
+ L: 260,
2248
+ S: this,
2249
+ A: [
2250
+ "entry",
2251
+ ""
2252
+ ]
2253
+ });
2254
+ entry.isOpen = false;
2255
+ this.emit("peer-disconnected", {
2256
+ peerId: connection.peerId
2257
+ });
2258
+ this._params.monitor?.recordPeerDisconnected(connection.peerId);
2259
+ void entry.reader.cancel().catch((err) => log9.catch(err, void 0, {
2260
+ F: __dxlog_file11,
2261
+ L: 266,
2262
+ S: this,
2263
+ C: (f, a) => f(...a)
2264
+ }));
2265
+ void entry.writer.abort().catch((err) => log9.catch(err, void 0, {
2266
+ F: __dxlog_file11,
2267
+ L: 267,
2186
2268
  S: this,
2187
2269
  C: (f, a) => f(...a)
2270
+ }));
2271
+ this._connections.delete(connection.peerId);
2272
+ }
2273
+ _emitPeerCandidate(connection) {
2274
+ this.emit("peer-candidate", {
2275
+ peerId: connection.peerId,
2276
+ peerMetadata: createEchoPeerMetadata()
2188
2277
  });
2189
- setTimeout(async () => {
2190
- void this._consumePipeline(new Context4(void 0, {
2191
- F: __dxlog_file10,
2192
- L: 130
2193
- }));
2278
+ }
2279
+ };
2280
+ _ts_decorate6([
2281
+ synchronized3
2282
+ ], EchoNetworkAdapter.prototype, "open", null);
2283
+ _ts_decorate6([
2284
+ synchronized3
2285
+ ], EchoNetworkAdapter.prototype, "close", null);
2286
+ _ts_decorate6([
2287
+ synchronized3
2288
+ ], EchoNetworkAdapter.prototype, "addReplicator", null);
2289
+ _ts_decorate6([
2290
+ synchronized3
2291
+ ], EchoNetworkAdapter.prototype, "removeReplicator", null);
2292
+ var createEchoPeerMetadata = () => ({
2293
+ // TODO(dmaretskyi): Refactor this.
2294
+ dxos_peerSource: "EchoNetworkAdapter"
2295
+ });
2296
+ var isEchoPeerMetadata = (metadata) => metadata?.dxos_peerSource === "EchoNetworkAdapter";
2297
+
2298
+ // packages/core/echo/echo-pipeline/src/automerge/heads-store.ts
2299
+ import { headsEncoding } from "@dxos/indexing";
2300
+ var HeadsStore = class {
2301
+ constructor({ db }) {
2302
+ this._db = db;
2303
+ }
2304
+ setHeads(documentId, heads, batch) {
2305
+ batch.put(documentId, heads, {
2306
+ sublevel: this._db,
2307
+ keyEncoding: "utf8",
2308
+ valueEncoding: headsEncoding
2194
2309
  });
2195
- await this._pipeline.start();
2196
- log8("started", void 0, {
2197
- F: __dxlog_file10,
2198
- L: 134,
2199
- S: this,
2200
- C: (f, a) => f(...a)
2310
+ }
2311
+ // TODO(dmaretskyi): Make batched.
2312
+ async getHeads(documentIds) {
2313
+ return this._db.getMany(documentIds, {
2314
+ keyEncoding: "utf8",
2315
+ valueEncoding: headsEncoding
2201
2316
  });
2202
2317
  }
2203
- async _processSnapshot(snapshot) {
2204
- await this._pipeline.setCursor(snapshot.timeframe);
2205
- for (const message of snapshot.messages ?? []) {
2206
- const result = await this._spaceStateMachine.process(message.credential, {
2207
- sourceFeed: message.feedKey,
2208
- skipVerification: true
2318
+ };
2319
+
2320
+ // packages/core/echo/echo-pipeline/src/automerge/leveldb-storage-adapter.ts
2321
+ import { LifecycleState as LifecycleState3, Resource as Resource4 } from "@dxos/context";
2322
+ var LevelDBStorageAdapter = class extends Resource4 {
2323
+ constructor(_params) {
2324
+ super();
2325
+ this._params = _params;
2326
+ }
2327
+ async load(keyArray) {
2328
+ try {
2329
+ if (this._lifecycleState !== LifecycleState3.OPEN) {
2330
+ return void 0;
2331
+ }
2332
+ const startMs = Date.now();
2333
+ const chunk = await this._params.db.get(keyArray, {
2334
+ ...encodingOptions
2209
2335
  });
2210
- if (!result) {
2211
- log8.warn("credential processing failed from snapshot", {
2212
- message
2213
- }, {
2214
- F: __dxlog_file10,
2215
- L: 147,
2216
- S: this,
2217
- C: (f, a) => f(...a)
2218
- });
2336
+ this._params.monitor?.recordBytesLoaded(chunk.byteLength);
2337
+ this._params.monitor?.recordLoadDuration(Date.now() - startMs);
2338
+ return chunk;
2339
+ } catch (err) {
2340
+ if (isLevelDbNotFoundError(err)) {
2341
+ return void 0;
2219
2342
  }
2343
+ throw err;
2220
2344
  }
2221
2345
  }
2222
- async _saveSnapshot() {
2223
- await this._pipeline.pause();
2224
- const snapshot = {
2225
- timeframe: this._pipeline.state.timeframe,
2226
- messages: this._spaceStateMachine.credentialEntries.map((entry) => ({
2227
- feedKey: entry.sourceFeed,
2228
- credential: entry.credential
2229
- }))
2230
- };
2231
- await this._pipeline.unpause();
2232
- log8("save snapshot", {
2233
- key: this._spaceKey,
2234
- snapshot
2235
- }, {
2236
- F: __dxlog_file10,
2237
- L: 163,
2238
- S: this,
2239
- C: (f, a) => f(...a)
2346
+ async save(keyArray, binary) {
2347
+ if (this._lifecycleState !== LifecycleState3.OPEN) {
2348
+ return void 0;
2349
+ }
2350
+ const startMs = Date.now();
2351
+ const batch = this._params.db.batch();
2352
+ await this._params.callbacks?.beforeSave?.({
2353
+ path: keyArray,
2354
+ batch
2240
2355
  });
2241
- await this._metadata.setSpaceControlPipelineSnapshot(this._spaceKey, snapshot);
2356
+ batch.put(keyArray, Buffer.from(binary), {
2357
+ ...encodingOptions
2358
+ });
2359
+ await batch.write();
2360
+ this._params.monitor?.recordBytesStored(binary.byteLength);
2361
+ await this._params.callbacks?.afterSave?.(keyArray);
2362
+ this._params.monitor?.recordStoreDuration(Date.now() - startMs);
2242
2363
  }
2243
- async _consumePipeline(ctx) {
2244
- for await (const msg of this._pipeline.consume()) {
2245
- const span = this._usage.beginRecording();
2246
- this._mutations.inc();
2247
- try {
2248
- await this._processMessage(ctx, msg);
2249
- } catch (err) {
2250
- log8.catch(err, void 0, {
2251
- F: __dxlog_file10,
2252
- L: 176,
2253
- S: this,
2254
- C: (f, a) => f(...a)
2255
- });
2256
- }
2257
- span.end();
2364
+ async remove(keyArray) {
2365
+ if (this._lifecycleState !== LifecycleState3.OPEN) {
2366
+ return void 0;
2258
2367
  }
2259
- }
2260
- async _processMessage(ctx, msg) {
2261
- log8("processing", {
2262
- key: msg.feedKey,
2263
- seq: msg.seq
2264
- }, {
2265
- F: __dxlog_file10,
2266
- L: 186,
2267
- S: this,
2268
- C: (f, a) => f(...a)
2368
+ await this._params.db.del(keyArray, {
2369
+ ...encodingOptions
2269
2370
  });
2270
- if (msg.data.payload.credential) {
2271
- const timer = tracer.mark("dxos.echo.pipeline.control");
2272
- const result = await this._spaceStateMachine.process(msg.data.payload.credential.credential, {
2273
- sourceFeed: PublicKey4.from(msg.feedKey)
2371
+ }
2372
+ async loadRange(keyPrefix) {
2373
+ if (this._lifecycleState !== LifecycleState3.OPEN) {
2374
+ return [];
2375
+ }
2376
+ const startMs = Date.now();
2377
+ const result = [];
2378
+ for await (const [key, value] of this._params.db.iterator({
2379
+ gte: keyPrefix,
2380
+ lte: [
2381
+ ...keyPrefix,
2382
+ "\uFFFF"
2383
+ ],
2384
+ ...encodingOptions
2385
+ })) {
2386
+ result.push({
2387
+ key,
2388
+ data: value
2274
2389
  });
2275
- timer.end();
2276
- if (!result) {
2277
- log8.warn("processing failed", {
2278
- msg
2279
- }, {
2280
- F: __dxlog_file10,
2281
- L: 195,
2282
- S: this,
2283
- C: (f, a) => f(...a)
2284
- });
2285
- } else {
2286
- await this._noteTargetStateIfNeeded(this._pipeline.state.pendingTimeframe);
2287
- }
2288
- this._snapshotTask.schedule();
2390
+ this._params.monitor?.recordBytesLoaded(value.byteLength);
2289
2391
  }
2392
+ this._params.monitor?.recordLoadDuration(Date.now() - startMs);
2393
+ return result;
2290
2394
  }
2291
- async _noteTargetStateIfNeeded(timeframe) {
2292
- if (Date.now() - this._lastTimeframeSaveTime > TIMEFRAME_SAVE_DEBOUNCE_INTERVAL) {
2293
- this._lastTimeframeSaveTime = Date.now();
2294
- await this._saveTargetTimeframe(timeframe);
2395
+ async removeRange(keyPrefix) {
2396
+ if (this._lifecycleState !== LifecycleState3.OPEN) {
2397
+ return void 0;
2295
2398
  }
2296
- }
2297
- async stop() {
2298
- log8("stopping...", void 0, {
2299
- F: __dxlog_file10,
2300
- L: 215,
2301
- S: this,
2302
- C: (f, a) => f(...a)
2303
- });
2304
- await this._ctx.dispose();
2305
- await this._pipeline.stop();
2306
- await this._saveTargetTimeframe(this._pipeline.state.timeframe);
2307
- log8("stopped", void 0, {
2308
- F: __dxlog_file10,
2309
- L: 219,
2310
- S: this,
2311
- C: (f, a) => f(...a)
2312
- });
2313
- }
2314
- async _saveTargetTimeframe(timeframe) {
2315
- try {
2316
- const newTimeframe = Timeframe3.merge(this._targetTimeframe ?? new Timeframe3(), timeframe);
2317
- await this._metadata.setSpaceControlLatestTimeframe(this._spaceKey, newTimeframe);
2318
- this._targetTimeframe = newTimeframe;
2319
- } catch (err) {
2320
- log8(err, void 0, {
2321
- F: __dxlog_file10,
2322
- L: 228,
2323
- S: this,
2324
- C: (f, a) => f(...a)
2399
+ const batch = this._params.db.batch();
2400
+ for await (const [key] of this._params.db.iterator({
2401
+ gte: keyPrefix,
2402
+ lte: [
2403
+ ...keyPrefix,
2404
+ "\uFFFF"
2405
+ ],
2406
+ ...encodingOptions
2407
+ })) {
2408
+ batch.del(key, {
2409
+ ...encodingOptions
2325
2410
  });
2326
2411
  }
2412
+ await batch.write();
2327
2413
  }
2328
2414
  };
2329
- _ts_decorate5([
2330
- trace2.metricsCounter()
2331
- ], ControlPipeline.prototype, "_usage", void 0);
2332
- _ts_decorate5([
2333
- trace2.metricsCounter()
2334
- ], ControlPipeline.prototype, "_mutations", void 0);
2335
- _ts_decorate5([
2336
- trace2.span({
2337
- showInBrowserTimeline: true
2338
- })
2339
- ], ControlPipeline.prototype, "start", null);
2340
- _ts_decorate5([
2341
- trace2.span()
2342
- ], ControlPipeline.prototype, "_consumePipeline", null);
2343
- _ts_decorate5([
2344
- trace2.span()
2345
- ], ControlPipeline.prototype, "_processMessage", null);
2346
- ControlPipeline = _ts_decorate5([
2347
- trace2.resource(),
2348
- trackLeaks("start", "stop")
2349
- ], ControlPipeline);
2415
+ var keyEncoder = {
2416
+ encode: (key) => Buffer.from(key.map((k) => k.replaceAll("%", "%25").replaceAll("-", "%2D")).join("-")),
2417
+ decode: (key) => Buffer.from(key).toString().split("-").map((k) => k.replaceAll("%2D", "-").replaceAll("%25", "%")),
2418
+ format: "buffer"
2419
+ };
2420
+ var encodingOptions = {
2421
+ keyEncoding: keyEncoder,
2422
+ valueEncoding: "buffer"
2423
+ };
2424
+ var isLevelDbNotFoundError = (err) => err.code === "LEVEL_NOT_FOUND";
2350
2425
 
2351
- // packages/core/echo/echo-pipeline/src/space/space.ts
2352
- function _ts_decorate6(decorators, target, key, desc) {
2426
+ // packages/core/echo/echo-pipeline/src/automerge/automerge-host.ts
2427
+ function _ts_decorate7(decorators, target, key, desc) {
2353
2428
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
2354
2429
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function")
2355
2430
  r = Reflect.decorate(decorators, target, key, desc);
@@ -2359,527 +2434,443 @@ function _ts_decorate6(decorators, target, key, desc) {
2359
2434
  r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
2360
2435
  return c > 3 && r && Object.defineProperty(target, key, r), r;
2361
2436
  }
2362
- var __dxlog_file11 = "/home/runner/work/dxos/dxos/packages/core/echo/echo-pipeline/src/space/space.ts";
2363
- var Space = class extends Resource5 {
2364
- constructor(params) {
2365
- super();
2366
- this.onCredentialProcessed = new Callback2();
2367
- this.stateUpdate = new Event5();
2368
- invariant9(params.spaceKey && params.feedProvider, void 0, {
2369
- F: __dxlog_file11,
2370
- L: 76,
2371
- S: this,
2372
- A: [
2373
- "params.spaceKey && params.feedProvider",
2374
- ""
2375
- ]
2376
- });
2377
- this._id = params.id;
2378
- this._key = params.spaceKey;
2379
- this._genesisFeedKey = params.genesisFeed.key;
2380
- this._feedProvider = params.feedProvider;
2381
- this._snapshotManager = params.snapshotManager;
2382
- this._controlPipeline = new ControlPipeline({
2383
- spaceKey: params.spaceKey,
2384
- genesisFeed: params.genesisFeed,
2385
- feedProvider: params.feedProvider,
2386
- metadataStore: params.metadataStore
2437
+ var __dxlog_file12 = "/home/runner/work/dxos/dxos/packages/core/echo/echo-pipeline/src/automerge/automerge-host.ts";
2438
+ var AutomergeHost = class extends Resource5 {
2439
+ constructor({ db, indexMetadataStore, dataMonitor }) {
2440
+ super();
2441
+ this._collectionSynchronizer = new CollectionSynchronizer({
2442
+ queryCollectionState: this._queryCollectionState.bind(this),
2443
+ sendCollectionState: this._sendCollectionState.bind(this),
2444
+ shouldSyncCollection: this._shouldSyncCollection.bind(this)
2387
2445
  });
2388
- this._controlPipeline.onFeedAdmitted.set(async (info) => {
2389
- const sparse = info.assertion.designation === AdmittedFeed2.Designation.DATA;
2390
- if (!info.key.equals(params.genesisFeed.key)) {
2391
- queueMicrotask(async () => {
2392
- this.protocol.addFeed(await params.feedProvider(info.key, {
2393
- sparse
2394
- }));
2395
- });
2396
- }
2446
+ this._db = db;
2447
+ this._storage = new LevelDBStorageAdapter({
2448
+ db: db.sublevel("automerge"),
2449
+ callbacks: {
2450
+ beforeSave: async (params) => this._beforeSave(params),
2451
+ afterSave: async (key) => this._afterSave(key)
2452
+ },
2453
+ monitor: dataMonitor
2397
2454
  });
2398
- this._controlPipeline.onCredentialProcessed.set(async (credential) => {
2399
- await this.onCredentialProcessed.callIfSet(credential);
2400
- log9("onCredentialProcessed", {
2401
- credential
2402
- }, {
2403
- F: __dxlog_file11,
2404
- L: 104,
2405
- S: this,
2406
- C: (f, a) => f(...a)
2407
- });
2408
- this.stateUpdate.emit();
2455
+ this._echoNetworkAdapter = new EchoNetworkAdapter({
2456
+ getContainingSpaceForDocument: this._getContainingSpaceForDocument.bind(this),
2457
+ onCollectionStateQueried: this._onCollectionStateQueried.bind(this),
2458
+ onCollectionStateReceived: this._onCollectionStateReceived.bind(this),
2459
+ monitor: dataMonitor
2409
2460
  });
2410
- this._controlPipeline.onDelegatedInvitation.set(async (invitation) => {
2411
- log9("onDelegatedInvitation", {
2412
- invitation
2413
- }, {
2414
- F: __dxlog_file11,
2415
- L: 108,
2416
- S: this,
2417
- C: (f, a) => f(...a)
2418
- });
2419
- await params.onDelegatedInvitationStatusChange(invitation, true);
2461
+ this._headsStore = new HeadsStore({
2462
+ db: db.sublevel("heads")
2420
2463
  });
2421
- this._controlPipeline.onDelegatedInvitationRemoved.set(async (invitation) => {
2422
- log9("onDelegatedInvitationRemoved", {
2423
- invitation
2424
- }, {
2425
- F: __dxlog_file11,
2426
- L: 112,
2427
- S: this,
2428
- C: (f, a) => f(...a)
2429
- });
2430
- await params.onDelegatedInvitationStatusChange(invitation, false);
2464
+ this._indexMetadataStore = indexMetadataStore;
2465
+ }
2466
+ async _open() {
2467
+ this._peerId = `host-${PublicKey6.random().toHex()}`;
2468
+ await this._storage.open?.();
2469
+ this._repo = new Repo({
2470
+ peerId: this._peerId,
2471
+ sharePolicy: this._sharePolicy.bind(this),
2472
+ storage: this._storage,
2473
+ network: [
2474
+ // Upstream swarm.
2475
+ this._echoNetworkAdapter
2476
+ ]
2431
2477
  });
2432
- this._controlPipeline.onMemberRoleChanged.set(async (changedMembers) => {
2433
- log9("onMemberRoleChanged", () => ({
2434
- changedMembers: changedMembers.map((m) => [
2435
- m.key,
2436
- m.role
2437
- ])
2438
- }), {
2439
- F: __dxlog_file11,
2440
- L: 116,
2441
- S: this,
2442
- C: (f, a) => f(...a)
2443
- });
2444
- await params.onMemberRolesChanged(changedMembers);
2478
+ Event5.wrap(this._echoNetworkAdapter, "peer-candidate").on(this._ctx, (e) => this._onPeerConnected(e.peerId));
2479
+ Event5.wrap(this._echoNetworkAdapter, "peer-disconnected").on(this._ctx, (e) => this._onPeerDisconnected(e.peerId));
2480
+ this._collectionSynchronizer.remoteStateUpdated.on(this._ctx, ({ collectionId, peerId }) => {
2481
+ this._onRemoteCollectionStateUpdated(collectionId, peerId);
2445
2482
  });
2446
- this.protocol = params.protocol;
2447
- this.protocol.addFeed(params.genesisFeed);
2483
+ await this._echoNetworkAdapter.open();
2484
+ await this._collectionSynchronizer.open();
2485
+ await this._echoNetworkAdapter.open();
2486
+ await this._echoNetworkAdapter.whenConnected();
2448
2487
  }
2449
- get id() {
2450
- return this._id;
2488
+ async _close() {
2489
+ await this._collectionSynchronizer.close();
2490
+ await this._storage.close?.();
2491
+ await this._echoNetworkAdapter.close();
2492
+ await this._ctx.dispose();
2451
2493
  }
2452
- get key() {
2453
- return this._key;
2494
+ /**
2495
+ * @deprecated To be abstracted away.
2496
+ */
2497
+ get repo() {
2498
+ return this._repo;
2454
2499
  }
2455
- get isOpen() {
2456
- return this._lifecycleState === LifecycleState3.OPEN;
2500
+ get peerId() {
2501
+ return this._peerId;
2457
2502
  }
2458
- get genesisFeedKey() {
2459
- return this._genesisFeedKey;
2503
+ get loadedDocsCount() {
2504
+ return Object.keys(this._repo.handles).length;
2460
2505
  }
2461
- get controlFeedKey() {
2462
- return this._controlFeed?.key;
2506
+ async addReplicator(replicator) {
2507
+ await this._echoNetworkAdapter.addReplicator(replicator);
2463
2508
  }
2464
- get dataFeedKey() {
2465
- return this._dataFeed?.key;
2509
+ async removeReplicator(replicator) {
2510
+ await this._echoNetworkAdapter.removeReplicator(replicator);
2466
2511
  }
2467
- get spaceState() {
2468
- return this._controlPipeline.spaceState;
2512
+ /**
2513
+ * Loads the document handle from the repo and waits for it to be ready.
2514
+ */
2515
+ async loadDoc(ctx, documentId, opts) {
2516
+ let handle;
2517
+ if (typeof documentId === "string") {
2518
+ handle = this._repo.handles[documentId];
2519
+ }
2520
+ if (!handle) {
2521
+ handle = this._repo.find(documentId);
2522
+ }
2523
+ if (!handle.isReady()) {
2524
+ if (!opts?.timeout) {
2525
+ await cancelWithContext2(ctx, handle.whenReady());
2526
+ } else {
2527
+ await cancelWithContext2(ctx, asyncTimeout(handle.whenReady(), opts.timeout));
2528
+ }
2529
+ }
2530
+ return handle;
2469
2531
  }
2470
2532
  /**
2471
- * @test-only
2533
+ * Create new persisted document.
2472
2534
  */
2473
- get controlPipeline() {
2474
- return this._controlPipeline.pipeline;
2535
+ createDoc(initialValue, opts) {
2536
+ if (opts?.preserveHistory) {
2537
+ if (!isAutomerge(initialValue)) {
2538
+ throw new TypeError("Initial value must be an Automerge document");
2539
+ }
2540
+ return this._repo.import(save(initialValue));
2541
+ } else {
2542
+ return this._repo.create(initialValue);
2543
+ }
2475
2544
  }
2476
- get snapshotManager() {
2477
- return this._snapshotManager;
2545
+ async waitUntilHeadsReplicated(heads) {
2546
+ const entries = heads.entries;
2547
+ if (!entries?.length) {
2548
+ return;
2549
+ }
2550
+ const documentIds = entries.map((entry) => entry.documentId);
2551
+ const documentHeads = await this.getHeads(documentIds);
2552
+ const headsToWait = entries.filter((entry, index) => {
2553
+ const targetHeads = entry.heads;
2554
+ if (!targetHeads || targetHeads.length === 0) {
2555
+ return false;
2556
+ }
2557
+ const currentHeads = documentHeads[index];
2558
+ return !(currentHeads !== null && headsEquals(currentHeads, targetHeads));
2559
+ });
2560
+ if (headsToWait.length > 0) {
2561
+ await Promise.all(headsToWait.map(async (entry, index) => {
2562
+ const handle = await this.loadDoc(Context5.default(void 0, {
2563
+ F: __dxlog_file12,
2564
+ L: 226
2565
+ }), entry.documentId);
2566
+ await waitForHeads(handle, entry.heads);
2567
+ }));
2568
+ }
2569
+ await this._repo.flush(documentIds.filter((documentId) => !!this._repo.handles[documentId]));
2478
2570
  }
2479
- async setControlFeed(feed) {
2480
- invariant9(!this._controlFeed, "Control feed already set.", {
2481
- F: __dxlog_file11,
2482
- L: 169,
2571
+ async reIndexHeads(documentIds) {
2572
+ for (const documentId of documentIds) {
2573
+ log10.info("re-indexing heads for document", {
2574
+ documentId
2575
+ }, {
2576
+ F: __dxlog_file12,
2577
+ L: 238,
2578
+ S: this,
2579
+ C: (f, a) => f(...a)
2580
+ });
2581
+ const handle = this._repo.find(documentId);
2582
+ await handle.whenReady([
2583
+ "ready",
2584
+ "requesting"
2585
+ ]);
2586
+ if (handle.inState([
2587
+ "requesting"
2588
+ ])) {
2589
+ log10.warn("document is not available locally, skipping", {
2590
+ documentId
2591
+ }, {
2592
+ F: __dxlog_file12,
2593
+ L: 242,
2594
+ S: this,
2595
+ C: (f, a) => f(...a)
2596
+ });
2597
+ continue;
2598
+ }
2599
+ const doc = handle.docSync();
2600
+ invariant8(doc, void 0, {
2601
+ F: __dxlog_file12,
2602
+ L: 247,
2603
+ S: this,
2604
+ A: [
2605
+ "doc",
2606
+ ""
2607
+ ]
2608
+ });
2609
+ const heads = getHeads(doc);
2610
+ const batch = this._db.batch();
2611
+ this._headsStore.setHeads(documentId, heads, batch);
2612
+ await batch.write();
2613
+ }
2614
+ log10.info("done re-indexing heads", void 0, {
2615
+ F: __dxlog_file12,
2616
+ L: 254,
2483
2617
  S: this,
2484
- A: [
2485
- "!this._controlFeed",
2486
- "'Control feed already set.'"
2487
- ]
2618
+ C: (f, a) => f(...a)
2488
2619
  });
2489
- this._controlFeed = feed;
2490
- await this._controlPipeline.setWriteFeed(feed);
2491
- return this;
2492
2620
  }
2493
- async setDataFeed(feed) {
2494
- invariant9(!this._dataFeed, "Data feed already set.", {
2495
- F: __dxlog_file11,
2496
- L: 176,
2497
- S: this,
2498
- A: [
2499
- "!this._dataFeed",
2500
- "'Data feed already set.'"
2501
- ]
2502
- });
2503
- this._dataFeed = feed;
2504
- return this;
2621
+ // TODO(dmaretskyi): Share based on HALO permissions and space affinity.
2622
+ // Hosts, running in the worker, don't share documents unless requested by other peers.
2623
+ // NOTE: If both peers return sharePolicy=false the replication will not happen
2624
+ // https://github.com/automerge/automerge-repo/pull/292
2625
+ async _sharePolicy(peerId, documentId) {
2626
+ if (peerId.startsWith("client-")) {
2627
+ return false;
2628
+ }
2629
+ if (!documentId) {
2630
+ return false;
2631
+ }
2632
+ const peerMetadata = this.repo.peerMetadataByPeerId[peerId];
2633
+ if (isEchoPeerMetadata(peerMetadata)) {
2634
+ return this._echoNetworkAdapter.shouldAdvertise(peerId, {
2635
+ documentId
2636
+ });
2637
+ }
2638
+ return false;
2639
+ }
2640
+ async _beforeSave({ path, batch }) {
2641
+ const handle = this._repo.handles[path[0]];
2642
+ if (!handle) {
2643
+ return;
2644
+ }
2645
+ const doc = handle.docSync();
2646
+ if (!doc) {
2647
+ return;
2648
+ }
2649
+ const heads = getHeads(doc);
2650
+ this._headsStore.setHeads(handle.documentId, heads, batch);
2651
+ const spaceKey = getSpaceKeyFromDoc(doc) ?? void 0;
2652
+ const objectIds = Object.keys(doc.objects ?? {});
2653
+ const encodedIds = objectIds.map((objectId) => objectPointerCodec.encode({
2654
+ documentId: handle.documentId,
2655
+ objectId,
2656
+ spaceKey
2657
+ }));
2658
+ const idToLastHash = new Map(encodedIds.map((id) => [
2659
+ id,
2660
+ heads
2661
+ ]));
2662
+ this._indexMetadataStore.markDirty(idToLastHash, batch);
2663
+ }
2664
+ _shouldSyncCollection(collectionId, peerId) {
2665
+ const peerMetadata = this._repo.peerMetadataByPeerId[peerId];
2666
+ if (isEchoPeerMetadata(peerMetadata)) {
2667
+ return this._echoNetworkAdapter.shouldSyncCollection(peerId, {
2668
+ collectionId
2669
+ });
2670
+ }
2671
+ return false;
2505
2672
  }
2506
2673
  /**
2507
- * Use for diagnostics.
2674
+ * Called by AutomergeStorageAdapter after levelDB batch commit.
2508
2675
  */
2509
- getControlFeeds() {
2510
- return Array.from(this._controlPipeline.spaceState.feeds.values());
2676
+ async _afterSave(path) {
2677
+ this._indexMetadataStore.notifyMarkedDirty();
2678
+ const documentId = path[0];
2679
+ const document = this._repo.handles[documentId]?.docSync();
2680
+ if (document) {
2681
+ const heads = getHeads(document);
2682
+ this._onHeadsChanged(documentId, heads);
2683
+ }
2511
2684
  }
2512
- async _open(ctx) {
2513
- log9("opening...", void 0, {
2514
- F: __dxlog_file11,
2515
- L: 190,
2516
- S: this,
2517
- C: (f, a) => f(...a)
2518
- });
2519
- await this._controlPipeline.start();
2520
- await this.protocol.start();
2521
- log9("opened", void 0, {
2522
- F: __dxlog_file11,
2523
- L: 196,
2524
- S: this,
2525
- C: (f, a) => f(...a)
2526
- });
2685
+ _automergePeers() {
2686
+ return this._repo.peers;
2527
2687
  }
2528
- async _close() {
2529
- log9("closing...", {
2530
- key: this._key
2531
- }, {
2532
- F: __dxlog_file11,
2533
- L: 201,
2534
- S: this,
2535
- C: (f, a) => f(...a)
2536
- });
2537
- await this.protocol.stop();
2538
- await this._controlPipeline.stop();
2539
- log9("closed", void 0, {
2540
- F: __dxlog_file11,
2541
- L: 207,
2542
- S: this,
2543
- C: (f, a) => f(...a)
2544
- });
2688
+ async _getContainingSpaceForDocument(documentId) {
2689
+ const doc = this._repo.handles[documentId]?.docSync();
2690
+ if (!doc) {
2691
+ return null;
2692
+ }
2693
+ const spaceKeyHex = getSpaceKeyFromDoc(doc);
2694
+ if (!spaceKeyHex) {
2695
+ return null;
2696
+ }
2697
+ return PublicKey6.from(spaceKeyHex);
2545
2698
  }
2546
- };
2547
- _ts_decorate6([
2548
- trace3.info()
2549
- ], Space.prototype, "protocol", void 0);
2550
- _ts_decorate6([
2551
- trace3.info()
2552
- ], Space.prototype, "_controlPipeline", void 0);
2553
- _ts_decorate6([
2554
- logInfo,
2555
- trace3.info()
2556
- ], Space.prototype, "id", null);
2557
- _ts_decorate6([
2558
- logInfo,
2559
- trace3.info()
2560
- ], Space.prototype, "key", null);
2561
- _ts_decorate6([
2562
- trace3.span()
2563
- ], Space.prototype, "_open", null);
2564
- _ts_decorate6([
2565
- synchronized3
2566
- ], Space.prototype, "_close", null);
2567
- Space = _ts_decorate6([
2568
- trackLeaks2("open", "close"),
2569
- trace3.resource()
2570
- ], Space);
2571
- var SPACE_IDS_CACHE = new ComplexMap2(PublicKey5.hash);
2572
- var createIdFromSpaceKey = async (spaceKey) => {
2573
- const cachedValue = SPACE_IDS_CACHE.get(spaceKey);
2574
- if (cachedValue !== void 0) {
2575
- return cachedValue;
2699
+ /**
2700
+ * Flush documents to disk.
2701
+ */
2702
+ async flush({ documentIds } = {}) {
2703
+ await this._repo.flush(documentIds);
2576
2704
  }
2577
- const digest = await subtleCrypto2.digest("SHA-256", spaceKey.asUint8Array());
2578
- const bytes = new Uint8Array(digest).slice(0, SpaceId2.byteLength);
2579
- const spaceId = SpaceId2.encode(bytes);
2580
- SPACE_IDS_CACHE.set(spaceKey, spaceId);
2581
- return spaceId;
2582
- };
2583
-
2584
- // packages/core/echo/echo-pipeline/src/space/admission-discovery-extension.ts
2585
- import { scheduleTask as scheduleTask3 } from "@dxos/async";
2586
- import { Context as Context5 } from "@dxos/context";
2587
- import { ProtocolError, schema as schema5 } from "@dxos/protocols";
2588
- import { RpcExtension as RpcExtension2 } from "@dxos/teleport";
2589
- var __dxlog_file12 = "/home/runner/work/dxos/dxos/packages/core/echo/echo-pipeline/src/space/admission-discovery-extension.ts";
2590
- var CredentialRetrieverExtension = class extends RpcExtension2 {
2591
- constructor(_request, _onResult) {
2592
- super({
2593
- requested: {
2594
- AdmissionDiscoveryService: schema5.getService("dxos.mesh.teleport.AdmissionDiscoveryService")
2705
+ async getHeads(documentIds) {
2706
+ const result = [];
2707
+ const storeRequestIds = [];
2708
+ const storeResultIndices = [];
2709
+ for (const documentId of documentIds) {
2710
+ const doc = this._repo.handles[documentId]?.docSync();
2711
+ if (doc) {
2712
+ result.push(getHeads(doc));
2713
+ } else {
2714
+ storeRequestIds.push(documentId);
2715
+ storeResultIndices.push(result.length);
2716
+ result.push(void 0);
2595
2717
  }
2596
- });
2597
- this._request = _request;
2598
- this._onResult = _onResult;
2599
- this._ctx = new Context5(void 0, {
2600
- F: __dxlog_file12,
2601
- L: 25
2602
- });
2603
- }
2604
- async getHandlers() {
2605
- return {};
2606
- }
2607
- async onOpen(context) {
2608
- await super.onOpen(context);
2609
- scheduleTask3(this._ctx, async () => {
2610
- try {
2611
- const result = await this.rpc.AdmissionDiscoveryService.getAdmissionCredential(this._request);
2612
- this._onResult.wake(result.admissionCredential);
2613
- } catch (err) {
2614
- context.close(err);
2718
+ }
2719
+ if (storeRequestIds.length > 0) {
2720
+ const storedHeads = await this._headsStore.getHeads(storeRequestIds);
2721
+ for (let i = 0; i < storedHeads.length; i++) {
2722
+ result[storeResultIndices[i]] = storedHeads[i];
2615
2723
  }
2616
- });
2724
+ }
2725
+ return result;
2617
2726
  }
2618
- async onClose() {
2619
- await this._ctx.dispose();
2727
+ //
2728
+ // Collection sync.
2729
+ //
2730
+ getLocalCollectionState(collectionId) {
2731
+ return this._collectionSynchronizer.getLocalCollectionState(collectionId);
2620
2732
  }
2621
- async onAbort() {
2622
- await this._ctx.dispose();
2733
+ getRemoteCollectionStates(collectionId) {
2734
+ return this._collectionSynchronizer.getRemoteCollectionStates(collectionId);
2623
2735
  }
2624
- };
2625
- var CredentialServerExtension = class extends RpcExtension2 {
2626
- constructor(_space) {
2627
- super({
2628
- exposed: {
2629
- AdmissionDiscoveryService: schema5.getService("dxos.mesh.teleport.AdmissionDiscoveryService")
2630
- }
2631
- });
2632
- this._space = _space;
2736
+ refreshCollection(collectionId) {
2737
+ this._collectionSynchronizer.refreshCollection(collectionId);
2633
2738
  }
2634
- async getHandlers() {
2635
- return {
2636
- AdmissionDiscoveryService: {
2637
- getAdmissionCredential: async (request) => {
2638
- const memberInfo = this._space.spaceState.members.get(request.memberKey);
2639
- if (!memberInfo?.credential) {
2640
- throw new ProtocolError("Space member not found.", request);
2641
- }
2642
- return {
2643
- admissionCredential: memberInfo.credential
2644
- };
2645
- }
2646
- }
2739
+ async getCollectionSyncState(collectionId) {
2740
+ const result = {
2741
+ peers: []
2647
2742
  };
2743
+ const localState = this.getLocalCollectionState(collectionId);
2744
+ const remoteState = this.getRemoteCollectionStates(collectionId);
2745
+ if (!localState) {
2746
+ return result;
2747
+ }
2748
+ for (const [peerId, state] of remoteState) {
2749
+ const diff = diffCollectionState(localState, state);
2750
+ result.peers.push({
2751
+ peerId,
2752
+ differentDocuments: diff.different.length
2753
+ });
2754
+ }
2755
+ return result;
2648
2756
  }
2649
- };
2650
-
2651
- // packages/core/echo/echo-pipeline/src/space/space-protocol.ts
2652
- import { discoveryKey, subtleCrypto as subtleCrypto3 } from "@dxos/crypto";
2653
- import { PublicKey as PublicKey6 } from "@dxos/keys";
2654
- import { log as log10, logInfo as logInfo2 } from "@dxos/log";
2655
- import { MMSTTopology } from "@dxos/network-manager";
2656
- import { Teleport } from "@dxos/teleport";
2657
- import { BlobSync } from "@dxos/teleport-extension-object-sync";
2658
- import { ReplicatorExtension } from "@dxos/teleport-extension-replicator";
2659
- import { trace as trace4 } from "@dxos/tracing";
2660
- import { ComplexMap as ComplexMap3 } from "@dxos/util";
2661
- function _ts_decorate7(decorators, target, key, desc) {
2662
- var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
2663
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function")
2664
- r = Reflect.decorate(decorators, target, key, desc);
2665
- else
2666
- for (var i = decorators.length - 1; i >= 0; i--)
2667
- if (d = decorators[i])
2668
- r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
2669
- return c > 3 && r && Object.defineProperty(target, key, r), r;
2670
- }
2671
- var __dxlog_file13 = "/home/runner/work/dxos/dxos/packages/core/echo/echo-pipeline/src/space/space-protocol.ts";
2672
- var MOCK_AUTH_PROVIDER = async (nonce) => Buffer.from("mock");
2673
- var MOCK_AUTH_VERIFIER = async (nonce, credential) => true;
2674
- var SpaceProtocol = class {
2675
- constructor({ topic, swarmIdentity, networkManager, onSessionAuth, onAuthFailure, blobStore }) {
2676
- this._feeds = /* @__PURE__ */ new Set();
2677
- this._sessions = new ComplexMap3(PublicKey6.hash);
2678
- // TODO(burdon): Move to config (with sensible defaults).
2679
- this._topology = new MMSTTopology({
2680
- originateConnections: 4,
2681
- maxPeers: 10,
2682
- sampleSize: 20
2683
- });
2684
- this._spaceKey = topic;
2685
- this._networkManager = networkManager;
2686
- this._swarmIdentity = swarmIdentity;
2687
- this._onSessionAuth = onSessionAuth;
2688
- this._onAuthFailure = onAuthFailure;
2689
- this.blobSync = new BlobSync({
2690
- blobStore
2757
+ /**
2758
+ * Update the local collection state based on the locally stored document heads.
2759
+ */
2760
+ async updateLocalCollectionState(collectionId, documentIds) {
2761
+ const heads = await this.getHeads(documentIds);
2762
+ const documents = Object.fromEntries(heads.map((heads2, index) => [
2763
+ documentIds[index],
2764
+ heads2 ?? []
2765
+ ]));
2766
+ this._collectionSynchronizer.setLocalCollectionState(collectionId, {
2767
+ documents
2691
2768
  });
2692
- this._topic = subtleCrypto3.digest("SHA-256", topic.asBuffer()).then(discoveryKey).then(PublicKey6.from);
2693
2769
  }
2694
- get sessions() {
2695
- return this._sessions;
2770
+ _onCollectionStateQueried(collectionId, peerId) {
2771
+ this._collectionSynchronizer.onCollectionStateQueried(collectionId, peerId);
2696
2772
  }
2697
- get feeds() {
2698
- return this._feeds;
2773
+ _onCollectionStateReceived(collectionId, peerId, state) {
2774
+ this._collectionSynchronizer.onRemoteStateReceived(collectionId, peerId, decodeCollectionState(state));
2699
2775
  }
2700
- get _ownPeerKey() {
2701
- return this._swarmIdentity.peerKey;
2776
+ _queryCollectionState(collectionId, peerId) {
2777
+ this._echoNetworkAdapter.queryCollectionState(collectionId, peerId);
2702
2778
  }
2703
- // TODO(burdon): Create abstraction for Space (e.g., add keys and have provider).
2704
- addFeed(feed) {
2705
- log10("addFeed", {
2706
- key: feed.key
2707
- }, {
2708
- F: __dxlog_file13,
2709
- L: 109,
2710
- S: this,
2711
- C: (f, a) => f(...a)
2712
- });
2713
- this._feeds.add(feed);
2714
- for (const session of this._sessions.values()) {
2715
- session.replicator.addFeed(feed);
2716
- }
2779
+ _sendCollectionState(collectionId, peerId, state) {
2780
+ this._echoNetworkAdapter.sendCollectionState(collectionId, peerId, encodeCollectionState(state));
2717
2781
  }
2718
- // TODO(burdon): Rename open? Common open/close interfaces for all services?
2719
- async start() {
2720
- if (this._connection) {
2782
+ _onPeerConnected(peerId) {
2783
+ this._collectionSynchronizer.onConnectionOpen(peerId);
2784
+ }
2785
+ _onPeerDisconnected(peerId) {
2786
+ this._collectionSynchronizer.onConnectionClosed(peerId);
2787
+ }
2788
+ _onRemoteCollectionStateUpdated(collectionId, peerId) {
2789
+ const localState = this._collectionSynchronizer.getLocalCollectionState(collectionId);
2790
+ const remoteState = this._collectionSynchronizer.getRemoteCollectionStates(collectionId).get(peerId);
2791
+ if (!localState || !remoteState) {
2721
2792
  return;
2722
2793
  }
2723
- const credentials = await this._swarmIdentity.credentialProvider(Buffer.from(""));
2724
- await this.blobSync.open();
2725
- log10("starting...", void 0, {
2726
- F: __dxlog_file13,
2727
- L: 128,
2728
- S: this,
2729
- C: (f, a) => f(...a)
2730
- });
2731
- const topic = await this._topic;
2732
- this._connection = await this._networkManager.joinSwarm({
2733
- protocolProvider: this._createProtocolProvider(credentials),
2734
- peerId: this._swarmIdentity.peerKey,
2735
- topic,
2736
- topology: this._topology,
2737
- label: `swarm ${topic.truncate()} for space ${this._spaceKey.truncate()}`
2738
- });
2739
- log10("started", void 0, {
2740
- F: __dxlog_file13,
2741
- L: 138,
2794
+ const { different } = diffCollectionState(localState, remoteState);
2795
+ if (different.length === 0) {
2796
+ return;
2797
+ }
2798
+ log10.info("replication documents after collection sync", {
2799
+ count: different.length
2800
+ }, {
2801
+ F: __dxlog_file12,
2802
+ L: 463,
2742
2803
  S: this,
2743
2804
  C: (f, a) => f(...a)
2744
2805
  });
2745
- }
2746
- updateTopology() {
2747
- this._topology.forceUpdate();
2748
- }
2749
- async stop() {
2750
- await this.blobSync.close();
2751
- if (this._connection) {
2752
- log10("stopping...", void 0, {
2753
- F: __dxlog_file13,
2754
- L: 149,
2755
- S: this,
2756
- C: (f, a) => f(...a)
2757
- });
2758
- await this._connection.close();
2759
- log10("stopped", void 0, {
2760
- F: __dxlog_file13,
2761
- L: 151,
2762
- S: this,
2763
- C: (f, a) => f(...a)
2764
- });
2806
+ for (const documentId of different) {
2807
+ this._repo.find(documentId);
2765
2808
  }
2766
2809
  }
2767
- _createProtocolProvider(credentials) {
2768
- return (wireParams) => {
2769
- const session = new SpaceProtocolSession({
2770
- wireParams,
2771
- swarmIdentity: this._swarmIdentity,
2772
- onSessionAuth: this._onSessionAuth,
2773
- onAuthFailure: this._onAuthFailure,
2774
- blobSync: this.blobSync
2775
- });
2776
- this._sessions.set(wireParams.remotePeerId, session);
2777
- for (const feed of this._feeds) {
2778
- session.replicator.addFeed(feed);
2810
+ _onHeadsChanged(documentId, heads) {
2811
+ for (const collectionId of this._collectionSynchronizer.getRegisteredCollectionIds()) {
2812
+ const state = this._collectionSynchronizer.getLocalCollectionState(collectionId);
2813
+ if (state?.documents[documentId]) {
2814
+ const newState = structuredClone(state);
2815
+ newState.documents[documentId] = heads;
2816
+ this._collectionSynchronizer.setLocalCollectionState(collectionId, newState);
2779
2817
  }
2780
- return session;
2781
- };
2818
+ }
2782
2819
  }
2783
2820
  };
2784
2821
  _ts_decorate7([
2785
- logInfo2,
2786
2822
  trace4.info()
2787
- ], SpaceProtocol.prototype, "_topic", void 0);
2823
+ ], AutomergeHost.prototype, "_peerId", void 0);
2788
2824
  _ts_decorate7([
2789
- trace4.info()
2790
- ], SpaceProtocol.prototype, "_spaceKey", void 0);
2825
+ trace4.info({
2826
+ depth: null
2827
+ })
2828
+ ], AutomergeHost.prototype, "_automergePeers", null);
2791
2829
  _ts_decorate7([
2792
- logInfo2
2793
- ], SpaceProtocol.prototype, "_ownPeerKey", null);
2794
- SpaceProtocol = _ts_decorate7([
2830
+ trace4.span({
2831
+ showInBrowserTimeline: true
2832
+ })
2833
+ ], AutomergeHost.prototype, "flush", null);
2834
+ AutomergeHost = _ts_decorate7([
2795
2835
  trace4.resource()
2796
- ], SpaceProtocol);
2797
- var AuthStatus;
2798
- (function(AuthStatus2) {
2799
- AuthStatus2["INITIAL"] = "INITIAL";
2800
- AuthStatus2["SUCCESS"] = "SUCCESS";
2801
- AuthStatus2["FAILURE"] = "FAILURE";
2802
- })(AuthStatus || (AuthStatus = {}));
2803
- var SpaceProtocolSession = class {
2804
- // TODO(dmaretskyi): Allow to pass in extra extensions.
2805
- constructor({ wireParams, swarmIdentity, onSessionAuth, onAuthFailure, blobSync }) {
2806
- // TODO(dmaretskyi): Start with upload=false when switching it on the fly works.
2807
- this.replicator = new ReplicatorExtension().setOptions({
2808
- upload: true
2809
- });
2810
- this._authStatus = "INITIAL";
2811
- this._wireParams = wireParams;
2812
- this._swarmIdentity = swarmIdentity;
2813
- this._onSessionAuth = onSessionAuth;
2814
- this._onAuthFailure = onAuthFailure;
2815
- this._blobSync = blobSync;
2816
- this._teleport = new Teleport(wireParams);
2817
- }
2818
- get authStatus() {
2819
- return this._authStatus;
2820
- }
2821
- get stats() {
2822
- return this._teleport.stats;
2823
- }
2824
- get stream() {
2825
- return this._teleport.stream;
2836
+ ], AutomergeHost);
2837
+ var getSpaceKeyFromDoc = (doc) => {
2838
+ const rawSpaceKey = doc.access?.spaceKey ?? doc.experimental_spaceKey;
2839
+ if (rawSpaceKey == null) {
2840
+ return null;
2826
2841
  }
2827
- async open(sessionId) {
2828
- await this._teleport.open(sessionId);
2829
- this._teleport.addExtension("dxos.mesh.teleport.auth", new AuthExtension({
2830
- provider: this._swarmIdentity.credentialProvider,
2831
- verifier: this._swarmIdentity.credentialAuthenticator,
2832
- onAuthSuccess: () => {
2833
- log10("Peer authenticated", void 0, {
2834
- F: __dxlog_file13,
2835
- L: 248,
2836
- S: this,
2837
- C: (f, a) => f(...a)
2838
- });
2839
- this._authStatus = "SUCCESS";
2840
- this._onSessionAuth?.(this._teleport);
2841
- },
2842
- onAuthFailure: () => {
2843
- this._authStatus = "FAILURE";
2844
- this._onAuthFailure?.(this._teleport);
2842
+ return String(rawSpaceKey);
2843
+ };
2844
+ var waitForHeads = async (handle, heads) => {
2845
+ const unavailableHeads = new Set(heads);
2846
+ await handle.whenReady();
2847
+ await Event5.wrap(handle, "change").waitForCondition(() => {
2848
+ for (const changeHash of unavailableHeads.values()) {
2849
+ if (changeIsPresentInDoc(handle.docSync(), changeHash)) {
2850
+ unavailableHeads.delete(changeHash);
2845
2851
  }
2846
- }));
2847
- this._teleport.addExtension("dxos.mesh.teleport.replicator", this.replicator);
2848
- this._teleport.addExtension("dxos.mesh.teleport.blobsync", this._blobSync.createExtension());
2849
- }
2850
- async close() {
2851
- log10("close", void 0, {
2852
- F: __dxlog_file13,
2853
- L: 264,
2854
- S: this,
2855
- C: (f, a) => f(...a)
2856
- });
2857
- await this._teleport.close();
2858
- }
2859
- async abort() {
2860
- await this._teleport.abort();
2861
- }
2852
+ }
2853
+ return unavailableHeads.size === 0;
2854
+ });
2855
+ };
2856
+ var changeIsPresentInDoc = (doc, changeHash) => {
2857
+ return !!getBackend(doc).getChangeByHash(changeHash);
2858
+ };
2859
+ var decodeCollectionState = (state) => {
2860
+ invariant8(typeof state === "object" && state !== null, "Invalid state", {
2861
+ F: __dxlog_file12,
2862
+ L: 516,
2863
+ S: void 0,
2864
+ A: [
2865
+ "typeof state === 'object' && state !== null",
2866
+ "'Invalid state'"
2867
+ ]
2868
+ });
2869
+ return state;
2870
+ };
2871
+ var encodeCollectionState = (state) => {
2872
+ return state;
2862
2873
  };
2863
- _ts_decorate7([
2864
- logInfo2
2865
- ], SpaceProtocolSession.prototype, "_wireParams", void 0);
2866
- _ts_decorate7([
2867
- logInfo2
2868
- ], SpaceProtocolSession.prototype, "authStatus", null);
2869
-
2870
- // packages/core/echo/echo-pipeline/src/space/space-manager.ts
2871
- import { synchronized as synchronized4, trackLeaks as trackLeaks3, Trigger as Trigger3 } from "@dxos/async";
2872
- import { failUndefined as failUndefined2 } from "@dxos/debug";
2873
- import { PublicKey as PublicKey8 } from "@dxos/keys";
2874
- import { log as log14 } from "@dxos/log";
2875
- import { trace as trace6 } from "@dxos/protocols";
2876
- import { ComplexMap as ComplexMap4 } from "@dxos/util";
2877
-
2878
- // packages/core/echo/echo-pipeline/src/db-host/data-service.ts
2879
- import { Stream } from "@dxos/codec-protobuf";
2880
- import { invariant as invariant12 } from "@dxos/invariant";
2881
- import { SpaceId as SpaceId3 } from "@dxos/keys";
2882
- import { log as log13 } from "@dxos/log";
2883
2874
 
2884
2875
  // packages/core/echo/echo-pipeline/src/automerge/mesh-echo-replicator.ts
2885
2876
  import { invariant as invariant11 } from "@dxos/invariant";
@@ -2890,10 +2881,10 @@ import { ComplexSet, defaultMap as defaultMap2 } from "@dxos/util";
2890
2881
  // packages/core/echo/echo-pipeline/src/automerge/mesh-echo-replicator-connection.ts
2891
2882
  import { cbor } from "@dxos/automerge/automerge-repo";
2892
2883
  import { Resource as Resource6 } from "@dxos/context";
2893
- import { invariant as invariant10 } from "@dxos/invariant";
2884
+ import { invariant as invariant9 } from "@dxos/invariant";
2894
2885
  import { log as log11 } from "@dxos/log";
2895
2886
  import { AutomergeReplicator } from "@dxos/teleport-extension-automerge-replicator";
2896
- var __dxlog_file14 = "/home/runner/work/dxos/dxos/packages/core/echo/echo-pipeline/src/automerge/mesh-echo-replicator-connection.ts";
2887
+ var __dxlog_file13 = "/home/runner/work/dxos/dxos/packages/core/echo/echo-pipeline/src/automerge/mesh-echo-replicator-connection.ts";
2897
2888
  var DEFAULT_FACTORY = (params) => new AutomergeReplicator(...params);
2898
2889
  var MeshReplicatorConnection = class extends Resource6 {
2899
2890
  constructor(_params) {
@@ -2911,8 +2902,8 @@ var MeshReplicatorConnection = class extends Resource6 {
2911
2902
  });
2912
2903
  this.writable = new WritableStream({
2913
2904
  write: async (message, controller) => {
2914
- invariant10(this._isEnabled, "Writing to a disabled connection", {
2915
- F: __dxlog_file14,
2905
+ invariant9(this._isEnabled, "Writing to a disabled connection", {
2906
+ F: __dxlog_file13,
2916
2907
  L: 48,
2917
2908
  S: this,
2918
2909
  A: [
@@ -2944,7 +2935,7 @@ var MeshReplicatorConnection = class extends Resource6 {
2944
2935
  thisPeerId: this.peerId,
2945
2936
  remotePeerId: remotePeerId.toHex()
2946
2937
  }, {
2947
- F: __dxlog_file14,
2938
+ F: __dxlog_file13,
2948
2939
  L: 82,
2949
2940
  S: this,
2950
2941
  C: (f, a) => f(...a)
@@ -2970,8 +2961,8 @@ var MeshReplicatorConnection = class extends Resource6 {
2970
2961
  }
2971
2962
  }
2972
2963
  get peerId() {
2973
- invariant10(this._remotePeerId != null, "Remote peer has not connected yet.", {
2974
- F: __dxlog_file14,
2964
+ invariant9(this._remotePeerId != null, "Remote peer has not connected yet.", {
2965
+ F: __dxlog_file13,
2975
2966
  L: 108,
2976
2967
  S: this,
2977
2968
  A: [
@@ -2992,8 +2983,8 @@ var MeshReplicatorConnection = class extends Resource6 {
2992
2983
  * Call after the remote peer has connected.
2993
2984
  */
2994
2985
  enable() {
2995
- invariant10(this._remotePeerId != null, "Remote peer has not connected yet.", {
2996
- F: __dxlog_file14,
2986
+ invariant9(this._remotePeerId != null, "Remote peer has not connected yet.", {
2987
+ F: __dxlog_file13,
2997
2988
  L: 125,
2998
2989
  S: this,
2999
2990
  A: [
@@ -3011,6 +3002,25 @@ var MeshReplicatorConnection = class extends Resource6 {
3011
3002
  }
3012
3003
  };
3013
3004
 
3005
+ // packages/core/echo/echo-pipeline/src/automerge/space-collection.ts
3006
+ import { invariant as invariant10 } from "@dxos/invariant";
3007
+ import { SpaceId as SpaceId2 } from "@dxos/keys";
3008
+ var __dxlog_file14 = "/home/runner/work/dxos/dxos/packages/core/echo/echo-pipeline/src/automerge/space-collection.ts";
3009
+ var deriveCollectionIdFromSpaceId = (spaceId) => `space:${spaceId}`;
3010
+ var getSpaceIdFromCollectionId = (collectionId) => {
3011
+ const spaceId = collectionId.replace(/^space:/, "");
3012
+ invariant10(SpaceId2.isValid(spaceId), void 0, {
3013
+ F: __dxlog_file14,
3014
+ L: 12,
3015
+ S: void 0,
3016
+ A: [
3017
+ "SpaceId.isValid(spaceId)",
3018
+ ""
3019
+ ]
3020
+ });
3021
+ return spaceId;
3022
+ };
3023
+
3014
3024
  // packages/core/echo/echo-pipeline/src/automerge/mesh-echo-replicator.ts
3015
3025
  var __dxlog_file15 = "/home/runner/work/dxos/dxos/packages/core/echo/echo-pipeline/src/automerge/mesh-echo-replicator.ts";
3016
3026
  var MeshEchoReplicator = class {
@@ -3703,7 +3713,7 @@ function _ts_decorate9(decorators, target, key, desc) {
3703
3713
  }
3704
3714
  var __dxlog_file17 = "/home/runner/work/dxos/dxos/packages/core/echo/echo-pipeline/src/space/space-manager.ts";
3705
3715
  var SpaceManager = class {
3706
- constructor({ feedStore, networkManager, metadataStore, snapshotStore, blobStore }) {
3716
+ constructor({ feedStore, networkManager, metadataStore, snapshotStore, blobStore, disableP2pReplication }) {
3707
3717
  this._spaces = new ComplexMap4(PublicKey8.hash);
3708
3718
  this._instanceId = PublicKey8.random().toHex();
3709
3719
  this._feedStore = feedStore;
@@ -3711,6 +3721,7 @@ var SpaceManager = class {
3711
3721
  this._metadataStore = metadataStore;
3712
3722
  this._snapshotStore = snapshotStore;
3713
3723
  this._blobStore = blobStore;
3724
+ this._disableP2pReplication = disableP2pReplication ?? false;
3714
3725
  }
3715
3726
  // TODO(burdon): Remove.
3716
3727
  get spaces() {
@@ -3728,7 +3739,7 @@ var SpaceManager = class {
3728
3739
  id: this._instanceId
3729
3740
  }), {
3730
3741
  F: __dxlog_file17,
3731
- L: 103,
3742
+ L: 114,
3732
3743
  S: this,
3733
3744
  C: (f, a) => f(...a)
3734
3745
  });
@@ -3736,7 +3747,7 @@ var SpaceManager = class {
3736
3747
  spaceKey: metadata.genesisFeedKey
3737
3748
  }, {
3738
3749
  F: __dxlog_file17,
3739
- L: 104,
3750
+ L: 115,
3740
3751
  S: this,
3741
3752
  C: (f, a) => f(...a)
3742
3753
  });
@@ -3749,7 +3760,8 @@ var SpaceManager = class {
3749
3760
  networkManager: this._networkManager,
3750
3761
  onSessionAuth: onAuthorizedConnection,
3751
3762
  onAuthFailure,
3752
- blobStore: this._blobStore
3763
+ blobStore: this._blobStore,
3764
+ disableP2pReplication: this._disableP2pReplication
3753
3765
  });
3754
3766
  const snapshotManager = new SnapshotManager(this._snapshotStore, this._blobStore, protocol.blobSync);
3755
3767
  const space = new Space({
@@ -3769,7 +3781,7 @@ var SpaceManager = class {
3769
3781
  id: this._instanceId
3770
3782
  }), {
3771
3783
  F: __dxlog_file17,
3772
- L: 135,
3784
+ L: 147,
3773
3785
  S: this,
3774
3786
  C: (f, a) => f(...a)
3775
3787
  });
@@ -3781,7 +3793,7 @@ var SpaceManager = class {
3781
3793
  id: this._instanceId
3782
3794
  }), {
3783
3795
  F: __dxlog_file17,
3784
- L: 141,
3796
+ L: 153,
3785
3797
  S: this,
3786
3798
  C: (f, a) => f(...a)
3787
3799
  });
@@ -3789,7 +3801,7 @@ var SpaceManager = class {
3789
3801
  spaceKey: params.spaceKey
3790
3802
  }, {
3791
3803
  F: __dxlog_file17,
3792
- L: 142,
3804
+ L: 154,
3793
3805
  S: this,
3794
3806
  C: (f, a) => f(...a)
3795
3807
  });
@@ -3805,7 +3817,8 @@ var SpaceManager = class {
3805
3817
  }, onCredentialResolved));
3806
3818
  },
3807
3819
  onAuthFailure: (session) => session.close(),
3808
- blobStore: this._blobStore
3820
+ blobStore: this._blobStore,
3821
+ disableP2pReplication: this._disableP2pReplication
3809
3822
  });
3810
3823
  try {
3811
3824
  await protocol.start();
@@ -3816,7 +3829,7 @@ var SpaceManager = class {
3816
3829
  id: this._instanceId
3817
3830
  }), {
3818
3831
  F: __dxlog_file17,
3819
- L: 165,
3832
+ L: 178,
3820
3833
  S: this,
3821
3834
  C: (f, a) => f(...a)
3822
3835
  });
@@ -3827,7 +3840,7 @@ var SpaceManager = class {
3827
3840
  error: err
3828
3841
  }), {
3829
3842
  F: __dxlog_file17,
3830
- L: 168,
3843
+ L: 181,
3831
3844
  S: this,
3832
3845
  C: (f, a) => f(...a)
3833
3846
  });
@@ -4198,12 +4211,6 @@ export {
4198
4211
  SnapshotStore,
4199
4212
  DocumentsSynchronizer,
4200
4213
  diffCollectionState,
4201
- LevelDBStorageAdapter,
4202
- encodingOptions,
4203
- AutomergeHost,
4204
- getSpaceKeyFromDoc,
4205
- deriveCollectionIdFromSpaceId,
4206
- getSpaceIdFromCollectionId,
4207
4214
  AuthExtension,
4208
4215
  mapTimeframeToFeedIndexes,
4209
4216
  mapFeedIndexesToTimeframe,
@@ -4220,10 +4227,16 @@ export {
4220
4227
  AuthStatus,
4221
4228
  SpaceProtocolSession,
4222
4229
  SpaceManager,
4230
+ LevelDBStorageAdapter,
4231
+ encodingOptions,
4232
+ AutomergeHost,
4233
+ getSpaceKeyFromDoc,
4234
+ deriveCollectionIdFromSpaceId,
4235
+ getSpaceIdFromCollectionId,
4223
4236
  MeshEchoReplicator,
4224
4237
  EchoDataMonitor,
4225
4238
  DataServiceImpl,
4226
4239
  MetadataStore,
4227
4240
  hasInvitationExpired
4228
4241
  };
4229
- //# sourceMappingURL=chunk-2MII6KJX.mjs.map
4242
+ //# sourceMappingURL=chunk-LNIQX6PQ.mjs.map