Synthigy client for the /data endpoint — JVM / babashka.
Connect once, then call; no client threading:
(connect! {:endpoint "http://localhost:7887" :client-id "my-service" :client-secret "secret"})
(search :user {:-where {:active {:-eq true}}} {:name nil :roles [:name :active]})
(sync :user {:name "alice" :active true})
THE INVARIANT: one process, one client, one backend. A process (BFF,
service, script) connects to Synthigy as exactly ONE OAuth client —
identity is multiplexed per-call with :acting-as, never with a second
client. binding synthigy.client.core/*client* exists for TESTS, nothing
else. Internals that pass the client VALUE do so for lifecycle correctness
(a watch must tear down against the client it registered in, even after a
REPL re-connect!) — not for multi-endpoint support, which is a non-goal.
Keys are kebab-case in both directions by default (:key-format nil
for raw snake_case wire keys).
Pure data helpers — op builders for batch, results->data/ok?,
compose-tree/compose-forest — live in synthigy.client.core.
Synthigy client for the /data endpoint — JVM / babashka.
Connect once, then call; no client threading:
(connect! {:endpoint "http://localhost:7887"
:client-id "my-service"
:client-secret "secret"})
(search :user
{:-where {:active {:-eq true}}}
{:name nil :roles [:name :active]})
(sync :user {:name "alice" :active true})
THE INVARIANT: one process, one client, one backend. A process (BFF,
service, script) connects to Synthigy as exactly ONE OAuth client —
identity is multiplexed per-call with `:acting-as`, never with a second
client. `binding synthigy.client.core/*client*` exists for TESTS, nothing
else. Internals that pass the client VALUE do so for lifecycle correctness
(a watch must tear down against the client it registered in, even after a
REPL re-`connect!`) — not for multi-endpoint support, which is a non-goal.
Keys are kebab-case in both directions by default (`:key-format nil`
for raw snake_case wire keys).
Pure data helpers — op builders for `batch`, `results->data`/`ok?`,
`compose-tree`/`compose-forest` — live in `synthigy.client.core`.Synthigy client for the /data endpoint — ClojureScript (promise-returning).
Same surface as the CLJ client; every network fn returns a Promise.
Connect once via connect!; pure data helpers live in
synthigy.client.core.
THE INVARIANT: one process, one client, one backend — identity is
multiplexed per-call with :acting-as, never with a second client.
Synthigy client for the /data endpoint — ClojureScript (promise-returning). Same surface as the CLJ client; every network fn returns a Promise. Connect once via `connect!`; pure data helpers live in `synthigy.client.core`. THE INVARIANT: one process, one client, one backend — identity is multiplexed per-call with `:acting-as`, never with a second client.
(batch operations & {:as opts})Execute multiple operations in a single request.
Returns vector of raw results in request order. Each result is {:ok true :data ...} or {:ok false :error {:message ... :code ...}}.
Use op-* builders to construct operations:
(let [ops [(core/op-sync :user {:name "alice"}) (core/op-search :user nil [:name]) (core/op-search :user_role nil [:name])] [synced users roles] (results->data (batch ops) ops)] (when (all-ok? [synced users roles]) (println synced users roles)))
Execute multiple operations in a single request.
Returns vector of raw results in request order. Each result is
{:ok true :data ...} or {:ok false :error {:message ... :code ...}}.
Use op-* builders to construct operations:
(let [ops [(core/op-sync :user {:name "alice"})
(core/op-search :user nil [:name])
(core/op-search :user_role nil [:name])]
[synced users roles] (results->data (batch ops) ops)]
(when (all-ok? [synced users roles])
(println synced users roles)))Execute multiple operations in a single request. Returns a Promise
of raw results vector; use results->data to extract.
Execute multiple operations in a single request. Returns a Promise of raw results vector; use `results->data` to extract.
(close-watch! h)Stop a handle from watch/watch-schema (a map with :close) or from
watch-query (an atom carrying :close in its metadata).
Stop a handle from `watch`/`watch-schema` (a map with :close) or from `watch-query` (an atom carrying :close in its metadata).
Stop a handle from watch/watch-schema (a map with :close) or from
watch-query/watch-xsql (an atom carrying :close in its metadata).
Stop a handle from `watch`/`watch-schema` (a map with :close) or from `watch-query`/`watch-xsql` (an atom carrying :close in its metadata).
(connect! opts)Create a client from opts (see synthigy.client.core/create-client) and
install it as the process-wide default — the Clojure server-restart idiom:
the PREVIOUS client (if any) is destroyed first (disconnect! — its SSE
listener stops, its watches close), then the new one replaces it. Call at
startup; call again to reconnect.
Create a client from `opts` (see `synthigy.client.core/create-client`) and install it as the process-wide default — the Clojure server-restart idiom: the PREVIOUS client (if any) is destroyed first (`disconnect!` — its SSE listener stops, its watches close), then the new one replaces it. Call at startup; call again to reconnect.
Create a client from opts and install it as the process-wide default —
the same server-restart idiom as the CLJ client: the PREVIOUS client (if
any) is destroyed first (disconnect! — its SSE listener stops, its
watches close), then the new one replaces it. Call again to reconnect
(e.g. after a silent-renew login/logout swap).
Create a client from `opts` and install it as the process-wide default — the same server-restart idiom as the CLJ client: the PREVIOUS client (if any) is destroyed first (`disconnect!` — its SSE listener stops, its watches close), then the new one replaces it. Call again to reconnect (e.g. after a silent-renew login/logout swap).
(delete entity data & {:as opts})Delete entity records.
Data keys accept kebab-case (normalized to snake_case).
Delete entity records. Data keys accept kebab-case (normalized to snake_case).
Delete entity records. Returns a Promise.
Delete entity records. Returns a Promise.
(deployed-model & {:as opts})Fetch the raw ERD model as deployed. Requires dataset:load scope.
Fetch the raw ERD model as deployed. Requires dataset:load scope.
Fetch the raw ERD model as deployed. Requires dataset:load scope. Returns a Promise.
Fetch the raw ERD model as deployed. Requires dataset:load scope. Returns a Promise.
(disconnect!)Destroy the connected client, server-restart style: stop the watch
multiplexer's SSE listener, drop all watches, best-effort clear the
server-side subscription set, and uninstall the client. No-op when not
connected. connect! calls this on the previous client automatically.
Destroy the connected client, server-restart style: stop the watch multiplexer's SSE listener, drop all watches, best-effort clear the server-side subscription set, and uninstall the client. No-op when not connected. `connect!` calls this on the previous client automatically.
Destroy the connected client, server-restart style: stop the watch
multiplexer's SSE listener, drop all watches, best-effort clear the
server-side subscription set, and uninstall the client. No-op when not
connected. connect! calls this on the previous client automatically.
Returns a Promise.
Destroy the connected client, server-restart style: stop the watch multiplexer's SSE listener, drop all watches, best-effort clear the server-side subscription set, and uninstall the client. No-op when not connected. `connect!` calls this on the previous client automatically. Returns a Promise.
(get entity args selection & {:as opts})Get a single entity by unique constraint.
Args and selection accept kebab-case (normalized to snake_case). Selection supports shorthand syntax.
Get a single entity by unique constraint. Args and selection accept kebab-case (normalized to snake_case). Selection supports shorthand syntax.
Get a single entity by unique constraint. Returns a Promise.
Get a single entity by unique constraint. Returns a Promise.
(get-tree entity root on selection & {:as opts})From root entity id, return root + descendants reachable via :on.
From `root` entity id, return root + descendants reachable via :on.
Get root + descendants via :on relation. Returns a Promise.
Get root + descendants via :on relation. Returns a Promise.
(history-diff record-xid from-ts to-ts & {:keys [tenant]})Difference in a record's state between two timestamps. Option: :tenant
Difference in a record's state between two timestamps. Option: :tenant
(history-events record-xid & {:keys [between tenant limit track]})Events for record-xid (nil = any record) over a time range. Options:
:between [from to] (defaults [nil now]) :tenant :limit :track
Events for `record-xid` (nil = any record) over a time range. Options: :between [from to] (defaults [nil now]) :tenant :limit :track
(history-get-at record-xid at & {:keys [tenant include-deleted?]})State of record-xid at timestamp at. Options: :tenant :include-deleted?
State of `record-xid` at timestamp `at`. Options: :tenant :include-deleted?
(history-since & {:keys [cursor tenant limit track]})Events strictly after the :cursor timestamp, oldest-first. Options: :cursor :tenant :limit :track
Events strictly after the :cursor timestamp, oldest-first. Options: :cursor :tenant :limit :track
(history-timeline & {:keys [between group-by tenant limit]})Events grouped by :group-by (":request"/":actor"/":scope"). Options: :between [from to] :group-by :tenant :limit
Events grouped by :group-by (":request"/":actor"/":scope").
Options: :between [from to] :group-by :tenant :limit(lint source & {:keys [entity op]})Check an XSQL source string against the IAM-projected schema and
return a seq of diagnostics. :entity enables schema-aware checks,
:op sets the wire op.
Check an XSQL `source` string against the IAM-projected schema and return a seq of diagnostics. :entity enables schema-aware checks, :op sets the wire op.
Check an XSQL source string against the IAM-projected schema.
Returns a Promise of the diagnostics seq. :entity enables
schema-aware checks, :op sets the wire op.
Check an XSQL `source` string against the IAM-projected schema. Returns a Promise of the diagnostics seq. :entity enables schema-aware checks, :op sets the wire op.
(listen on-event)(listen on-event opts)Stream SSE change events from /data/events, calling on-event with each
delta envelope. :type is the delta kind — record/insert|update|delete
(with :record-xid + :before/:after), relation/link|unlink (with
:data [subscribed other]), or the coalesced pokes entity/touched
(:entity) / relation/touched (:relation).
BLOCKS the calling thread — wrap in (future ...) for background use. Auto-reconnects with Last-Event-ID + exponential backoff (1s → 30s).
Events are notification-only — fetch the updated records via
search/get/batch after receiving one.
Options: :stop? — 0-arg predicate; truthy stops the loop between frames :max-delay — cap for reconnect backoff in ms (default 30000)
UNAUTHORIZED/FORBIDDEN propagate; other transport errors reconnect.
(subscribe c :user) (future (listen c prn))
Stream SSE change events from /data/events, calling `on-event` with each delta envelope. `:type` is the delta kind — record/insert|update|delete (with `:record-xid` + `:before`/`:after`), relation/link|unlink (with `:data` [subscribed other]), or the coalesced pokes entity/touched (`:entity`) / relation/touched (`:relation`). BLOCKS the calling thread — wrap in (future ...) for background use. Auto-reconnects with Last-Event-ID + exponential backoff (1s → 30s). Events are notification-only — fetch the updated records via `search`/`get`/`batch` after receiving one. Options: :stop? — 0-arg predicate; truthy stops the loop between frames :max-delay — cap for reconnect backoff in ms (default 30000) UNAUTHORIZED/FORBIDDEN propagate; other transport errors reconnect. (subscribe c :user) (future (listen c prn))
Stream SSE notifications from /data/events. Calls on-event with
each {:type :entity :relations :xids} notification. Returns a
0-arg stop function that aborts the listener.
Auto-reconnects with Last-Event-ID + exponential backoff. Does NOT block — runs on the microtask queue via fetch streaming.
Options: :max-delay — reconnect backoff cap ms (default 30000) :on-parse-error — (fn [error raw]) for malformed frames
Stream SSE notifications from /data/events. Calls `on-event` with
each `{:type :entity :relations :xids}` notification. Returns a
0-arg `stop` function that aborts the listener.
Auto-reconnects with Last-Event-ID + exponential backoff. Does
NOT block — runs on the microtask queue via fetch streaming.
Options:
:max-delay — reconnect backoff cap ms (default 30000)
:on-parse-error — (fn [error raw]) for malformed frames(onboard xid & {:keys [reset methods ttl-seconds return-url]})Mint a one-time account-claim link (POST /oauth/onboard). Confidential client whose principal administers the account — RBAC update on User plus the row inside its owner-group write scope, which the shipped User Provisioner role grants — else :code "PROVISION_FORBIDDEN". The connected client's own client_credentials identity IS that principal, so no separate credential is needed here.
xid : an EXISTING account's xid. Onboarding no longer creates
accounts — create it first over /data (with person_info,
roles, groups in one tree), then mint a ticket for it.
A blank xid throws :code "XID_REQUIRED"; one that doesn't
resolve throws :code "USER_NOT_FOUND".
:reset : soft-recycle this account first — strip its federated
identities, null its password, revoke live sessions/tokens
— before minting. Does NOT touch active; that flag is
your data, write it yourself.
:methods : restrict the claim page, e.g. ["password"] or ["google"];
omitted = every active federation provider plus password
:ttl-seconds : claim-link lifetime; server default 24h
:return-url : where a successful DIRECT (browser) claim redirects instead
of Synthigy's generic status page. Must match one of THIS
client's registered redirections (or be a loopback URI);
an unregistered value throws :code "RETURN_URL_NOT_REGISTERED".
-> {:onboard_url ... :expires_at ... :user {:xid ...}} (wire keys as-is, like schema/lint). Throws ex-info with :code (e.g. "PROVISION_FORBIDDEN", "XID_REQUIRED", "USER_NOT_FOUND") and :status on failure.
Mint a one-time account-claim link (POST /oauth/onboard). Confidential
client whose principal administers the account — RBAC update on User plus
the row inside its owner-group write scope, which the shipped User
Provisioner role grants — else :code "PROVISION_FORBIDDEN". The
connected client's own client_credentials identity IS that principal, so
no separate credential is needed here.
xid : an EXISTING account's xid. Onboarding no longer creates
accounts — create it first over `/data` (with person_info,
roles, groups in one tree), then mint a ticket for it.
A blank xid throws :code "XID_REQUIRED"; one that doesn't
resolve throws :code "USER_NOT_FOUND".
:reset : soft-recycle this account first — strip its federated
identities, null its password, revoke live sessions/tokens
— before minting. Does NOT touch `active`; that flag is
your data, write it yourself.
:methods : restrict the claim page, e.g. ["password"] or ["google"];
omitted = every active federation provider plus password
:ttl-seconds : claim-link lifetime; server default 24h
:return-url : where a successful DIRECT (browser) claim redirects instead
of Synthigy's generic status page. Must match one of THIS
client's registered redirections (or be a loopback URI);
an unregistered value throws :code "RETURN_URL_NOT_REGISTERED".
-> {:onboard_url ... :expires_at ... :user {:xid ...}} (wire keys as-is,
like schema/lint).
Throws ex-info with :code (e.g. "PROVISION_FORBIDDEN", "XID_REQUIRED",
"USER_NOT_FOUND") and :status on failure.Mint a one-time account-claim link (POST /oauth/onboard). Confidential client whose principal administers the account — RBAC update on User plus the row inside its owner-group write scope, which the shipped User Provisioner role grants — else :code "PROVISION_FORBIDDEN". The connected client's own client_credentials identity IS that principal, so no separate credential is needed here.
xid : an EXISTING account's xid. Onboarding no longer creates
accounts — create it first over /data (with person_info,
roles, groups in one tree), then mint a ticket for it.
A blank xid rejects with :code "XID_REQUIRED"; one that
doesn't resolve rejects with :code "USER_NOT_FOUND".
:reset : soft-recycle this account first — strip its federated
identities, null its password, revoke live sessions/tokens
— before minting. Does NOT touch active; that flag is
your data, write it yourself.
:methods : restrict the claim page, e.g. ["password"] or ["google"];
omitted = every active federation provider plus password
:ttl-seconds : claim-link lifetime; server default 24h
:return-url : where a successful DIRECT (browser) claim redirects instead
of Synthigy's generic status page. Must match one of THIS
client's registered redirections (or be a loopback URI);
an unregistered value rejects with :code
"RETURN_URL_NOT_REGISTERED".
Returns a Promise of {:onboard_url ... :expires_at ... :user {:xid ...}} (wire keys as-is, like schema/lint). Rejects with ex-info carrying :code (e.g. "PROVISION_FORBIDDEN", "XID_REQUIRED", "USER_NOT_FOUND") and :status on failure.
Mint a one-time account-claim link (POST /oauth/onboard). Confidential
client whose principal administers the account — RBAC update on User plus
the row inside its owner-group write scope, which the shipped User
Provisioner role grants — else :code "PROVISION_FORBIDDEN". The
connected client's own client_credentials identity IS that principal, so
no separate credential is needed here.
xid : an EXISTING account's xid. Onboarding no longer creates
accounts — create it first over `/data` (with person_info,
roles, groups in one tree), then mint a ticket for it.
A blank xid rejects with :code "XID_REQUIRED"; one that
doesn't resolve rejects with :code "USER_NOT_FOUND".
:reset : soft-recycle this account first — strip its federated
identities, null its password, revoke live sessions/tokens
— before minting. Does NOT touch `active`; that flag is
your data, write it yourself.
:methods : restrict the claim page, e.g. ["password"] or ["google"];
omitted = every active federation provider plus password
:ttl-seconds : claim-link lifetime; server default 24h
:return-url : where a successful DIRECT (browser) claim redirects instead
of Synthigy's generic status page. Must match one of THIS
client's registered redirections (or be a loopback URI);
an unregistered value rejects with :code
"RETURN_URL_NOT_REGISTERED".
Returns a Promise of {:onboard_url ... :expires_at ... :user {:xid ...}}
(wire keys as-is, like schema/lint).
Rejects with ex-info carrying :code (e.g.
"PROVISION_FORBIDDEN", "XID_REQUIRED", "USER_NOT_FOUND") and :status
on failure.(onboard-complete ticket)Redeem an onboarding ticket without a browser (POST
/oauth/onboard/complete) — the indirect face of the SAME ticket onboard
mints. Must be called by the SAME client that minted the ticket; any
other client's bearer is rejected with :code "CLAIM_INVALID", and the
client must still administer the account (:code "PROVISION_FORBIDDEN").
The caller runs its own out-of-band proofing (email link, SMS OTP, push approval, KYC, a phone call — Synthigy never learns which) and, once satisfied, redeems the ticket itself instead of bouncing the user's browser through /oauth/claim. This never sets a credential — credentials are subject-only. The account activates with none; give it one via the claim page (password or a federated identity) or a later ticket.
ticket : the token minted by onboard (parse it out of :onboard_url)
-> {:user {:xid ...} :active true} Throws ex-info with :code (e.g. "CLAIM_INVALID") and :status on failure.
Redeem an onboarding ticket without a browser (POST
/oauth/onboard/complete) — the indirect face of the SAME ticket `onboard`
mints. Must be called by the SAME client that minted the ticket; any
other client's bearer is rejected with :code "CLAIM_INVALID", and the
client must still administer the account (:code "PROVISION_FORBIDDEN").
The caller runs its own out-of-band proofing (email link, SMS OTP, push
approval, KYC, a phone call — Synthigy never learns which) and, once
satisfied, redeems the ticket itself instead of bouncing the user's
browser through /oauth/claim. This never sets a credential — credentials
are subject-only. The account activates with none; give it one via the
claim page (password or a federated identity) or a later ticket.
ticket : the token minted by `onboard` (parse it out of :onboard_url)
-> {:user {:xid ...} :active true}
Throws ex-info with :code (e.g. "CLAIM_INVALID") and :status on failure.Redeem an onboarding ticket without a browser (POST
/oauth/onboard/complete) — the indirect face of the SAME ticket onboard
mints. Must be called by the SAME client that minted the ticket; any
other client's bearer is rejected with :code "CLAIM_INVALID", and the
client must still administer the account (:code "PROVISION_FORBIDDEN").
The caller runs its own out-of-band proofing (email link, SMS OTP, push approval, KYC, a phone call — Synthigy never learns which) and, once satisfied, redeems the ticket itself instead of bouncing the user's browser through /oauth/claim. This never sets a credential — credentials are subject-only. The account activates with none; give it one via the claim page (password or a federated identity) or a later ticket.
ticket : the token minted by onboard (parse it out of :onboard_url)
Returns a Promise of {:user {:xid ...} :active true}. Rejects with ex-info carrying :code (e.g. "CLAIM_INVALID") and :status on failure.
Redeem an onboarding ticket without a browser (POST
/oauth/onboard/complete) — the indirect face of the SAME ticket `onboard`
mints. Must be called by the SAME client that minted the ticket; any
other client's bearer is rejected with :code "CLAIM_INVALID", and the
client must still administer the account (:code "PROVISION_FORBIDDEN").
The caller runs its own out-of-band proofing (email link, SMS OTP, push
approval, KYC, a phone call — Synthigy never learns which) and, once
satisfied, redeems the ticket itself instead of bouncing the user's
browser through /oauth/claim. This never sets a credential — credentials
are subject-only. The account activates with none; give it one via the
claim page (password or a federated identity) or a later ticket.
ticket : the token minted by `onboard` (parse it out of :onboard_url)
Returns a Promise of {:user {:xid ...} :active true}. Rejects with
ex-info carrying :code (e.g. "CLAIM_INVALID") and :status on failure.(purge entity args selection & {:as opts})Find and delete matching records, return deleted data.
Args and selection accept kebab-case (normalized to snake_case). Selection supports shorthand syntax.
Find and delete matching records, return deleted data. Args and selection accept kebab-case (normalized to snake_case). Selection supports shorthand syntax.
Find and delete matching records. Returns a Promise of deleted data.
Find and delete matching records. Returns a Promise of deleted data.
(query xsql params & {:keys [op] :or {op "search"} :as opts})Run an XSQL query with optional ?name:type[] params. STRICT wire: sends
the xsql DOCUMENT op ({:op "xsql" :xsql <document> :params …}) — a
bare rooted body gets a synthetic @<op> _q header client-side; the
server derives verb/entity/selections/args from the document. :op
defaults to "search" — pass :op :get for a unique-key read.
Run an XSQL query with optional ?name:type[] params. STRICT wire: sends
the `xsql` DOCUMENT op ({:op "xsql" :xsql <document> :params …}) — a
bare rooted body gets a synthetic `@<op> _q` header client-side; the
server derives verb/entity/selections/args from the document. `:op`
defaults to "search" — pass :op :get for a unique-key read.Run an XSQL query with optional ?name:type[] params. STRICT wire: sends
the xsql DOCUMENT op ({:op "xsql" :xsql <document> :params …}) — a
bare rooted body gets a synthetic @<op> _q header client-side; the
server derives verb/entity/selections/args from the document. :op
defaults to "search" — pass :op :get for a unique-key read. Returns a Promise.
Run an XSQL query with optional ?name:type[] params. STRICT wire: sends
the `xsql` DOCUMENT op ({:op "xsql" :xsql <document> :params …}) — a
bare rooted body gets a synthetic `@<op> _q` header client-side; the
server derives verb/entity/selections/args from the document. `:op`
defaults to "search" — pass :op :get for a unique-key read. Returns a Promise.(runtime-model & {:as opts})Fetch the runtime ERD model (deployed + identity/audit/ref expansion). Requires dataset:load scope.
Fetch the runtime ERD model (deployed + identity/audit/ref expansion). Requires dataset:load scope.
Fetch the runtime ERD model (deployed + identity/audit/ref expansion). Requires dataset:load scope. Returns a Promise.
Fetch the runtime ERD model (deployed + identity/audit/ref expansion). Requires dataset:load scope. Returns a Promise.
(schema)(schema entities)Fetch the IAM-filtered deployed model via GET /schema.
Without entities: full schema.
With a seq of entity names (kebab-case strings or keywords): narrow
the response to those entities. Returns {:id-key :entities}.
Fetch the IAM-filtered deployed model via GET /schema.
Without `entities`: full schema.
With a seq of entity names (kebab-case strings or keywords): narrow
the response to those entities. Returns {:id-key :entities}.Fetch the IAM-filtered deployed model via GET /schema. Returns a Promise.
Fetch the IAM-filtered deployed model via GET /schema. Returns a Promise.
(search entity args selection & {:as opts})Search for entities matching args.
Args and selection accept kebab-case (normalized to snake_case). Selection supports shorthand syntax.
Search for entities matching args. Args and selection accept kebab-case (normalized to snake_case). Selection supports shorthand syntax.
Search for entities. Returns a Promise of the result data.
Search for entities. Returns a Promise of the result data.
(search-tree entity on args selection & {:as opts})Search matching entities + walk :on relation UP to their ancestors.
on is a keyword/string naming the tree (self-FK) relation.
Search matching entities + walk :on relation UP to their ancestors. `on` is a keyword/string naming the tree (self-FK) relation.
Search matching entities + walk :on relation UP to ancestors. Returns a Promise.
Search matching entities + walk :on relation UP to ancestors. Returns a Promise.
(slice entity args selection & {:as opts})Slice relations from entity.
Args and selection accept kebab-case (normalized to snake_case). Selection supports shorthand syntax.
Slice relations from entity. Args and selection accept kebab-case (normalized to snake_case). Selection supports shorthand syntax.
Slice relations from entity. Returns a Promise.
Slice relations from entity. Returns a Promise.
(sql-template template params & {:keys [cached] :or {cached true} :as opts})Execute an ERD-aware SQL template. Auto-generates FROM and JOINs from {entity.field} / {entity->rel.field} placeholders; supports junction, self-FK (tree), and field-ref traversal.
Execute an ERD-aware SQL template.
Auto-generates FROM and JOINs from {entity.field} / {entity->rel.field}
placeholders; supports junction, self-FK (tree), and field-ref traversal.Execute an ERD-aware SQL template. Returns a Promise.
Execute an ERD-aware SQL template. Returns a Promise.
(stack entity data & {:keys [returning] :as opts})Stack data on top of current state — returns {:count n}.
Data keys accept kebab-case (normalized to snake_case). Same :returning
contract as sync.
Stack data on top of current state — returns {:count n}.
Data keys accept kebab-case (normalized to snake_case). Same `:returning`
contract as sync.Stack data on top of current state — resolves to {:count n}. Same
:returning contract as sync.
Stack data on top of current state — resolves to {:count n}. Same
`:returning` contract as sync.(sync entity data & {:keys [returning] :as opts})Sync (upsert) entity data — returns {:count n}.
Data keys accept kebab-case (normalized to snake_case). Pass
:returning true for the written records; mint ids with
synthigy.client.core/new-xid when you need them up front.
Sync (upsert) entity data — returns {:count n}.
Data keys accept kebab-case (normalized to snake_case). Pass
`:returning true` for the written records; mint ids with
`synthigy.client.core/new-xid` when you need them up front.Sync (upsert) entity data — resolves to {:count n}. Pass :returning true
for the written records.
Sync (upsert) entity data — resolves to {:count n}. Pass `:returning true`
for the written records.(token)(token {:keys [audience]})Resolve a bearer access token via the client's provider.
Without opts: default token (for /data — Synthigy itself).
With :audience "svc": a token for another service that trusts
Synthigy as IdP.
Resolve a bearer access token via the client's provider. Without opts: default token (for /data — Synthigy itself). With `:audience "svc"`: a token for another service that trusts Synthigy as IdP.
Resolve a bearer access token via the client's provider. Returns a promise — the CLJS transport is async throughout.
Resolve a bearer access token via the client's provider. Returns a promise — the CLJS transport is async throughout.
(watch interest on-event & {:as _opts})Open a live SSE subscription for interest and stream change events to
on-event. Mirrors the TypeScript SDK's client.watch(interest).
Registers with the client's shared multiplexer: ALL watch / watch-query on a client share ONE /data/events stream and ONE consolidated subscription (their interests are unioned). No clobber, one connection.
interest is a map with any of:
:records — record xids (data-track)
:entities — entity names (entity-track: any change to the entity)
:relations — relation labels (relation-track)
:operations — optional op filter for the record track
Non-blocking. Returns a handle map: :interest (fn []) → current interest :set-interest (fn [interest]) → replace interest (re-unions + re-POSTs) :add (fn [xids]) → add record xids :remove (fn [xids]) → remove record xids :close (fn []) → deregister (stream stops when last closes)
Events are notification-only — refetch via search/get/batch on receipt (RLS-correct notify-then-refetch).
on-event runs on its OWN dedicated thread, decoupled via a
sliding-buffered core.async channel — a slow or blocking callback
(I/O, a lock, a slow UI redraw) delays only ITS OWN delivery, never
another watcher's, and never the shared SSE stream. Overflow (100
pending events) drops the oldest for that watcher only.
(def w (watch {:entities ["user"]} (fn [ev] (prn ev)))) ((:add w) ["some-xid"]) ((:close w))
Open a live SSE subscription for `interest` and stream change events to
`on-event`. Mirrors the TypeScript SDK's `client.watch(interest)`.
Registers with the client's shared multiplexer: ALL watch / watch-query
on a client share ONE /data/events stream and ONE consolidated
subscription (their interests are unioned). No clobber, one connection.
`interest` is a map with any of:
:records — record xids (data-track)
:entities — entity names (entity-track: any change to the entity)
:relations — relation labels (relation-track)
:operations — optional op filter for the record track
Non-blocking. Returns a handle map:
:interest (fn []) → current interest
:set-interest (fn [interest]) → replace interest (re-unions + re-POSTs)
:add (fn [xids]) → add record xids
:remove (fn [xids]) → remove record xids
:close (fn []) → deregister (stream stops when last closes)
Events are notification-only — refetch via search/get/batch on receipt
(RLS-correct notify-then-refetch).
`on-event` runs on its OWN dedicated thread, decoupled via a
sliding-buffered core.async channel — a slow or blocking callback
(I/O, a lock, a slow UI redraw) delays only ITS OWN delivery, never
another watcher's, and never the shared SSE stream. Overflow (100
pending events) drops the oldest for that watcher only.
(def w (watch {:entities ["user"]} (fn [ev] (prn ev))))
((:add w) ["some-xid"])
((:close w))Open a live SSE subscription for interest and stream change events to
on-event. Mirrors the TypeScript SDK's client.watch(interest) and
the CLJ SDK's watch — same interest shape, same shared multiplexer.
interest is a map with any of:
:records — record xids (data-track)
:entities — entity names (entity-track: any change to the entity)
:relations — relation labels (relation-track)
:operations — optional op filter for the record track
Non-blocking. Returns a handle map: :interest (fn []) → current interest :set-interest (fn [interest]) → replace interest (re-unions + re-POSTs) :add (fn [xids]) → add record xids :remove (fn [xids]) → remove record xids :close (fn []) → deregister (stream stops when last closes)
Events are notification-only — refetch via search/get/batch on receipt (RLS-correct notify-then-refetch).
(def w (watch {:entities ["user"]} (fn [ev] (js/console.log ev)))) ((:add w) ["some-xid"]) ((:close w))
Open a live SSE subscription for `interest` and stream change events to
`on-event`. Mirrors the TypeScript SDK's `client.watch(interest)` and
the CLJ SDK's `watch` — same interest shape, same shared multiplexer.
`interest` is a map with any of:
:records — record xids (data-track)
:entities — entity names (entity-track: any change to the entity)
:relations — relation labels (relation-track)
:operations — optional op filter for the record track
Non-blocking. Returns a handle map:
:interest (fn []) → current interest
:set-interest (fn [interest]) → replace interest (re-unions + re-POSTs)
:add (fn [xids]) → add record xids
:remove (fn [xids]) → remove record xids
:close (fn []) → deregister (stream stops when last closes)
Events are notification-only — refetch via search/get/batch on receipt
(RLS-correct notify-then-refetch).
(def w (watch {:entities ["user"]} (fn [ev] (js/console.log ev))))
((:add w) ["some-xid"])
((:close w))(watch-query entity args selection & {:as opts})Live, RLS-scoped result-set for a search. Clojure-flavored: returns an
ATOM holding the current result vector, kept fresh in the background —
deref it, add-watch it, close-watch! it.
(def msgs (watch-query "Chat Message" {:-where {...}} [:content] :acting-as user-xid)) @msgs ; current results (add-watch msgs :ui (fn [_ _ old new] ...)) ; react to changes (close-watch! msgs) ; stop
Mirrors the TS SDK's watchQuery: snapshot via search, register the
entity interest with the shared multiplexer, and on each matching event
do a coalesced re-run + reset! the atom (notify-then-refetch — RLS stays
correct because the refetch carries the same :acting-as). Many
watch-queries with different :acting-as / interests now coexist on ONE
client and ONE stream — the multiplexer unions their interests.
Options: :acting-as / :key-format — forwarded to the snapshot + every refetch :relations / :records — widen the watch interest beyond the entity :entity-track — false to DROP the entity-wide interest and watch only :records/:relations (default true). Use with :records to scope a live view to a "room" — e.g. a group record whose link events fire when children attach to it. :track-rows — also keep the CURRENT result rows' xids in the interest, re-registered after every refetch. With :entity-track false this makes edits/deletes of already-visible rows fire (their events carry only the row's own xid). :debounce-ms — coalesce window for bursts (default 80)
Live, RLS-scoped result-set for a `search`. Clojure-flavored: returns an
ATOM holding the current result vector, kept fresh in the background —
deref it, `add-watch` it, `close-watch!` it.
(def msgs (watch-query "Chat Message" {:-where {...}} [:content]
:acting-as user-xid))
@msgs ; current results
(add-watch msgs :ui (fn [_ _ old new] ...)) ; react to changes
(close-watch! msgs) ; stop
Mirrors the TS SDK's watchQuery: snapshot via `search`, register the
entity interest with the shared multiplexer, and on each matching event
do a coalesced re-run + reset! the atom (notify-then-refetch — RLS stays
correct because the refetch carries the same :acting-as). Many
watch-queries with different :acting-as / interests now coexist on ONE
client and ONE stream — the multiplexer unions their interests.
Options:
:acting-as / :key-format — forwarded to the snapshot + every refetch
:relations / :records — widen the watch interest beyond the entity
:entity-track — false to DROP the entity-wide interest and
watch only :records/:relations (default true).
Use with :records to scope a live view to a
"room" — e.g. a group record whose link
events fire when children attach to it.
:track-rows — also keep the CURRENT result rows' xids in
the interest, re-registered after every
refetch. With :entity-track false this makes
edits/deletes of already-visible rows fire
(their events carry only the row's own xid).
:debounce-ms — coalesce window for bursts (default 80)Live, RLS-scoped result-set for a search. Returns a Promise of an
ATOM holding the current result vector, kept fresh in the background —
await it once, then deref/add-watch/close-watch! it like any atom.
(p/let [msgs (watch-query "Chat Message" {:-where {...}} [:content])] (add-watch msgs :ui (fn [_ _ old new] ...)) (js/console.log @msgs))
Mirrors the TS SDK's watchQuery / the CLJ SDK's watch-query: snapshot
via search, register the entity interest with the shared multiplexer,
and on each matching event do a coalesced re-run + reset! the atom
(notify-then-refetch — RLS stays correct because the refetch carries the
same :acting-as). Many watch-queries now coexist on ONE client and ONE
stream — the multiplexer unions their interests.
Options: same as the CLJ SDK's watch-query (:acting-as/:key-format, :relations/:records, :entity-track, :track-rows, :debounce-ms).
Live, RLS-scoped result-set for a `search`. Returns a Promise of an
ATOM holding the current result vector, kept fresh in the background —
await it once, then deref/add-watch/close-watch! it like any atom.
(p/let [msgs (watch-query "Chat Message" {:-where {...}} [:content])]
(add-watch msgs :ui (fn [_ _ old new] ...))
(js/console.log @msgs))
Mirrors the TS SDK's watchQuery / the CLJ SDK's watch-query: snapshot
via `search`, register the entity interest with the shared multiplexer,
and on each matching event do a coalesced re-run + reset! the atom
(notify-then-refetch — RLS stays correct because the refetch carries the
same :acting-as). Many watch-queries now coexist on ONE client and ONE
stream — the multiplexer unions their interests.
Options: same as the CLJ SDK's watch-query (:acting-as/:key-format,
:relations/:records, :entity-track, :track-rows, :debounce-ms).(watch-schema on-event & {:keys [raw]})Stream model-deploy events over the shared multiplexer. Mirrors the TS
SDK's client.watchSchema(). :raw true watches the raw deployed model;
default the runtime model. Returns a handle with :close.
Stream model-deploy events over the shared multiplexer. Mirrors the TS SDK's client.watchSchema(). `:raw true` watches the raw deployed model; default the runtime model. Returns a handle with :close.
(watch-sql-template template params & {:keys [entities] :as opts})Live, RLS-scoped result for a SQL template — the ad-hoc twin of the
watch-<name> a codegen'd @watch sql-template emits. Returns the same
closeable atom as watch-query.
(def stats (watch-sql-template "SELECT count(*) AS n FROM {movie}" nil :entities ["Movie"])) @stats ; current rows (close-watch! stats) ; stop
:entities is required: a template has no root entity, so there is nothing
to infer the multiplexer interest from. Otherwise takes watch-query's
options.
Live, RLS-scoped result for a SQL template — the ad-hoc twin of the
`watch-<name>` a codegen'd `@watch` sql-template emits. Returns the same
closeable atom as `watch-query`.
(def stats (watch-sql-template "SELECT count(*) AS n FROM {movie}" nil
:entities ["Movie"]))
@stats ; current rows
(close-watch! stats) ; stop
`:entities` is required: a template has no root entity, so there is nothing
to infer the multiplexer interest from. Otherwise takes `watch-query`'s
options.Live, RLS-scoped result for a SQL template — the ad-hoc twin of the
watch-<name> a codegen'd @watch sql-template emits. Returns the same
Promise<atom> as watch-query; close-watch! stops it.
:entities is required: a template has no root entity, so there is nothing
to infer the multiplexer interest from. Otherwise takes watch-query's
options.
Live, RLS-scoped result for a SQL template — the ad-hoc twin of the `watch-<name>` a codegen'd `@watch` sql-template emits. Returns the same Promise<atom> as `watch-query`; `close-watch!` stops it. `:entities` is required: a template has no root entity, so there is nothing to infer the multiplexer interest from. Otherwise takes `watch-query`'s options.
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |