Lix is a JavaScript SDK for change control that enables Git-like capabilities for apps and agents: Change proposals, versioning (branching), history, blame, and more.
Install the Lix SDK package:
Open a lix with openLix()
and provide plugins for files types you are going to use.
import { openLix } from "@lix-js/sdk";
import { plugin as jsonPlugin } from "@lix-js/plugin-json";
const lix = await openLix({
providePlugins: [jsonPlugin],
});
Key points:
jsonPlugin
enables Lix to understand JSON file changes at a granular level (e.g., detecting which specific properties changed)Inserting a file is the first step to start controlling changes with Lix. When you insert a file:
import { openLix } from "@lix-js/sdk";
import { plugin as jsonPlugin } from "@lix-js/plugin-json";
const lix = await openLix({
providePlugins: [jsonPlugin],
});
const json = {
name: "Peter",
age: 50,
};
await lix.db
.insertInto("file")
.values({
path: "/example.json",
data: new TextEncoder().encode(JSON.stringify(json)),
})
.execute();
const fileAfterInsert = await lix.db
.selectFrom("file")
.select(["path", "data"])
.executeTakeFirstOrThrow();
console.log("File after insert:", [
{
...fileAfterInsert,
data: JSON.parse(new TextDecoder().decode(fileAfterInsert.data)),
},
]);
Key points:
Uint8Array
(binary data), making Lix format-agnosticpath
acts as the unique identifier for the fileOnce a file is under Lix's change control, updates work just like regular database operations. No special commands or workflows - just update the data and Lix handles the rest.
import { openLix } from "@lix-js/sdk";
import { plugin as jsonPlugin } from "@lix-js/plugin-json";
const lix = await openLix({
providePlugins: [jsonPlugin],
});
const json = {
name: "Peter",
age: 50,
};
await lix.db
.insertInto("file")
.values({
path: "/example.json",
data: new TextEncoder().encode(JSON.stringify(json)),
})
.execute();
const fileAfterInsert = await lix.db
.selectFrom("file")
.select(["path", "data"])
.executeTakeFirstOrThrow();
console.log("File after insert:", [
{
...fileAfterInsert,
data: JSON.parse(new TextDecoder().decode(fileAfterInsert.data)),
},
]);
// we update the user's age to 51
await lix.db
.updateTable("file")
.where("path", "=", "/example.json")
.set({
data: new TextEncoder().encode(
JSON.stringify({ name: "Peter", age: 51 })
),
})
.execute();
const fileAfterChange = await lix.db
.selectFrom("file")
.where("path", "=", "/example.json")
.select([
"path",
"data",
// TODO https://github.com/opral/lix-sdk/issues/348 expose change set id
// "lixcol_change_set_id",
])
.executeTakeFirstOrThrow();
console.log("File after change:", [
{
...fileAfterChange,
data: JSON.parse(new TextDecoder().decode(fileAfterChange.data)),
},
]);
Key points:
age
property from 50 to 51)With files under change control, you can query their complete history using standard SQL. Every change is preserved and accessible.
import { openLix } from "@lix-js/sdk";
import { plugin as jsonPlugin } from "@lix-js/plugin-json";
const lix = await openLix({
providePlugins: [jsonPlugin],
});
const json = {
name: "Peter",
age: 50,
};
await lix.db
.insertInto("file")
.values({
path: "/example.json",
data: new TextEncoder().encode(JSON.stringify(json)),
})
.execute();
const fileAfterInsert = await lix.db
.selectFrom("file")
.select(["path", "data"])
.executeTakeFirstOrThrow();
console.log("File after insert:", [
{
...fileAfterInsert,
data: JSON.parse(new TextDecoder().decode(fileAfterInsert.data)),
},
]);
// we update the user's age to 51
await lix.db
.updateTable("file")
.where("path", "=", "/example.json")
.set({
data: new TextEncoder().encode(
JSON.stringify({ name: "Peter", age: 51 })
),
})
.execute();
const fileAfterChange = await lix.db
.selectFrom("file")
.where("path", "=", "/example.json")
.select([
"path",
"data",
// TODO https://github.com/opral/lix-sdk/issues/348 expose change set id
// "lixcol_change_set_id",
])
.executeTakeFirstOrThrow();
console.log("File after change:", [
{
...fileAfterChange,
data: JSON.parse(new TextDecoder().decode(fileAfterChange.data)),
},
]);
const activeVersion = await lix.db
.selectFrom("active_version")
.innerJoin("version", "active_version.version_id", "version.id")
.select("version.change_set_id")
.executeTakeFirstOrThrow();
// Query file history from the current change set
const fileHistory = await lix.db
.selectFrom("file_history")
.where("path", "=", "/example.json")
.where("lixcol_root_change_set_id", "=", activeVersion.change_set_id)
.orderBy("lixcol_depth", "asc")
.select(["path", "data", "lixcol_depth"])
.execute();
console.log(
"File history:",
fileHistory.map((f) => ({
...f,
data: JSON.parse(new TextDecoder().decode(f.data)),
}))
);
Key points:
file_history
provides access to all previous states of a fileTo learn more about Lix and its capabilities, check out: