@vue-skuilder/db 0.2.19 → 0.2.20

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.
@@ -7543,6 +7543,14 @@ var init_BaseUserDB = __esm({
7543
7543
  _hydration = { state: "not-required" };
7544
7544
  /** In-flight hydration, so concurrent callers can wait for a real answer. */
7545
7545
  _hydrationPromise = null;
7546
+ /**
7547
+ * Sequence the local mirror was filled to by a hydration that succeeded in
7548
+ * THIS init(), handed to startSync() so the live pull can skip history it
7549
+ * would otherwise re-walk. Undefined whenever there is no such guarantee —
7550
+ * hydration skipped, not required, or failed. Never persisted; see the
7551
+ * `since` note in CouchDBSyncStrategy.startSync().
7552
+ */
7553
+ _hydratedSeq;
7546
7554
  /**
7547
7555
  * How far the local mirror can be trusted, RIGHT NOW. See
7548
7556
  * {@link UserHydrationStatus}.
@@ -8029,7 +8037,7 @@ Currently logged-in as ${this._username}.`
8029
8037
  this.setDBandQ();
8030
8038
  this._hydrationPromise = this.hydrateLocalMirror();
8031
8039
  await this._hydrationPromise;
8032
- this.syncStrategy.startSync(this.localDB, this.remoteDB);
8040
+ this.syncStrategy.startSync(this.localDB, this.remoteDB, this._hydratedSeq);
8033
8041
  this.applyDesignDocs().catch((error) => {
8034
8042
  log3(`Error in applyDesignDocs background task: ${error}`);
8035
8043
  if (error && typeof error === "object") {
@@ -8057,6 +8065,7 @@ Currently logged-in as ${this._username}.`
8057
8065
  * ceiling). Callers decide what a failure means for them.
8058
8066
  */
8059
8067
  async hydrateLocalMirror() {
8068
+ this._hydratedSeq = void 0;
8060
8069
  if (this.localDB.name === this.remoteDB.name || !this.syncStrategy.hydrate) {
8061
8070
  this._hydration = { state: "not-required" };
8062
8071
  return;
@@ -8068,11 +8077,12 @@ Currently logged-in as ${this._username}.`
8068
8077
  this._hydration = { state: "hydrating" };
8069
8078
  const start = Date.now();
8070
8079
  try {
8071
- const { docsWritten } = await withTimeout(
8080
+ const { docsWritten, lastSeq } = await withTimeout(
8072
8081
  this.syncStrategy.hydrate(this.localDB, this.remoteDB),
8073
8082
  HYDRATION_TIMEOUT_MS,
8074
8083
  `Hydration of local mirror for ${this._username}`
8075
8084
  );
8085
+ this._hydratedSeq = lastSeq;
8076
8086
  await this.writeHydrationMarker();
8077
8087
  this._hydration = {
8078
8088
  state: "hydrated",
@@ -9010,22 +9020,60 @@ var init_CouchDBSyncStrategy = __esm({
9010
9020
  * before we know what the remote already has, which is the conflict-leaf
9011
9021
  * problem hydration exists to avoid. Local changes go up when startSync()
9012
9022
  * takes over.
9023
+ *
9024
+ * Into an EMPTY local DB this skips deleted documents, because a mirror that
9025
+ * never held a document does not need to be told it was removed. That
9026
+ * matters more than it sounds: scheduled reviews (`card_review_*`) are
9027
+ * created and deleted once per review completed, so the changes feed is
9028
+ * dominated by tombstones and grows without bound, while the durable record
9029
+ * lives in card history. One real account measured 385 live documents behind
9030
+ * 8,370 deletions — an unfiltered pull moved 9,520 documents and took ~20s,
9031
+ * blowing the hydration timeout on a phone; filtered, the same pull moves
9032
+ * 404 and takes ~2s, reaching a byte-identical local state.
9033
+ *
9034
+ * The filter runs server-side, so `last_seq` still reports the source's true
9035
+ * sequence. Returning it lets startSync() begin the live pull from there
9036
+ * instead of re-walking the same history in the background — without that,
9037
+ * the cost is merely deferred, not removed.
9013
9038
  */
9014
9039
  async hydrate(localDB, remoteDB) {
9015
- const replication = pouchdb_setup_default.replicate(remoteDB, localDB, {});
9040
+ const pristine = (await localDB.info()).doc_count === 0;
9041
+ const replication = pouchdb_setup_default.replicate(
9042
+ remoteDB,
9043
+ localDB,
9044
+ pristine ? { selector: { _deleted: { $exists: false } } } : {}
9045
+ );
9016
9046
  this.hydrationHandle = replication;
9017
9047
  try {
9018
9048
  const info = await replication;
9019
- return { docsWritten: info.docs_written };
9049
+ return {
9050
+ docsWritten: info.docs_written,
9051
+ // Only a pristine pull is a trustworthy starting point for the live
9052
+ // sync; otherwise leave startSync() to its own checkpoint.
9053
+ lastSeq: pristine ? info.last_seq : void 0
9054
+ };
9020
9055
  } finally {
9021
9056
  this.hydrationHandle = void 0;
9022
9057
  }
9023
9058
  }
9024
- startSync(localDB, remoteDB) {
9059
+ startSync(localDB, remoteDB, since) {
9025
9060
  if (localDB.name !== remoteDB.name) {
9026
9061
  this.syncHandle = pouchdb_setup_default.sync(localDB, remoteDB, {
9027
9062
  live: true,
9028
- retry: true
9063
+ retry: true,
9064
+ // `since` applies to the pull only, and only when hydrate() just
9065
+ // established that local matches remote at that sequence. Without it,
9066
+ // a filtered hydration leaves the pull with no usable checkpoint (the
9067
+ // filter changes the replication id), so it restarts from zero and
9068
+ // re-walks in the background exactly the tombstone history hydration
9069
+ // just skipped — same work, now competing with the study session.
9070
+ //
9071
+ // Deliberately NOT persisted across launches: on later boots hydration
9072
+ // is skipped and the pull's own checkpoint is the correct resume
9073
+ // point, so a stored sequence could only be stale. If the app dies
9074
+ // before that first checkpoint is written, the next launch simply
9075
+ // walks the full feed once.
9076
+ ...since !== void 0 ? { pull: { since } } : {}
9029
9077
  });
9030
9078
  }
9031
9079
  }