Skip to content

[Sync Framework] Build centralized sync framework in src/lib/sync/ #23

Description

@Michael-Obele

Overview

Build the centralized sync framework in src/lib/sync/ — the foundation for all app migrations. No app changes in this PR, just the framework code.

Design doc: docs/plans/2026-06-21-sync-framework-design.md

What to build

Client-side (src/lib/sync/client/)

  • sync-manager.svelte.tsSyncManager<TStores> class using Svelte 5 $state in class fields
    • Reactive state: needsBackup, isBackingUp, isSyncing, lastSyncAt, syncError
    • Methods: syncOnMount(), handleManualBackup(), handleAutoBackup(), handleSync(), markDirty(), destroy()
    • Reads/writes through svelte-idb stores (db.store.getAll(), db.store.put(), db.store.clear())
    • Uses $state.snapshot() before writing reactive data to IndexedDB
  • merge-strategies.tstimestampMerge, serverWins, localWins + customMerge() hook
    • MergeStrategy interface with merge(local, server, storeName) method
  • triggers.svelte.tsAutoBackupTrigger, PollingTrigger, ManualTrigger
    • AutoBackupTrigger: debounced timer, watches needsBackup via $effect, checks isAuthenticated
    • PollingTrigger: interval + exponential backoff on failure (base * 2^failures, capped at max)
    • ManualTrigger: no automatic sync
  • types.tsSyncConfig, SyncStatus, StoreMap types

Server-side (src/lib/sync/server/)

  • create-load-query.tscreateLoadQuery() factory generating query() functions
    • Maps store names to Prisma findMany calls with where: { userId }, include, orderBy
    • Returns empty arrays for unauthenticated users
  • create-backup-form.tscreateBackupForm() factory generating form() functions
    • Single hidden field with serialized JSON (progressive enhancement compatible)
    • Server handler parses JSON, validates with Valibot, saves to Prisma in transaction
  • create-backup-command.tscreateBackupCommand() factory generating command() functions
    • Accepts structured object directly (programmatic, for auto-backup)
    • Validates with Valibot, saves to Prisma in transaction
  • prisma-helpers.ts — generic upsert/replace/delete helpers
  • types.tsLoadConfig, BackupConfig types

Root

  • index.ts — barrel exports for client + server
  • README.md — framework documentation + migration guide

Key design decisions

  • Uses svelte-idb (createReactiveDB + liveAll()) for local persistence — replaces PersistedState wrapper
  • form for manual backup (progressive enhancement), command for auto-backup timer (programmatic)
  • query for loading data (confirmed supported in SvelteKit 2.27+)
  • Class-based with $state fields (Svelte 5 class reactivity — confirmed in docs)
  • Fresh start acceptable — new svelte-idb DB names, no migration from old PersistedState keys

Testing

  • Unit tests for merge strategies (timestamp, serverWins, localWins, custom)
  • Unit tests for SyncManager (mock svelte-idb + mock remote functions)
  • Unit tests for trigger classes (AutoBackupTrigger timer, PollingTrigger backoff)
  • bun check passes with 0 errors
  • svelte-autofixer passes with 0 issues

References

Blocked by

None — this is the first PR in the migration sequence.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions