Skip to content

Commit 018bee4

Browse files
committed
New repo
0 parents  commit 018bee4

378 files changed

Lines changed: 140588 additions & 0 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.gitignore

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2+
3+
# dependencies
4+
node_modules
5+
.pnpm-store
6+
.pnp
7+
.pnp.*
8+
.yarn/*
9+
!.yarn/patches
10+
!.yarn/plugins
11+
!.yarn/releases
12+
!.yarn/versions
13+
.wrangler
14+
15+
# testing
16+
coverage
17+
18+
# next.js
19+
.next/
20+
out/
21+
22+
# production
23+
build
24+
25+
# misc
26+
.DS_Store
27+
*.pem
28+
29+
# debug
30+
npm-debug.log*
31+
yarn-debug.log*
32+
yarn-error.log*
33+
.pnpm-debug.log*
34+
35+
# env files (can opt-in for committing if needed)
36+
.env*
37+
38+
# vercel
39+
.vercel
40+
41+
# typescript
42+
*.tsbuildinfo
43+
next-env.d.ts
44+
45+
.claude
46+
.mcp.json

README.md

Lines changed: 286 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,286 @@
1+
# @codemix/graph
2+
3+
A full type safe,TypeScript-first in-memory property graph database with a Cypher query language parser, multiple index types, and pluggable storage adapters.
4+
5+
## Packages
6+
7+
| Package | Version | Description |
8+
| -------------------------------------------------------- | ------- | -------------------------------------------------------------------------------- |
9+
| [`@codemix/graph`](./packages/graph) | 0.0.1 | Core graph database with Cypher query support |
10+
| [`@codemix/text-search`](./packages/text-search) | 0.0.1 | BM25-based full-text search with English stemming |
11+
| [`@codemix/y-graph-storage`](./packages/y-graph-storage) | 0.0.1 | [Yjs](https://yjs.dev/) CRDT storage adapter for collaborative/offline-first use |
12+
13+
---
14+
15+
## `@codemix/graph`
16+
17+
A fully typed, in-memory graph database. Vertices and edges are strongly typed against a user-defined schema. Queries are written in a subset of [Cypher](https://neo4j.com/docs/cypher-manual/current/).
18+
19+
### Features
20+
21+
- **Cypher query language** — parsed via a PEG grammar, supporting `MATCH`, `WHERE`, `RETURN`, `CREATE`, `SET`, `DELETE`, `MERGE`, `UNWIND`, `UNION`, `WITH`, multi-statement queries, and more
22+
- **Strongly typed schema** — vertex/edge labels and their properties are defined once and inferred throughout
23+
- **Standard Schema validation** — property values are validated using [Standard Schema](https://standardschema.dev/) compatible validators (works with Zod, Valibot, etc.)
24+
- **Multiple index types** — hash (O(1) equality), B-tree (O(log n) range), and full-text (BM25-scored)
25+
- **Async/distributed graph**`AsyncGraph` supports serializable operations for use across network boundaries
26+
- **Readonly mode** — parse queries with `readonly: true` to prevent mutation steps
27+
28+
### Installation
29+
30+
```bash
31+
pnpm add @codemix/graph
32+
```
33+
34+
### Quick Start
35+
36+
```typescript
37+
import { Graph, GraphSchema } from "@codemix/graph";
38+
import { InMemoryGraphStorage } from "@codemix/graph";
39+
import * as z from "zod";
40+
41+
// 1. Define your schema
42+
const schema = {
43+
vertices: {
44+
Person: {
45+
properties: {
46+
name: { type: z.string() },
47+
age: { type: z.number() },
48+
},
49+
},
50+
},
51+
edges: {
52+
knows: { properties: {} },
53+
},
54+
} as const satisfies GraphSchema;
55+
56+
// 2. Create the graph
57+
const graph = new Graph({
58+
schema,
59+
storage: new InMemoryGraphStorage(),
60+
});
61+
62+
// 3. Add data
63+
const alice = graph.addVertex("Person", { name: "Alice", age: 30 });
64+
const bob = graph.addVertex("Person", { name: "Bob", age: 25 });
65+
graph.addEdge(alice, "knows", bob, {});
66+
67+
// 4. Query with Cypher
68+
const results = graph.query(
69+
"MATCH (a:Person)-[:knows]->(b:Person) RETURN a.name, b.name",
70+
);
71+
// [{ a: { name: "Alice" }, b: { name: "Bob" } }]
72+
```
73+
74+
### Defining a Schema
75+
76+
```typescript
77+
import { GraphSchema } from "@codemix/graph";
78+
import * as z from "zod";
79+
80+
const schema = {
81+
vertices: {
82+
Product: {
83+
properties: {
84+
sku: { type: z.string() },
85+
price: { type: z.number().positive() },
86+
name: {
87+
type: z.string(),
88+
index: { type: "fulltext" }, // full-text search index
89+
},
90+
},
91+
indexes: {
92+
sku: { type: "hash", unique: true }, // unique hash index
93+
},
94+
},
95+
},
96+
edges: {
97+
PURCHASED: {
98+
properties: {
99+
quantity: { type: z.number().int().positive() },
100+
},
101+
},
102+
},
103+
} as const satisfies GraphSchema;
104+
```
105+
106+
### Index Types
107+
108+
| Type | Lookup | Use case |
109+
| ---------- | ----------- | -------------------------------------------------------- |
110+
| `hash` | O(1) | Equality (`=`) on high-cardinality properties |
111+
| `btree` | O(log n) | Range queries (`>`, `<`, `>=`, `<=`, `BETWEEN`) |
112+
| `fulltext` | BM25 scored | `CONTAINS` / free-text search via `@codemix/text-search` |
113+
114+
All index types support a `unique: true` constraint that throws `UniqueConstraintViolationError` on duplicate values.
115+
116+
### Supported Cypher Features
117+
118+
- `MATCH` with node and relationship patterns, variable-length paths (`*1..5`), shortest path
119+
- `WHERE` with boolean logic, property access, `IN`, `STARTS WITH`, `ENDS WITH`, `CONTAINS`, `IS NULL`, `IS NOT NULL`, label expressions (`IS LABELED`)
120+
- `RETURN` with aliases (`AS`), `DISTINCT`, `ORDER BY`, `SKIP`, `LIMIT`
121+
- `CREATE`, `SET`, `DELETE`, `REMOVE`, `MERGE`
122+
- `WITH` (pipeline intermediate results)
123+
- `UNWIND`
124+
- `UNION` / `UNION ALL`
125+
- Multi-statement queries (semicolon-separated)
126+
- `CALL` procedures (built-in and custom via `ProcedureRegistry`)
127+
- Aggregation functions: `COUNT`, `SUM`, `AVG`, `MIN`, `MAX`, `COLLECT`
128+
- List comprehensions, pattern comprehensions, `REDUCE`, `EXISTS` subqueries
129+
- Temporal types: `date`, `datetime`, `localtime`, `localdatetime`, `duration`
130+
- Arithmetic, string functions, math functions, type conversion functions
131+
- `CASE` expressions (simple and searched)
132+
133+
### Parsing Queries
134+
135+
For advanced use cases, you can parse a query to a step plan without running it:
136+
137+
```typescript
138+
import { parseQueryToSteps } from "@codemix/graph";
139+
140+
const { steps, postprocess } = parseQueryToSteps(
141+
"MATCH (n:Person) RETURN n.name",
142+
{ readonly: true }, // throws ReadonlyGraphError if query mutates
143+
);
144+
```
145+
146+
### Custom Functions and Procedures
147+
148+
```typescript
149+
import { functionRegistry, procedureRegistry } from "@codemix/graph";
150+
151+
// Register a custom scalar function
152+
functionRegistry.register("myLib.greet", {
153+
call: ([name]) => `Hello, ${name}!`,
154+
});
155+
156+
// Register a custom procedure
157+
procedureRegistry.register("myLib.listItems", {
158+
call: function* (ctx, [], yields) {
159+
yield { item: "foo" };
160+
yield { item: "bar" };
161+
},
162+
});
163+
```
164+
165+
### AsyncGraph
166+
167+
`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.
168+
169+
```typescript
170+
import { AsyncGraph } from "@codemix/graph";
171+
172+
const asyncGraph = new AsyncGraph({
173+
schema,
174+
storage: new InMemoryGraphStorage(),
175+
});
176+
177+
// Subscribe to operations emitted by writes
178+
asyncGraph.on("operation", (op) => {
179+
sendToRemote(op); // op is a plain JSON-serializable object
180+
});
181+
182+
asyncGraph.addVertex("Person", { name: "Alice", age: 30 });
183+
```
184+
185+
---
186+
187+
## `@codemix/text-search`
188+
189+
A lightweight, dependency-free full-text search library used internally by `@codemix/graph` for full-text indexes.
190+
191+
### Features
192+
193+
- BM25-inspired relevance scoring
194+
- English word stemming
195+
- Works on plain strings — no indexing infrastructure required
196+
197+
### Usage
198+
199+
```typescript
200+
import { createMatcher, rankDocuments } from "@codemix/text-search";
201+
202+
// Score a single document
203+
const match = createMatcher("quick brown fox");
204+
match("The quick brown fox jumps over the lazy dog"); // ~0.85
205+
match("A slow gray elephant"); // ~0.0
206+
207+
// Rank a list of documents
208+
const results = rankDocuments("database performance", [
209+
"How to improve database query performance",
210+
"Database connection pooling best practices",
211+
"Unrelated article about cooking",
212+
]);
213+
// Returns documents sorted by relevance score descending
214+
```
215+
216+
---
217+
218+
## `@codemix/y-graph-storage`
219+
220+
A [Yjs](https://yjs.dev/) CRDT-backed storage adapter for `@codemix/graph`. Enables real-time collaborative and offline-first graph databases that sync automatically between peers.
221+
222+
### Features
223+
224+
- Stores graph data inside a `Y.Doc` — compatible with any Yjs provider (WebSocket, WebRTC, IndexedDB, etc.)
225+
- Observable — subscribe to vertex/edge changes reactively
226+
- Zod integration via `ZodYTypes` helpers for schema validation
227+
228+
### Usage
229+
230+
```typescript
231+
import * as Y from "yjs";
232+
import { Graph } from "@codemix/graph";
233+
import { YGraphStorage } from "@codemix/y-graph-storage";
234+
235+
const doc = new Y.Doc();
236+
const storage = new YGraphStorage(doc, schema);
237+
const graph = new Graph({ schema, storage });
238+
239+
// Changes to the graph are automatically reflected in the Y.Doc
240+
// and will sync to connected peers via any Yjs provider
241+
graph.addVertex("Person", { name: "Alice", age: 30 });
242+
```
243+
244+
---
245+
246+
## Development
247+
248+
This is a pnpm monorepo managed with [pnpm workspaces](https://pnpm.io/workspaces).
249+
250+
### Prerequisites
251+
252+
- Node.js 20+
253+
- pnpm 9+
254+
255+
### Setup
256+
257+
```bash
258+
pnpm install
259+
```
260+
261+
### Common Commands
262+
263+
| Command | Description |
264+
| -------------------- | ------------------------------------------ |
265+
| `pnpm test` | Run all tests across packages (watch mode) |
266+
| `pnpm test:coverage` | Run all tests with coverage report |
267+
| `pnpm build` | Build all packages |
268+
| `pnpm typecheck` | Type-check all packages |
269+
| `pnpm lint` | Lint with oxlint |
270+
| `pnpm lint:fix` | Lint and auto-fix |
271+
| `pnpm format` | Format with Prettier |
272+
| `pnpm format:check` | Check formatting |
273+
274+
### Package-level commands
275+
276+
```bash
277+
# Run tests for a single package
278+
pnpm --filter @codemix/graph test
279+
280+
# Build a single package
281+
pnpm --filter @codemix/graph build
282+
```
283+
284+
## License
285+
286+
MIT

oxlint.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"$schema": "https://raw.githubusercontent.com/oxc-project/oxc/main/npm/oxlint/configuration_schema.json",
3+
"rules": {}
4+
}

package.json

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
{
2+
"name": "@codemix/graph-monorepo",
3+
"version": "1.0.0",
4+
"description": "The codemix graph database monorepo",
5+
"workspaces": [
6+
"packages/*"
7+
],
8+
"scripts": {
9+
"build": "pnpm run -r build",
10+
"typecheck": "pnpm run -r typecheck",
11+
"test": "vitest",
12+
"test:coverage": "vitest run --coverage",
13+
"lint": "oxlint --ignore-pattern '**/grammar.js' .",
14+
"lint:fix": "pnpm run lint --fix",
15+
"format": "prettier --write .",
16+
"format:check": "prettier --check ."
17+
},
18+
"keywords": [],
19+
"license": "MIT",
20+
"private": true,
21+
"devDependencies": {
22+
"@vitest/coverage-istanbul": "catalog:",
23+
"oxlint": "^1.59.0",
24+
"oxfmt": "^0.44.0",
25+
"typescript": "catalog:",
26+
"vitest": "catalog:"
27+
}
28+
}

packages/graph/.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
dist
2+
coverage

0 commit comments

Comments
 (0)