@drakkar.software/starfish-client 3.0.0-alpha.36 → 3.0.0-alpha.38

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.
package/dist/index.js CHANGED
@@ -723,6 +723,8 @@ var SyncManager = class {
723
723
  localData = {};
724
724
  aborted = false;
725
725
  lastFromCache = false;
726
+ /** True once {@link seedFromCache} has successfully seeded localData from the cache. */
727
+ seeded = false;
726
728
  constructor(options) {
727
729
  this.client = options.client;
728
730
  this.pullPath = options.pullPath;
@@ -744,6 +746,24 @@ var SyncManager = class {
744
746
  getData() {
745
747
  return { ...this.localData };
746
748
  }
749
+ /**
750
+ * Returns true when `pull()` / `ingest()` should merge against the current
751
+ * `localData` rather than replace it wholesale.
752
+ *
753
+ * Two situations establish a merge baseline:
754
+ * - A successful prior pull/ingest advanced `lastCheckpoint` beyond 0 (the
755
+ * normal steady-state case, unchanged since alpha.36).
756
+ * - A cache seed painted `localData` via {@link seedFromCache} AND the store
757
+ * uses a custom conflict resolver (i.e. NOT the default `deepMerge`). For a
758
+ * union/custom resolver the seeded snapshot is a real baseline that must not
759
+ * be clobbered by a short first live response (a cache-fallback on 429/5xx
760
+ * or a momentarily-short concurrent server snapshot). For the default
761
+ * `deepMerge` resolver we keep the pre-fix wholesale-replace behaviour so
762
+ * non-union stores are byte-identical to alpha.36.
763
+ */
764
+ hasMergeBaseline() {
765
+ return this.lastCheckpoint > 0 || this.seeded && this.onConflict !== deepMerge;
766
+ }
747
767
  /**
748
768
  * Merge a remote snapshot with local (optimistic) data using this manager's
749
769
  * conflict resolver — the same resolver the push-conflict path uses. A plain
@@ -778,7 +798,19 @@ var SyncManager = class {
778
798
  * WITHOUT touching the network, decrypting in memory for E2E collections.
779
799
  * Returns whether anything was seeded (false on a miss, an expired entry, or
780
800
  * a decrypt failure — e.g. keyring skew). Call once on store creation before
781
- * the initial live {@link pull}, which then supersedes the seeded snapshot.
801
+ * the initial live {@link pull}.
802
+ *
803
+ * `lastCheckpoint` is intentionally left at 0 so the first live pull sends a
804
+ * full (re)sync request to the server, not a delta. However, for stores with
805
+ * a custom conflict resolver (e.g. `createUnionMerge`) the seeded snapshot is
806
+ * treated as a merge baseline: {@link hasMergeBaseline} returns true, so the
807
+ * first pull/ingest merges against the seed rather than replacing it wholesale.
808
+ * This closes the bootstrap window where a short first-pull response (a cache-
809
+ * fallback on 429/5xx or a momentarily-short concurrent snapshot) would
810
+ * silently drop items the resolver was configured to preserve. For the default
811
+ * `deepMerge` resolver the first pull still takes the snapshot wholesale —
812
+ * behaviour is byte-identical to alpha.36.
813
+ *
782
814
  * Requires the client to have been built with a `cache`.
783
815
  */
784
816
  async seedFromCache() {
@@ -794,6 +826,7 @@ var SyncManager = class {
794
826
  if (this.aborted) return false;
795
827
  this.localData = data;
796
828
  this.lastHash = cached.hash;
829
+ this.seeded = true;
797
830
  this.lastFromCache = true;
798
831
  return true;
799
832
  }
@@ -807,12 +840,15 @@ var SyncManager = class {
807
840
  * {@link StarfishClientOptions.onRevalidated}) into the store.
808
841
  *
809
842
  * Like {@link pull}, `ingest` conflict-merges the snapshot against the
810
- * established baseline via `this.onConflict` when a checkpoint exists — so a
811
- * union-merge store does not lose array items when a revalidation result
812
- * (e.g. a stale cache-fallback on 429/5xx) is a shorter snapshot. The first
813
- * ingest (no checkpoint yet) takes the snapshot wholesale. Sets
814
- * `lastFromCache = false` (a revalidation is a live response) so the binding
815
- * can clear its `stale` flag.
843
+ * established baseline via `this.onConflict` when a merge baseline exists
844
+ * ({@link hasMergeBaseline}) — so a union-merge store does not lose array
845
+ * items when a revalidation result (e.g. a stale cache-fallback on 429/5xx)
846
+ * is a shorter snapshot. The baseline is established by either a prior
847
+ * pull/ingest that advanced `lastCheckpoint`, or by a successful
848
+ * {@link seedFromCache} for a store with a custom resolver. The first ingest
849
+ * without such a baseline takes the snapshot wholesale (default `deepMerge`
850
+ * stores are byte-identical to alpha.36). Sets `lastFromCache = false` (a
851
+ * revalidation is a live response) so the binding can clear its `stale` flag.
816
852
  *
817
853
  * **Staleness guard**: if a `push()` advanced `lastCheckpoint` between the
818
854
  * time the revalidation request was sent and the time it resolves, the
@@ -831,7 +867,7 @@ var SyncManager = class {
831
867
  } else {
832
868
  incoming = result.data;
833
869
  }
834
- this.localData = this.lastCheckpoint > 0 ? this.onConflict(this.localData, incoming) : incoming;
870
+ this.localData = this.hasMergeBaseline() ? this.onConflict(this.localData, incoming) : incoming;
835
871
  this.lastHash = result.hash;
836
872
  this.lastCheckpoint = result.timestamp;
837
873
  this.lastFromCache = false;
@@ -851,7 +887,7 @@ var SyncManager = class {
851
887
  } else {
852
888
  incoming = result.data;
853
889
  }
854
- this.localData = this.lastCheckpoint > 0 ? this.onConflict(this.localData, incoming) : incoming;
890
+ this.localData = this.hasMergeBaseline() ? this.onConflict(this.localData, incoming) : incoming;
855
891
  result.data = this.localData;
856
892
  this.lastHash = result.hash;
857
893
  this.lastCheckpoint = result.timestamp;