Files
atomsync/AGENTS.md
2026-04-16 20:18:45 +02:00

1.7 KiB

Atomsync

Clojure/ClojureScript offline-first synced atom library backed by IndexedDB (CLJS) or SQLite (CLJ).

Async: use promesa, not core.async

This project uses promesa (funcool/promesa) for all async operations. Do not introduce core.async.

  • Store protocol (store.cljc): all methods return promesa promises
    • Sync operations (e.g. MemoryStore): (p/resolved val)
    • Async operations (e.g. IDBStore): (p/create (fn [resolve reject] ...))
  • HTTP / sync (sync.cljc): CLJ uses p/vthread, CLJS returns native JS Promise chains
  • Core (core.cljc): p/let for sequential async, p/all for parallel
  • Sync loop: platform timers (setInterval / ScheduledExecutorService), not go-loop
  • Push coalescing: debounced with 50ms timer + pushing? atom guard, not channels

Why not core.async

  • Channels-as-one-shot-promises add ceremony (chan + put! + close!) for what is conceptually a return value
  • promesa maps to native primitives: JS Promises on CLJS, CompletableFuture on CLJ
  • p/let reads cleaner than go + <! for sequential async
  • Timer-based sync loop is simpler than go-loop + alts! for this use case

Project structure

src/atomsync/
  store.cljc        — PStore protocol (promises)
  store/memory.cljc — in-memory store (testing/JVM)
  store/idb.cljs    — IndexedDB store (browser)
  sync.cljc         — HTTP pull/push, SSE, connectivity
  core.cljc         — SyncedAtom, sync loop, public API
  transit.cljc      — Transit encoding/decoding
  db.clj            — SQLite server storage (nippy)
  server.clj        — HTTP sync server (http-kit)

Running tests

clj -M:dev:test