git-sqlite-vfs 0.0.2 → 0.0.6
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/README.md +67 -32
- package/bin/cli.js +53 -0
- package/c/Makefile +31 -16
- package/c/download-sqlite.cjs +17 -0
- package/c/git-merge-sqlitevfs.c +210 -186
- package/c/gitvfs.c +69 -21
- package/c/output/git-merge-sqlitevfs.exe +0 -0
- package/c/output/gitvfs.dll +0 -0
- package/c/output/gitvfs.o +0 -0
- package/c/output/gitvfs_test.exe +0 -0
- package/c/output/main.o +0 -0
- package/c/output/sqlite3.o +0 -0
- package/c/sqlite-autoconf-3450200/INSTALL +370 -0
- package/c/sqlite-autoconf-3450200/Makefile.am +20 -0
- package/c/sqlite-autoconf-3450200/Makefile.fallback +19 -0
- package/c/sqlite-autoconf-3450200/Makefile.in +1050 -0
- package/c/sqlite-autoconf-3450200/Makefile.msc +1069 -0
- package/c/sqlite-autoconf-3450200/README.txt +113 -0
- package/c/sqlite-autoconf-3450200/Replace.cs +223 -0
- package/c/sqlite-autoconf-3450200/aclocal.m4 +10204 -0
- package/c/sqlite-autoconf-3450200/compile +348 -0
- package/c/sqlite-autoconf-3450200/config.guess +1754 -0
- package/c/sqlite-autoconf-3450200/config.sub +1890 -0
- package/c/sqlite-autoconf-3450200/configure +16887 -0
- package/c/sqlite-autoconf-3450200/configure.ac +270 -0
- package/c/sqlite-autoconf-3450200/depcomp +791 -0
- package/c/sqlite-autoconf-3450200/install-sh +541 -0
- package/c/sqlite-autoconf-3450200/ltmain.sh +11251 -0
- package/c/sqlite-autoconf-3450200/missing +215 -0
- package/c/sqlite-autoconf-3450200/shell.c +29659 -0
- package/c/sqlite-autoconf-3450200/sqlite3.1 +161 -0
- package/c/sqlite-autoconf-3450200/sqlite3.c +255811 -0
- package/c/sqlite-autoconf-3450200/sqlite3.h +13357 -0
- package/c/sqlite-autoconf-3450200/sqlite3.pc.in +13 -0
- package/c/sqlite-autoconf-3450200/sqlite3.rc +83 -0
- package/c/sqlite-autoconf-3450200/sqlite3ext.h +719 -0
- package/c/sqlite-autoconf-3450200/sqlite3rc.h +3 -0
- package/c/sqlite-autoconf-3450200/tea/Makefile.in +475 -0
- package/c/sqlite-autoconf-3450200/tea/README +36 -0
- package/c/sqlite-autoconf-3450200/tea/aclocal.m4 +9 -0
- package/c/sqlite-autoconf-3450200/tea/configure +10179 -0
- package/c/sqlite-autoconf-3450200/tea/configure.ac +227 -0
- package/c/sqlite-autoconf-3450200/tea/doc/sqlite3.n +15 -0
- package/c/sqlite-autoconf-3450200/tea/generic/tclsqlite3.c +4080 -0
- package/c/sqlite-autoconf-3450200/tea/license.terms +6 -0
- package/c/sqlite-autoconf-3450200/tea/pkgIndex.tcl.in +10 -0
- package/c/sqlite-autoconf-3450200/tea/tclconfig/install-sh +528 -0
- package/c/sqlite-autoconf-3450200/tea/tclconfig/tcl.m4 +4067 -0
- package/c/sqlite-autoconf-3450200/tea/win/makefile.vc +430 -0
- package/c/sqlite-autoconf-3450200/tea/win/nmakehlp.c +815 -0
- package/c/sqlite-autoconf-3450200/tea/win/rules.vc +711 -0
- package/c/sqlite-autoconf-3450200.tar.gz +0 -0
- package/c/sqlite3.c +255811 -0
- package/c/sqlite3.h +13357 -0
- package/c/sqlite3ext.h +719 -0
- package/downloader.js +63 -0
- package/index.d.ts +14 -0
- package/index.js +103 -51
- package/install.js +13 -49
- package/package.json +22 -3
- package/c/output/git-merge-sqlitevfs +0 -0
- package/c/output/gitvfs.so +0 -0
- package/c/output/gitvfs_test +0 -0
- package/test.js +0 -209
package/README.md
CHANGED
|
@@ -1,56 +1,91 @@
|
|
|
1
1
|
# git-sqlite-vfs
|
|
2
2
|
|
|
3
|
-
A Git-Versioned SQLite Database
|
|
3
|
+
A Git-Versioned SQLite Database powered by a custom native Virtual File System (VFS).
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
By combining the robustness of native SQLite, Drizzle ORM, and libSQL with the distributed tracking power of Git, `git-sqlite-vfs` enables you to version, diff, and merge your application's database exactly like your source code. It works out-of-the-box in both **Node.js** and **Deno**.
|
|
6
6
|
|
|
7
|
-
##
|
|
7
|
+
## Architecture
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
Traditional SQLite databases are stored as a single flat file, making them difficult to version control because a 1-byte insertion can trigger a cascading byte shift across the entire file, rendering delta-compression useless.
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
We solve this by replacing the POSIX I/O layer with a custom SQLite Virtual File System (VFS) written in C. Instead of writing to a single `.db` file, `gitvfs` dynamically shards the database into isolated, deterministic 4KB hexadecimal `.bin` pages (e.g., `.db/pages/0A/1B/0A1B2C.bin`).
|
|
11
|
+
This package dynamically loads a specialized SQLite C Extension that overrides the default VFS. It shards your database into 4KB deterministic binary pages inside a targeted directory (e.g. `.my-db`).
|
|
13
12
|
|
|
14
|
-
|
|
13
|
+
When you merge branches, our custom `git-merge-sqlitevfs` driver is natively hooked into Git's conflict resolution pipeline to properly reconcile binary B-Tree page conflicts, ensuring absolute data integrity!
|
|
15
14
|
|
|
16
|
-
##
|
|
15
|
+
## Installation
|
|
17
16
|
|
|
18
|
-
|
|
17
|
+
Install the VFS package alongside your libSQL and Drizzle tools:
|
|
19
18
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
3. Using the `EXCEPT` operator, it calculates full-row tuples and structural DDL schema diffs instantly.
|
|
24
|
-
4. It performs a true mathematically sound 3-Way Logical Merge—resolving schema evolutions, propagating insertions/deletions, and mitigating exact row-level conflicts (preferring `HEAD`)—then stages the physically reconciled `.bin` pages back to Git.
|
|
19
|
+
```bash
|
|
20
|
+
npm install git-sqlite-vfs @libsql/client drizzle-orm
|
|
21
|
+
```
|
|
25
22
|
|
|
26
|
-
##
|
|
23
|
+
## Git Setup
|
|
27
24
|
|
|
28
|
-
|
|
25
|
+
To enable Git versioning and binary merging, you must configure your repository to use the custom merge driver. We provide a convenient CLI to wire everything up:
|
|
29
26
|
|
|
30
27
|
```bash
|
|
31
|
-
|
|
28
|
+
npx git-sqlite-setup --vfs-dir .my-db
|
|
32
29
|
```
|
|
33
30
|
|
|
34
|
-
|
|
31
|
+
This will:
|
|
32
|
+
1. Register `git-merge-sqlitevfs` as a custom Git merge driver in your local `.git/config`.
|
|
33
|
+
2. Create or append to a `.gitattributes` file in your repo root to route all files in `.my-db/*` through the custom merge driver.
|
|
34
|
+
|
|
35
|
+
## Usage (Isomorphic)
|
|
36
|
+
|
|
37
|
+
The VFS works transparently in both Node.js and Deno. By using the `bootstrapGitVFS()` method before you initialize `@libsql/client`, the native memory spaces are perfectly synchronized.
|
|
35
38
|
|
|
36
|
-
|
|
37
|
-
const GitSQLite = require('git-sqlite-vfs');
|
|
39
|
+
### Using `@libsql/client` with Drizzle ORM
|
|
38
40
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
+
```typescript
|
|
42
|
+
import { createClient } from '@libsql/client'; // In Deno: 'npm:@libsql/client/node'
|
|
43
|
+
import { drizzle } from 'drizzle-orm/libsql';
|
|
44
|
+
import { sqliteTable, integer, text } from 'drizzle-orm/sqlite-core';
|
|
45
|
+
import { bootstrapGitVFS } from 'git-sqlite-vfs';
|
|
41
46
|
|
|
42
|
-
//
|
|
43
|
-
|
|
44
|
-
const db = GitSQLite.open('.db');
|
|
47
|
+
// 1. Load the native extension process-wide and set the VFS directory
|
|
48
|
+
await bootstrapGitVFS({ dir: '.my-db' });
|
|
45
49
|
|
|
46
|
-
//
|
|
47
|
-
|
|
48
|
-
db.
|
|
50
|
+
// 2. Initialize your database connection using a standard file: URL
|
|
51
|
+
const client = createClient({
|
|
52
|
+
url: 'file:.my-db/local.db'
|
|
53
|
+
});
|
|
49
54
|
|
|
50
|
-
|
|
51
|
-
|
|
55
|
+
// 3. Wrap with Drizzle ORM
|
|
56
|
+
const db = drizzle(client);
|
|
52
57
|
|
|
53
|
-
|
|
58
|
+
// 4. Define your schema
|
|
59
|
+
const users = sqliteTable('users', {
|
|
60
|
+
id: integer('id').primaryKey(),
|
|
61
|
+
name: text('name')
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
// 5. Query natively! All I/O is safely intercepted by the Git VFS.
|
|
65
|
+
await db.insert(users).values({ id: 1, name: 'Alice' });
|
|
66
|
+
const allUsers = await db.select().from(users);
|
|
67
|
+
|
|
68
|
+
console.log(allUsers);
|
|
54
69
|
```
|
|
55
70
|
|
|
56
|
-
|
|
71
|
+
## Preventing Git Bloat
|
|
72
|
+
|
|
73
|
+
Because SQLite usually zeroes out deleted data pages rather than shrinking the file, you might accumulate "zombie" `.bin` pages in your repository over time. To ensure the Git VFS automatically garbage-collects these abandoned chunks, you must configure SQLite to run `FULL` auto-vacuuming and use `DELETE` journaling.
|
|
74
|
+
|
|
75
|
+
Run these PRAGMAs once when initializing your database connection:
|
|
76
|
+
|
|
77
|
+
```sql
|
|
78
|
+
PRAGMA auto_vacuum = FULL;
|
|
79
|
+
PRAGMA journal_mode = DELETE;
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Or run `VACUUM;` periodically. When SQLite explicitly shrinks the database file, the underlying VFS `xTruncate` routine will physically `unlink()` the out-of-bounds `.bin` shards, keeping your Git tracking history perfectly compressed!
|
|
83
|
+
|
|
84
|
+
## Compatibility
|
|
85
|
+
|
|
86
|
+
- **Node.js**: v22.5+ (using the new `node:sqlite` API internally) or fallback to `better-sqlite3`.
|
|
87
|
+
- **Deno**: Supported automatically (loads the extension dynamically via `jsr:@db/sqlite`).
|
|
88
|
+
|
|
89
|
+
## License
|
|
90
|
+
|
|
91
|
+
ISC
|
package/bin/cli.js
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
#!/usr/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { parseArgs } from 'node:util';
|
|
5
|
+
import { configureGitIntegration } from '../index.js';
|
|
6
|
+
|
|
7
|
+
const options = {
|
|
8
|
+
'repo-dir': {
|
|
9
|
+
type: 'string',
|
|
10
|
+
short: 'r',
|
|
11
|
+
},
|
|
12
|
+
'vfs-dir': {
|
|
13
|
+
type: 'string',
|
|
14
|
+
short: 'v',
|
|
15
|
+
},
|
|
16
|
+
help: {
|
|
17
|
+
type: 'boolean',
|
|
18
|
+
short: 'h',
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const { values, positionals } = parseArgs({ options, allowPositionals: true });
|
|
23
|
+
|
|
24
|
+
if (values.help) {
|
|
25
|
+
console.log(`
|
|
26
|
+
Usage: git-sqlite-setup [options]
|
|
27
|
+
|
|
28
|
+
Configure the current Git repository to use the git-sqlite-vfs merge driver.
|
|
29
|
+
This natively intercepts merge conflicts on your SQLite B-Tree binary shards.
|
|
30
|
+
|
|
31
|
+
Options:
|
|
32
|
+
-r, --repo-dir <path> Path to the Git repository (default: current working directory)
|
|
33
|
+
-v, --vfs-dir <path> The VFS shard directory to apply the merge driver to (default: .db)
|
|
34
|
+
-h, --help Show this help message
|
|
35
|
+
`);
|
|
36
|
+
process.exit(0);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const repoDir = values['repo-dir'] ? path.resolve(values['repo-dir']) : process.cwd();
|
|
40
|
+
const vfsDir = values['vfs-dir'] || '.db';
|
|
41
|
+
|
|
42
|
+
console.log(`Configuring Git Integration...`);
|
|
43
|
+
console.log(`Repository: ${repoDir}`);
|
|
44
|
+
console.log(`VFS Target Directory: ${vfsDir}`);
|
|
45
|
+
|
|
46
|
+
try {
|
|
47
|
+
await configureGitIntegration({ repoDir, vfsDir });
|
|
48
|
+
console.log(`\nSuccessfully configured the SQLite VFS merge driver!`);
|
|
49
|
+
console.log(`Git will now use the custom C merge driver for conflicts inside: ${vfsDir}/*`);
|
|
50
|
+
} catch (err) {
|
|
51
|
+
console.error(`\nFailed to configure Git integration:`, err.message);
|
|
52
|
+
process.exit(1);
|
|
53
|
+
}
|
package/c/Makefile
CHANGED
|
@@ -1,38 +1,53 @@
|
|
|
1
|
-
CC
|
|
2
|
-
CFLAGS = -Wall -Wextra -g -O2 -std=c99 -D_POSIX_C_SOURCE=200809L
|
|
3
|
-
LDFLAGS =
|
|
1
|
+
CC ?= gcc
|
|
2
|
+
CFLAGS = -Wall -Wextra -g -O2 -std=c99 -D_POSIX_C_SOURCE=200809L -I.
|
|
3
|
+
LDFLAGS =
|
|
4
4
|
|
|
5
|
-
# On macOS, loadable extensions must dynamically look up SQLite symbols
|
|
6
5
|
UNAME_S := $(shell uname -s)
|
|
7
6
|
ifeq ($(UNAME_S),Darwin)
|
|
8
7
|
SHARED_LDFLAGS = -undefined dynamic_lookup
|
|
8
|
+
EXT = dylib
|
|
9
|
+
EXE =
|
|
10
|
+
else ifeq ($(OS),Windows_NT)
|
|
11
|
+
SHARED_LDFLAGS =
|
|
12
|
+
EXT = dll
|
|
13
|
+
EXE = .exe
|
|
9
14
|
else
|
|
10
15
|
SHARED_LDFLAGS =
|
|
16
|
+
EXT = so
|
|
17
|
+
EXE =
|
|
11
18
|
endif
|
|
12
19
|
|
|
13
20
|
SRC_DIR = .
|
|
14
21
|
OUT_DIR = output
|
|
15
22
|
|
|
16
|
-
all: $(OUT_DIR)/gitvfs_test $(OUT_DIR)/git-merge-sqlitevfs $(OUT_DIR)/gitvfs
|
|
23
|
+
all: $(OUT_DIR)/gitvfs_test$(EXE) $(OUT_DIR)/git-merge-sqlitevfs$(EXE) $(OUT_DIR)/gitvfs.$(EXT)
|
|
24
|
+
|
|
25
|
+
$(SRC_DIR)/sqlite3.c:
|
|
26
|
+
node download-sqlite.cjs
|
|
27
|
+
|
|
28
|
+
$(SRC_DIR)/sqlite3.h: $(SRC_DIR)/sqlite3.c
|
|
29
|
+
$(SRC_DIR)/sqlite3ext.h: $(SRC_DIR)/sqlite3.c
|
|
30
|
+
|
|
31
|
+
$(OUT_DIR)/sqlite3.o: $(SRC_DIR)/sqlite3.c $(SRC_DIR)/sqlite3.h | $(OUT_DIR)
|
|
32
|
+
$(CC) -g -O2 -c $(SRC_DIR)/sqlite3.c -o $(OUT_DIR)/sqlite3.o
|
|
17
33
|
|
|
18
|
-
$(OUT_DIR)/gitvfs
|
|
19
|
-
$(CC) $(CFLAGS) -fPIC -shared -DCOMPILE_SQLITE_EXTENSION $(SRC_DIR)/gitvfs.c -o $(OUT_DIR)/gitvfs
|
|
34
|
+
$(OUT_DIR)/gitvfs.$(EXT): $(SRC_DIR)/gitvfs.c $(SRC_DIR)/gitvfs.h $(SRC_DIR)/sqlite3.h $(SRC_DIR)/sqlite3ext.h | $(OUT_DIR)
|
|
35
|
+
$(CC) $(CFLAGS) -fPIC -shared -DCOMPILE_SQLITE_EXTENSION $(SRC_DIR)/gitvfs.c -o $(OUT_DIR)/gitvfs.$(EXT) $(SHARED_LDFLAGS)
|
|
20
36
|
|
|
21
|
-
$(OUT_DIR)/gitvfs.o: $(SRC_DIR)/gitvfs.c $(SRC_DIR)/gitvfs.h | $(OUT_DIR)
|
|
37
|
+
$(OUT_DIR)/gitvfs.o: $(SRC_DIR)/gitvfs.c $(SRC_DIR)/gitvfs.h $(SRC_DIR)/sqlite3.h $(SRC_DIR)/sqlite3ext.h | $(OUT_DIR)
|
|
22
38
|
$(CC) $(CFLAGS) -c $(SRC_DIR)/gitvfs.c -o $(OUT_DIR)/gitvfs.o
|
|
23
39
|
|
|
24
|
-
$(OUT_DIR)/main.o: $(SRC_DIR)/main.c $(SRC_DIR)/gitvfs.h | $(OUT_DIR)
|
|
40
|
+
$(OUT_DIR)/main.o: $(SRC_DIR)/main.c $(SRC_DIR)/gitvfs.h $(SRC_DIR)/sqlite3.h | $(OUT_DIR)
|
|
25
41
|
$(CC) $(CFLAGS) -c $(SRC_DIR)/main.c -o $(OUT_DIR)/main.o
|
|
26
42
|
|
|
27
|
-
$(OUT_DIR)/gitvfs_test: $(OUT_DIR)/main.o $(OUT_DIR)/gitvfs.o | $(OUT_DIR)
|
|
28
|
-
$(CC) $(CFLAGS) $(OUT_DIR)/main.o $(OUT_DIR)/gitvfs.o -o $(OUT_DIR)/gitvfs_test $(LDFLAGS)
|
|
43
|
+
$(OUT_DIR)/gitvfs_test$(EXE): $(OUT_DIR)/main.o $(OUT_DIR)/gitvfs.o $(OUT_DIR)/sqlite3.o | $(OUT_DIR)
|
|
44
|
+
$(CC) $(CFLAGS) $(OUT_DIR)/main.o $(OUT_DIR)/gitvfs.o $(OUT_DIR)/sqlite3.o -o $(OUT_DIR)/gitvfs_test$(EXE) $(LDFLAGS)
|
|
29
45
|
|
|
30
|
-
$(OUT_DIR)/git-merge-sqlitevfs: $(SRC_DIR)/git-merge-sqlitevfs.c $(OUT_DIR)/gitvfs.o | $(OUT_DIR)
|
|
31
|
-
$(CC) $(CFLAGS) $(SRC_DIR)/git-merge-sqlitevfs.c $(OUT_DIR)/gitvfs.o -o $(OUT_DIR)/git-merge-sqlitevfs $(LDFLAGS)
|
|
46
|
+
$(OUT_DIR)/git-merge-sqlitevfs$(EXE): $(SRC_DIR)/git-merge-sqlitevfs.c $(OUT_DIR)/gitvfs.o $(OUT_DIR)/sqlite3.o $(SRC_DIR)/sqlite3.h | $(OUT_DIR)
|
|
47
|
+
$(CC) $(CFLAGS) $(SRC_DIR)/git-merge-sqlitevfs.c $(OUT_DIR)/gitvfs.o $(OUT_DIR)/sqlite3.o -o $(OUT_DIR)/git-merge-sqlitevfs$(EXE) $(LDFLAGS)
|
|
32
48
|
|
|
33
49
|
$(OUT_DIR):
|
|
34
|
-
|
|
50
|
+
node -e "const fs=require('fs'); if (!fs.existsSync('$(OUT_DIR)')) fs.mkdirSync('$(OUT_DIR)');"
|
|
35
51
|
|
|
36
52
|
clean:
|
|
37
|
-
|
|
38
|
-
rm -rf .db .git
|
|
53
|
+
node -e "const fs=require('fs'); fs.rmSync('$(OUT_DIR)', {recursive: true, force: true}); fs.rmSync('.db', {recursive: true, force: true}); fs.rmSync('.git', {recursive: true, force: true}); fs.rmSync('sqlite3.c', {force: true}); fs.rmSync('sqlite3.h', {force: true}); fs.rmSync('sqlite3ext.h', {force: true});"
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const { execSync } = require('child_process');
|
|
4
|
+
|
|
5
|
+
if (!fs.existsSync('sqlite3.c') || !fs.existsSync('sqlite3.h') || !fs.existsSync('sqlite3ext.h')) {
|
|
6
|
+
console.log('Downloading SQLite source...');
|
|
7
|
+
try {
|
|
8
|
+
execSync('curl -L -O https://www.sqlite.org/2024/sqlite-autoconf-3450200.tar.gz', { stdio: 'inherit' });
|
|
9
|
+
execSync('tar -xzf sqlite-autoconf-3450200.tar.gz', { stdio: 'inherit' });
|
|
10
|
+
fs.copyFileSync('sqlite-autoconf-3450200/sqlite3.c', 'sqlite3.c');
|
|
11
|
+
fs.copyFileSync('sqlite-autoconf-3450200/sqlite3.h', 'sqlite3.h');
|
|
12
|
+
fs.copyFileSync('sqlite-autoconf-3450200/sqlite3ext.h', 'sqlite3ext.h');
|
|
13
|
+
} catch (e) {
|
|
14
|
+
console.error('Failed to download or extract SQLite:', e.message);
|
|
15
|
+
process.exit(1);
|
|
16
|
+
}
|
|
17
|
+
}
|