@dxos/echo-pipeline 0.1.36-next.ef27157 → 0.1.36

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 (48) hide show
  1. package/dist/lib/browser/{chunk-LIPDNX3G.mjs → chunk-4YAT2XJW.mjs} +159 -125
  2. package/dist/lib/browser/chunk-4YAT2XJW.mjs.map +7 -0
  3. package/dist/lib/browser/index.mjs +3 -3
  4. package/dist/lib/browser/meta.json +1 -1
  5. package/dist/lib/browser/testing/index.mjs +5 -5
  6. package/dist/lib/browser/testing/index.mjs.map +3 -3
  7. package/dist/lib/node/index.cjs +158 -124
  8. package/dist/lib/node/index.cjs.map +4 -4
  9. package/dist/lib/node/meta.json +1 -1
  10. package/dist/lib/node/testing/index.cjs +158 -124
  11. package/dist/lib/node/testing/index.cjs.map +4 -4
  12. package/dist/types/src/dbhost/data-service-host.d.ts.map +1 -1
  13. package/dist/types/src/dbhost/{database-backend.d.ts → database-host.d.ts} +2 -2
  14. package/dist/types/src/dbhost/database-host.d.ts.map +1 -0
  15. package/dist/types/src/dbhost/index.d.ts +1 -1
  16. package/dist/types/src/dbhost/index.d.ts.map +1 -1
  17. package/dist/types/src/metadata/metadata-store.d.ts.map +1 -1
  18. package/dist/types/src/pipeline/pipeline.d.ts +1 -0
  19. package/dist/types/src/pipeline/pipeline.d.ts.map +1 -1
  20. package/dist/types/src/pipeline/timeframe-clock.d.ts +11 -1
  21. package/dist/types/src/pipeline/timeframe-clock.d.ts.map +1 -1
  22. package/dist/types/src/space/data-pipeline.d.ts +8 -9
  23. package/dist/types/src/space/data-pipeline.d.ts.map +1 -1
  24. package/dist/types/src/space/data-pipeline.test.d.ts +1 -0
  25. package/dist/types/src/space/data-pipeline.test.d.ts.map +1 -0
  26. package/dist/types/src/space/space-manager.d.ts +1 -0
  27. package/dist/types/src/space/space-manager.d.ts.map +1 -1
  28. package/dist/types/src/testing/database-test-rig.d.ts +4 -4
  29. package/dist/types/src/testing/database-test-rig.d.ts.map +1 -1
  30. package/dist/types/src/testing/util.d.ts +4 -4
  31. package/dist/types/src/testing/util.d.ts.map +1 -1
  32. package/package.json +31 -29
  33. package/src/common/feeds.ts +1 -1
  34. package/src/dbhost/data-service-host.ts +16 -9
  35. package/src/dbhost/{database-backend.ts → database-host.ts} +1 -1
  36. package/src/dbhost/index.ts +1 -1
  37. package/src/metadata/metadata-store.ts +24 -20
  38. package/src/pipeline/pipeline.ts +6 -1
  39. package/src/pipeline/timeframe-clock.ts +22 -3
  40. package/src/space/data-pipeline.test.ts +3 -0
  41. package/src/space/data-pipeline.ts +63 -64
  42. package/src/space/space-manager.ts +2 -1
  43. package/src/testing/database-test-rig.ts +6 -6
  44. package/src/testing/util.ts +4 -4
  45. package/src/tests/database-unit.test.ts +13 -0
  46. package/src/tests/database.test.ts +39 -1
  47. package/dist/lib/browser/chunk-LIPDNX3G.mjs.map +0 -7
  48. package/dist/types/src/dbhost/database-backend.d.ts.map +0 -1
@@ -78,12 +78,12 @@ var DataServiceHost = class {
78
78
  this._itemDemuxer.mutation.on(ctx, (message) => {
79
79
  var _a;
80
80
  const { batch, meta } = message;
81
- (0, import_log.log)("Object update", {
81
+ (0, import_log.log)("message", {
82
82
  batch,
83
83
  meta
84
84
  }, {
85
85
  file: "data-service-host.ts",
86
- line: 50,
86
+ line: 49,
87
87
  scope: this,
88
88
  callSite: (f, a) => f(...a)
89
89
  });
@@ -107,36 +107,59 @@ var DataServiceHost = class {
107
107
  });
108
108
  }
109
109
  async write(request) {
110
- var _a;
110
+ var _a, _b, _c;
111
111
  (0, import_node_assert.default)(this._writeStream, "Cannot write mutations in readonly mode");
112
- const receipt = await this._writeStream.write({
112
+ (0, import_log.log)("write", {
113
+ clientTag: request.clientTag,
114
+ objectCount: (_b = (_a = request.batch.objects) == null ? void 0 : _a.length) != null ? _b : 0
115
+ }, {
116
+ file: "data-service-host.ts",
117
+ line: 77,
118
+ scope: this,
119
+ callSite: (f, a) => f(...a)
120
+ });
121
+ const message = {
113
122
  batch: {
114
- objects: (_a = request.batch.objects) == null ? void 0 : _a.map((object) => {
123
+ objects: (_c = request.batch.objects) == null ? void 0 : _c.map((object) => {
115
124
  var _a2;
116
125
  return {
117
126
  ...object,
118
- mutations: (_a2 = object.mutations) == null ? void 0 : _a2.map((m) => ({
119
- ...m,
127
+ mutations: (_a2 = object.mutations) == null ? void 0 : _a2.map((mutation) => ({
128
+ ...mutation,
120
129
  meta: void 0
121
130
  })),
122
131
  meta: void 0
123
132
  };
124
133
  })
125
134
  }
135
+ };
136
+ const receipt = await this._writeStream.write(message, {
137
+ afterWrite: async (receipt2) => {
138
+ if (request.clientTag) {
139
+ (0, import_log.log)("tag", {
140
+ clientTag: request.clientTag,
141
+ feedKey: receipt2.feedKey,
142
+ seq: receipt2.seq
143
+ }, {
144
+ file: "data-service-host.ts",
145
+ line: 96,
146
+ scope: this,
147
+ callSite: (f, a) => f(...a)
148
+ });
149
+ this._clientTagMap.set([
150
+ receipt2.feedKey,
151
+ receipt2.seq
152
+ ], request.clientTag);
153
+ }
154
+ }
126
155
  });
127
- if (request.clientTag) {
128
- this._clientTagMap.set([
129
- receipt.feedKey,
130
- receipt.seq
131
- ], request.clientTag);
132
- }
133
156
  return receipt;
134
157
  }
135
158
  };
136
159
 
137
- // packages/core/echo/echo-pipeline/src/dbhost/database-backend.ts
160
+ // packages/core/echo/echo-pipeline/src/dbhost/database-host.ts
138
161
  var import_echo_db2 = require("@dxos/echo-db");
139
- var DatabaseBackendHost = class {
162
+ var DatabaseHost = class {
140
163
  constructor(_outboundStream, _snapshot) {
141
164
  this._outboundStream = _outboundStream;
142
165
  this._snapshot = _snapshot;
@@ -325,8 +348,10 @@ var DataServiceImpl = class {
325
348
  };
326
349
 
327
350
  // packages/core/echo/echo-pipeline/src/metadata/metadata-store.ts
351
+ var import_crc_32 = __toESM(require("crc-32"));
328
352
  var import_node_assert3 = __toESM(require("node:assert"));
329
353
  var import_async2 = require("@dxos/async");
354
+ var import_errors = require("@dxos/errors");
330
355
  var import_log3 = require("@dxos/log");
331
356
  var import_protocols3 = require("@dxos/protocols");
332
357
  var __decorate2 = function(decorators, target, key, desc) {
@@ -371,31 +396,35 @@ var MetadataStore = class {
371
396
  const file = this._directory.getOrCreateFile("EchoMetadata");
372
397
  try {
373
398
  const { size: fileLength } = await file.stat();
374
- if (fileLength < 4) {
399
+ if (fileLength < 8) {
375
400
  return;
376
401
  }
377
402
  const dataSize = fromBytesInt32(await file.read(0, 4));
403
+ const checksum = fromBytesInt32(await file.read(4, 4));
378
404
  (0, import_log3.log)("loaded", {
379
- size: dataSize
405
+ size: dataSize,
406
+ checksum
380
407
  }, {
381
408
  file: "metadata-store.ts",
382
- line: 67,
409
+ line: 70,
383
410
  scope: this,
384
411
  callSite: (f, a) => f(...a)
385
412
  });
386
- {
387
- if (fileLength < dataSize + 4) {
388
- throw new Error("Metadata storage is corrupted");
389
- }
413
+ if (fileLength < dataSize + 8) {
414
+ throw new import_errors.DataCorruptionError("Metadata size is smaller than expected.");
415
+ }
416
+ const data = await file.read(8, dataSize);
417
+ const calculatedChecksum = import_crc_32.default.buf(data);
418
+ if (calculatedChecksum !== checksum) {
419
+ throw new import_errors.DataCorruptionError("Metadata checksum is invalid.");
390
420
  }
391
- const data = await file.read(4, dataSize);
392
421
  this._metadata = import_protocols3.schema.getCodecForType("dxos.echo.metadata.EchoMetadata").decode(data);
393
422
  } catch (err) {
394
423
  import_log3.log.error("failed to load metadata", {
395
424
  err
396
425
  }, {
397
426
  file: "metadata-store.ts",
398
- line: 79,
427
+ line: 85,
399
428
  scope: this,
400
429
  callSite: (f, a) => f(...a)
401
430
  });
@@ -415,16 +444,21 @@ var MetadataStore = class {
415
444
  const file = this._directory.getOrCreateFile("EchoMetadata");
416
445
  try {
417
446
  const encoded = Buffer.from(import_protocols3.schema.getCodecForType("dxos.echo.metadata.EchoMetadata").encode(data));
418
- await file.write(0, toBytesInt32(encoded.length));
447
+ const checksum = import_crc_32.default.buf(encoded);
448
+ const result = Buffer.alloc(8 + encoded.length);
449
+ result.writeInt32LE(encoded.length, 0);
450
+ result.writeInt32LE(checksum, 4);
451
+ encoded.copy(result, 8);
452
+ await file.write(0, result);
419
453
  (0, import_log3.log)("saved", {
420
- size: encoded.length
454
+ size: encoded.length,
455
+ checksum
421
456
  }, {
422
457
  file: "metadata-store.ts",
423
- line: 102,
458
+ line: 116,
424
459
  scope: this,
425
460
  callSite: (f, a) => f(...a)
426
461
  });
427
- await file.write(4, encoded);
428
462
  } finally {
429
463
  await file.close();
430
464
  }
@@ -435,7 +469,7 @@ var MetadataStore = class {
435
469
  async clear() {
436
470
  (0, import_log3.log)("clearing all metadata", {}, {
437
471
  file: "metadata-store.ts",
438
- line: 115,
472
+ line: 126,
439
473
  scope: this,
440
474
  callSite: (f, a) => f(...a)
441
475
  });
@@ -481,11 +515,6 @@ __decorate2([
481
515
  __decorate2([
482
516
  import_async2.synchronized
483
517
  ], MetadataStore.prototype, "_save", null);
484
- var toBytesInt32 = (num) => {
485
- const buf = Buffer.alloc(4);
486
- buf.writeInt32LE(num, 0);
487
- return buf;
488
- };
489
518
  var fromBytesInt32 = (buf) => buf.readInt32LE(0);
490
519
 
491
520
  // packages/core/echo/echo-pipeline/src/space/auth.ts
@@ -603,7 +632,7 @@ var createMappedFeedWriter = (mapper, writer) => {
603
632
  (0, import_node_assert5.default)(mapper);
604
633
  (0, import_node_assert5.default)(writer);
605
634
  return {
606
- write: async (data) => await writer.write(await mapper(data))
635
+ write: async (data, options) => await writer.write(await mapper(data), options)
607
636
  };
608
637
  };
609
638
 
@@ -652,17 +681,31 @@ var TimeframeClock = class {
652
681
  constructor(_timeframe = new import_timeframe.Timeframe()) {
653
682
  this._timeframe = _timeframe;
654
683
  this.update = new import_async4.Event();
684
+ this._pendingTimeframe = _timeframe;
655
685
  }
686
+ /**
687
+ * Timeframe that was processed by ECHO.
688
+ */
656
689
  get timeframe() {
657
690
  return this._timeframe;
658
691
  }
659
- updateTimeframe(key, seq) {
660
- this._timeframe = import_timeframe.Timeframe.merge(this._timeframe, new import_timeframe.Timeframe([
692
+ /**
693
+ * Timeframe that is currently being processed by ECHO.
694
+ * Will be equal to `timeframe` after the processing is complete.
695
+ */
696
+ get pendingTimeframe() {
697
+ return this._pendingTimeframe;
698
+ }
699
+ updatePendingTimeframe(key, seq) {
700
+ this._pendingTimeframe = import_timeframe.Timeframe.merge(this._pendingTimeframe, new import_timeframe.Timeframe([
661
701
  [
662
702
  key,
663
703
  seq
664
704
  ]
665
705
  ]));
706
+ }
707
+ updateTimeframe() {
708
+ this._timeframe = this._pendingTimeframe;
666
709
  this.update.emit(this._timeframe);
667
710
  }
668
711
  hasGaps(timeframe) {
@@ -675,7 +718,7 @@ var TimeframeClock = class {
675
718
  current: this._timeframe
676
719
  }, {
677
720
  file: "timeframe-clock.ts",
678
- line: 48,
721
+ line: 67,
679
722
  scope: this,
680
723
  callSite: (f, a) => f(...a)
681
724
  });
@@ -686,7 +729,7 @@ var TimeframeClock = class {
686
729
  deps: import_timeframe.Timeframe.dependencies(target, this._timeframe)
687
730
  }, {
688
731
  file: "timeframe-clock.ts",
689
- line: 50,
732
+ line: 69,
690
733
  scope: this,
691
734
  callSite: (f, a) => f(...a)
692
735
  });
@@ -730,6 +773,9 @@ var PipelineState = class {
730
773
  get timeframe() {
731
774
  return this._timeframeClock.timeframe;
732
775
  }
776
+ get pendingTimeframe() {
777
+ return this._timeframeClock.pendingTimeframe;
778
+ }
733
779
  get targetTimeframe() {
734
780
  return this._targetTimeframe ? this._targetTimeframe : new import_timeframe2.Timeframe();
735
781
  }
@@ -757,7 +803,7 @@ var PipelineState = class {
757
803
  target: this.targetTimeframe
758
804
  }, {
759
805
  file: "pipeline.ts",
760
- line: 101,
806
+ line: 105,
761
807
  scope: this,
762
808
  callSite: (f, a) => f(...a)
763
809
  });
@@ -787,7 +833,7 @@ var PipelineState = class {
787
833
  dependencies: import_timeframe2.Timeframe.dependencies(this.targetTimeframe, this.timeframe)
788
834
  }, {
789
835
  file: "pipeline.ts",
790
- line: 127,
836
+ line: 131,
791
837
  scope: this,
792
838
  callSite: (f, a) => f(...a)
793
839
  });
@@ -813,7 +859,7 @@ var Pipeline = class {
813
859
  this._feedSetIterator.stalled.on((iterator) => {
814
860
  import_log6.log.warn(`Stalled after ${iterator.options.stallTimeout}ms with ${iterator.size} feeds.`, {}, {
815
861
  file: "pipeline.ts",
816
- line: 203,
862
+ line: 207,
817
863
  scope: this,
818
864
  callSite: (f, a) => f(...a)
819
865
  });
@@ -848,14 +894,14 @@ var Pipeline = class {
848
894
  async start() {
849
895
  (0, import_log6.log)("starting...", {}, {
850
896
  file: "pipeline.ts",
851
- line: 245,
897
+ line: 249,
852
898
  scope: this,
853
899
  callSite: (f, a) => f(...a)
854
900
  });
855
901
  await this._feedSetIterator.open();
856
902
  (0, import_log6.log)("started", {}, {
857
903
  file: "pipeline.ts",
858
- line: 247,
904
+ line: 251,
859
905
  scope: this,
860
906
  callSite: (f, a) => f(...a)
861
907
  });
@@ -863,7 +909,7 @@ var Pipeline = class {
863
909
  async stop() {
864
910
  (0, import_log6.log)("stopping...", {}, {
865
911
  file: "pipeline.ts",
866
- line: 252,
912
+ line: 256,
867
913
  scope: this,
868
914
  callSite: (f, a) => f(...a)
869
915
  });
@@ -871,7 +917,7 @@ var Pipeline = class {
871
917
  await this._processingTrigger.wait();
872
918
  (0, import_log6.log)("stopped", {}, {
873
919
  file: "pipeline.ts",
874
- line: 255,
920
+ line: 259,
875
921
  scope: this,
876
922
  callSite: (f, a) => f(...a)
877
923
  });
@@ -885,9 +931,10 @@ var Pipeline = class {
885
931
  this._isOpen = true;
886
932
  for await (const block of this._feedSetIterator) {
887
933
  this._processingTrigger.reset();
934
+ this._timeframeClock.updatePendingTimeframe(import_keys2.PublicKey.from(block.feedKey), block.seq);
888
935
  yield block;
889
936
  this._processingTrigger.wake();
890
- this._timeframeClock.updateTimeframe(import_keys2.PublicKey.from(block.feedKey), block.seq);
937
+ this._timeframeClock.updateTimeframe();
891
938
  }
892
939
  this._isOpen = false;
893
940
  }
@@ -1411,10 +1458,11 @@ var SpaceManager = class SpaceManager2 {
1411
1458
  }
1412
1459
  async open() {
1413
1460
  import_log10.log.trace("dxos.echo.space-manager", import_protocols6.trace.begin({
1414
- id: this._instanceId
1461
+ id: this._instanceId,
1462
+ parentId: this._traceParent
1415
1463
  }), {
1416
1464
  file: "space-manager.ts",
1417
- line: 65,
1465
+ line: 66,
1418
1466
  scope: this,
1419
1467
  callSite: (f, a) => f(...a)
1420
1468
  });
@@ -1427,7 +1475,7 @@ var SpaceManager = class SpaceManager2 {
1427
1475
  id: this._instanceId
1428
1476
  }), {
1429
1477
  file: "space-manager.ts",
1430
- line: 71,
1478
+ line: 72,
1431
1479
  scope: this,
1432
1480
  callSite: (f, a) => f(...a)
1433
1481
  });
@@ -1438,7 +1486,7 @@ var SpaceManager = class SpaceManager2 {
1438
1486
  spaceKey: metadata.genesisFeedKey
1439
1487
  }, {
1440
1488
  file: "space-manager.ts",
1441
- line: 75,
1489
+ line: 76,
1442
1490
  scope: this,
1443
1491
  callSite: (f, a) => f(...a)
1444
1492
  });
@@ -1503,7 +1551,8 @@ var DataPipeline = class DataPipeline2 {
1503
1551
  this._ctx = new import_context3.Context();
1504
1552
  this._lastAutomaticSnapshotTimeframe = new import_timeframe3.Timeframe();
1505
1553
  this._isOpen = false;
1506
- this.onTimeframeReached = new import_async8.Event();
1554
+ this._lastTimeframeSaveTime = 0;
1555
+ this._lastSnapshotSaveTime = 0;
1507
1556
  }
1508
1557
  get isOpen() {
1509
1558
  return this._isOpen;
@@ -1542,64 +1591,14 @@ var DataPipeline = class DataPipeline2 {
1542
1591
  const feedWriter = createMappedFeedWriter((data) => ({
1543
1592
  data
1544
1593
  }), (_c = this._pipeline.writer) != null ? _c : (0, import_debug6.failUndefined)());
1545
- this.databaseBackend = new DatabaseBackendHost(feedWriter, (_d = this._snapshot) == null ? void 0 : _d.database);
1594
+ this.databaseBackend = new DatabaseHost(feedWriter, (_d = this._snapshot) == null ? void 0 : _d.database);
1546
1595
  this._itemManager = new import_echo_db3.ItemManager(this._params.modelFactory);
1547
1596
  await this.databaseBackend.open(this._itemManager, this._params.modelFactory);
1548
1597
  (0, import_async8.scheduleTask)(this._ctx, async () => {
1549
1598
  await this._consumePipeline();
1550
1599
  });
1551
- this._createPeriodicSnapshots();
1552
1600
  this._isOpen = true;
1553
1601
  }
1554
- _createPeriodicSnapshots() {
1555
- this.onTimeframeReached.debounce(TIMEFRAME_SAVE_DEBOUNCE_INTERVAL).on(this._ctx, async () => {
1556
- await this._saveLatestTimeframe();
1557
- });
1558
- if (!DISABLE_SNAPSHOT_CACHE) {
1559
- this.onTimeframeReached.debounce(AUTOMATIC_SNAPSHOT_DEBOUNCE_INTERVAL).on(this._ctx, async () => {
1560
- var _a, _b;
1561
- const latestTimeframe = (_a = this._pipeline) == null ? void 0 : _a.state.timeframe;
1562
- if (!latestTimeframe) {
1563
- return;
1564
- }
1565
- if (latestTimeframe.totalMessages() - this._lastAutomaticSnapshotTimeframe.totalMessages() > MESSAGES_PER_SNAPSHOT) {
1566
- const snapshot = await this._saveSnapshot();
1567
- this._lastAutomaticSnapshotTimeframe = (_b = snapshot.timeframe) != null ? _b : (0, import_debug6.failUndefined)();
1568
- (0, import_log11.log)("save", {
1569
- snapshot
1570
- }, {
1571
- file: "data-pipeline.ts",
1572
- line: 161,
1573
- scope: this,
1574
- callSite: (f, a) => f(...a)
1575
- });
1576
- }
1577
- });
1578
- }
1579
- }
1580
- async _saveSnapshot() {
1581
- const snapshot = await this.createSnapshot();
1582
- const snapshotKey = await this._params.snapshotManager.store(snapshot);
1583
- await this._params.metadataStore.setSpaceSnapshot(this._params.spaceKey, snapshotKey);
1584
- return snapshot;
1585
- }
1586
- async _saveLatestTimeframe() {
1587
- var _a, _b;
1588
- const latestTimeframe = (_a = this._pipeline) == null ? void 0 : _a.state.timeframe;
1589
- (0, import_log11.log)("save latest timeframe", {
1590
- latestTimeframe,
1591
- spaceKey: this._params.spaceKey
1592
- }, {
1593
- file: "data-pipeline.ts",
1594
- line: 176,
1595
- scope: this,
1596
- callSite: (f, a) => f(...a)
1597
- });
1598
- if (latestTimeframe) {
1599
- const newTimeframe = import_timeframe3.Timeframe.merge((_b = this._targetTimeframe) != null ? _b : new import_timeframe3.Timeframe(), latestTimeframe);
1600
- await this._params.metadataStore.setSpaceLatestTimeframe(this._params.spaceKey, newTimeframe);
1601
- }
1602
- }
1603
1602
  async close() {
1604
1603
  var _a, _b, _c;
1605
1604
  if (!this._isOpen) {
@@ -1607,37 +1606,32 @@ var DataPipeline = class DataPipeline2 {
1607
1606
  }
1608
1607
  (0, import_log11.log)("close", {}, {
1609
1608
  file: "data-pipeline.ts",
1610
- line: 188,
1609
+ line: 145,
1611
1610
  scope: this,
1612
1611
  callSite: (f, a) => f(...a)
1613
1612
  });
1614
1613
  this._isOpen = false;
1614
+ await this._ctx.dispose();
1615
+ await ((_a = this._pipeline) == null ? void 0 : _a.stop());
1615
1616
  try {
1616
- await this._saveLatestTimeframe();
1617
- await this._saveSnapshot();
1617
+ if (this._pipeline) {
1618
+ await this._saveTargetTimeframe(this._pipeline.state.timeframe);
1619
+ if (!DISABLE_SNAPSHOT_CACHE) {
1620
+ await this._saveSnapshot(this._pipeline.state.timeframe);
1621
+ }
1622
+ }
1618
1623
  } catch (err) {
1619
1624
  import_log11.log.catch(err, {}, {
1620
1625
  file: "data-pipeline.ts",
1621
- line: 195,
1626
+ line: 160,
1622
1627
  scope: this,
1623
1628
  callSite: (f, a) => f(...a)
1624
1629
  });
1625
1630
  }
1626
- await this._ctx.dispose();
1627
- await ((_a = this._pipeline) == null ? void 0 : _a.stop());
1628
1631
  await ((_b = this.databaseBackend) == null ? void 0 : _b.close());
1629
1632
  await ((_c = this._itemManager) == null ? void 0 : _c.destroy());
1630
1633
  await this._params.snapshotManager.close();
1631
1634
  }
1632
- createSnapshot() {
1633
- var _a, _b;
1634
- (0, import_node_assert9.default)(this.databaseBackend, "Database backend is not initialized.");
1635
- return {
1636
- spaceKey: this._params.spaceKey.asUint8Array(),
1637
- timeframe: (_b = (_a = this._pipeline) == null ? void 0 : _a.state.timeframe) != null ? _b : new import_timeframe3.Timeframe(),
1638
- database: this.databaseBackend.createSnapshot()
1639
- };
1640
- }
1641
1635
  async _consumePipeline() {
1642
1636
  (0, import_node_assert9.default)(this._pipeline, "Pipeline is not initialized.");
1643
1637
  for await (const msg of this._pipeline.consume()) {
@@ -1646,7 +1640,7 @@ var DataPipeline = class DataPipeline2 {
1646
1640
  msg
1647
1641
  }, {
1648
1642
  file: "data-pipeline.ts",
1649
- line: 217,
1643
+ line: 172,
1650
1644
  scope: this,
1651
1645
  callSite: (f, a) => f(...a)
1652
1646
  });
@@ -1658,7 +1652,7 @@ var DataPipeline = class DataPipeline2 {
1658
1652
  feedKey
1659
1653
  }, {
1660
1654
  file: "data-pipeline.ts",
1661
- line: 223,
1655
+ line: 178,
1662
1656
  scope: this,
1663
1657
  callSite: (f, a) => f(...a)
1664
1658
  });
@@ -1673,18 +1667,58 @@ var DataPipeline = class DataPipeline2 {
1673
1667
  memberKey: feedInfo.assertion.identityKey
1674
1668
  }
1675
1669
  });
1676
- this.onTimeframeReached.emit(data.timeframe);
1670
+ await this._noteTargetStateIfNeeded(this._pipeline.state.pendingTimeframe);
1677
1671
  }
1678
1672
  } catch (err) {
1679
1673
  import_log11.log.catch(err, {}, {
1680
1674
  file: "data-pipeline.ts",
1681
- line: 239,
1675
+ line: 196,
1682
1676
  scope: this,
1683
1677
  callSite: (f, a) => f(...a)
1684
1678
  });
1685
1679
  }
1686
1680
  }
1687
1681
  }
1682
+ _createSnapshot(timeframe) {
1683
+ (0, import_node_assert9.default)(this.databaseBackend, "Database backend is not initialized.");
1684
+ return {
1685
+ spaceKey: this._params.spaceKey.asUint8Array(),
1686
+ timeframe,
1687
+ database: this.databaseBackend.createSnapshot()
1688
+ };
1689
+ }
1690
+ async _saveSnapshot(timeframe) {
1691
+ const snapshot = await this._createSnapshot(timeframe);
1692
+ const snapshotKey = await this._params.snapshotManager.store(snapshot);
1693
+ await this._params.metadataStore.setSpaceSnapshot(this._params.spaceKey, snapshotKey);
1694
+ return snapshot;
1695
+ }
1696
+ async _saveTargetTimeframe(timeframe) {
1697
+ var _a;
1698
+ const newTimeframe = import_timeframe3.Timeframe.merge((_a = this._targetTimeframe) != null ? _a : new import_timeframe3.Timeframe(), timeframe);
1699
+ await this._params.metadataStore.setSpaceLatestTimeframe(this._params.spaceKey, newTimeframe);
1700
+ this._targetTimeframe = newTimeframe;
1701
+ }
1702
+ async _noteTargetStateIfNeeded(timeframe) {
1703
+ var _a;
1704
+ if (Date.now() - this._lastTimeframeSaveTime > TIMEFRAME_SAVE_DEBOUNCE_INTERVAL) {
1705
+ this._lastTimeframeSaveTime = Date.now();
1706
+ await this._saveTargetTimeframe(timeframe);
1707
+ }
1708
+ if (!DISABLE_SNAPSHOT_CACHE && Date.now() - this._lastSnapshotSaveTime > AUTOMATIC_SNAPSHOT_DEBOUNCE_INTERVAL && timeframe.totalMessages() - this._lastAutomaticSnapshotTimeframe.totalMessages() > MESSAGES_PER_SNAPSHOT) {
1709
+ this._lastSnapshotSaveTime = Date.now();
1710
+ const snapshot = await this._saveSnapshot(timeframe);
1711
+ this._lastAutomaticSnapshotTimeframe = (_a = snapshot.timeframe) != null ? _a : (0, import_debug6.failUndefined)();
1712
+ (0, import_log11.log)("save", {
1713
+ snapshot
1714
+ }, {
1715
+ file: "data-pipeline.ts",
1716
+ line: 239,
1717
+ scope: this,
1718
+ callSite: (f, a) => f(...a)
1719
+ });
1720
+ }
1721
+ }
1688
1722
  async waitUntilTimeframe(timeframe) {
1689
1723
  (0, import_node_assert9.default)(this._pipeline, "Pipeline is not initialized.");
1690
1724
  await this._pipeline.state.waitUntilTimeframe(timeframe);
@@ -1870,7 +1904,7 @@ var import_model_factory2 = require("@dxos/model-factory");
1870
1904
  var import_timeframe4 = require("@dxos/timeframe");
1871
1905
  var createMemoryDatabase = async (modelFactory) => {
1872
1906
  const feed = new import_testing2.MockFeedWriter();
1873
- const backend = new DatabaseBackendHost(feed, void 0);
1907
+ const backend = new DatabaseHost(feed, void 0);
1874
1908
  feed.written.on(([data, meta]) => backend.echoProcessor({
1875
1909
  batch: data.batch,
1876
1910
  meta: {
@@ -1896,7 +1930,7 @@ var createRemoteDatabaseFromDataServiceHost = async (modelFactory, dataServiceHo
1896
1930
  const dataService = new DataServiceImpl(dataServiceSubscriptions);
1897
1931
  const spaceKey = import_keys7.PublicKey.random();
1898
1932
  dataServiceSubscriptions.registerSpace(spaceKey, dataServiceHost);
1899
- const backend = new import_echo_db4.DatabaseBackendProxy(dataService, spaceKey);
1933
+ const backend = new import_echo_db4.DatabaseProxy(dataService, spaceKey);
1900
1934
  const itemManager = new import_echo_db4.ItemManager(modelFactory);
1901
1935
  await backend.open(itemManager, new import_model_factory2.ModelFactory().registerModel(import_document_model2.DocumentModel));
1902
1936
  return {