@vantage-ui/client
v0.6.0
Published
A vantage-ui project as a typed, live object graph: lazy references and collections, optimistic edits, server-side actions, React bindings.
Maintainers
Readme
@vantage-ui/client
The TypeScript client for a Vantage API. It turns the API into typed models. Reads are lazy and stay current: the server pushes changes, and a React component re-renders on exactly the fields it read. Writes are optimistic and roll back when the server refuses them.
It runs in the browser and in Node 22 or later, and has no runtime dependencies. The React bindings need React 18 or later.
npm install @vantage-ui/clientGenerate the models
The models come from the API's schema at /v1/schema:
npx vantage-client generate --from http://127.0.0.1:8090/v1/schema --out src/vantage.gen.ts--from also takes a saved copy of the schema (schema.json). Regenerate
after the API changes; never edit the generated file. Per entity it has
XFields (the record), XInput (what create() takes) and the model X,
with typed columns, lazy references, has-many collections and a method per
row action.
Connect
A page served by the API connects to /v1/ws on its own origin:
import { createVantage } from "@vantage-ui/client";
import { schema, type Schema } from "./vantage.gen";
const db = createVantage<Schema>({ schema, token: () => sessionStorage.getItem("vantage.token") ?? undefined });Outside a browser, pass url: "ws://127.0.0.1:8090/v1/ws". db.ready
resolves once the API's instant entities have loaded, and db.connection
reports connecting, open, closed (reconnecting), refused or
protocol_mismatch.
Read
const open = db.issues.where({ status: "open" }).sort("created_at", "desc");
await open.load();
const titles = open.map((issue) => issue.title);
const page = await db.issues.window(0, 50).whenLoaded(); // one window of a long list
const issue = await db.issues.ref("JIR-12").load();
const project = await issue?.project; // references load on first readThe server filters, sorts and pages; the API decides which columns may be
filtered and sorted. A refused query never throws: the collection's error
holds the server's message.
Write
const result = await issue.patch({ status: "done" });
if (!result.ok) console.warn(result.error.message, issue.$errors);
const draft = issue.edit();
draft.title = "Login fails on Safari";
await draft.$flash();
await db.issues.create({ title: "Export to CSV", project: "JIR", status: "open" });Actions and runs
Row actions are methods on the model, and $actions lists the ones the row
allows right now. Every action runs as a run, with shared state and messages,
that survives a reconnect or a page reload without running twice:
if (issue.$actions.includes("close_issue")) await issue.close_issue({ resolution: "fixed" });
const run = db.run.triage.start({ project: "JIR" });
run.on("flagged", (m) => console.warn(`${m.issue} has no assignee`));
const outcome = await run.done;React
import { VantageProvider, observer, useCollection, useVantage } from "@vantage-ui/client/react";
const OpenIssues = observer(() => {
const db = useVantage<Schema>();
const { items, error } = useCollection(db.issues.where({ status: "open" }));
if (error) return <p role="alert">{error}</p>;
return <ul>{items.map((issue) => <li key={issue.id}>{issue.title}</li>)}</ul>;
});
<VantageProvider db={db} fallback={<p>Connecting…</p>}>
<OpenIssues />
</VantageProvider>;There are also hooks for windows (useWindow), records, actions, runs
(useRun, useShared, useRunMessage) and the connection (useConnection).
Testing
@vantage-ui/client/testing has MockServer, an in-memory server with no
network, for unit tests and Storybook:
import { createTestVantage } from "@vantage-ui/client/testing";
const { db, server } = createTestVantage<Schema>(schema, {
tables: { issues: [{ id: "JIR-1", title: "Login fails", status: "open", project: "JIR" }] },
actions: { "issues.close_issue": () => ({ status: "done" }) },
});Full documentation
The package ships an agent skill that is also its reference documentation:
skills/vantage-client/SKILL.md, with references/react.md,
references/testing.md and references/protocol.md. Every example in it is
type-checked and run in this package's tests.
License
MIT
