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.
Files changed (64) hide show
  1. package/README.md +67 -32
  2. package/bin/cli.js +53 -0
  3. package/c/Makefile +31 -16
  4. package/c/download-sqlite.cjs +17 -0
  5. package/c/git-merge-sqlitevfs.c +210 -186
  6. package/c/gitvfs.c +69 -21
  7. package/c/output/git-merge-sqlitevfs.exe +0 -0
  8. package/c/output/gitvfs.dll +0 -0
  9. package/c/output/gitvfs.o +0 -0
  10. package/c/output/gitvfs_test.exe +0 -0
  11. package/c/output/main.o +0 -0
  12. package/c/output/sqlite3.o +0 -0
  13. package/c/sqlite-autoconf-3450200/INSTALL +370 -0
  14. package/c/sqlite-autoconf-3450200/Makefile.am +20 -0
  15. package/c/sqlite-autoconf-3450200/Makefile.fallback +19 -0
  16. package/c/sqlite-autoconf-3450200/Makefile.in +1050 -0
  17. package/c/sqlite-autoconf-3450200/Makefile.msc +1069 -0
  18. package/c/sqlite-autoconf-3450200/README.txt +113 -0
  19. package/c/sqlite-autoconf-3450200/Replace.cs +223 -0
  20. package/c/sqlite-autoconf-3450200/aclocal.m4 +10204 -0
  21. package/c/sqlite-autoconf-3450200/compile +348 -0
  22. package/c/sqlite-autoconf-3450200/config.guess +1754 -0
  23. package/c/sqlite-autoconf-3450200/config.sub +1890 -0
  24. package/c/sqlite-autoconf-3450200/configure +16887 -0
  25. package/c/sqlite-autoconf-3450200/configure.ac +270 -0
  26. package/c/sqlite-autoconf-3450200/depcomp +791 -0
  27. package/c/sqlite-autoconf-3450200/install-sh +541 -0
  28. package/c/sqlite-autoconf-3450200/ltmain.sh +11251 -0
  29. package/c/sqlite-autoconf-3450200/missing +215 -0
  30. package/c/sqlite-autoconf-3450200/shell.c +29659 -0
  31. package/c/sqlite-autoconf-3450200/sqlite3.1 +161 -0
  32. package/c/sqlite-autoconf-3450200/sqlite3.c +255811 -0
  33. package/c/sqlite-autoconf-3450200/sqlite3.h +13357 -0
  34. package/c/sqlite-autoconf-3450200/sqlite3.pc.in +13 -0
  35. package/c/sqlite-autoconf-3450200/sqlite3.rc +83 -0
  36. package/c/sqlite-autoconf-3450200/sqlite3ext.h +719 -0
  37. package/c/sqlite-autoconf-3450200/sqlite3rc.h +3 -0
  38. package/c/sqlite-autoconf-3450200/tea/Makefile.in +475 -0
  39. package/c/sqlite-autoconf-3450200/tea/README +36 -0
  40. package/c/sqlite-autoconf-3450200/tea/aclocal.m4 +9 -0
  41. package/c/sqlite-autoconf-3450200/tea/configure +10179 -0
  42. package/c/sqlite-autoconf-3450200/tea/configure.ac +227 -0
  43. package/c/sqlite-autoconf-3450200/tea/doc/sqlite3.n +15 -0
  44. package/c/sqlite-autoconf-3450200/tea/generic/tclsqlite3.c +4080 -0
  45. package/c/sqlite-autoconf-3450200/tea/license.terms +6 -0
  46. package/c/sqlite-autoconf-3450200/tea/pkgIndex.tcl.in +10 -0
  47. package/c/sqlite-autoconf-3450200/tea/tclconfig/install-sh +528 -0
  48. package/c/sqlite-autoconf-3450200/tea/tclconfig/tcl.m4 +4067 -0
  49. package/c/sqlite-autoconf-3450200/tea/win/makefile.vc +430 -0
  50. package/c/sqlite-autoconf-3450200/tea/win/nmakehlp.c +815 -0
  51. package/c/sqlite-autoconf-3450200/tea/win/rules.vc +711 -0
  52. package/c/sqlite-autoconf-3450200.tar.gz +0 -0
  53. package/c/sqlite3.c +255811 -0
  54. package/c/sqlite3.h +13357 -0
  55. package/c/sqlite3ext.h +719 -0
  56. package/downloader.js +63 -0
  57. package/index.d.ts +14 -0
  58. package/index.js +103 -51
  59. package/install.js +13 -49
  60. package/package.json +22 -3
  61. package/c/output/git-merge-sqlitevfs +0 -0
  62. package/c/output/gitvfs.so +0 -0
  63. package/c/output/gitvfs_test +0 -0
  64. 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 via a Custom Virtual File System (VFS).
3
+ A Git-Versioned SQLite Database powered by a custom native Virtual File System (VFS).
4
4
 
5
- This project bridges the mathematical robustness of SQLite's B-Tree engine with the distributed version control capabilities of Git, neutralizing the fundamental friction between binary databases and text-based source control.
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
- ## The Architecture
7
+ ## Architecture
8
8
 
9
- By default, standard monolithic SQLite databases undergo "cascading byte shifts" during standard operations (e.g., page splits, rebalancing). This destroys Git's ability to efficiently delta-compress the binary, causing massive repository bloat.
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
- **The GitVFS Sharding Engine:**
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
- Because changes are mathematically isolated to specific physical files, Git's `xdelta` sliding window algorithm achieves near-perfect binary compression. Operations like `VACUUM` naturally trigger `xTruncate`, unlinking dead pages and shrinking the physical directory footprint.
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
- ## The Custom Merge Strategy
15
+ ## Installation
17
16
 
18
- Standard Git auto-merges (`ort`) operate on a file-by-file basis. Merging isolated binary pages from divergent branches silently corrupts the mathematical integrity of a B-Tree graph.
17
+ Install the VFS package alongside your libSQL and Drizzle tools:
19
18
 
20
- This package provides a **Native Git Merge Strategy** (`git-merge-sqlitevfs`) that elevates the merge context from the file level to the database level. When Git encounters a branch merge, it delegates the entire operation to our C executable:
21
- 1. `git-merge-sqlitevfs` uses `git archive` to safely reconstruct `MERGE_HEAD` and the Ancestor database states without index-lock collisions.
22
- 2. It uses `ATTACH DATABASE` to instantly mount all three branches (Local, Remote, Ancestor) into a single unified SQLite VDBE engine.
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
- ## Usage
23
+ ## Git Setup
27
24
 
28
- Install the package via npm (requires `better-sqlite3` and `make`):
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
- npm install git-sqlite-vfs
28
+ npx git-sqlite-setup --vfs-dir .my-db
32
29
  ```
33
30
 
34
- Initialize your version-controlled connection in Node.js:
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
- ```javascript
37
- const GitSQLite = require('git-sqlite-vfs');
39
+ ### Using `@libsql/client` with Drizzle ORM
38
40
 
39
- // Configure Git optimizations and register the VFS merge driver
40
- GitSQLite.setupGit();
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
- // Open a connection. Our Node wrapper automatically loads the C extension
43
- // and routes the URI query via better-sqlite3.
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
- // Execute standard SQL natively
47
- db.exec("CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT);");
48
- db.exec("INSERT INTO users (name) VALUES ('Alice');");
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
- const row = db.prepare("SELECT * FROM users WHERE name = ?").get('Alice');
51
- console.log(row.name); // 'Alice'
55
+ // 3. Wrap with Drizzle ORM
56
+ const db = drizzle(client);
52
57
 
53
- db.close();
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
- Because the underlying files are flawlessly tracked, you can seamlessly branch, commit, and `git reset --hard HEAD~1` to time travel instantly!
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 = gcc
2
- CFLAGS = -Wall -Wextra -g -O2 -std=c99 -D_POSIX_C_SOURCE=200809L
3
- LDFLAGS = -lsqlite3
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.so
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.so: $(SRC_DIR)/gitvfs.c $(SRC_DIR)/gitvfs.h | $(OUT_DIR)
19
- $(CC) $(CFLAGS) -fPIC -shared -DCOMPILE_SQLITE_EXTENSION $(SRC_DIR)/gitvfs.c -o $(OUT_DIR)/gitvfs.so $(SHARED_LDFLAGS)
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
- mkdir -p $(OUT_DIR)
50
+ node -e "const fs=require('fs'); if (!fs.existsSync('$(OUT_DIR)')) fs.mkdirSync('$(OUT_DIR)');"
35
51
 
36
52
  clean:
37
- rm -rf $(OUT_DIR)
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
+ }