minigraf 0.22.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/Cargo.toml ADDED
@@ -0,0 +1,17 @@
1
+ [package]
2
+ name = "minigraf-node"
3
+ version = "0.25.0"
4
+ edition = "2024"
5
+ publish = false
6
+
7
+ [lib]
8
+ crate-type = ["cdylib"]
9
+
10
+ [dependencies]
11
+ napi = { version = "2.16", features = ["napi6"] }
12
+ napi-derive = "2.16"
13
+ minigraf = { path = ".." }
14
+ serde_json = "1.0"
15
+
16
+ [build-dependencies]
17
+ napi-build = "2.1"
package/build.rs ADDED
@@ -0,0 +1,3 @@
1
+ fn main() {
2
+ napi_build::setup();
3
+ }
package/index.d.ts ADDED
@@ -0,0 +1,9 @@
1
+ /* tslint:disable */
2
+ /* eslint-disable */
3
+
4
+ export class MiniGrafDb {
5
+ constructor(path: string)
6
+ static inMemory(): MiniGrafDb
7
+ execute(datalog: string): string
8
+ checkpoint(): void
9
+ }
package/index.js ADDED
@@ -0,0 +1,35 @@
1
+ 'use strict'
2
+
3
+ const { platform, arch } = process
4
+
5
+ let nativeBinding = null
6
+ const loadErrors = []
7
+
8
+ if (platform === 'linux') {
9
+ if (arch === 'x64') {
10
+ try { nativeBinding = require('@minigraf/linux-x64-gnu') }
11
+ catch (e) { loadErrors.push(e) }
12
+ } else if (arch === 'arm64') {
13
+ try { nativeBinding = require('@minigraf/linux-arm64-gnu') }
14
+ catch (e) { loadErrors.push(e) }
15
+ }
16
+ } else if (platform === 'darwin') {
17
+ try { nativeBinding = require('@minigraf/darwin-universal') }
18
+ catch (e) { loadErrors.push(e) }
19
+ } else if (platform === 'win32') {
20
+ if (arch === 'x64') {
21
+ try { nativeBinding = require('@minigraf/win32-x64-msvc') }
22
+ catch (e) { loadErrors.push(e) }
23
+ }
24
+ }
25
+
26
+ if (!nativeBinding) {
27
+ const errorMessages = loadErrors.map(e => e.message).join('\n')
28
+ throw new Error(
29
+ `Failed to load native Minigraf addon for ${platform}-${arch}.\n` +
30
+ `This platform may not be supported yet.\n` +
31
+ (errorMessages ? `Errors:\n${errorMessages}` : '')
32
+ )
33
+ }
34
+
35
+ module.exports = nativeBinding
Binary file
Binary file
Binary file
Binary file
package/package.json ADDED
@@ -0,0 +1,45 @@
1
+ {
2
+ "name": "minigraf",
3
+ "version": "0.22.0",
4
+ "description": "Zero-config, single-file, embedded graph database with bi-temporal Datalog queries",
5
+ "main": "index.js",
6
+ "types": "index.d.ts",
7
+ "license": "MIT OR Apache-2.0",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "https://github.com/adityamukho/minigraf.git"
11
+ },
12
+ "keywords": [
13
+ "graph",
14
+ "datalog",
15
+ "bitemporal",
16
+ "embedded",
17
+ "database"
18
+ ],
19
+ "engines": {
20
+ "node": ">=18"
21
+ },
22
+ "napi": {
23
+ "name": "minigraf",
24
+ "triples": {
25
+ "defaults": true,
26
+ "additional": [
27
+ "aarch64-unknown-linux-gnu",
28
+ "universal-apple-darwin"
29
+ ]
30
+ }
31
+ },
32
+ "scripts": {
33
+ "build": "napi build --platform --release",
34
+ "test": "node --test test/basic.test.mjs"
35
+ },
36
+ "devDependencies": {
37
+ "@napi-rs/cli": "^2.18.0"
38
+ },
39
+ "optionalDependencies": {
40
+ "@minigraf/linux-x64-gnu": "0.25.0",
41
+ "@minigraf/linux-arm64-gnu": "0.25.0",
42
+ "@minigraf/darwin-universal": "0.25.0",
43
+ "@minigraf/win32-x64-msvc": "0.25.0"
44
+ }
45
+ }
@@ -0,0 +1,20 @@
1
+ {
2
+ "name": "@minigraf/darwin-universal",
3
+ "version": "0.22.0",
4
+ "description": "Zero-config, single-file, embedded graph database with bi-temporal Datalog queries (macOS universal)",
5
+ "os": [
6
+ "darwin"
7
+ ],
8
+ "cpu": [
9
+ "x64",
10
+ "arm64"
11
+ ],
12
+ "main": "minigraf.darwin-universal.node",
13
+ "files": [
14
+ "minigraf.darwin-universal.node"
15
+ ],
16
+ "license": "MIT OR Apache-2.0",
17
+ "engines": {
18
+ "node": ">=18"
19
+ }
20
+ }
@@ -0,0 +1,22 @@
1
+ {
2
+ "name": "@minigraf/linux-arm64-gnu",
3
+ "version": "0.22.0",
4
+ "description": "Zero-config, single-file, embedded graph database with bi-temporal Datalog queries (Linux arm64 GNU)",
5
+ "os": [
6
+ "linux"
7
+ ],
8
+ "cpu": [
9
+ "arm64"
10
+ ],
11
+ "libc": [
12
+ "glibc"
13
+ ],
14
+ "main": "minigraf.linux-arm64-gnu.node",
15
+ "files": [
16
+ "minigraf.linux-arm64-gnu.node"
17
+ ],
18
+ "license": "MIT OR Apache-2.0",
19
+ "engines": {
20
+ "node": ">=18"
21
+ }
22
+ }
@@ -0,0 +1,22 @@
1
+ {
2
+ "name": "@minigraf/linux-x64-gnu",
3
+ "version": "0.22.0",
4
+ "description": "Zero-config, single-file, embedded graph database with bi-temporal Datalog queries (Linux x64 GNU)",
5
+ "os": [
6
+ "linux"
7
+ ],
8
+ "cpu": [
9
+ "x64"
10
+ ],
11
+ "libc": [
12
+ "glibc"
13
+ ],
14
+ "main": "minigraf.linux-x64-gnu.node",
15
+ "files": [
16
+ "minigraf.linux-x64-gnu.node"
17
+ ],
18
+ "license": "MIT OR Apache-2.0",
19
+ "engines": {
20
+ "node": ">=18"
21
+ }
22
+ }
@@ -0,0 +1,19 @@
1
+ {
2
+ "name": "@minigraf/win32-x64-msvc",
3
+ "version": "0.22.0",
4
+ "description": "Zero-config, single-file, embedded graph database with bi-temporal Datalog queries (Windows x64 MSVC)",
5
+ "os": [
6
+ "win32"
7
+ ],
8
+ "cpu": [
9
+ "x64"
10
+ ],
11
+ "main": "minigraf.win32-x64-msvc.node",
12
+ "files": [
13
+ "minigraf.win32-x64-msvc.node"
14
+ ],
15
+ "license": "MIT OR Apache-2.0",
16
+ "engines": {
17
+ "node": ">=18"
18
+ }
19
+ }
package/src/lib.rs ADDED
@@ -0,0 +1,91 @@
1
+ #![deny(clippy::all)]
2
+
3
+ use minigraf::{QueryResult, Value};
4
+ use napi::bindgen_prelude::*;
5
+ use napi_derive::napi;
6
+ use std::sync::{Arc, Mutex};
7
+
8
+ // ─── Value → JSON ─────────────────────────────────────────────────────────────
9
+
10
+ fn value_to_json(v: &Value) -> serde_json::Value {
11
+ use serde_json::Value as J;
12
+ match v {
13
+ Value::String(s) => J::String(s.clone()),
14
+ Value::Integer(i) => serde_json::json!(i),
15
+ Value::Float(f) => serde_json::Number::from_f64(*f)
16
+ .map(J::Number)
17
+ .unwrap_or(J::Null),
18
+ Value::Boolean(b) => J::Bool(*b),
19
+ Value::Ref(u) => J::String(u.to_string()),
20
+ Value::Keyword(k) => J::String(k.clone()),
21
+ Value::Null => J::Null,
22
+ }
23
+ }
24
+
25
+ fn query_result_to_json(result: QueryResult) -> String {
26
+ let val = match result {
27
+ QueryResult::Transacted(tx) => serde_json::json!({"transacted": tx}),
28
+ QueryResult::Retracted(tx) => serde_json::json!({"retracted": tx}),
29
+ QueryResult::Ok => serde_json::json!({"ok": true}),
30
+ QueryResult::QueryResults { vars, results } => {
31
+ let rows: Vec<Vec<serde_json::Value>> = results
32
+ .iter()
33
+ .map(|r| r.iter().map(value_to_json).collect())
34
+ .collect();
35
+ serde_json::json!({"variables": vars, "results": rows})
36
+ }
37
+ };
38
+ val.to_string()
39
+ }
40
+
41
+ // ─── MiniGrafDb ───────────────────────────────────────────────────────────────
42
+
43
+ #[napi]
44
+ pub struct MiniGrafDb {
45
+ inner: Arc<Mutex<minigraf::Minigraf>>,
46
+ }
47
+
48
+ #[napi]
49
+ impl MiniGrafDb {
50
+ /// Open a file-backed database. Throws on error.
51
+ #[napi(constructor)]
52
+ pub fn new(path: String) -> Result<Self> {
53
+ let db = minigraf::Minigraf::open(&path)
54
+ .map_err(|e| Error::new(Status::GenericFailure, e.to_string()))?;
55
+ Ok(Self {
56
+ inner: Arc::new(Mutex::new(db)),
57
+ })
58
+ }
59
+
60
+ /// Open an in-memory database. Throws on error.
61
+ #[napi(factory)]
62
+ pub fn in_memory() -> Result<Self> {
63
+ let db = minigraf::Minigraf::in_memory()
64
+ .map_err(|e| Error::new(Status::GenericFailure, e.to_string()))?;
65
+ Ok(Self {
66
+ inner: Arc::new(Mutex::new(db)),
67
+ })
68
+ }
69
+
70
+ /// Execute a Datalog string. Returns a JSON string. Throws on error.
71
+ #[napi]
72
+ pub fn execute(&self, datalog: String) -> Result<String> {
73
+ let result = self
74
+ .inner
75
+ .lock()
76
+ .map_err(|_| Error::new(Status::GenericFailure, "mutex poisoned"))?
77
+ .execute(&datalog)
78
+ .map_err(|e| Error::new(Status::GenericFailure, e.to_string()))?;
79
+ Ok(query_result_to_json(result))
80
+ }
81
+
82
+ /// Flush the WAL to disk. Throws on error.
83
+ #[napi]
84
+ pub fn checkpoint(&self) -> Result<()> {
85
+ self.inner
86
+ .lock()
87
+ .map_err(|_| Error::new(Status::GenericFailure, "mutex poisoned"))?
88
+ .checkpoint()
89
+ .map_err(|e| Error::new(Status::GenericFailure, e.to_string()))
90
+ }
91
+ }
@@ -0,0 +1,49 @@
1
+ import { test } from 'node:test'
2
+ import assert from 'node:assert/strict'
3
+ import { MiniGrafDb } from '../index.js'
4
+
5
+ test('open in-memory database', () => {
6
+ const db = MiniGrafDb.inMemory()
7
+ assert.ok(db, 'expected non-null db')
8
+ })
9
+
10
+ test('transact and query', () => {
11
+ const db = MiniGrafDb.inMemory()
12
+ const txJson = db.execute('(transact [[:alice :name "Alice"]])')
13
+ const tx = JSON.parse(txJson)
14
+ assert.ok('transacted' in tx, 'expected transacted key')
15
+
16
+ const queryJson = db.execute('(query [:find ?n :where [?e :name ?n]])')
17
+ const qr = JSON.parse(queryJson)
18
+ assert.equal(qr.results[0][0], 'Alice')
19
+ })
20
+
21
+ test('invalid datalog throws', () => {
22
+ const db = MiniGrafDb.inMemory()
23
+ assert.throws(() => db.execute('not valid datalog !!!'))
24
+ })
25
+
26
+ test('file-backed roundtrip', async (t) => {
27
+ const os = await import('node:os')
28
+ const path = await import('node:path')
29
+ const fs = await import('node:fs')
30
+
31
+ const tmpDir = os.default.tmpdir()
32
+ const dbPath = path.default.join(tmpDir, `minigraf_node_test_${Date.now()}.graph`)
33
+ const walPath = dbPath + '.wal'
34
+
35
+ t.after(() => {
36
+ try { fs.default.unlinkSync(dbPath) } catch {}
37
+ try { fs.default.unlinkSync(walPath) } catch {}
38
+ })
39
+
40
+ {
41
+ const db = new MiniGrafDb(dbPath)
42
+ db.execute('(transact [[:bob :name "Bob"]])')
43
+ db.checkpoint()
44
+ }
45
+
46
+ const db2 = new MiniGrafDb(dbPath)
47
+ const qr = JSON.parse(db2.execute('(query [:find ?n :where [?e :name ?n]])'))
48
+ assert.equal(qr.results[0][0], 'Bob')
49
+ })