npm.io
0.0.3 • Published 14h ago

@tumbati/umodzi

Licence
MIT
Version
0.0.3
Deps
0
Size
178 kB
Vulns
0
Weekly
0

@tumbati/umodzi

TypeScript SDK for Umodzi — a thin client for talking to a running Umodzi node's local HTTP API. Works in Node 18+, modern browsers, and React Native.

The node is the source of truth — this SDK never embeds one. If you need an in-process node, use the JVM :sdk:kotlin module via UmodziEngine.

Install

npm install @tumbati/umodzi

Zero runtime dependencies. Native fetch only.

Usage

import { UmodziClient } from '@tumbati/umodzi'

const client = new UmodziClient({ baseUrl: 'http://localhost:8080' })

const event = await client.emit(
  'CREATE_PATIENT',
  JSON.stringify({ entityId: 'p-001', name: 'Chimwemwe' })
)
console.log(`Persisted ${event.id} at clock`, event.vectorClock)

// Read everything, or only what's new since a known clock.
const all = await client.getEvents()
const newer = await client.getEvents(event.vectorClock)

// Subscribe to live updates over Server-Sent Events. Replays everything since
// the supplied clock (or from the beginning when omitted), then yields live
// events as they're persisted on the node.
const ctrl = new AbortController()
;(async () => {
  for await (const e of client.events(undefined, { signal: ctrl.signal })) {
    console.log('new event:', e.type, e.id)
  }
})()
// later: ctrl.abort()

// Inspect.
const status = await client.getStatus()
const peers = await client.getPeers()

// Push a sync round (defaults to all reachable peers).
await client.triggerSync()
await client.triggerSync('REG-01')   // or target a single peer

API surface

Method HTTP
emit(type, payload, schemaVersion?) POST /events
getEvents(since?) GET /events[?since=<encoded VectorClock>]
getEvent(id) GET /events/{id} — returns null on 404
events(since?, { signal? }) GET /events/stream — SSE async iterable, replay then live
getPeers() GET /peers
getStatus() GET /status
triggerSync(peerId?) POST /sync/trigger
close() no-op (kept for symmetry with the Kotlin SDK)

deviceId is server-stamped from the running node's id. The SDK does not let you set it; supplying one in raw HTTP gets rejected with 403.

Errors

Non-2xx responses throw UmodziHttpError with status and body properties:

import { UmodziHttpError, UmodziVersionMismatchError } from '@tumbati/umodzi'

try {
  await client.emit('X', 'invalid')
} catch (err) {
  if (err instanceof UmodziHttpError) {
    console.error(err.status, err.body)
  } else if (err instanceof UmodziVersionMismatchError) {
    console.error(`Node ${err.actual} is older than required ${err.required}`)
  } else throw err
}

Version compatibility check

By default, on the first call after construction the SDK fetches /status and verifies the node's version is at least MIN_NODE_VERSION. If older, all subsequent calls throw UmodziVersionMismatchError until you upgrade the node. The result is cached for the client's lifetime — there's only one extra round-trip.

Disable with verifyVersion: false (useful in tests, or when talking to a pre-v0.0.2 node that doesn't carry the field).

Custom fetch

For tests, polyfills, or interceptors:

new UmodziClient({
  fetch: (url, init) => myInstrumentedFetch(url, init)
})

What's not here yet

  • Offline emit queue — semantics are still being designed (see RFC-005).
  • Generated types from OpenAPI — the node now serves GET /openapi.json (Phase 10b). Switching src/types.ts from hand-maintained to openapi-typescript-generated is a follow-up.

Develop

cd sdk/js
npm install
npm test          # vitest run
npm run typecheck # tsc --noEmit
npm run build     # tsup → dist/index.{js,cjs,d.ts}

License

MIT

Keywords