@rocicorp/zero 1.11.0-canary.21 → 1.11.0-canary.23

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 (109) hide show
  1. package/out/zero/package.js +1 -1
  2. package/out/zero/package.js.map +1 -1
  3. package/out/zero-cache/src/config/normalize.d.ts.map +1 -1
  4. package/out/zero-cache/src/config/normalize.js +1 -2
  5. package/out/zero-cache/src/config/normalize.js.map +1 -1
  6. package/out/zero-cache/src/config/zero-config.d.ts +0 -22
  7. package/out/zero-cache/src/config/zero-config.d.ts.map +1 -1
  8. package/out/zero-cache/src/config/zero-config.js +38 -42
  9. package/out/zero-cache/src/config/zero-config.js.map +1 -1
  10. package/out/zero-cache/src/server/change-streamer.d.ts.map +1 -1
  11. package/out/zero-cache/src/server/change-streamer.js +90 -73
  12. package/out/zero-cache/src/server/change-streamer.js.map +1 -1
  13. package/out/zero-cache/src/services/change-source/common/change-stream-multiplexer.d.ts.map +1 -1
  14. package/out/zero-cache/src/services/change-source/common/change-stream-multiplexer.js +1 -1
  15. package/out/zero-cache/src/services/change-source/common/change-stream-multiplexer.js.map +1 -1
  16. package/out/zero-cache/src/services/change-source/common/init-cleanup.d.ts +24 -0
  17. package/out/zero-cache/src/services/change-source/common/init-cleanup.d.ts.map +1 -0
  18. package/out/zero-cache/src/services/change-source/common/init-cleanup.js +44 -0
  19. package/out/zero-cache/src/services/change-source/common/init-cleanup.js.map +1 -0
  20. package/out/zero-cache/src/services/change-source/common/replica-restore.d.ts +49 -1
  21. package/out/zero-cache/src/services/change-source/common/replica-restore.d.ts.map +1 -1
  22. package/out/zero-cache/src/services/change-source/common/replica-restore.js.map +1 -1
  23. package/out/zero-cache/src/services/change-source/custom/change-source.d.ts +1 -1
  24. package/out/zero-cache/src/services/change-source/custom/change-source.d.ts.map +1 -1
  25. package/out/zero-cache/src/services/change-source/custom/change-source.js +7 -3
  26. package/out/zero-cache/src/services/change-source/custom/change-source.js.map +1 -1
  27. package/out/zero-cache/src/services/change-source/pg/change-source-init.d.ts +10 -5
  28. package/out/zero-cache/src/services/change-source/pg/change-source-init.d.ts.map +1 -1
  29. package/out/zero-cache/src/services/change-source/pg/change-source-init.js +65 -21
  30. package/out/zero-cache/src/services/change-source/pg/change-source-init.js.map +1 -1
  31. package/out/zero-cache/src/services/change-source/pg/change-source.d.ts.map +1 -1
  32. package/out/zero-cache/src/services/change-source/pg/change-source.js.map +1 -1
  33. package/out/zero-cache/src/services/change-source/pg/initial-sync.d.ts +2 -1
  34. package/out/zero-cache/src/services/change-source/pg/initial-sync.d.ts.map +1 -1
  35. package/out/zero-cache/src/services/change-source/pg/initial-sync.js +3 -1
  36. package/out/zero-cache/src/services/change-source/pg/initial-sync.js.map +1 -1
  37. package/out/zero-cache/src/services/change-source/pg/logical-replication/stream.d.ts +31 -0
  38. package/out/zero-cache/src/services/change-source/pg/logical-replication/stream.d.ts.map +1 -1
  39. package/out/zero-cache/src/services/change-source/pg/logical-replication/stream.js +100 -2
  40. package/out/zero-cache/src/services/change-source/pg/logical-replication/stream.js.map +1 -1
  41. package/out/zero-cache/src/services/change-streamer/backfill-state-tracker.d.ts +6 -0
  42. package/out/zero-cache/src/services/change-streamer/backfill-state-tracker.d.ts.map +1 -1
  43. package/out/zero-cache/src/services/change-streamer/backfill-state-tracker.js +8 -0
  44. package/out/zero-cache/src/services/change-streamer/backfill-state-tracker.js.map +1 -1
  45. package/out/zero-cache/src/services/change-streamer/backup-cleanup-monitor-factory.d.ts.map +1 -1
  46. package/out/zero-cache/src/services/change-streamer/backup-cleanup-monitor-factory.js +3 -16
  47. package/out/zero-cache/src/services/change-streamer/backup-cleanup-monitor-factory.js.map +1 -1
  48. package/out/zero-cache/src/services/change-streamer/change-log-initializer.d.ts +17 -0
  49. package/out/zero-cache/src/services/change-streamer/change-log-initializer.d.ts.map +1 -1
  50. package/out/zero-cache/src/services/change-streamer/change-log-initializer.js +44 -1
  51. package/out/zero-cache/src/services/change-streamer/change-log-initializer.js.map +1 -1
  52. package/out/zero-cache/src/services/change-streamer/change-streamer-service.d.ts +7 -0
  53. package/out/zero-cache/src/services/change-streamer/change-streamer-service.d.ts.map +1 -1
  54. package/out/zero-cache/src/services/change-streamer/change-streamer-service.js +27 -8
  55. package/out/zero-cache/src/services/change-streamer/change-streamer-service.js.map +1 -1
  56. package/out/zero-cache/src/services/change-streamer/schema/tables.d.ts +57 -0
  57. package/out/zero-cache/src/services/change-streamer/schema/tables.d.ts.map +1 -1
  58. package/out/zero-cache/src/services/change-streamer/schema/tables.js +103 -23
  59. package/out/zero-cache/src/services/change-streamer/schema/tables.js.map +1 -1
  60. package/out/zero-cache/src/services/change-streamer/storer.d.ts +86 -1
  61. package/out/zero-cache/src/services/change-streamer/storer.d.ts.map +1 -1
  62. package/out/zero-cache/src/services/change-streamer/storer.js +272 -77
  63. package/out/zero-cache/src/services/change-streamer/storer.js.map +1 -1
  64. package/out/zero-cache/src/services/change-streamer/upstream-acker.d.ts +7 -5
  65. package/out/zero-cache/src/services/change-streamer/upstream-acker.d.ts.map +1 -1
  66. package/out/zero-cache/src/services/change-streamer/upstream-acker.js +9 -5
  67. package/out/zero-cache/src/services/change-streamer/upstream-acker.js.map +1 -1
  68. package/out/zero-cache/src/services/litestream/commands.d.ts +3 -11
  69. package/out/zero-cache/src/services/litestream/commands.d.ts.map +1 -1
  70. package/out/zero-cache/src/services/litestream/commands.js +29 -23
  71. package/out/zero-cache/src/services/litestream/commands.js.map +1 -1
  72. package/out/zero-cache/src/services/litestream/config-v5.yml +0 -7
  73. package/out/zero-cache/src/services/profz.d.ts.map +1 -1
  74. package/out/zero-cache/src/services/profz.js +19 -1
  75. package/out/zero-cache/src/services/profz.js.map +1 -1
  76. package/out/zero-cache/src/services/replicator/replication-status.js +1 -1
  77. package/out/zero-cache/src/services/replicator/replication-status.js.map +1 -1
  78. package/out/zero-cache/src/services/view-syncer/advancement-metrics.d.ts +26 -0
  79. package/out/zero-cache/src/services/view-syncer/advancement-metrics.d.ts.map +1 -0
  80. package/out/zero-cache/src/services/view-syncer/advancement-metrics.js +26 -0
  81. package/out/zero-cache/src/services/view-syncer/advancement-metrics.js.map +1 -0
  82. package/out/zero-cache/src/services/view-syncer/pipeline-driver.d.ts +10 -2
  83. package/out/zero-cache/src/services/view-syncer/pipeline-driver.d.ts.map +1 -1
  84. package/out/zero-cache/src/services/view-syncer/pipeline-driver.js +102 -122
  85. package/out/zero-cache/src/services/view-syncer/pipeline-driver.js.map +1 -1
  86. package/out/zero-cache/src/services/view-syncer/snapshotter.d.ts +8 -9
  87. package/out/zero-cache/src/services/view-syncer/snapshotter.d.ts.map +1 -1
  88. package/out/zero-cache/src/services/view-syncer/snapshotter.js +15 -3
  89. package/out/zero-cache/src/services/view-syncer/snapshotter.js.map +1 -1
  90. package/out/zero-cache/src/services/view-syncer/view-syncer.d.ts.map +1 -1
  91. package/out/zero-cache/src/services/view-syncer/view-syncer.js +50 -28
  92. package/out/zero-cache/src/services/view-syncer/view-syncer.js.map +1 -1
  93. package/out/zero-client/src/client/version.js +1 -1
  94. package/out/zero-client/src/client/zero.d.ts.map +1 -1
  95. package/out/zero-client/src/client/zero.js +2 -1
  96. package/out/zero-client/src/client/zero.js.map +1 -1
  97. package/out/zero-react/src/use-query.js +12 -3
  98. package/out/zero-react/src/use-query.js.map +1 -1
  99. package/out/zql/src/ivm/array-view.d.ts.map +1 -1
  100. package/out/zql/src/ivm/array-view.js +21 -12
  101. package/out/zql/src/ivm/array-view.js.map +1 -1
  102. package/out/zqlite/src/db.d.ts.map +1 -1
  103. package/out/zqlite/src/db.js +34 -6
  104. package/out/zqlite/src/db.js.map +1 -1
  105. package/package.json +1 -1
  106. package/out/zero-cache/src/services/change-streamer/vfs-watermark-poller.d.ts +0 -34
  107. package/out/zero-cache/src/services/change-streamer/vfs-watermark-poller.d.ts.map +0 -1
  108. package/out/zero-cache/src/services/change-streamer/vfs-watermark-poller.js +0 -184
  109. package/out/zero-cache/src/services/change-streamer/vfs-watermark-poller.js.map +0 -1
@@ -2,6 +2,7 @@ import { assert, unreachable } from "../../../../shared/src/asserts.js";
2
2
  import { deepEqual } from "../../../../shared/src/json.js";
3
3
  import { getOrInsertComputed } from "../../../../shared/src/map.js";
4
4
  import { must } from "../../../../shared/src/must.js";
5
+ import { assignProperty } from "../../../../shared/src/objects.js";
5
6
  import { skipYields } from "../../../../zql/src/ivm/skip-yields.js";
6
7
  import { throwOutput } from "../../../../zql/src/ivm/operator.js";
7
8
  import { MeasurePushOperator } from "../../../../zql/src/query/measure-push-operator.js";
@@ -92,6 +93,8 @@ var PipelineDriver = class {
92
93
  #planWarningThresholds;
93
94
  #yieldThresholdMs;
94
95
  #streamer = null;
96
+ /** The row changes of the push in progress, until #push yields them. */
97
+ #pushedRowChanges = null;
95
98
  #hydrateContext = null;
96
99
  #advanceContext = null;
97
100
  #replicaVersion = null;
@@ -172,6 +175,7 @@ var PipelineDriver = class {
172
175
  primaryKeys.clear();
173
176
  for (const [table, spec] of this.#tableSpecs.entries()) primaryKeys.set(table, spec.tableSpec.primaryKey);
174
177
  buildPrimaryKeys(clientSchema, primaryKeys);
178
+ this.#streamer ??= new Streamer(primaryKeys, this.#tableSpecs);
175
179
  const { replicaVersion } = getSubscriptionState(db);
176
180
  this.#replicaVersion = replicaVersion;
177
181
  }
@@ -446,7 +450,7 @@ var PipelineDriver = class {
446
450
  }, planWarnings);
447
451
  const schema = input.getSchema();
448
452
  input.setOutput({ push: (change) => this.#streamPushed(queryID, schema, change) });
449
- for (const change of hydrateInternal(input, queryID, must(this.#primaryKeys), this.#tableSpecs)) {
453
+ for (const change of must(this.#streamer).streamNodes(queryID, schema, 0, input.fetch({}))) {
450
454
  if (change !== "yield") hydrationRowCount++;
451
455
  yield change;
452
456
  }
@@ -675,17 +679,26 @@ var PipelineDriver = class {
675
679
  const diff = this.#snapshotter.advance(this.#tableSpecs, this.#allTableNames, this.#tables, "divergent");
676
680
  const { prev, curr, changes } = diff;
677
681
  this.#lc.debug?.(`advance ${prev.version} => ${curr.version}: ${changes} changes`);
682
+ const progress = { changesProcessed: 0 };
678
683
  return {
679
684
  version: curr.version,
680
685
  numChanges: changes,
681
- changes: this.#trackRowSetSignatures(this.#advance(diff, timer, changes))
686
+ changes: this.#trackRowSetSignatures(this.#advance(diff, timer, changes, progress)),
687
+ progress: () => ({
688
+ changesScanned: diff.changesScanned,
689
+ changesProcessed: progress.changesProcessed,
690
+ changesSkipped: diff.changesSkipped
691
+ })
682
692
  };
683
693
  }
684
- *#advance(diff, timer, numChanges) {
694
+ *#advance(diff, timer, numChanges, progress) {
685
695
  assert(this.#hydrateContext === null, "Cannot advance while hydration is in progress");
686
696
  const totalHydrationTimeMs = this.totalHydrationTimeMs();
687
697
  const advanceContext = {
688
698
  timer,
699
+ progress,
700
+ diff,
701
+ completedChanges: 0,
689
702
  totalHydrationTimeMs,
690
703
  numChanges,
691
704
  reservedRows: void 0,
@@ -704,15 +717,20 @@ var PipelineDriver = class {
704
717
  for (const table of this.#tables.values()) table.setDeferWrites(deferWrites);
705
718
  this.#lc.debug?.(`starting pipeline advancement of ${numChanges} changes with an advancement time limited based on total hydration time of ${totalHydrationTimeMs} ms (${deferWrites ? "deferred" : "write-through"}).`);
706
719
  for (const { table, prevValues: probedPrevValues, nextValue, rowKey } of diff) {
720
+ advanceContext.completedChanges = diff.changesScanned - 1;
707
721
  if (this.#shouldAdvanceYieldMaybeAbortAdvance()) yield "yield";
708
722
  const start = timer.totalElapsed();
709
723
  advanceContext.currentChangeStartMs = start;
710
724
  advanceContext.currentTable = table;
711
725
  let holds = "fits";
726
+ let pushCompleted = false;
712
727
  try {
713
728
  try {
714
729
  const tableSource = this.#tables.get(table);
715
- if (!tableSource) continue;
730
+ if (!tableSource) {
731
+ pushCompleted = true;
732
+ continue;
733
+ }
716
734
  const primaryKey = mustGetPrimaryKey(this.#primaryKeys, table);
717
735
  if (nextValue !== null && !this.#reserveSecondRow(advanceContext, rowKey, primaryKey)) holds = "rows";
718
736
  const prevValues = tableSource.reconcilePendingConflicts(probedPrevValues, nextValue, rowKey, this.#tableSpecs.get(table)?.tableSpec.uniqueKeys ?? []);
@@ -724,8 +742,13 @@ var PipelineDriver = class {
724
742
  }
725
743
  if (nextValue) if (editOldRow) yield* this.#push(tableSource, makeSourceChangeEdit(nextValue, editOldRow));
726
744
  else yield* this.#push(tableSource, makeSourceChangeAdd(nextValue));
745
+ pushCompleted = true;
727
746
  } finally {
728
747
  advanceContext.pos++;
748
+ if (pushCompleted) {
749
+ progress.changesProcessed++;
750
+ advanceContext.completedChanges = diff.changesScanned;
751
+ }
729
752
  }
730
753
  this.#shouldAdvanceYieldMaybeAbortAdvance(false);
731
754
  if (holds === "fits") holds = this.#holdPendingRows(advanceContext);
@@ -736,6 +759,7 @@ var PipelineDriver = class {
736
759
  this.#advanceTime.recordMs(elapsed, { table });
737
760
  if (holds !== "fits") yield* this.#writeThrough(advanceContext, diff, holds);
738
761
  }
762
+ advanceContext.completedChanges = diff.changesScanned;
739
763
  const { curr } = diff;
740
764
  for (const table of this.#tables.values()) table.setDB(curr.db.db);
741
765
  this.#ensureCostModelExistsIfEnabled(curr.db.db);
@@ -854,13 +878,31 @@ var PipelineDriver = class {
854
878
  }
855
879
  };
856
880
  }
881
+ /** Compute once for an admitted log, without clocks or allocations per change. */
882
+ #advancementLogFields({ timer, diff, progress, completedChanges, queryStats, totalHydrationTimeMs }) {
883
+ const advancementTimeMs = timer.totalElapsed();
884
+ let queryPushTimeMs = 0;
885
+ for (const stats of queryStats.values()) queryPushTimeMs += stats.timeMs;
886
+ return {
887
+ advancementTimeMs,
888
+ queryPushTimeMs,
889
+ outsideQueryPushTimeMs: Math.max(0, advancementTimeMs - queryPushTimeMs),
890
+ totalHydrationTimeMs,
891
+ changesScanned: diff.changesScanned,
892
+ changesCompleted: completedChanges,
893
+ changesProcessed: progress.changesProcessed,
894
+ changesSkipped: diff.changesSkipped
895
+ };
896
+ }
857
897
  /**
858
898
  * Logs each query whose pushes took longer than the slow advance
859
899
  * threshold, at most once per query shape per
860
900
  * {@link SLOW_ADVANCE_LOG_WINDOW_MS}.
861
901
  */
862
- #logSlowAdvances({ queryStats, numChanges }) {
902
+ #logSlowAdvances(context) {
863
903
  if (!this.#lc.warn) return;
904
+ const { queryStats, numChanges } = context;
905
+ let advancementFields;
864
906
  for (const [queryID, stats] of queryStats) {
865
907
  if (stats.timeMs <= this.#logConfig.slowAdvanceThreshold) continue;
866
908
  const query = this.#queryForLog(queryID);
@@ -873,6 +915,7 @@ var PipelineDriver = class {
873
915
  advanceTimeMs: stats.timeMs,
874
916
  changes: stats.changes,
875
917
  advancementChanges: numChanges,
918
+ ...advancementFields ??= this.#advancementLogFields(context),
876
919
  timeMsByTable: Object.fromEntries(stats.timeMsByTable),
877
920
  ...suppressed > 0 && { suppressedSinceLastLog: suppressed },
878
921
  zql: query.shape.zql
@@ -882,10 +925,12 @@ var PipelineDriver = class {
882
925
  /**
883
926
  * Logs the queries that took the most time in an advancement that timed
884
927
  * out, since the reset that follows would otherwise not say what was slow.
885
- * Throttled per query shape of the slowest query.
928
+ * Throttled per query shape of the slowest query, with a shared fallback
929
+ * when the timeout happens before any query push timings are recorded.
886
930
  */
887
- #logAdvanceTimeout({ queryStats, numChanges }, reset) {
931
+ #logAdvanceTimeout(context, reset) {
888
932
  if (!this.#lc.warn) return;
933
+ const { queryStats, numChanges } = context;
889
934
  const slowest = [...queryStats].toSorted(([, a], [, b]) => b.timeMs - a.timeMs).slice(0, ADVANCE_TIMEOUT_LOG_MAX_QUERIES).flatMap(([queryID, stats]) => {
890
935
  const query = this.#queryForLog(queryID);
891
936
  return query ? [{
@@ -893,14 +938,15 @@ var PipelineDriver = class {
893
938
  stats
894
939
  }] : [];
895
940
  });
896
- if (slowest.length === 0) return;
897
- const suppressed = slowAdvanceLogThrottle.admit(`timeout:${slowest[0].query.queryName ?? ""}:${slowest[0].query.shape.hash}`);
941
+ const leadingQuery = slowest[0]?.query;
942
+ const suppressed = slowAdvanceLogThrottle.admit(leadingQuery ? `timeout:${leadingQuery.queryName ?? ""}:${leadingQuery.shape.hash}` : "timeout:outside-query-push");
898
943
  if (suppressed === void 0) return;
899
944
  const summary = slowest.map(({ query, stats }) => `${query.queryName ?? query.fields.queryHash} (${Math.round(stats.timeMs)} ms)`).join(", ");
900
- this.#lc.warn(`Advancement of ${numChanges} changes timed out. The queries that took the most time: ${summary}`, {
945
+ this.#lc.warn(`Advancement of ${numChanges} changes timed out. ` + (summary ? `The queries that took the most time: ${summary}` : "No query push timings were recorded."), {
901
946
  zeroEvent: "query-advance-timeout",
902
947
  reason: reset.message,
903
948
  advancementChanges: numChanges,
949
+ ...this.#advancementLogFields(context),
904
950
  queries: slowest.map(({ query, stats }) => ({
905
951
  ...query.fields,
906
952
  advanceTimeMs: stats.timeMs,
@@ -933,14 +979,14 @@ var PipelineDriver = class {
933
979
  * checks; a single pathological push always resets.
934
980
  */
935
981
  #shouldAdvanceYieldMaybeAbortAdvance(checkYield = true) {
936
- const { currentChangeStartMs, pos, numChanges, timer: advanceTimer, totalHydrationTimeMs } = must(this.#advanceContext);
982
+ const { currentChangeStartMs, completedChanges, numChanges, timer: advanceTimer, totalHydrationTimeMs } = must(this.#advanceContext);
937
983
  const elapsed = advanceTimer.totalElapsed();
938
984
  const currentChangeElapsedMs = currentChangeStartMs === void 0 ? void 0 : elapsed - currentChangeStartMs;
939
- if (currentChangeElapsedMs !== void 0 && shouldResetSlowCurrentChange(currentChangeElapsedMs, totalHydrationTimeMs)) this.#throwSlowCurrentChangeReset(pos, numChanges, elapsed, currentChangeElapsedMs, totalHydrationTimeMs);
940
- const projectedTotalTimeMs = projectedAdvancementTimeMs(elapsed, pos, numChanges);
941
- const shouldFinish = shouldFinishLateAdvancement(pos, numChanges);
942
- if (!shouldFinish && shouldResetProjectedAdvancement(elapsed, projectedTotalTimeMs, pos, numChanges, totalHydrationTimeMs)) this.#throwProjectedAdvancementReset(pos, numChanges, elapsed, projectedTotalTimeMs, totalHydrationTimeMs);
943
- if (!shouldFinish && elapsed > MIN_ADVANCEMENT_TIME_LIMIT_MS && elapsed > totalHydrationTimeMs / 2 && (projectedTotalTimeMs === void 0 || projectedTotalTimeMs - elapsed > totalHydrationTimeMs)) throw new ResetPipelinesSignal(`Advancement exceeded timeout at ${pos} of ${numChanges} changes after ${elapsed} ms. Advancement time limited based on total hydration time of ${totalHydrationTimeMs} ms.`, "advancement-timeout");
985
+ if (currentChangeElapsedMs !== void 0 && shouldResetSlowCurrentChange(currentChangeElapsedMs, totalHydrationTimeMs)) this.#throwSlowCurrentChangeReset(completedChanges, numChanges, elapsed, currentChangeElapsedMs, totalHydrationTimeMs);
986
+ const projectedTotalTimeMs = projectedAdvancementTimeMs(elapsed, completedChanges, numChanges);
987
+ const shouldFinish = shouldFinishLateAdvancement(completedChanges, numChanges);
988
+ if (!shouldFinish && shouldResetProjectedAdvancement(elapsed, projectedTotalTimeMs, completedChanges, numChanges, totalHydrationTimeMs)) this.#throwProjectedAdvancementReset(completedChanges, numChanges, elapsed, projectedTotalTimeMs, totalHydrationTimeMs);
989
+ if (!shouldFinish && elapsed > MIN_ADVANCEMENT_TIME_LIMIT_MS && elapsed > totalHydrationTimeMs / 2 && (projectedTotalTimeMs === void 0 || projectedTotalTimeMs - elapsed > totalHydrationTimeMs)) throw new ResetPipelinesSignal(`Advancement exceeded timeout at ${completedChanges} of ${numChanges} changes after ${elapsed} ms. Advancement time limited based on total hydration time of ${totalHydrationTimeMs} ms.`, "advancement-timeout");
944
990
  return checkYield && advanceTimer.elapsedLap() > this.#yieldThresholdMs();
945
991
  }
946
992
  /**
@@ -977,15 +1023,17 @@ var PipelineDriver = class {
977
1023
  return this.#storage.createStorage();
978
1024
  }
979
1025
  *#push(source, change) {
980
- this.#startAccumulating();
1026
+ assert(this.#pushedRowChanges === null, "push already in progress");
981
1027
  try {
1028
+ this.#pushedRowChanges = [];
982
1029
  for (const val of source.genPush(change)) {
983
1030
  if (val === "yield") yield "yield";
984
- for (const changeOrYield of this.#stopAccumulating().stream()) yield changeOrYield;
985
- this.#startAccumulating();
1031
+ const rowChanges = this.#pushedRowChanges;
1032
+ this.#pushedRowChanges = [];
1033
+ yield* rowChanges;
986
1034
  }
987
1035
  } finally {
988
- if (this.#streamer !== null) this.#stopAccumulating();
1036
+ this.#pushedRowChanges = null;
989
1037
  }
990
1038
  }
991
1039
  /**
@@ -998,106 +1046,54 @@ var PipelineDriver = class {
998
1046
  * rows that are also pushed separately are counted twice.
999
1047
  */
1000
1048
  *#streamPushed(queryID, schema, change) {
1001
- const streamer = this.#streamer;
1002
- assert(streamer, "must #startAccumulating() before pushing changes");
1003
- for (const rowChange of streamer.streamChange(queryID, schema, change)) {
1049
+ assert(this.#pushedRowChanges, "changes are only pushed by #push");
1050
+ for (const rowChange of must(this.#streamer).streamChange(queryID, schema, change)) {
1004
1051
  if (rowChange === "yield") {
1005
1052
  yield rowChange;
1006
1053
  continue;
1007
1054
  }
1008
- must(this.#streamer).add(rowChange);
1055
+ must(this.#pushedRowChanges).push(rowChange);
1009
1056
  }
1010
1057
  }
1011
- #startAccumulating() {
1012
- assert(this.#streamer === null, "Streamer already started");
1013
- this.#streamer = new Streamer(must(this.#primaryKeys), this.#tableSpecs, (queryID, error) => this.#logQueryFailure(queryID, "query pipeline failed", error));
1014
- }
1015
- #stopAccumulating() {
1016
- const streamer = this.#streamer;
1017
- assert(streamer, "Streamer not started");
1018
- this.#streamer = null;
1019
- return streamer;
1020
- }
1021
- #logQueryFailure(queryID, message, error) {
1022
- const pipeline = this.#pipelines.get(queryID);
1023
- const queryInfo = pipeline ? {
1024
- queryHash: queryID,
1025
- transformationHash: pipeline.transformationHash,
1026
- queryName: pipeline.queryName
1027
- } : void 0;
1028
- logQueryFailure(this.#lc, queryInfo, message, error);
1029
- }
1030
1058
  };
1059
+ /** Turns the output of query pipelines into the row changes sent to clients. */
1031
1060
  var Streamer = class {
1032
1061
  #primaryKeys;
1033
1062
  #tableSpecs;
1034
- #logQueryFailure;
1035
- constructor(primaryKeys, tableSpecs, logQueryFailure) {
1063
+ constructor(primaryKeys, tableSpecs) {
1036
1064
  this.#primaryKeys = primaryKeys;
1037
1065
  this.#tableSpecs = tableSpecs;
1038
- this.#logQueryFailure = logQueryFailure;
1039
- }
1040
- #changes = [];
1041
- /** Row changes that were already produced by {@link streamChange}. */
1042
- #rowChanges = [];
1043
- add(rowChange) {
1044
- this.#rowChanges.push(rowChange);
1045
- }
1046
- streamChange(queryID, schema, change) {
1047
- return this.#streamChanges(queryID, schema, [change]);
1048
- }
1049
- accumulate(queryID, schema, changes) {
1050
- this.#changes.push([
1051
- queryID,
1052
- schema,
1053
- changes
1054
- ]);
1055
- return this;
1056
1066
  }
1057
- *stream() {
1058
- yield* this.#rowChanges;
1059
- for (const [queryID, schema, changes] of this.#changes) try {
1060
- yield* this.#streamChanges(queryID, schema, changes);
1061
- } catch (e) {
1062
- this.#logQueryFailure?.(queryID, e);
1063
- throw e;
1064
- }
1065
- }
1066
- *#streamChanges(queryID, schema, changes) {
1067
+ /** The row changes for a change pushed out of the pipeline of `queryID`. */
1068
+ *streamChange(queryID, schema, change) {
1067
1069
  if (schema.system === "permissions") return;
1068
- for (const change of changes) {
1069
- if (change === "yield") {
1070
- yield change;
1071
- continue;
1072
- }
1073
- const type = change[0];
1074
- switch (type) {
1075
- case 1:
1076
- case 0:
1077
- yield* this.#streamNodes(queryID, schema, type, () => [change[1]]);
1078
- break;
1079
- case 3: {
1080
- const child = change[2];
1081
- const childSchema = must(schema.relationships[child.relationshipName]);
1082
- yield* this.#streamChanges(queryID, childSchema, [child.change]);
1083
- break;
1084
- }
1085
- case 2:
1086
- yield* this.#streamNodes(queryID, schema, type, () => [{
1087
- row: change[1].row,
1088
- relationships: {}
1089
- }]);
1090
- break;
1091
- default: unreachable(change[0]);
1070
+ const type = change[0];
1071
+ switch (type) {
1072
+ case 1:
1073
+ case 0:
1074
+ yield* this.streamNodes(queryID, schema, type, [change[1]]);
1075
+ break;
1076
+ case 3: {
1077
+ const child = change[2];
1078
+ yield* this.streamChange(queryID, must(schema.relationships[child.relationshipName]), child.change);
1079
+ break;
1092
1080
  }
1081
+ case 2:
1082
+ yield* this.streamNodes(queryID, schema, type, [{
1083
+ row: change[1].row,
1084
+ relationships: {}
1085
+ }]);
1086
+ break;
1087
+ default: unreachable(change[0]);
1093
1088
  }
1094
1089
  }
1095
- *#streamNodes(queryID, schema, op, nodes) {
1090
+ /** A row change of type `op` for each of `nodes` and their descendants. */
1091
+ *streamNodes(queryID, schema, op, nodes) {
1096
1092
  const { tableName: table, system } = schema;
1093
+ if (system === "permissions") return;
1097
1094
  const primaryKey = must(this.#primaryKeys.get(table));
1098
1095
  const spec = must(this.#tableSpecs.get(table)).tableSpec;
1099
- if (system === "permissions") return;
1100
- for (const node of nodes()) {
1096
+ for (const node of nodes) {
1101
1097
  if (node === "yield") {
1102
1098
  yield node;
1103
1099
  continue;
@@ -1119,9 +1115,9 @@ var Streamer = class {
1119
1115
  rowKey,
1120
1116
  row: op === 1 ? void 0 : row
1121
1117
  };
1122
- for (const [relationship, children] of Object.entries(relationships)) {
1118
+ for (const relationship of Object.keys(relationships)) {
1123
1119
  const childSchema = must(schema.relationships[relationship]);
1124
- yield* this.#streamNodes(queryID, childSchema, op, children);
1120
+ if (childSchema.system !== "permissions") yield* this.streamNodes(queryID, childSchema, op, relationships[relationship]());
1125
1121
  }
1126
1122
  }
1127
1123
  }
@@ -1187,21 +1183,10 @@ function logQueryFailure(lc, queryInfo, message, error) {
1187
1183
  }
1188
1184
  queryLC.error?.(message, error);
1189
1185
  }
1190
- function* toAdds(nodes) {
1191
- for (const node of nodes) {
1192
- if (node === "yield") {
1193
- yield node;
1194
- continue;
1195
- }
1196
- yield [
1197
- 0,
1198
- node,
1199
- null
1200
- ];
1201
- }
1202
- }
1203
1186
  function getRowKey(cols, row) {
1204
- return Object.fromEntries(cols.map((col) => [col, must(row[col])]));
1187
+ const rowKey = {};
1188
+ for (const col of cols) assignProperty(rowKey, col, must(row[col]));
1189
+ return rowKey;
1205
1190
  }
1206
1191
  /** Whether `rowKey` has exactly the columns of `primaryKey`. */
1207
1192
  function isKeyedBy(rowKey, primaryKey) {
@@ -1218,12 +1203,7 @@ function isKeyedBy(rowKey, primaryKey) {
1218
1203
  * queries the same way the view-syncer does in production.
1219
1204
  */
1220
1205
  function hydrate(input, hash, clientSchema, tableSpecs) {
1221
- const res = input.fetch({});
1222
- return new Streamer(buildPrimaryKeys(clientSchema), tableSpecs).accumulate(hash, input.getSchema(), toAdds(res)).stream();
1223
- }
1224
- function hydrateInternal(input, hash, primaryKeys, tableSpecs) {
1225
- const res = input.fetch({});
1226
- return new Streamer(primaryKeys, tableSpecs).accumulate(hash, input.getSchema(), toAdds(res)).stream();
1206
+ return new Streamer(buildPrimaryKeys(clientSchema), tableSpecs).streamNodes(hash, input.getSchema(), 0, input.fetch({}));
1227
1207
  }
1228
1208
  function buildPrimaryKeys(clientSchema, primaryKeys = /* @__PURE__ */ new Map()) {
1229
1209
  for (const [tableName, { primaryKey }] of Object.entries(clientSchema.tables)) primaryKeys.set(tableName, primaryKey);