@dxos/echo-pipeline 0.1.50 → 0.1.51-main.04cd027

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 (37) hide show
  1. package/dist/lib/browser/{chunk-CSFWTLPD.mjs → chunk-KTQITKUX.mjs} +191 -60
  2. package/dist/lib/browser/chunk-KTQITKUX.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 +4 -3
  6. package/dist/lib/browser/testing/index.mjs.map +3 -3
  7. package/dist/lib/node/index.cjs +197 -66
  8. package/dist/lib/node/index.cjs.map +3 -3
  9. package/dist/lib/node/meta.json +1 -1
  10. package/dist/lib/node/testing/index.cjs +207 -75
  11. package/dist/lib/node/testing/index.cjs.map +3 -3
  12. package/dist/types/src/dbhost/snapshot-manager.d.ts +2 -1
  13. package/dist/types/src/dbhost/snapshot-manager.d.ts.map +1 -1
  14. package/dist/types/src/metadata/metadata-store.d.ts +2 -1
  15. package/dist/types/src/metadata/metadata-store.d.ts.map +1 -1
  16. package/dist/types/src/pipeline/pipeline.d.ts +1 -0
  17. package/dist/types/src/pipeline/pipeline.d.ts.map +1 -1
  18. package/dist/types/src/space/control-pipeline.d.ts +9 -1
  19. package/dist/types/src/space/control-pipeline.d.ts.map +1 -1
  20. package/dist/types/src/space/data-pipeline.d.ts +10 -0
  21. package/dist/types/src/space/data-pipeline.d.ts.map +1 -1
  22. package/dist/types/src/space/space.d.ts +2 -2
  23. package/dist/types/src/space/space.d.ts.map +1 -1
  24. package/dist/types/src/testing/test-agent-builder.d.ts.map +1 -1
  25. package/package.json +30 -30
  26. package/src/dbhost/snapshot-manager.ts +4 -3
  27. package/src/metadata/metadata-store.ts +12 -1
  28. package/src/pipeline/pipeline.ts +24 -0
  29. package/src/space/control-pipeline.test.ts +4 -0
  30. package/src/space/control-pipeline.ts +36 -1
  31. package/src/space/data-pipeline.ts +62 -8
  32. package/src/space/space-manager.ts +1 -1
  33. package/src/space/space.test.ts +2 -5
  34. package/src/space/space.ts +11 -7
  35. package/src/testing/test-agent-builder.ts +4 -1
  36. package/src/testing/util.ts +1 -1
  37. package/dist/lib/browser/chunk-CSFWTLPD.mjs.map +0 -7
@@ -200,6 +200,7 @@ var DatabaseHost = class {
200
200
 
201
201
  // packages/core/echo/echo-pipeline/src/dbhost/snapshot-manager.ts
202
202
  var import_async = require("@dxos/async");
203
+ var import_context = require("@dxos/context");
203
204
  var import_debug = require("@dxos/debug");
204
205
  var import_log2 = require("@dxos/log");
205
206
  var import_protocols = require("@dxos/protocols");
@@ -226,7 +227,7 @@ var SnapshotManager = class SnapshotManager2 {
226
227
  snapshot
227
228
  }, {
228
229
  file: "snapshot-manager.ts",
229
- line: 30,
230
+ line: 31,
230
231
  scope: this,
231
232
  callSite: (f, a) => f(...a)
232
233
  });
@@ -243,7 +244,7 @@ var SnapshotManager = class SnapshotManager2 {
243
244
  data
244
245
  }, {
245
246
  file: "snapshot-manager.ts",
246
- line: 40,
247
+ line: 41,
247
248
  scope: this,
248
249
  callSite: (f, a) => f(...a)
249
250
  });
@@ -261,12 +262,12 @@ var SnapshotManager = class SnapshotManager2 {
261
262
  async close() {
262
263
  await this._objectSync.close();
263
264
  }
264
- async load(id) {
265
- const local = await this._snapshotStore.loadSnapshot(id);
265
+ async load(ctx, id) {
266
+ const local = await (0, import_context.cancelWithContext)(ctx, this._snapshotStore.loadSnapshot(id));
266
267
  if (local) {
267
268
  return local;
268
269
  }
269
- const remote = await this._objectSync.download(id);
270
+ const remote = await (0, import_context.cancelWithContext)(ctx, this._objectSync.download(ctx, id));
270
271
  return import_protocols.schema.getCodecForType("dxos.echo.snapshot.SpaceSnapshot").decode(remote.payload.value);
271
272
  }
272
273
  async store(snapshot) {
@@ -505,6 +506,10 @@ var MetadataStore = class {
505
506
  }
506
507
  }
507
508
  _getSpace(spaceKey) {
509
+ var _a;
510
+ if ((_a = this._metadata.identity) == null ? void 0 : _a.haloSpace.key.equals(spaceKey)) {
511
+ return this._metadata.identity.haloSpace;
512
+ }
508
513
  const space = this.spaces.find((space2) => space2.key === spaceKey);
509
514
  (0, import_node_assert3.default)(space, "Space not found");
510
515
  return space;
@@ -515,7 +520,7 @@ var MetadataStore = class {
515
520
  async clear() {
516
521
  (0, import_log4.log)("clearing all metadata", {}, {
517
522
  file: "metadata-store.ts",
518
- line: 131,
523
+ line: 136,
519
524
  scope: this,
520
525
  callSite: (f, a) => f(...a)
521
526
  });
@@ -536,10 +541,14 @@ var MetadataStore = class {
536
541
  ((_c = (_b = this._metadata).spaces) != null ? _c : _b.spaces = []).push(record);
537
542
  await this._save();
538
543
  }
539
- async setSpaceLatestTimeframe(spaceKey, timeframe) {
544
+ async setSpaceDataLatestTimeframe(spaceKey, timeframe) {
540
545
  this._getSpace(spaceKey).dataTimeframe = timeframe;
541
546
  await this._save();
542
547
  }
548
+ async setSpaceControlLatestTimeframe(spaceKey, timeframe) {
549
+ this._getSpace(spaceKey).controlTimeframe = timeframe;
550
+ await this._save();
551
+ }
543
552
  async setCache(spaceKey, cache) {
544
553
  this._getSpace(spaceKey).cache = cache;
545
554
  await this._save();
@@ -562,7 +571,7 @@ var fromBytesInt32 = (buf) => buf.readInt32LE(0);
562
571
  // packages/core/echo/echo-pipeline/src/space/auth.ts
563
572
  var import_node_assert4 = __toESM(require("node:assert"));
564
573
  var import_async3 = require("@dxos/async");
565
- var import_context = require("@dxos/context");
574
+ var import_context2 = require("@dxos/context");
566
575
  var import_crypto2 = require("@dxos/crypto");
567
576
  var import_log5 = require("@dxos/log");
568
577
  var import_protocols4 = require("@dxos/protocols");
@@ -579,7 +588,7 @@ var AuthExtension = class extends import_teleport.RpcExtension {
579
588
  timeout: 60 * 1e3
580
589
  });
581
590
  this._authParams = _authParams;
582
- this._ctx = new import_context.Context({
591
+ this._ctx = new import_context2.Context({
583
592
  onError: (err) => {
584
593
  import_log5.log.catch(err, {}, {
585
594
  file: "auth.ts",
@@ -657,12 +666,13 @@ var import_credentials = require("@dxos/credentials");
657
666
  var import_keys3 = require("@dxos/keys");
658
667
  var import_log8 = require("@dxos/log");
659
668
  var import_credentials2 = require("@dxos/protocols/proto/dxos/halo/credentials");
669
+ var import_timeframe3 = require("@dxos/timeframe");
660
670
  var import_util4 = require("@dxos/util");
661
671
 
662
672
  // packages/core/echo/echo-pipeline/src/pipeline/pipeline.ts
663
673
  var import_node_assert7 = __toESM(require("node:assert"));
664
674
  var import_async5 = require("@dxos/async");
665
- var import_context2 = require("@dxos/context");
675
+ var import_context3 = require("@dxos/context");
666
676
  var import_debug5 = require("@dxos/debug");
667
677
  var import_feed_store = require("@dxos/feed-store");
668
678
  var import_keys2 = require("@dxos/keys");
@@ -814,6 +824,7 @@ var PipelineState = class {
814
824
  this._timeframeClock = _timeframeClock;
815
825
  this.timeframeUpdate = this._timeframeClock.update;
816
826
  this.stalled = new import_async5.Event();
827
+ this._startTimeframe = new import_timeframe2.Timeframe();
817
828
  }
818
829
  /**
819
830
  * Latest theoretical timeframe based on the last mutation in each feed.
@@ -826,6 +837,9 @@ var PipelineState = class {
826
837
  index: feed.length - 1
827
838
  })));
828
839
  }
840
+ get startTimeframe() {
841
+ return this._startTimeframe;
842
+ }
829
843
  get timeframe() {
830
844
  return this._timeframeClock.timeframe;
831
845
  }
@@ -851,7 +865,7 @@ var PipelineState = class {
851
865
  *
852
866
  * @param timeout Timeout in milliseconds to specify the maximum wait time.
853
867
  */
854
- async waitUntilReachedTargetTimeframe({ ctx = new import_context2.Context(), timeout, breakOnStall = true } = {}) {
868
+ async waitUntilReachedTargetTimeframe({ ctx = new import_context3.Context(), timeout, breakOnStall = true } = {}) {
855
869
  var _a;
856
870
  (0, import_log7.log)("waitUntilReachedTargetTimeframe", {
857
871
  timeout,
@@ -859,7 +873,7 @@ var PipelineState = class {
859
873
  target: this.targetTimeframe
860
874
  }, {
861
875
  file: "pipeline.ts",
862
- line: 109,
876
+ line: 118,
863
877
  scope: this,
864
878
  callSite: (f, a) => f(...a)
865
879
  });
@@ -874,7 +888,7 @@ var PipelineState = class {
874
888
  let done = false;
875
889
  if (timeout) {
876
890
  return Promise.race([
877
- (0, import_context2.rejectOnDispose)(ctx),
891
+ (0, import_context3.rejectOnDispose)(ctx),
878
892
  this._reachedTargetPromise.then(() => {
879
893
  done = true;
880
894
  }),
@@ -889,7 +903,7 @@ var PipelineState = class {
889
903
  dependencies: import_timeframe2.Timeframe.dependencies(this.targetTimeframe, this.timeframe)
890
904
  }, {
891
905
  file: "pipeline.ts",
892
- line: 135,
906
+ line: 144,
893
907
  scope: this,
894
908
  callSite: (f, a) => f(...a)
895
909
  });
@@ -946,7 +960,7 @@ var Pipeline = class {
946
960
  async start() {
947
961
  (0, import_log7.log)("starting...", {}, {
948
962
  file: "pipeline.ts",
949
- line: 254,
963
+ line: 263,
950
964
  scope: this,
951
965
  callSite: (f, a) => f(...a)
952
966
  });
@@ -954,7 +968,7 @@ var Pipeline = class {
954
968
  await this._feedSetIterator.open();
955
969
  (0, import_log7.log)("started", {}, {
956
970
  file: "pipeline.ts",
957
- line: 257,
971
+ line: 266,
958
972
  scope: this,
959
973
  callSite: (f, a) => f(...a)
960
974
  });
@@ -964,7 +978,7 @@ var Pipeline = class {
964
978
  var _a;
965
979
  (0, import_log7.log)("stopping...", {}, {
966
980
  file: "pipeline.ts",
967
- line: 263,
981
+ line: 272,
968
982
  scope: this,
969
983
  callSite: (f, a) => f(...a)
970
984
  });
@@ -973,7 +987,7 @@ var Pipeline = class {
973
987
  await this._processingTrigger.wait();
974
988
  (0, import_log7.log)("stopped", {}, {
975
989
  file: "pipeline.ts",
976
- line: 267,
990
+ line: 276,
977
991
  scope: this,
978
992
  callSite: (f, a) => f(...a)
979
993
  });
@@ -984,7 +998,35 @@ var Pipeline = class {
984
998
  */
985
999
  async setCursor(timeframe) {
986
1000
  (0, import_node_assert7.default)(!this._isStarted || this._isPaused, "Invalid state.");
1001
+ this._state._startTimeframe = timeframe;
987
1002
  this._timeframeClock.setTimeframe(timeframe);
1003
+ for (const [key, seq] of timeframe.frames()) {
1004
+ const feed = this._feeds.get(key);
1005
+ if (!feed) {
1006
+ throw new Error("Feed not found");
1007
+ }
1008
+ feed.undownload({
1009
+ callback: () => (0, import_log7.log)("Undownloaded", {}, {
1010
+ file: "pipeline.ts",
1011
+ line: 297,
1012
+ scope: this,
1013
+ callSite: (f, a) => f(...a)
1014
+ })
1015
+ });
1016
+ feed.download({
1017
+ start: seq + 1,
1018
+ linear: true
1019
+ }).catch((err) => {
1020
+ (0, import_log7.log)("failed to download feed", {
1021
+ err
1022
+ }, {
1023
+ file: "pipeline.ts",
1024
+ line: 300,
1025
+ scope: this,
1026
+ callSite: (f, a) => f(...a)
1027
+ });
1028
+ });
1029
+ }
988
1030
  if (this._feedSetIterator) {
989
1031
  await this._feedSetIterator.close();
990
1032
  await this._initIterator();
@@ -1047,7 +1089,7 @@ var Pipeline = class {
1047
1089
  this._feedSetIterator.stalled.on((iterator) => {
1048
1090
  import_log7.log.warn(`Stalled after ${iterator.options.stallTimeout}ms with ${iterator.size} feeds.`, {}, {
1049
1091
  file: "pipeline.ts",
1050
- line: 357,
1092
+ line: 381,
1051
1093
  scope: this,
1052
1094
  callSite: (f, a) => f(...a)
1053
1095
  });
@@ -1075,9 +1117,13 @@ __decorate4([
1075
1117
  ], Pipeline.prototype, "unpause", null);
1076
1118
 
1077
1119
  // packages/core/echo/echo-pipeline/src/space/control-pipeline.ts
1120
+ var TIMEFRAME_SAVE_DEBOUNCE_INTERVAL = 500;
1078
1121
  var ControlPipeline = class {
1079
- constructor({ spaceKey, genesisFeed, feedProvider }) {
1122
+ constructor({ spaceKey, genesisFeed, feedProvider, metadataStore }) {
1123
+ this._lastTimeframeSaveTime = Date.now();
1080
1124
  this.onFeedAdmitted = new import_util4.Callback();
1125
+ this._spaceKey = spaceKey;
1126
+ this._metadata = metadataStore;
1081
1127
  this._pipeline = new Pipeline();
1082
1128
  void this._pipeline.addFeed(genesisFeed);
1083
1129
  this._spaceStateMachine = new import_credentials.SpaceStateMachine(spaceKey);
@@ -1086,7 +1132,7 @@ var ControlPipeline = class {
1086
1132
  key: info.key
1087
1133
  }, {
1088
1134
  file: "control-pipeline.ts",
1089
- line: 39,
1135
+ line: 51,
1090
1136
  scope: this,
1091
1137
  callSite: (f, a) => f(...a)
1092
1138
  });
@@ -1097,7 +1143,7 @@ var ControlPipeline = class {
1097
1143
  } catch (err) {
1098
1144
  import_log8.log.catch(err, {}, {
1099
1145
  file: "control-pipeline.ts",
1100
- line: 47,
1146
+ line: 59,
1101
1147
  scope: this,
1102
1148
  callSite: (f, a) => f(...a)
1103
1149
  });
@@ -1120,7 +1166,7 @@ var ControlPipeline = class {
1120
1166
  async start() {
1121
1167
  (0, import_log8.log)("starting...", {}, {
1122
1168
  file: "control-pipeline.ts",
1123
- line: 71,
1169
+ line: 83,
1124
1170
  scope: this,
1125
1171
  callSite: (f, a) => f(...a)
1126
1172
  });
@@ -1132,7 +1178,7 @@ var ControlPipeline = class {
1132
1178
  seq: msg.seq
1133
1179
  }, {
1134
1180
  file: "control-pipeline.ts",
1135
- line: 76,
1181
+ line: 88,
1136
1182
  scope: this,
1137
1183
  callSite: (f, a) => f(...a)
1138
1184
  });
@@ -1143,16 +1189,18 @@ var ControlPipeline = class {
1143
1189
  msg
1144
1190
  }, {
1145
1191
  file: "control-pipeline.ts",
1146
- line: 83,
1192
+ line: 95,
1147
1193
  scope: this,
1148
1194
  callSite: (f, a) => f(...a)
1149
1195
  });
1196
+ } else {
1197
+ await this._noteTargetStateIfNeeded(this._pipeline.state.pendingTimeframe);
1150
1198
  }
1151
1199
  }
1152
1200
  } catch (err) {
1153
1201
  import_log8.log.catch(err, {}, {
1154
1202
  file: "control-pipeline.ts",
1155
- line: 87,
1203
+ line: 101,
1156
1204
  scope: this,
1157
1205
  callSite: (f, a) => f(...a)
1158
1206
  });
@@ -1162,36 +1210,59 @@ var ControlPipeline = class {
1162
1210
  await this._pipeline.start();
1163
1211
  (0, import_log8.log)("started", {}, {
1164
1212
  file: "control-pipeline.ts",
1165
- line: 93,
1213
+ line: 107,
1166
1214
  scope: this,
1167
1215
  callSite: (f, a) => f(...a)
1168
1216
  });
1169
1217
  }
1218
+ async _noteTargetStateIfNeeded(timeframe) {
1219
+ if (Date.now() - this._lastTimeframeSaveTime > TIMEFRAME_SAVE_DEBOUNCE_INTERVAL) {
1220
+ this._lastTimeframeSaveTime = Date.now();
1221
+ await this._saveTargetTimeframe(timeframe);
1222
+ }
1223
+ }
1170
1224
  async stop() {
1171
1225
  (0, import_log8.log)("stopping...", {}, {
1172
1226
  file: "control-pipeline.ts",
1173
- line: 97,
1227
+ line: 121,
1174
1228
  scope: this,
1175
1229
  callSite: (f, a) => f(...a)
1176
1230
  });
1177
1231
  await this._pipeline.stop();
1232
+ await this._saveTargetTimeframe(this._pipeline.state.timeframe);
1178
1233
  (0, import_log8.log)("stopped", {}, {
1179
1234
  file: "control-pipeline.ts",
1180
- line: 99,
1235
+ line: 124,
1181
1236
  scope: this,
1182
1237
  callSite: (f, a) => f(...a)
1183
1238
  });
1184
1239
  }
1240
+ async _saveTargetTimeframe(timeframe) {
1241
+ var _a;
1242
+ try {
1243
+ const newTimeframe = import_timeframe3.Timeframe.merge((_a = this._targetTimeframe) != null ? _a : new import_timeframe3.Timeframe(), timeframe);
1244
+ await this._metadata.setSpaceControlLatestTimeframe(this._spaceKey, newTimeframe);
1245
+ this._targetTimeframe = newTimeframe;
1246
+ } catch (err) {
1247
+ (0, import_log8.log)(err, {}, {
1248
+ file: "control-pipeline.ts",
1249
+ line: 133,
1250
+ scope: this,
1251
+ callSite: (f, a) => f(...a)
1252
+ });
1253
+ }
1254
+ }
1185
1255
  };
1186
1256
 
1187
1257
  // packages/core/echo/echo-pipeline/src/space/data-pipeline.ts
1188
1258
  var import_node_assert8 = __toESM(require("node:assert"));
1189
1259
  var import_async6 = require("@dxos/async");
1190
- var import_context3 = require("@dxos/context");
1260
+ var import_context4 = require("@dxos/context");
1191
1261
  var import_credentials3 = require("@dxos/credentials");
1192
1262
  var import_echo_db3 = require("@dxos/echo-db");
1263
+ var import_errors2 = require("@dxos/errors");
1193
1264
  var import_log9 = require("@dxos/log");
1194
- var import_timeframe3 = require("@dxos/timeframe");
1265
+ var import_timeframe4 = require("@dxos/timeframe");
1195
1266
  var __decorate5 = function(decorators, target, key, desc) {
1196
1267
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1197
1268
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function")
@@ -1204,15 +1275,16 @@ var __decorate5 = function(decorators, target, key, desc) {
1204
1275
  };
1205
1276
  var MESSAGES_PER_SNAPSHOT = 10;
1206
1277
  var AUTOMATIC_SNAPSHOT_DEBOUNCE_INTERVAL = 5e3;
1207
- var TIMEFRAME_SAVE_DEBOUNCE_INTERVAL = 500;
1278
+ var TIMEFRAME_SAVE_DEBOUNCE_INTERVAL2 = 500;
1208
1279
  var DataPipeline = class DataPipeline2 {
1209
1280
  constructor(_params) {
1210
1281
  this._params = _params;
1211
- this._ctx = new import_context3.Context();
1212
- this._lastAutomaticSnapshotTimeframe = new import_timeframe3.Timeframe();
1282
+ this._ctx = new import_context4.Context();
1283
+ this._lastAutomaticSnapshotTimeframe = new import_timeframe4.Timeframe();
1213
1284
  this._isOpen = false;
1214
1285
  this._lastTimeframeSaveTime = 0;
1215
1286
  this._lastSnapshotSaveTime = 0;
1287
+ this._lastProcessedEpoch = -1;
1216
1288
  this.onNewEpoch = new import_async6.Event();
1217
1289
  }
1218
1290
  get isOpen() {
@@ -1236,17 +1308,10 @@ var DataPipeline = class DataPipeline2 {
1236
1308
  if (!(0, import_credentials3.checkCredentialType)(credential, "dxos.halo.credentials.Epoch")) {
1237
1309
  return;
1238
1310
  }
1239
- (0, import_log9.log)("new epoch", {
1240
- credential
1241
- }, {
1242
- file: "data-pipeline.ts",
1243
- line: 109,
1244
- scope: this,
1245
- callSite: (f, a) => f(...a)
1246
- });
1247
- await this._processEpoch(credential.subject.assertion);
1248
1311
  this.currentEpoch = credential;
1249
- this.onNewEpoch.emit(credential);
1312
+ if (this._isOpen) {
1313
+ await this._processEpochInSeparateTask(credential);
1314
+ }
1250
1315
  }
1251
1316
  };
1252
1317
  }
@@ -1285,7 +1350,7 @@ var DataPipeline = class DataPipeline2 {
1285
1350
  }
1286
1351
  (0, import_log9.log)("close", {}, {
1287
1352
  file: "data-pipeline.ts",
1288
- line: 161,
1353
+ line: 174,
1289
1354
  scope: this,
1290
1355
  callSite: (f, a) => f(...a)
1291
1356
  });
@@ -1300,7 +1365,7 @@ var DataPipeline = class DataPipeline2 {
1300
1365
  } catch (err) {
1301
1366
  import_log9.log.catch(err, {}, {
1302
1367
  file: "data-pipeline.ts",
1303
- line: 174,
1368
+ line: 187,
1304
1369
  scope: this,
1305
1370
  callSite: (f, a) => f(...a)
1306
1371
  });
@@ -1310,6 +1375,11 @@ var DataPipeline = class DataPipeline2 {
1310
1375
  await this._params.snapshotManager.close();
1311
1376
  }
1312
1377
  async _consumePipeline() {
1378
+ if (this.currentEpoch) {
1379
+ const waitForOneEpoch = this.onNewEpoch.waitForCount(1);
1380
+ await this._processEpochInSeparateTask(this.currentEpoch);
1381
+ await waitForOneEpoch;
1382
+ }
1313
1383
  (0, import_node_assert8.default)(this._pipeline, "Pipeline is not initialized.");
1314
1384
  for await (const msg of this._pipeline.consume()) {
1315
1385
  const { feedKey, seq, data } = msg;
@@ -1318,7 +1388,7 @@ var DataPipeline = class DataPipeline2 {
1318
1388
  seq
1319
1389
  }, {
1320
1390
  file: "data-pipeline.ts",
1321
- line: 186,
1391
+ line: 205,
1322
1392
  scope: this,
1323
1393
  callSite: (f, a) => f(...a)
1324
1394
  });
@@ -1330,7 +1400,7 @@ var DataPipeline = class DataPipeline2 {
1330
1400
  feedKey
1331
1401
  }, {
1332
1402
  file: "data-pipeline.ts",
1333
- line: 192,
1403
+ line: 211,
1334
1404
  scope: this,
1335
1405
  callSite: (f, a) => f(...a)
1336
1406
  });
@@ -1345,12 +1415,22 @@ var DataPipeline = class DataPipeline2 {
1345
1415
  memberKey: feedInfo.assertion.identityKey
1346
1416
  }
1347
1417
  });
1418
+ import_log9.log.trace("dxos.echo.data-pipeline.processed", {
1419
+ feedKey: feedKey.toHex(),
1420
+ seq,
1421
+ spaceKey: this._params.spaceKey.toHex()
1422
+ }, {
1423
+ file: "data-pipeline.ts",
1424
+ line: 225,
1425
+ scope: this,
1426
+ callSite: (f, a) => f(...a)
1427
+ });
1348
1428
  await this._noteTargetStateIfNeeded(this._pipeline.state.pendingTimeframe);
1349
1429
  }
1350
1430
  } catch (err) {
1351
1431
  import_log9.log.catch(err, {}, {
1352
1432
  file: "data-pipeline.ts",
1353
- line: 210,
1433
+ line: 235,
1354
1434
  scope: this,
1355
1435
  callSite: (f, a) => f(...a)
1356
1436
  });
@@ -1367,8 +1447,8 @@ var DataPipeline = class DataPipeline2 {
1367
1447
  }
1368
1448
  async _saveTargetTimeframe(timeframe) {
1369
1449
  var _a;
1370
- const newTimeframe = import_timeframe3.Timeframe.merge((_a = this._targetTimeframe) != null ? _a : new import_timeframe3.Timeframe(), timeframe);
1371
- await this._params.metadataStore.setSpaceLatestTimeframe(this._params.spaceKey, newTimeframe);
1450
+ const newTimeframe = import_timeframe4.Timeframe.merge((_a = this._targetTimeframe) != null ? _a : new import_timeframe4.Timeframe(), timeframe);
1451
+ await this._params.metadataStore.setSpaceDataLatestTimeframe(this._params.spaceKey, newTimeframe);
1372
1452
  this._targetTimeframe = newTimeframe;
1373
1453
  }
1374
1454
  async _saveCache() {
@@ -1385,7 +1465,7 @@ var DataPipeline = class DataPipeline2 {
1385
1465
  } catch (err) {
1386
1466
  import_log9.log.warn("Failed to cache properties", err, {
1387
1467
  file: "data-pipeline.ts",
1388
- line: 244,
1468
+ line: 269,
1389
1469
  scope: this,
1390
1470
  callSite: (f, a) => f(...a)
1391
1471
  });
@@ -1393,7 +1473,7 @@ var DataPipeline = class DataPipeline2 {
1393
1473
  await this._params.metadataStore.setCache(this._params.spaceKey, cache);
1394
1474
  }
1395
1475
  async _noteTargetStateIfNeeded(timeframe) {
1396
- if (Date.now() - this._lastTimeframeSaveTime > TIMEFRAME_SAVE_DEBOUNCE_INTERVAL) {
1476
+ if (Date.now() - this._lastTimeframeSaveTime > TIMEFRAME_SAVE_DEBOUNCE_INTERVAL2) {
1397
1477
  this._lastTimeframeSaveTime = Date.now();
1398
1478
  await this._saveTargetTimeframe(timeframe);
1399
1479
  }
@@ -1401,24 +1481,68 @@ var DataPipeline = class DataPipeline2 {
1401
1481
  await this._saveCache();
1402
1482
  }
1403
1483
  }
1404
- async _processEpoch(epoch) {
1405
- (0, import_node_assert8.default)(this._isOpen);
1484
+ async _processEpochInSeparateTask(epoch) {
1485
+ var _a;
1486
+ if (epoch.subject.assertion.number <= this._lastProcessedEpoch) {
1487
+ return;
1488
+ }
1489
+ await ((_a = this._epochCtx) == null ? void 0 : _a.dispose());
1490
+ const ctx = new import_context4.Context({
1491
+ onError: (err) => {
1492
+ if (err instanceof import_errors2.CancelledError) {
1493
+ (0, import_log9.log)("Epoch processing cancelled.", {}, {
1494
+ file: "data-pipeline.ts",
1495
+ line: 301,
1496
+ scope: this,
1497
+ callSite: (f, a) => f(...a)
1498
+ });
1499
+ } else {
1500
+ import_log9.log.catch(err, {}, {
1501
+ file: "data-pipeline.ts",
1502
+ line: 303,
1503
+ scope: this,
1504
+ callSite: (f, a) => f(...a)
1505
+ });
1506
+ }
1507
+ }
1508
+ });
1509
+ this._epochCtx = ctx;
1510
+ (0, import_async6.scheduleTask)(ctx, async () => {
1511
+ if (!this._isOpen) {
1512
+ return;
1513
+ }
1514
+ (0, import_log9.log)("process epoch", {
1515
+ epoch
1516
+ }, {
1517
+ file: "data-pipeline.ts",
1518
+ line: 313,
1519
+ scope: this,
1520
+ callSite: (f, a) => f(...a)
1521
+ });
1522
+ await this._processEpoch(ctx, epoch.subject.assertion);
1523
+ this.appliedEpoch = epoch;
1524
+ this.onNewEpoch.emit(epoch);
1525
+ });
1526
+ }
1527
+ async _processEpoch(ctx, epoch) {
1528
+ (0, import_node_assert8.default)(this._isOpen, "Space is closed.");
1406
1529
  (0, import_node_assert8.default)(this._pipeline);
1530
+ this._lastProcessedEpoch = epoch.number;
1407
1531
  (0, import_log9.log)("Processing epoch", {
1408
1532
  epoch
1409
1533
  }, {
1410
1534
  file: "data-pipeline.ts",
1411
- line: 273,
1535
+ line: 327,
1412
1536
  scope: this,
1413
1537
  callSite: (f, a) => f(...a)
1414
1538
  });
1415
1539
  if (epoch.snapshotCid) {
1416
- const snapshot = await this._params.snapshotManager.load(epoch.snapshotCid);
1540
+ const snapshot = await this._params.snapshotManager.load(ctx, epoch.snapshotCid);
1417
1541
  this.databaseHost._itemDemuxer.restoreFromSnapshot(snapshot.database);
1418
1542
  }
1419
1543
  (0, import_log9.log)("restarting pipeline for epoch", {}, {
1420
1544
  file: "data-pipeline.ts",
1421
- line: 281,
1545
+ line: 335,
1422
1546
  scope: this,
1423
1547
  callSite: (f, a) => f(...a)
1424
1548
  });
@@ -1492,20 +1616,26 @@ var Space = class Space2 {
1492
1616
  this._controlPipeline = new ControlPipeline({
1493
1617
  spaceKey: params.spaceKey,
1494
1618
  genesisFeed: params.genesisFeed,
1495
- feedProvider: params.feedProvider
1619
+ feedProvider: params.feedProvider,
1620
+ metadataStore: params.metadataStore
1496
1621
  });
1497
1622
  this._controlPipeline.onFeedAdmitted.set(async (info) => {
1623
+ const sparse = info.assertion.designation === import_credentials4.AdmittedFeed.Designation.DATA;
1498
1624
  if (info.assertion.designation === import_credentials4.AdmittedFeed.Designation.DATA) {
1499
1625
  await this._addFeedLock.executeSynchronized(async () => {
1500
1626
  if (this._dataPipeline.pipeline) {
1501
1627
  if (!this._dataPipeline.pipeline.hasFeed(info.key)) {
1502
- return this._dataPipeline.pipeline.addFeed(await this._feedProvider(info.key));
1628
+ return this._dataPipeline.pipeline.addFeed(await this._feedProvider(info.key, {
1629
+ sparse
1630
+ }));
1503
1631
  }
1504
1632
  }
1505
1633
  });
1506
1634
  }
1507
1635
  if (!info.key.equals(params.genesisFeed.key)) {
1508
- this.protocol.addFeed(await params.feedProvider(info.key));
1636
+ this.protocol.addFeed(await params.feedProvider(info.key, {
1637
+ sparse
1638
+ }));
1509
1639
  }
1510
1640
  });
1511
1641
  this._controlPipeline.onCredentialProcessed.set(async (credential) => {
@@ -1514,7 +1644,7 @@ var Space = class Space2 {
1514
1644
  credential
1515
1645
  }, {
1516
1646
  file: "space.ts",
1517
- line: 106,
1647
+ line: 110,
1518
1648
  scope: this,
1519
1649
  callSite: (f, a) => f(...a)
1520
1650
  });
@@ -1537,7 +1667,9 @@ var Space = class Space2 {
1537
1667
  await this._addFeedLock.executeSynchronized(async () => {
1538
1668
  for (const feed of this._controlPipeline.spaceState.feeds.values()) {
1539
1669
  if (feed.assertion.designation === import_credentials4.AdmittedFeed.Designation.DATA && !pipeline.hasFeed(feed.key)) {
1540
- await pipeline.addFeed(await this._feedProvider(feed.key));
1670
+ await pipeline.addFeed(await this._feedProvider(feed.key, {
1671
+ sparse: true
1672
+ }));
1541
1673
  }
1542
1674
  }
1543
1675
  });
@@ -1604,7 +1736,7 @@ var Space = class Space2 {
1604
1736
  async open() {
1605
1737
  (0, import_log10.log)("opening...", {}, {
1606
1738
  file: "space.ts",
1607
- line: 208,
1739
+ line: 212,
1608
1740
  scope: this,
1609
1741
  callSite: (f, a) => f(...a)
1610
1742
  });
@@ -1614,10 +1746,11 @@ var Space = class Space2 {
1614
1746
  await this._controlPipeline.start();
1615
1747
  await this.protocol.start();
1616
1748
  this._dataPipelineCredentialConsumer = this._controlPipeline.spaceState.registerProcessor(this._dataPipeline.createCredentialProcessor());
1749
+ await this._dataPipelineCredentialConsumer.open();
1617
1750
  this._isOpen = true;
1618
1751
  (0, import_log10.log)("opened", {}, {
1619
1752
  file: "space.ts",
1620
- line: 222,
1753
+ line: 228,
1621
1754
  scope: this,
1622
1755
  callSite: (f, a) => f(...a)
1623
1756
  });
@@ -1628,7 +1761,7 @@ var Space = class Space2 {
1628
1761
  key: this._key
1629
1762
  }, {
1630
1763
  file: "space.ts",
1631
- line: 227,
1764
+ line: 233,
1632
1765
  scope: this,
1633
1766
  callSite: (f, a) => f(...a)
1634
1767
  });
@@ -1643,7 +1776,7 @@ var Space = class Space2 {
1643
1776
  this._isOpen = false;
1644
1777
  (0, import_log10.log)("closed", {}, {
1645
1778
  file: "space.ts",
1646
- line: 241,
1779
+ line: 247,
1647
1780
  scope: this,
1648
1781
  callSite: (f, a) => f(...a)
1649
1782
  });
@@ -1651,14 +1784,12 @@ var Space = class Space2 {
1651
1784
  async initializeDataPipeline() {
1652
1785
  (0, import_log10.log)("initializeDataPipeline", {}, {
1653
1786
  file: "space.ts",
1654
- line: 246,
1787
+ line: 252,
1655
1788
  scope: this,
1656
1789
  callSite: (f, a) => f(...a)
1657
1790
  });
1658
1791
  (0, import_node_assert9.default)(this._isOpen, "Space must be open to initialize data pipeline.");
1659
1792
  await this._dataPipeline.open();
1660
- (0, import_node_assert9.default)(this._dataPipelineCredentialConsumer);
1661
- await this._dataPipelineCredentialConsumer.open();
1662
1793
  }
1663
1794
  };
1664
1795
  __decorate6([
@@ -1933,7 +2064,7 @@ var SpaceManager = class SpaceManager2 {
1933
2064
  spaceKey,
1934
2065
  protocol,
1935
2066
  genesisFeed,
1936
- feedProvider: (feedKey) => this._feedStore.openFeed(feedKey),
2067
+ feedProvider: (feedKey, opts) => this._feedStore.openFeed(feedKey, opts),
1937
2068
  modelFactory: this._modelFactory,
1938
2069
  metadataStore: this._metadataStore,
1939
2070
  snapshotManager,
@@ -2063,7 +2194,8 @@ var TestAgent = class {
2063
2194
  writable: true
2064
2195
  });
2065
2196
  const dataFeed = await this.feedStore.openFeed(dataKey != null ? dataKey : await this.keyring.createKey(), {
2066
- writable: true
2197
+ writable: true,
2198
+ sparse: true
2067
2199
  });
2068
2200
  const metadata = {
2069
2201
  key: spaceKey,
@@ -2148,7 +2280,7 @@ var import_echo_db4 = require("@dxos/echo-db");
2148
2280
  var import_testing2 = require("@dxos/feed-store/testing");
2149
2281
  var import_keys7 = require("@dxos/keys");
2150
2282
  var import_model_factory2 = require("@dxos/model-factory");
2151
- var import_timeframe4 = require("@dxos/timeframe");
2283
+ var import_timeframe5 = require("@dxos/timeframe");
2152
2284
  var createMemoryDatabase = async (modelFactory) => {
2153
2285
  const feed = new import_testing2.MockFeedWriter();
2154
2286
  const backend = new DatabaseHost(feed);
@@ -2157,7 +2289,7 @@ var createMemoryDatabase = async (modelFactory) => {
2157
2289
  meta: {
2158
2290
  ...meta,
2159
2291
  memberKey: import_keys7.PublicKey.random(),
2160
- timeframe: new import_timeframe4.Timeframe([
2292
+ timeframe: new import_timeframe5.Timeframe([
2161
2293
  [
2162
2294
  meta.feedKey,
2163
2295
  meta.seq
@@ -2200,7 +2332,7 @@ var testLocalDatabase = async (create, check = create) => {
2200
2332
  ]
2201
2333
  }
2202
2334
  }));
2203
- await (0, import_async9.asyncTimeout)(check.databaseHost._itemDemuxer.mutation.waitForCondition(() => check.itemManager.entities.has(objectId)), 500);
2335
+ await (0, import_async9.asyncTimeout)(check.databaseHost._itemDemuxer.mutation.waitForCondition(() => check.itemManager.entities.has(objectId)), 1e3);
2204
2336
  };
2205
2337
  // Annotate the CommonJS export names for ESM import in node:
2206
2338
  0 && (module.exports = {