npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@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-json

Quick 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: true

Query 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: applyChanges patches 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.