@lix-js/plugin-json
v1.0.1
Published
Plugin for [Lix](https://lix.dev) that adds support for `.json` files.
Readme
Lix Plugin .json
Plugin for Lix that adds support for .json files.
It parses JSON files, tracks leaf values as individual entities using JSON Pointer, and enables granular history and updates.
Installation
npm install @lix-js/sdk @lix-js/plugin-jsonQuick start
import { openLix, newLixFile } from "@lix-js/sdk";
import { plugin as jsonPlugin } from "@lix-js/plugin-json";
const lixFile = await newLixFile();
const lix = await openLix({
blob: lixFile,
providePlugins: [jsonPlugin],
});Insert a JSON file
const file = await lix.db
.insertInto("file")
.values({
path: "/config.json",
data: new TextEncoder().encode(
JSON.stringify({ apiKey: "abc", features: { search: true } }, null, 2),
),
})
.returningAll()
.executeTakeFirstOrThrow();Update the file
The plugin automatically detects changes at the pointer level (e.g., /features/search).
await lix.db
.updateTable("file")
.set({
data: new TextEncoder().encode(
JSON.stringify(
{ apiKey: "abc", features: { search: false, ai: true } },
null,
2,
),
),
})
.where("id", "=", file.id)
.execute();Query file history
Retrieve previous versions of the file as a whole:
const history = await lix.db
.selectFrom("file_history")
.where("path", "=", "/config.json")
.select(["data", "lixcol_commit_id"])
.execute();
for (const version of history) {
const content = new TextDecoder().decode(version.data);
console.log(`Commit ${version.lixcol_commit_id}: ${content}`);
}Advanced usage
Query specific JSON values
Every leaf value (string, number, boolean, null) is stored as a separate entity addressed by its JSON Pointer. You can query these directly:
const values = await lix.db
.selectFrom("state")
.where("file_id", "=", file.id)
.where("schema_key", "=", "plugin_json_pointer_value")
.select(["snapshot_content"])
.execute();
for (const row of values) {
const { path, value } = row.snapshot_content;
console.log(`${path}: ${value}`);
}
// Output:
// /apiKey: "abc"
// /features/search: false
// /features/ai: trueQuery the history of a specific property
Track how a specific value changed over time without parsing the entire file history.
First, find the entity ID for the pointer you are interested in (or filter by content if supported):
// 1. Find the entity ID for '/features/search'
const entity = await lix.db
.selectFrom("state")
.where("file_id", "=", file.id)
.where("schema_key", "=", "plugin_json_pointer_value")
.selectAll()
.execute()
.then((rows) =>
rows.find((row) => row.snapshot_content.path === "/features/search"),
);
if (entity) {
// 2. Query its history
const history = await lix.db
.selectFrom("state_history")
.where("entity_id", "=", entity.entity_id)
.orderBy("lixcol_commit_id", "desc")
.select(["snapshot_content", "lixcol_commit_id"])
.execute();
for (const state of history) {
console.log(
`Value: ${state.snapshot_content.value} (commit: ${state.lixcol_commit_id})`,
);
}
}Programmatically update JSON content
You can update a specific value in the database without rewriting the file text. The plugin will reconstruct the file content automatically.
// Update just the 'apiKey'
// Note: You usually need the entity_id, which you can look up as shown above.
await lix.db
.updateTable("state")
.set({
snapshot_content: {
path: "/apiKey",
value: "xyz_new_key",
},
})
.where("entity_id", "=", apiKeyEntityId)
.execute();Schemas
The plugin uses a single schema to represent leaf nodes in the JSON tree.
| Schema key | Description |
| --------------------------- | ----------------------------------------------------------------------------------------- |
| plugin_json_pointer_value | Represents a leaf value (string, number, boolean, null) at a specific JSON Pointer path. |
Snapshot content structure
The snapshot_content for plugin_json_pointer_value looks like this:
{
path: "/features/search", // RFC 6901 JSON Pointer
value: true // The value (string, number, boolean, or null)
}How it works
- JSON Pointer granularity: Every leaf value is addressed by its pointer (e.g.,
/features/search), and each pointer becomes a persisted entity in Lix. - Object diffing: Property additions, edits, and deletions are tracked independently per key.
- Array handling: Array items are addressed by index (e.g.,
/items/0,/items/1).- Note: Insertions or reorderings in arrays will change the indices of subsequent items, appearing as updates to those pointers.
- Apply phase:
applyChangespatches the parsed JSON with the incoming pointer changes and writes a serialized JSON document back to the file.
Limitations and tips
- Arrays are position-based: Reordering items or inserting into the middle of an array will shift indices, causing "changes" for all subsequent items. Use stable objects (keyed maps) instead of arrays where possible if minimizing diff noise is important.
- Valid JSON required: The plugin expects valid JSON input; invalid JSON cannot be parsed or diffed.
- Leaf-only tracking: Container objects and arrays themselves are not stored as entities; only their leaf descendants are. The structure is implied by the union of all paths.
