git-sqlite-vfs 0.0.10 → 0.0.12

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 CHANGED
@@ -1,85 +1,110 @@
1
1
  # git-sqlite-vfs
2
2
 
3
- A Git-versioned SQLite Database powered by a custom Virtual File System (VFS).
3
+ A Git-versioned SQLite database utilizing a custom Virtual File System (VFS).
4
4
 
5
- By integrating native SQLite, Drizzle ORM, and libSQL with Git's version control capabilities, `git-sqlite-vfs` allows you to version, diff, and merge your application's database in a manner similar to source code. It is compatible with both **Node.js** and **Deno**.
5
+ By integrating SQLite, Drizzle ORM, and libSQL with Git, `git-sqlite-vfs` enables versioning, diffing, and merging of SQLite databases. It is compatible with Node.js and Deno.
6
6
 
7
7
  ## Architecture
8
8
 
9
- Standard SQLite databases are stored as a single flat file. This structure presents challenges for version control, as a minor insertion can result in widespread byte shifts across the file, reducing the effectiveness of delta-compression and making binary merge conflicts difficult to resolve.
9
+ Standard SQLite databases are stored as a single file. This limits version control compatibility, as minor insertions cause cascading byte shifts, negating delta-compression and creating unresolvable binary merge conflicts.
10
10
 
11
- This package provides a loadable SQLite C Extension that overrides the default VFS behavior. It shards the database into 4KB deterministic binary pages within a specified directory (e.g. `.my-db`).
11
+ This package provides a loadable SQLite C extension that overrides the default VFS behavior. It shards the database into deterministic 4KB binary pages within a specified directory (e.g. `.my-db`).
12
12
 
13
- During a Git merge, a provided `git-merge-sqlitevfs` driver integrates with Git's conflict resolution pipeline to reconcile binary B-Tree page conflicts, maintaining data integrity.
13
+ During a Git merge, a custom `git-merge-sqlitevfs` driver integrates with Git's conflict resolution pipeline to reconcile B-Tree page conflicts.
14
14
 
15
15
  ## Installation
16
16
 
17
- Install the package alongside your libSQL and Drizzle ORM dependencies:
18
-
19
17
  ```bash
20
18
  npm install git-sqlite-vfs @libsql/client drizzle-orm
21
19
  ```
22
20
 
23
21
  ## Git Configuration
24
22
 
25
- To enable Git versioning and binary merging, the repository must be configured to use the custom merge driver. This is done **automatically** when you call `bootstrapGitVFS()`:
23
+ The repository must be configured to use the custom merge driver. This configuration occurs automatically when `bootstrapGitVFS()` is called:
26
24
 
27
- 1. It registers `git-merge-sqlitevfs` as a custom Git merge driver in the local `.git/config`.
28
- 2. It adds or appends to a `.gitattributes` file in the repository root to route all files matching your configured directory (e.g., `.my-db/*`) through the custom merge driver.
29
- 3. It creates or updates a `.gitignore` to ignore SQLite transient files (`*-journal`, `*-wal`, `*-shm`).
25
+ 1. Registers `git-merge-sqlitevfs` as a custom Git merge driver in the local `.git/config`.
26
+ 2. Updates `.gitattributes` in the repository root to route files matching the configured directory (e.g., `.my-db/*`) through the custom merge driver.
27
+ 3. Updates `.gitignore` to ignore SQLite transient files (`*-journal`, `*-wal`, `*-shm`).
30
28
 
31
- ### Safe alongside Source Code
29
+ ### Source Code Compatibility
32
30
 
33
- The custom SQLite merge driver **will not interfere with the merging of standard source code or text files**. By utilizing the `.gitattributes` file, Git explicitly scopes the custom driver strictly to the files within your designated database directory (e.g., `.my-db/* merge=sqlitevfs`). All other files in your repository will continue to use Git's default text-based merge algorithms.
31
+ The custom SQLite merge driver scopes to the designated database directory via `.gitattributes`. Other repository files continue using Git's default text-based merge algorithms.
34
32
 
35
33
  ## Usage
36
34
 
37
- The VFS works in both Node.js and Deno environments. By calling the `bootstrapGitVFS()` method prior to initializing `@libsql/client`, the extension is loaded process-wide.
35
+ The VFS operates in Node.js and Deno environments. Calling `bootstrapGitVFS()` prior to initializing `@libsql/client` loads the extension process-wide.
36
+
37
+ To ensure the VFS correctly intercepts database connections and executes required PRAGMAs, use `createVFSClient` to initialize your connection instead of `@libsql/client`'s `createClient`.
38
+
39
+ ### Native Binding Isolation (libsql version mismatch)
40
+
41
+ `git-sqlite-vfs` internally loads the `libsql` native C extension. If your project uses a different version of `@libsql/client` (and thus a different `libsql` binding), Node/Deno may spawn two isolated native C instances in memory, causing the VFS registration to fail silently.
42
+
43
+ To prevent this, you can inject your own `libsql` instance directly into the VFS via `options.libsql`:
44
+
45
+ ```typescript
46
+ import * as myLibsql from 'libsql';
47
+ import { bootstrapGitVFS } from 'git-sqlite-vfs';
48
+
49
+ await bootstrapGitVFS({ dir: '.my-db', libsql: myLibsql });
50
+ ```
38
51
 
39
52
  ### Example with `@libsql/client` and Drizzle ORM
40
53
 
41
54
  ```typescript
42
- import { createClient } from '@libsql/client'; // In Deno: 'npm:@libsql/client/node'
55
+ import { bootstrapGitVFS, createVFSClient } from 'git-sqlite-vfs';
43
56
  import { drizzle } from 'drizzle-orm/libsql';
44
57
  import { sqliteTable, integer, text } from 'drizzle-orm/sqlite-core';
45
- import { bootstrapGitVFS } from 'git-sqlite-vfs';
46
58
 
47
- // 1. Load the native extension process-wide and set the VFS directory
59
+ // Load the native extension process-wide and set the VFS directory
48
60
  await bootstrapGitVFS({ dir: '.my-db' });
49
61
 
50
- // 2. Initialize the database connection using a standard file: URL
51
- const client = createClient({
62
+ // Initialize the database connection (automates required PRAGMAs and natively supports Deno)
63
+ const client = await createVFSClient({
52
64
  url: 'file:.my-db/local.db'
53
65
  });
54
66
 
55
- // 3. Wrap with Drizzle ORM
67
+ // Wrap with Drizzle ORM
56
68
  const db = drizzle(client);
57
69
 
58
- // 4. Define the schema
70
+ // Define the schema
59
71
  const users = sqliteTable('users', {
60
72
  id: integer('id').primaryKey(),
61
73
  name: text('name')
62
74
  });
63
75
 
64
- // 5. Execute queries. File I/O is intercepted by the Git VFS.
76
+ // Execute queries
65
77
  await db.insert(users).values({ id: 1, name: 'Alice' });
66
78
  const allUsers = await db.select().from(users);
67
79
 
68
80
  console.log(allUsers);
69
81
  ```
70
82
 
71
- ## Preventing Repository Bloat
83
+ ### Usage with Deno
84
+
85
+ By default, Deno resolves `npm:@libsql/client` to its browser-compatible implementation, which bypasses native C extensions entirely. This means the VFS never runs.
86
+
87
+ To work around this, lock the dependency directly to the Node environment. `createVFSClient` will gracefully default to the Node native bindings under the hood (`npm:@libsql/client/node`).
88
+
89
+ If you still need to bypass `createVFSClient`, import using the `/node` path directly:
90
+ ```typescript
91
+ import { createClient } from 'npm:@libsql/client@0.14.0/node';
92
+ ```
93
+
94
+ ## Database Compaction
95
+
96
+ SQLite often zeroes out deleted data pages rather than shrinking the file size, causing unneeded `.bin` pages to remain. To allow the Git VFS to remove these unused shards, it requires `FULL` auto-vacuuming and `DELETE` journaling to actively split and compact out-of-bounds shards.
72
97
 
73
- Because SQLite often zeroes out deleted data pages rather than shrinking the database file size, "zombie" `.bin` pages may accumulate in the repository over time. To allow the Git VFS to automatically garbage-collect these unused chunks, configure SQLite to use `FULL` auto-vacuuming and `DELETE` journaling.
98
+ When you use `createVFSClient()`, it automatically executes these PRAGMAs for you upon connection initialization.
74
99
 
75
- Execute these PRAGMA statements when initializing the database connection:
100
+ If you create your client manually without `createVFSClient`, you must run them yourself:
76
101
 
77
102
  ```sql
78
103
  PRAGMA auto_vacuum = FULL;
79
104
  PRAGMA journal_mode = DELETE;
80
105
  ```
81
106
 
82
- Alternatively, you can run `VACUUM;` periodically. When SQLite explicitly reduces the database file size, the underlying VFS `xTruncate` implementation will remove the out-of-bounds `.bin` shards, keeping the repository history compressed.
107
+ Alternatively, executing `VACUUM;` periodically reduces the database file size, and the VFS `xTruncate` implementation will remove out-of-bounds `.bin` shards.
83
108
 
84
109
  ## Compatibility
85
110
 
package/index.d.ts CHANGED
@@ -2,10 +2,18 @@ export const GITVFS_EXTENSION_PATH: string;
2
2
 
3
3
  export interface BootstrapOptions {
4
4
  dir?: string;
5
+ libsql?: any;
5
6
  }
6
7
 
7
8
  export function bootstrapGitVFS(options?: BootstrapOptions): Promise<void>;
8
9
 
10
+ export interface CreateVFSClientOptions {
11
+ clientOptions: any;
12
+ createClient?: any;
13
+ }
14
+
15
+ export function createVFSClient(options: CreateVFSClientOptions | any): Promise<any>;
16
+
9
17
  export interface ConfigureGitOptions {
10
18
  repoDir: string;
11
19
  vfsDir: string;
package/index.js CHANGED
@@ -33,16 +33,18 @@ export async function bootstrapGitVFS(options = {}) {
33
33
 
34
34
  let currentExtPath = extensionPath;
35
35
  if (!fs.existsSync(currentExtPath)) {
36
- const writableDir = path.join(process.cwd(), '.git-sqlite-vfs-bin');
36
+ const writableDir = path.join(__dirname, '.git-sqlite-vfs-bin');
37
37
  await downloadOrBuild(writableDir);
38
38
  currentExtPath = path.join(writableDir, `gitvfs.${ext}`);
39
39
  }
40
40
 
41
41
  // Dynamically import libsql so that we load the extension into its isolated native memory space.
42
42
  let Database;
43
- if (typeof Deno !== 'undefined') {
43
+ if (options.libsql) {
44
+ Database = options.libsql.default || options.libsql.Database || options.libsql;
45
+ } else if (typeof Deno !== 'undefined') {
44
46
  // Deno environment
45
- const lib = await import('libsql');
47
+ const lib = await import('npm:libsql');
46
48
  Database = lib.default || lib.Database || lib;
47
49
  } else {
48
50
  // Node.js environment
@@ -63,6 +65,28 @@ export async function bootstrapGitVFS(options = {}) {
63
65
  }
64
66
  }
65
67
 
68
+ export async function createVFSClient(options) {
69
+ let createClientFn = options.createClient;
70
+ if (!createClientFn) {
71
+ if (typeof Deno !== 'undefined') {
72
+ // Gracefully default to Node native bindings in Deno to prevent bypassing the VFS
73
+ const mod = await import('npm:@libsql/client/node');
74
+ createClientFn = mod.createClient;
75
+ } else {
76
+ const mod = await import('@libsql/client');
77
+ createClientFn = mod.createClient;
78
+ }
79
+ }
80
+
81
+ const client = createClientFn(options.clientOptions || options);
82
+
83
+ // Execute required PRAGMAs for the VFS to actively split and compact out-of-bounds shards
84
+ await client.execute('PRAGMA auto_vacuum = FULL;');
85
+ await client.execute('PRAGMA journal_mode = DELETE;');
86
+
87
+ return client;
88
+ }
89
+
66
90
  export async function configureGitIntegration({ repoDir, vfsDir }) {
67
91
  let driverDir = path.resolve(__dirname, 'c', 'output');
68
92
  let driverPath = path.join(driverDir, 'git-merge-sqlitevfs');
@@ -71,7 +95,7 @@ export async function configureGitIntegration({ repoDir, vfsDir }) {
71
95
  }
72
96
 
73
97
  if (!fs.existsSync(driverPath)) {
74
- driverDir = path.join(process.cwd(), '.git-sqlite-vfs-bin');
98
+ driverDir = path.join(__dirname, '.git-sqlite-vfs-bin');
75
99
  await downloadOrBuild(driverDir);
76
100
  driverPath = path.join(driverDir, 'git-merge-sqlitevfs');
77
101
  if (platform === 'win32' && !fs.existsSync(driverPath) && fs.existsSync(driverPath + '.exe')) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "git-sqlite-vfs",
3
- "version": "0.0.10",
3
+ "version": "0.0.12",
4
4
  "description": "A Git-Versioned SQLite Database via a Custom Virtual File System (VFS)",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",
@@ -29,8 +29,10 @@
29
29
  "test:deno": "npm run build && rm -rf .db test.db .test-db .git .gitattributes .gitignore && git init --initial-branch=master && deno test -A test/*.deno.test.ts"
30
30
  },
31
31
  "dependencies": {
32
+ "drizzle-orm": "^0.33.0"
33
+ },
34
+ "peerDependencies": {
32
35
  "@libsql/client": "^0.14.0",
33
- "drizzle-orm": "^0.33.0",
34
36
  "libsql": "^0.4.5"
35
37
  },
36
38
  "author": "",