A fully type-safe, TypeScript-first in-memory property graph database with a Cypher-like query language, a type-safe TinkerPop / Gremlin-style traversal API (GraphTraversal), multiple index types, and pluggable storage adapters including a Yjs CRDT-based adapter for collaborative/realtime/offline-first use.
This is the knowledge graph database for the codemix product intelligence platform.
| Package | Description |
|---|---|
@codemix/graph |
Core graph database: Cypher queries + type-safe Gremlin-style traversals (GraphTraversal) |
@codemix/text-search |
BM25-based full-text search with English stemming |
@codemix/y-graph-storage |
Yjs CRDT storage adapter for collaborative/offline-first use |
@codemix/graphA fully typed, in-memory graph database. Vertices and edges are strongly typed against a user-defined schema. You can query with a subset of Cypher or with a fluent, type-safe Apache TinkerPop / Gremlin-style API — see GraphTraversal in the package docs.
GraphTraversal exposes familiar steps (V(), E(), out(), in(), both(), hasLabel(), as() / select(), repeat() …) with schema-derived typing on paths and properties; pairs with AsyncGraph.query for remote executionMATCH, WHERE, RETURN, CREATE, SET, DELETE, MERGE, UNWIND, UNION, WITH, multi-statement queries, and moreAsyncGraph supports serializable operations for use across network boundariesreadonly: true to prevent mutation stepspnpm add @codemix/graph
Running pnpm install in the monorepo configures Git to use the tracked hooks in .githooks.
The pre-commit hook formats staged supported source files with oxfmt and restages them automatically. You can re-run the hook setup at any time with pnpm run setup:hooks.
import { Graph, GraphSchema } from "@codemix/graph";
import { InMemoryGraphStorage } from "@codemix/graph";
import * as z from "zod";
// 1. Define your schema
const schema = {
vertices: {
Person: {
properties: {
name: { type: z.string() },
age: { type: z.number() },
},
},
},
edges: {
knows: { properties: {} },
},
} as const satisfies GraphSchema;
// 2. Create the graph
const graph = new Graph({
schema,
storage: new InMemoryGraphStorage(),
});
// 3. Add data
const alice = graph.addVertex("Person", { name: "Alice", age: 30 });
const bob = graph.addVertex("Person", { name: "Bob", age: 25 });
graph.addEdge(alice, "knows", bob, {});
// 4. Query with Cypher
const results = graph.query("MATCH (a:Person)-[:knows]->(b:Person) RETURN a.name, b.name");
// [{ a: { name: "Alice" }, b: { name: "Bob" } }]
The same graph is navigable with a fluent API modeled on Apache TinkerPop Gremlin; labels and properties stay typed end-to-end:
import { GraphTraversal } from "@codemix/graph";
const g = new GraphTraversal(graph);
for (const path of g.V().hasLabel("Person").out("knows")) {
console.log(path.value.get("name"));
}
See the type-safe TinkerPop / Gremlin traversal API section in @codemix/graph for the full step reference.
import { GraphSchema } from "@codemix/graph";
import * as z from "zod";
const schema = {
vertices: {
Product: {
properties: {
sku: { type: z.string() },
price: { type: z.number().positive() },
name: {
type: z.string(),
index: { type: "fulltext" }, // full-text search index
},
},
indexes: {
sku: { type: "hash", unique: true }, // unique hash index
},
},
},
edges: {
PURCHASED: {
properties: {
quantity: { type: z.number().int().positive() },
},
},
},
} as const satisfies GraphSchema;
| Type | Lookup | Use case |
|---|---|---|
hash |
O(1) | Equality (=) on high-cardinality properties |
btree |
O(log n) | Range queries (>, <, >=, <=, BETWEEN) |
fulltext |
BM25 scored | CONTAINS / free-text search via @codemix/text-search |
All index types support a unique: true constraint that throws UniqueConstraintViolationError on duplicate values.
MATCH with node and relationship patterns, variable-length paths (*1..5), shortest pathWHERE with boolean logic, property access, IN, STARTS WITH, ENDS WITH, CONTAINS, IS NULL, IS NOT NULL, label expressions (IS LABELED)RETURN with aliases (AS), DISTINCT, ORDER BY, SKIP, LIMITCREATE, SET, DELETE, REMOVE, MERGEWITH (pipeline intermediate results)UNWINDUNION / UNION ALLCALL procedures (built-in and custom via ProcedureRegistry)COUNT, SUM, AVG, MIN, MAX, COLLECTREDUCE, EXISTS subqueriesdate, datetime, localtime, localdatetime, durationCASE expressions (simple and searched)For advanced use cases, you can parse a query to a step plan without running it:
import { parseQueryToSteps } from "@codemix/graph";
const { steps, postprocess } = parseQueryToSteps(
"MATCH (n:Person) RETURN n.name",
{ readonly: true }, // throws ReadonlyGraphError if query mutates
);
import { functionRegistry, procedureRegistry } from "@codemix/graph";
// Register a custom scalar function
functionRegistry.register("myLib.greet", {
call: ([name]) => `Hello, ${name}!`,
});
// Register a custom procedure
procedureRegistry.register("myLib.listItems", {
call: function* (ctx, [], yields) {
yield { item: "foo" };
yield { item: "bar" };
},
});
AsyncGraph wraps a regular Graph and exposes mutations as serializable operation objects. This is useful for sending graph mutations over a network or message bus.
import { AsyncGraph } from "@codemix/graph";
const asyncGraph = new AsyncGraph({
schema,
storage: new InMemoryGraphStorage(),
});
// Subscribe to operations emitted by writes
asyncGraph.on("operation", (op) => {
sendToRemote(op); // op is a plain JSON-serializable object
});
asyncGraph.addVertex("Person", { name: "Alice", age: 30 });
@codemix/text-searchA lightweight, dependency-free full-text search library used internally by @codemix/graph for full-text indexes.
import { createMatcher, rankDocuments } from "@codemix/text-search";
// Score a single document
const match = createMatcher("quick brown fox");
match("The quick brown fox jumps over the lazy dog"); // ~0.85
match("A slow gray elephant"); // ~0.0
// Rank a list of documents
const results = rankDocuments("database performance", [
"How to improve database query performance",
"Database connection pooling best practices",
"Unrelated article about cooking",
]);
// Returns documents sorted by relevance score descending
@codemix/y-graph-storageA Yjs CRDT-backed storage adapter for @codemix/graph. Enables real-time collaborative and offline-first graph databases that sync automatically between peers.
Y.Doc — compatible with any Yjs provider (WebSocket, WebRTC, IndexedDB, etc.)ZodYTypes helpers for schema validationimport * as Y from "yjs";
import { Graph } from "@codemix/graph";
import { YGraphStorage } from "@codemix/y-graph-storage";
const doc = new Y.Doc();
const storage = new YGraphStorage(doc, schema);
const graph = new Graph({ schema, storage });
// Changes to the graph are automatically reflected in the Y.Doc
// and will sync to connected peers via any Yjs provider
graph.addVertex("Person", { name: "Alice", age: 30 });
This is a pnpm monorepo managed with pnpm workspaces.
pnpm install
| Command | Description |
|---|---|
pnpm test |
Run all tests across packages (watch mode) |
pnpm test:coverage |
Run all tests with coverage report |
pnpm build |
Build all packages |
pnpm typecheck |
Type-check all packages |
pnpm lint |
Lint with oxlint |
pnpm lint:fix |
Lint and auto-fix |
pnpm format |
Format with oxfmt |
pnpm format:check |
Check formatting |
# Run tests for a single package
pnpm --filter @codemix/graph test
# Build a single package
pnpm --filter @codemix/graph build
MIT
$ claude mcp add graph \
-- python -m otcore.mcp_server <graph>