pgserve 0.1.5 → 1.0.2

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.
@@ -0,0 +1,11 @@
1
+ {
2
+ "permissions": {
3
+ "allow": [
4
+ "Bash(kill:*)",
5
+ "Bash(pkill:*)",
6
+ "Bash(xargs:*)"
7
+ ],
8
+ "deny": [],
9
+ "ask": []
10
+ }
11
+ }
@@ -0,0 +1,2 @@
1
+ pnpm exec lint-staged
2
+ pnpm deadcode
package/README.md CHANGED
@@ -1,323 +1,348 @@
1
- # pgserve
1
+ <div align="center">
2
+ <h1>pgserve</h1>
3
+ <p><strong>Embedded PostgreSQL Server with TRUE Concurrent Connections</strong></p>
2
4
 
3
- **Multi-tenant PostgreSQL router using PGlite** - Single port, auto-provisioning, zero config.
5
+ <p>
6
+ <a href="https://www.npmjs.com/package/pgserve"><img src="https://img.shields.io/npm/v/pgserve?style=flat-square&color=00D9FF" alt="npm version"></a>
7
+ <img src="https://img.shields.io/badge/node-%3E%3D18.0.0-brightgreen?style=flat-square" alt="Node.js">
8
+ <img src="https://img.shields.io/badge/PostgreSQL-17.7-blue?style=flat-square" alt="PostgreSQL">
9
+ <a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-green?style=flat-square" alt="License"></a>
10
+ <a href="https://discord.gg/xcW8c7fF3R"><img src="https://img.shields.io/discord/1095114867012292758?style=flat-square&color=00D9FF&label=discord" alt="Discord"></a>
11
+ </p>
4
12
 
5
- Perfect for multi-user apps, AI agents, and embedded databases.
13
+ <p><em>Zero config, auto-provision databases, unlimited concurrent connections. Just works.</em></p>
6
14
 
7
- ## ✨ Features
15
+ <p>
16
+ <a href="#-quick-start">Quick Start</a> •
17
+ <a href="#-features">Features</a> •
18
+ <a href="#-cli-reference">CLI</a> •
19
+ <a href="#-api">API</a> •
20
+ <a href="#-performance">Performance</a>
21
+ </p>
22
+ </div>
8
23
 
9
- - 🎯 **Multi-Tenant** - Single port, multiple isolated databases (one per user/app)
10
- - 🚀 **Auto-Provisioning** - Databases created on demand from connection string
11
- - 🔌 **Single Endpoint** - `postgresql://localhost:8432/dbname` routes to correct PGlite instance
12
- - ⚡ **High Performance** - MVCC, row-level locking, concurrent writes
13
- - 🎛️ **Zero Configuration** - Auto-tuned for your hardware (CPU, RAM)
14
- - 📦 **PostgreSQL Compatible** - Works with any PostgreSQL client (psql, Prisma, pg, etc.)
15
- - 🛡️ **Data Isolation** - Each database = separate PGlite instance
16
- - 💾 **Persistent** - Data survives restarts
24
+ <br>
17
25
 
18
- ## 🎯 Use Cases
19
-
20
- ### Perfect For
21
-
22
- - 🤖 **AI Agents** - Each agent gets isolated database (sessions, memory, state)
23
- - 👥 **Multi-User Apps** - One database per user, single endpoint
24
- - 🏢 **SaaS Applications** - Tenant isolation without infrastructure complexity
25
- - 🧪 **Development** - Local PostgreSQL without Docker
26
- - 📱 **Desktop Apps** - Electron, Tauri with embedded multi-tenant DB
27
-
28
- ### Real-World Examples
29
-
30
- - **AI Agent Swarms**: 100+ agents, each with isolated database
31
- - **Multi-Tenant SaaS**: Single endpoint, automatic tenant provisioning
32
- - **Desktop Apps**: Embedded PostgreSQL with multi-user support
33
-
34
- ## 🚀 Quick Start
35
-
36
- ### Installation
26
+ ## Quick Start
37
27
 
38
28
  ```bash
39
- npm install pgserve
40
- # or
41
- pnpm add pgserve
42
- ```
43
-
44
- ### Multi-Tenant Mode (Recommended)
45
-
46
- ```javascript
47
- import { startMultiTenantServer } from 'pgserve';
48
-
49
- // Start multi-tenant router on single port
50
- const router = await startMultiTenantServer({
51
- port: 8432, // Single port for all databases
52
- baseDir: './data', // Base directory (creates ./data/dbname/ per DB)
53
- autoProvision: true, // Auto-create databases (default: true)
54
- maxInstances: 100, // Max concurrent databases
55
- logLevel: 'info'
56
- });
57
-
58
- // Clients connect like normal PostgreSQL:
59
- // postgresql://localhost:8432/user123 → ./data/user123/
60
- // postgresql://localhost:8432/app456 → ./data/app456/
61
- ```
62
-
63
- ### Usage with PostgreSQL Clients
64
-
65
- ```javascript
66
- import pg from 'pg';
67
-
68
- // Connect to database "user123" (auto-created)
69
- const client1 = new pg.Client({
70
- connectionString: 'postgresql://localhost:8432/user123'
71
- });
72
-
73
- await client1.connect();
74
- await client1.query('CREATE TABLE users (id SERIAL, name TEXT)');
75
- await client1.query("INSERT INTO users (name) VALUES ('Alice')");
76
-
77
- // Connect to database "app456" (different isolated instance)
78
- const client2 = new pg.Client({
79
- connectionString: 'postgresql://localhost:8432/app456'
80
- });
81
-
82
- await client2.connect();
83
- await client2.query('CREATE TABLE posts (id SERIAL, title TEXT)');
84
-
85
- // Each database is completely isolated!
29
+ npx pgserve
86
30
  ```
87
31
 
88
- ### With Prisma
89
-
90
- ```javascript
91
- // prisma/schema.prisma
92
- datasource db {
93
- provider = "postgresql"
94
- url = env("DATABASE_URL")
95
- }
96
-
97
- // .env
98
- DATABASE_URL="postgresql://localhost:8432/myapp"
99
- ```
32
+ Connect from any PostgreSQL client — databases auto-create on first connection:
100
33
 
101
34
  ```bash
102
- # Auto-provisions "myapp" database
103
- npx prisma migrate dev
35
+ psql postgresql://localhost:5432/myapp
104
36
  ```
105
37
 
106
- ## 📖 API Reference
107
-
108
- ### `startMultiTenantServer(options)`
38
+ <br>
39
+
40
+ ## Features
41
+
42
+ <table>
43
+ <tr>
44
+ <td><b>Unlimited Concurrency</b></td>
45
+ <td>Native PostgreSQL process forking — no connection locks</td>
46
+ </tr>
47
+ <tr>
48
+ <td><b>Zero Config</b></td>
49
+ <td>Just run <code>pgserve</code>, connect to any database name</td>
50
+ </tr>
51
+ <tr>
52
+ <td><b>Auto-Provision</b></td>
53
+ <td>Databases created automatically on first connection</td>
54
+ </tr>
55
+ <tr>
56
+ <td><b>Memory Mode</b></td>
57
+ <td>Fast and ephemeral for development (default)</td>
58
+ </tr>
59
+ <tr>
60
+ <td><b>Persistent Mode</b></td>
61
+ <td>Use <code>--data ./path</code> for durable storage</td>
62
+ </tr>
63
+ <tr>
64
+ <td><b>Async Replication</b></td>
65
+ <td>Sync to real PostgreSQL with zero performance impact</td>
66
+ </tr>
67
+ <tr>
68
+ <td><b>Cross-Platform</b></td>
69
+ <td>Linux x64, macOS ARM64/x64, Windows x64</td>
70
+ </tr>
71
+ <tr>
72
+ <td><b>Any Client Works</b></td>
73
+ <td>psql, node-postgres, Prisma, Drizzle, TypeORM</td>
74
+ </tr>
75
+ </table>
76
+
77
+ <br>
78
+
79
+ ## Installation
109
80
 
110
- Start multi-tenant router server.
81
+ ```bash
82
+ # Zero install (recommended)
83
+ npx pgserve
111
84
 
112
- ```javascript
113
- const router = await startMultiTenantServer({
114
- port: 8432, // Port to listen on (default: 8432)
115
- host: '127.0.0.1', // Host to bind (default: 127.0.0.1)
116
- baseDir: './data', // Base data directory (default: './data')
117
- autoProvision: true, // Auto-create databases (default: true)
118
- maxInstances: 100, // Max concurrent databases (default: 100)
119
- logLevel: 'info', // Log level: error, warn, info, debug (default: 'info')
120
- inspect: false // Enable wire protocol debugging (default: false)
121
- });
85
+ # Global install
86
+ npm install -g pgserve
122
87
 
123
- // Returns MultiTenantRouter instance
88
+ # Project dependency
89
+ npm install pgserve
124
90
  ```
125
91
 
126
- ### Router Methods
127
-
128
- ```javascript
129
- // Get router stats
130
- const stats = router.getStats();
131
- // {
132
- // port: 8432,
133
- // activeConnections: 2,
134
- // pool: {
135
- // totalInstances: 3,
136
- // maxInstances: 100,
137
- // instances: [...]
138
- // }
139
- // }
140
-
141
- // List all databases
142
- const databases = router.listDatabases();
143
- // [
144
- // { dbName: 'user123', locked: false, queueLength: 0, ... },
145
- // { dbName: 'app456', locked: true, queueLength: 1, ... }
146
- // ]
147
-
148
- // Stop router (closes all instances)
149
- await router.stop();
150
- ```
92
+ > PostgreSQL binaries are automatically downloaded on first run.
151
93
 
152
- ## 🏗️ Architecture
94
+ <br>
153
95
 
154
- ### Single Port, Multi-Tenant Routing
96
+ ## CLI Reference
155
97
 
156
98
  ```
157
- Client 1: postgresql://localhost:8432/user123
158
- Client 2: postgresql://localhost:8432/app456
159
- Client 3: postgresql://localhost:8432/tenant789
160
-
161
- ┌────────────────────────────────────────┐
162
- Multi-Tenant Router (port 8432)
163
- - Parses connection database name │
164
- - Routes to correct PGlite instance │
165
- - Auto-provisions new databases │
166
- └────────────────────────────────────────┘
167
-
168
- ┌────────────────────────────────────────┐
169
- Instance Pool │
170
- ├─ user123 → PGlite('./data/user123') │
171
- │ ├─ app456 → PGlite('./data/app456') │
172
- │ └─ tenant789 → PGlite('./data/tenant789') │
173
- └────────────────────────────────────────┘
99
+ pgserve [options]
100
+
101
+ Options:
102
+ --port <number> PostgreSQL port (default: 5432)
103
+ --data <path> Data directory for persistence (default: in-memory)
104
+ --host <host> Host to bind to (default: 127.0.0.1)
105
+ --log <level> Log level: error, warn, info, debug (default: info)
106
+ --cluster Force cluster mode (auto-enabled on multi-core)
107
+ --no-cluster Force single-process mode
108
+ --workers <n> Number of worker processes (default: CPU cores)
109
+ --no-provision Disable auto-provisioning of databases
110
+ --sync-to <url> Sync to real PostgreSQL (async replication)
111
+ --sync-databases <p> Database patterns to sync (comma-separated)
112
+ --help Show help message
174
113
  ```
175
114
 
176
- ### How It Works
115
+ <details>
116
+ <summary><b>Examples</b></summary>
177
117
 
178
- 1. **Client connects**: `postgresql://localhost:8432/myapp`
179
- 2. **Router parses** PostgreSQL startup message → extracts database name: `myapp`
180
- 3. **Pool checks** for existing PGlite instance for `myapp`
181
- 4. **Auto-provision** creates `./data/myapp/` if doesn't exist
182
- 5. **Route connection** to PGlite instance
183
- 6. **Client communicates** with isolated database
118
+ ```bash
119
+ # Development (memory mode, auto-clusters on multi-core)
120
+ pgserve
184
121
 
185
- ### Connection Lifecycle
122
+ # Persistent storage
123
+ pgserve --data /var/lib/pgserve
186
124
 
187
- - **First connection to DB**: PGlite instance created, database initialized
188
- - **Subsequent connections**: Reuses existing PGlite instance
189
- - **Concurrent connections**: Queued (PGlite is single-connection per instance)
190
- - **Connection closes**: Instance unlocked, ready for next connection
125
+ # Custom port
126
+ pgserve --port 5433
191
127
 
192
- ## 📊 Performance
128
+ # Sync to production PostgreSQL
129
+ pgserve --sync-to "postgresql://user:pass@db.example.com:5432/prod"
130
+ ```
193
131
 
194
- ### vs Multiple Port Approach
132
+ </details>
195
133
 
196
- | Approach | Ports Used | Management | Scalability |
197
- |----------|-----------|------------|-------------|
198
- | **Multi-tenant** | 1 | Auto | ✅ Excellent (100+ DBs) |
199
- | Multi-port | 1 per DB | Manual | ⚠️ Limited (port exhaustion) |
134
+ <br>
200
135
 
201
- ### Benchmarks
136
+ ## API
202
137
 
203
- - **DB creation**: ~50ms per database (lazy initialization)
204
- - **Connection routing**: < 1ms overhead
205
- - **Concurrent databases**: Tested with 100+ isolated instances
206
- - **Memory**: ~10-30MB per PGlite instance (depends on data)
138
+ ```javascript
139
+ import { startMultiTenantServer } from 'pgserve';
207
140
 
208
- ## 🔧 CLI Usage
141
+ const server = await startMultiTenantServer({
142
+ port: 5432,
143
+ host: '127.0.0.1',
144
+ baseDir: null, // null = memory mode
145
+ logLevel: 'info',
146
+ autoProvision: true,
147
+ syncTo: null, // Optional: PostgreSQL URL for replication
148
+ syncDatabases: null // Optional: patterns like "myapp,tenant_*"
149
+ });
209
150
 
210
- ### Install Globally
151
+ // Get stats
152
+ console.log(server.getStats());
211
153
 
212
- ```bash
213
- npm install -g pgserve
154
+ // Graceful shutdown
155
+ await server.stop();
214
156
  ```
215
157
 
216
- ### Commands
217
-
218
- ```bash
219
- # Start multi-tenant router
220
- pgserve start --port 8432 --dir ./data
221
-
222
- # Check router status
223
- pgserve router-stats
224
-
225
- # List all databases
226
- pgserve list-databases
158
+ <br>
227
159
 
228
- # Stop router
229
- pgserve stop-router
230
- ```
160
+ ## Framework Integration
231
161
 
232
- ## 🛠️ Advanced Usage
233
-
234
- ### Custom Instance Pool
162
+ <details>
163
+ <summary><b>node-postgres</b></summary>
235
164
 
236
165
  ```javascript
237
- import { InstancePool } from 'pgserve';
166
+ import pg from 'pg';
238
167
 
239
- const pool = new InstancePool({
240
- baseDir: './databases',
241
- maxInstances: 50,
242
- autoProvision: true
168
+ const client = new pg.Client({
169
+ connectionString: 'postgresql://localhost:5432/myapp'
243
170
  });
244
171
 
245
- // Get or create instance
246
- const instance = await pool.getOrCreate('mydb');
247
-
248
- // Access PGlite directly
249
- const result = await instance.db.query('SELECT version()');
172
+ await client.connect();
173
+ await client.query('CREATE TABLE users (id SERIAL, name TEXT)');
174
+ await client.query("INSERT INTO users (name) VALUES ('Alice')");
175
+ const result = await client.query('SELECT * FROM users');
176
+ console.log(result.rows);
177
+ await client.end();
250
178
  ```
251
179
 
252
- ### Connection Queueing
253
-
254
- PGlite is **single-connection** per instance. When multiple clients connect to the same database:
255
-
256
- ```javascript
257
- // Client 1 connects to "mydb" → locks instance
258
- const client1 = new pg.Client({ database: 'mydb', ... });
259
- await client1.connect(); // ✅ Connected
260
-
261
- // Client 2 tries to connect to "mydb" → queued
262
- const client2 = new pg.Client({ database: 'mydb', ... });
263
- await client2.connect(); // ⏳ Waits for client1 to disconnect
180
+ </details>
264
181
 
265
- // Client 1 disconnects
266
- await client1.end();
182
+ <details>
183
+ <summary><b>Prisma</b></summary>
267
184
 
268
- // Client 2 auto-connects
269
- // ✅ Connected
185
+ ```prisma
186
+ // prisma/schema.prisma
187
+ datasource db {
188
+ provider = "postgresql"
189
+ url = env("DATABASE_URL")
190
+ }
270
191
  ```
271
192
 
272
- Default timeout: **30 seconds**. Customize in `pool.acquire()`:
193
+ ```bash
194
+ # .env
195
+ DATABASE_URL="postgresql://localhost:5432/myapp"
273
196
 
274
- ```javascript
275
- await pool.acquire('mydb', socket, timeout = 60000); // 60s timeout
197
+ # Run migrations
198
+ npx prisma migrate dev
276
199
  ```
277
200
 
278
- ## 🔐 Security Notes
201
+ </details>
279
202
 
280
- - **No authentication**: PGlite doesn't support auth (embedded use case)
281
- - **Bind to localhost**: Default `127.0.0.1` (local only)
282
- - **Production**: Use proper PostgreSQL for external access
203
+ <details>
204
+ <summary><b>Drizzle</b></summary>
283
205
 
284
- ## 📁 File Structure
285
-
286
- ```
287
- ./data/
288
- ├─ user123/ (PGlite data for "user123" database)
289
- │ ├─ base/
290
- │ ├─ pg_wal/
291
- │ └─ PG_VERSION
292
- ├─ app456/ (PGlite data for "app456" database)
293
- └─ tenant789/ (PGlite data for "tenant789" database)
294
- ```
206
+ ```typescript
207
+ import { drizzle } from 'drizzle-orm/node-postgres';
208
+ import { Pool } from 'pg';
295
209
 
296
- ## 🤝 Contributing
210
+ const pool = new Pool({
211
+ connectionString: 'postgresql://localhost:5432/myapp'
212
+ });
297
213
 
298
- Contributions welcome! Please:
214
+ const db = drizzle(pool);
215
+ const users = await db.select().from(usersTable);
216
+ ```
299
217
 
300
- 1. Fork the repository
301
- 2. Create a feature branch
302
- 3. Add tests for new features
303
- 4. Submit a pull request
218
+ </details>
304
219
 
305
- ## 📄 License
220
+ <br>
306
221
 
307
- MIT License - Copyright (c) 2025 Namastex Labs
222
+ ## Async Replication
308
223
 
309
- ## 🙏 Credits
224
+ Sync ephemeral pgserve data to a real PostgreSQL database. Uses native logical replication for **zero performance impact** on the hot path.
310
225
 
311
- Built on top of:
312
- - [@electric-sql/pglite](https://pglite.dev) - PostgreSQL WASM
313
- - [@electric-sql/pglite-socket](https://www.npmjs.com/package/@electric-sql/pglite-socket) - Wire protocol server
226
+ ```bash
227
+ # Sync all databases
228
+ pgserve --sync-to "postgresql://user:pass@db.example.com:5432/mydb"
314
229
 
315
- ## 📧 Support
230
+ # Sync specific databases (supports wildcards)
231
+ pgserve --sync-to "postgresql://..." --sync-databases "myapp,tenant_*"
232
+ ```
316
233
 
317
- - **Issues**: [GitHub Issues](https://github.com/namastexlabs/pgserve/issues)
318
- - **Email**: labs@namastex.com
319
- - **Website**: [namastex.com](https://namastex.com)
234
+ > Replication is handled by PostgreSQL's WAL writer process, completely off the Node.js event loop. Sync failures don't affect main server operation.
235
+
236
+ <br>
237
+
238
+ ## Performance
239
+
240
+ <table>
241
+ <tr>
242
+ <th>Scenario</th>
243
+ <th>SQLite</th>
244
+ <th>PGlite</th>
245
+ <th>Docker PG</th>
246
+ <th>pgserve</th>
247
+ </tr>
248
+ <tr>
249
+ <td><b>Concurrent Writes</b> (10 agents)</td>
250
+ <td>100 qps</td>
251
+ <td>219 qps</td>
252
+ <td>758 qps</td>
253
+ <td><b>855 qps 🏆</b></td>
254
+ </tr>
255
+ <tr>
256
+ <td><b>Mixed Workload</b> (messages)</td>
257
+ <td>335 qps</td>
258
+ <td>506 qps</td>
259
+ <td>940 qps</td>
260
+ <td><b>1034 qps 🏆</b></td>
261
+ </tr>
262
+ <tr>
263
+ <td><b>Write Lock</b> (50 writers)</td>
264
+ <td>98 qps</td>
265
+ <td>201 qps</td>
266
+ <td><b>478 qps 🏆</b></td>
267
+ <td>391 qps</td>
268
+ </tr>
269
+ </table>
270
+
271
+ > pgserve beats Docker PostgreSQL in 2/3 scenarios. For development, CI/CD, and ephemeral deployments — better-than-Docker performance without Docker.
272
+ >
273
+ > Run benchmarks: `npm run bench`
274
+
275
+ <br>
276
+
277
+ ## Use Cases
278
+
279
+ <table>
280
+ <tr>
281
+ <td width="50%">
282
+ <h4>Development & Testing</h4>
283
+ <ul>
284
+ <li><b>Local Development</b> — PostgreSQL without Docker</li>
285
+ <li><b>Integration Testing</b> — Real PostgreSQL, not mocks</li>
286
+ <li><b>CI/CD Pipelines</b> — Fresh databases per test run</li>
287
+ <li><b>E2E Testing</b> — Isolated database for Playwright/Cypress</li>
288
+ </ul>
289
+ </td>
290
+ <td width="50%">
291
+ <h4>AI & Agents</h4>
292
+ <ul>
293
+ <li><b>AI Agent Memory</b> — Isolated, concurrent-safe database</li>
294
+ <li><b>LLM Tool Use</b> — Give AI models a real PostgreSQL</li>
295
+ <li><b>RAG Applications</b> — Store embeddings with pgvector</li>
296
+ </ul>
297
+ </td>
298
+ </tr>
299
+ <tr>
300
+ <td width="50%">
301
+ <h4>Multi-Tenant & SaaS</h4>
302
+ <ul>
303
+ <li><b>Tenant Isolation</b> — Auto-provision per tenant</li>
304
+ <li><b>Demo Environments</b> — Instant sandboxed PostgreSQL</li>
305
+ <li><b>Microservices Dev</b> — Each service gets its own DB</li>
306
+ </ul>
307
+ </td>
308
+ <td width="50%">
309
+ <h4>Edge & Embedded</h4>
310
+ <ul>
311
+ <li><b>IoT Devices</b> — Full PostgreSQL on Raspberry Pi</li>
312
+ <li><b>Desktop Apps</b> — Electron with embedded PostgreSQL</li>
313
+ <li><b>Offline-First</b> — Local DB that syncs when online</li>
314
+ </ul>
315
+ </td>
316
+ </tr>
317
+ </table>
318
+
319
+ <br>
320
+
321
+ ## Requirements
322
+
323
+ - **Node.js** >= 18.0.0
324
+ - **Platform**: Linux x64, macOS ARM64/x64, Windows x64
325
+
326
+ <br>
327
+
328
+ ## Contributing
329
+
330
+ Contributions welcome! Fork the repo, create a feature branch, add tests, and submit a PR.
331
+
332
+ <br>
320
333
 
321
334
  ---
322
335
 
323
- **Made with ❤️ by [Namastex Labs](https://namastex.com)**
336
+ <div align="center">
337
+ <p>
338
+ <b>MIT License</b> — Copyright (c) 2025 Namastex Labs
339
+ </p>
340
+ <p>
341
+ <a href="https://github.com/namastexlabs/pgserve">GitHub</a> •
342
+ <a href="https://www.npmjs.com/package/pgserve">npm</a> •
343
+ <a href="https://github.com/namastexlabs/pgserve/issues">Issues</a>
344
+ </p>
345
+ <p>
346
+ Made with love by <a href="https://namastex.ai">Namastex Labs</a>
347
+ </p>
348
+ </div>