@runappsai/sdk 0.6.2 → 0.6.4

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.
@@ -51,6 +51,9 @@ export interface SyncState {
51
51
  /** Local changes not yet on the server. */
52
52
  pending: number;
53
53
  lastSyncedAt?: number;
54
+ /** When a sync last moved data — pushed local writes or applied
55
+ * another device's. A quiet pull that found nothing leaves it. */
56
+ lastChangeAt?: number;
54
57
  error?: string;
55
58
  }
56
59
 
@@ -435,6 +438,9 @@ export class SyncEngine {
435
438
 
436
439
  // ── State ────────────────────────────────────────────────────────
437
440
 
441
+ /** Set while a sync cycle pushes or applies something. */
442
+ private moved = false;
443
+
438
444
  private setState(patch: Partial<SyncState>): void {
439
445
  const next = { ...this.state, ...patch };
440
446
  if (patch.state && patch.state !== "error") delete next.error;
@@ -442,6 +448,7 @@ export class SyncEngine {
442
448
  next.state !== this.state.state ||
443
449
  next.pending !== this.state.pending ||
444
450
  next.lastSyncedAt !== this.state.lastSyncedAt ||
451
+ next.lastChangeAt !== this.state.lastChangeAt ||
445
452
  next.error !== this.state.error;
446
453
  this.state = next;
447
454
  if (changed) this.deps.events.emit("sync:state", { ...next });
@@ -516,6 +523,7 @@ export class SyncEngine {
516
523
  return;
517
524
  }
518
525
  this.setState({ state: "syncing" });
526
+ this.moved = false;
519
527
  try {
520
528
  await this.loadMeta();
521
529
  await this.ensureOwner(userId);
@@ -525,7 +533,8 @@ export class SyncEngine {
525
533
  this.backoffMs = 0;
526
534
  this.backoffUntil = 0;
527
535
  await this.refreshPending();
528
- this.setState({ state: "idle", lastSyncedAt: Date.now() });
536
+ const now = Date.now();
537
+ this.setState({ state: "idle", lastSyncedAt: now, ...(this.moved ? { lastChangeAt: now } : {}) });
529
538
  } catch (err) {
530
539
  await this.refreshPending();
531
540
  this.fail(err);
@@ -610,6 +619,7 @@ export class SyncEngine {
610
619
  }
611
620
  await this.log.setMeta(etagUpdates);
612
621
  await this.log.remove(done);
622
+ if (done.length > 0) this.moved = true;
613
623
  await this.refreshPending();
614
624
  }
615
625
 
@@ -765,6 +775,7 @@ export class SyncEngine {
765
775
  }
766
776
 
767
777
  private emitChange(change: DbChange): void {
778
+ this.moved = true;
768
779
  this.deps.events.emit("db:change", change);
769
780
  try {
770
781
  this.channel?.postMessage(change);