Getting Started
This walks through opening Lix, registering a schema, writing a row, isolating a change in a separate version, previewing the merge, and merging.
Install
npm install @lix-js/sdk
openLix() with no arguments opens an in-memory Lix, enough for tests and demos. For persistent local files, use FsBackend; see Persistence.
Open Lix
import { openLix } from "@lix-js/sdk";
const lix = await openLix();
import { FsBackend, openLix } from "@lix-js/sdk";
const lix = await openLix({
backend: new FsBackend({ path: "./workspace" }),
});
Register a schema
Lix stores application state as typed entities. Register a schema once, then read and write through the generated SQL table named after x-lix-key.
await lix.execute(
"INSERT INTO lix_registered_schema (value) VALUES (lix_json($1))",
[
JSON.stringify({
$schema: "https://json-schema.org/draft/2020-12/schema",
"x-lix-key": "task",
"x-lix-primary-key": ["/id"],
type: "object",
required: ["id", "title", "done"],
properties: {
id: { type: "string" },
title: { type: "string" },
done: { type: "boolean" },
},
additionalProperties: false,
}),
],
);
lix_json($1) parses the JSON text into the JSON-typed value column. Schema details (the x-lix-* fields, primary keys, uniqueness) are covered in Schemas.
Write and read state
await lix.execute("INSERT INTO task (id, title, done) VALUES ($1, $2, $3)", [
"task-1",
"Review agent changes",
false,
]);
const result = await lix.execute(
"SELECT id, title, done FROM task WHERE id = $1",
["task-1"],
);
const row = result.rows[0]!;
console.log(row.value("title").asText(), row.value("done").asBoolean());
execute() returns { columns, rows, rowsAffected, notices }.
Read result values
Each row is a Row. Use row.value(name) for a typed Value, or row.get(name) / row.toObject() for plain JavaScript values.
| Accessor | Use for |
|---|---|
asText() | text columns |
asBoolean() | boolean columns |
asInteger() | integer columns |
asReal() | decimal columns |
asJson() | JSON columns such as snapshot_content and entity_pk |
asBytes() | binary columns such as lix_file.data |
Use asBytes() for byte content:
const file = await lix.execute("SELECT data FROM lix_file WHERE path = $1", [
"/orders.xlsx",
]);
const bytes = file.rows[0]!.value("data").asBytes();
Isolate a change in a version
A version is an isolated line of state. Create one for the change, switch into it, and edit:
const main = await lix.activeVersionId();
const draft = await lix.createVersion({ name: "Agent draft" });
await lix.switchVersion({ versionId: draft.id });
await lix.execute("UPDATE task SET done = $1 WHERE id = $2", [true, "task-1"]);
await lix.switchVersion({ versionId: main });
The active version is now main again, and task-1 is still done = false here. The draft change is isolated until you merge.
Preview and merge
const preview = await lix.mergeVersionPreview({ sourceVersionId: draft.id });
console.log(preview.outcome, preview.changeStats);
// fastForward { total: 1, added: 0, modified: 1, removed: 0 }
if (preview.conflicts.length === 0) {
await lix.mergeVersion({ sourceVersionId: draft.id });
}
mergeVersionPreview() reports the same merge decision as mergeVersion() without advancing refs. It returns the per-row conflict list when both sides changed the same entity. See Versions & Merging.
The loop
- Open Lix.
- Register schemas for the entities you want to version.
- Write and read through generated tables.
- Create versions for isolated work.
- Preview, then merge or discard.
- Query
lix_changefor audit and undo.