pgserve 0.1.1

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 (158) hide show
  1. package/.genie/AGENTS.md +13 -0
  2. package/.genie/agents/README.md +110 -0
  3. package/.genie/agents/analyze.md +176 -0
  4. package/.genie/agents/forge.md +290 -0
  5. package/.genie/agents/garbage-cleaner.md +324 -0
  6. package/.genie/agents/garbage-collector.md +596 -0
  7. package/.genie/agents/github-issue-gc.md +618 -0
  8. package/.genie/agents/review.md +380 -0
  9. package/.genie/agents/semantic-analyzer/find-duplicates.md +90 -0
  10. package/.genie/agents/semantic-analyzer/find-orphans.md +99 -0
  11. package/.genie/agents/semantic-analyzer.md +101 -0
  12. package/.genie/agents/update.md +182 -0
  13. package/.genie/agents/wish.md +357 -0
  14. package/.genie/code/AGENTS.md +692 -0
  15. package/.genie/code/agents/audit/risk.md +173 -0
  16. package/.genie/code/agents/audit/security.md +189 -0
  17. package/.genie/code/agents/audit.md +145 -0
  18. package/.genie/code/agents/challenge.md +230 -0
  19. package/.genie/code/agents/change-reviewer.md +295 -0
  20. package/.genie/code/agents/code-garbage-collector.md +425 -0
  21. package/.genie/code/agents/code-quality.md +410 -0
  22. package/.genie/code/agents/commit-suggester.md +255 -0
  23. package/.genie/code/agents/commit.md +124 -0
  24. package/.genie/code/agents/consensus.md +204 -0
  25. package/.genie/code/agents/daily-standup.md +722 -0
  26. package/.genie/code/agents/docgen.md +48 -0
  27. package/.genie/code/agents/explore.md +79 -0
  28. package/.genie/code/agents/fix.md +100 -0
  29. package/.genie/code/agents/git/commit-advisory.md +219 -0
  30. package/.genie/code/agents/git/workflows/issue.md +244 -0
  31. package/.genie/code/agents/git/workflows/pr.md +179 -0
  32. package/.genie/code/agents/git/workflows/release.md +460 -0
  33. package/.genie/code/agents/git/workflows/report.md +342 -0
  34. package/.genie/code/agents/git.md +432 -0
  35. package/.genie/code/agents/implementor.md +161 -0
  36. package/.genie/code/agents/install.md +515 -0
  37. package/.genie/code/agents/issue-creator.md +344 -0
  38. package/.genie/code/agents/polish.md +116 -0
  39. package/.genie/code/agents/qa.md +653 -0
  40. package/.genie/code/agents/refactor.md +294 -0
  41. package/.genie/code/agents/release.md +1129 -0
  42. package/.genie/code/agents/roadmap.md +885 -0
  43. package/.genie/code/agents/tests.md +557 -0
  44. package/.genie/code/agents/tracer.md +50 -0
  45. package/.genie/code/agents/update/upstream-update.md +85 -0
  46. package/.genie/code/agents/update/versions/generic-update.md +305 -0
  47. package/.genie/code/agents/vibe.md +1317 -0
  48. package/.genie/code/spells/agent-configuration.md +58 -0
  49. package/.genie/code/spells/automated-rc-publishing.md +106 -0
  50. package/.genie/code/spells/branch-tracker-guidance.md +28 -0
  51. package/.genie/code/spells/debug.md +320 -0
  52. package/.genie/code/spells/emoji-naming-convention.md +303 -0
  53. package/.genie/code/spells/evidence-storage.md +26 -0
  54. package/.genie/code/spells/file-naming-rules.md +35 -0
  55. package/.genie/code/spells/forge-code-blueprints.md +195 -0
  56. package/.genie/code/spells/genie-integration.md +153 -0
  57. package/.genie/code/spells/publishing-protocol.md +61 -0
  58. package/.genie/code/spells/team-consultation-protocol.md +284 -0
  59. package/.genie/code/spells/tool-requirements.md +20 -0
  60. package/.genie/code/spells/triad-maintenance-protocol.md +154 -0
  61. package/.genie/code/teams/tech-council/council.md +328 -0
  62. package/.genie/code/teams/tech-council/jt.md +352 -0
  63. package/.genie/code/teams/tech-council/nayr.md +305 -0
  64. package/.genie/code/teams/tech-council/oettam.md +375 -0
  65. package/.genie/neurons/README.md +193 -0
  66. package/.genie/neurons/forge.md +106 -0
  67. package/.genie/neurons/genie.md +63 -0
  68. package/.genie/neurons/review.md +106 -0
  69. package/.genie/neurons/wish.md +104 -0
  70. package/.genie/product/README.md +20 -0
  71. package/.genie/product/cli-automation.md +359 -0
  72. package/.genie/product/environment.md +60 -0
  73. package/.genie/product/mission.md +60 -0
  74. package/.genie/product/roadmap.md +44 -0
  75. package/.genie/product/tech-stack.md +34 -0
  76. package/.genie/product/templates/context-template.md +218 -0
  77. package/.genie/product/templates/qa-done-report-template.md +68 -0
  78. package/.genie/product/templates/review-report-template.md +89 -0
  79. package/.genie/product/templates/wish-template.md +120 -0
  80. package/.genie/scripts/helpers/analyze-commit.js +195 -0
  81. package/.genie/scripts/helpers/bullet-counter.js +194 -0
  82. package/.genie/scripts/helpers/bullet-find.js +289 -0
  83. package/.genie/scripts/helpers/bullet-id.js +244 -0
  84. package/.genie/scripts/helpers/check-secrets.js +237 -0
  85. package/.genie/scripts/helpers/count-tokens.js +200 -0
  86. package/.genie/scripts/helpers/create-frontmatter.js +456 -0
  87. package/.genie/scripts/helpers/detect-markers.js +293 -0
  88. package/.genie/scripts/helpers/detect-todos.js +267 -0
  89. package/.genie/scripts/helpers/detect-unlabeled-blocks.js +135 -0
  90. package/.genie/scripts/helpers/embeddings.js +344 -0
  91. package/.genie/scripts/helpers/find-empty-sections.js +158 -0
  92. package/.genie/scripts/helpers/index.js +319 -0
  93. package/.genie/scripts/helpers/validate-frontmatter.js +578 -0
  94. package/.genie/scripts/helpers/validate-links.js +207 -0
  95. package/.genie/scripts/helpers/validate-paths.js +373 -0
  96. package/.genie/spells/README.md +9 -0
  97. package/.genie/spells/ace-protocol.md +118 -0
  98. package/.genie/spells/ask-one-at-a-time.md +175 -0
  99. package/.genie/spells/backup-analyzer.md +542 -0
  100. package/.genie/spells/blocker.md +12 -0
  101. package/.genie/spells/break-things-move-fast.md +56 -0
  102. package/.genie/spells/context-candidates.md +72 -0
  103. package/.genie/spells/context-critic.md +51 -0
  104. package/.genie/spells/defer-to-expertise.md +278 -0
  105. package/.genie/spells/delegate-dont-do.md +292 -0
  106. package/.genie/spells/error-investigation-protocol.md +328 -0
  107. package/.genie/spells/evidence-based-completion.md +273 -0
  108. package/.genie/spells/experiment.md +65 -0
  109. package/.genie/spells/file-creation-protocol.md +229 -0
  110. package/.genie/spells/forge-integration.md +281 -0
  111. package/.genie/spells/forge-orchestration.md +514 -0
  112. package/.genie/spells/gather-context.md +18 -0
  113. package/.genie/spells/global-health-check.md +34 -0
  114. package/.genie/spells/global-noop-roundtrip.md +25 -0
  115. package/.genie/spells/install-genie.md +1232 -0
  116. package/.genie/spells/install.md +82 -0
  117. package/.genie/spells/investigate-before-commit.md +112 -0
  118. package/.genie/spells/know-yourself.md +288 -0
  119. package/.genie/spells/learn.md +828 -0
  120. package/.genie/spells/mcp-diagnostic-protocol.md +246 -0
  121. package/.genie/spells/mcp-first.md +124 -0
  122. package/.genie/spells/multi-step-execution.md +67 -0
  123. package/.genie/spells/orchestration-boundary-protocol.md +256 -0
  124. package/.genie/spells/orchestrator-not-implementor.md +189 -0
  125. package/.genie/spells/prompt.md +746 -0
  126. package/.genie/spells/reflect.md +404 -0
  127. package/.genie/spells/routing-decision-matrix.md +368 -0
  128. package/.genie/spells/run-in-parallel.md +12 -0
  129. package/.genie/spells/session-state-updater-example.md +196 -0
  130. package/.genie/spells/session-state-updater.md +220 -0
  131. package/.genie/spells/track-long-running-tasks.md +133 -0
  132. package/.genie/spells/troubleshoot-infrastructure.md +176 -0
  133. package/.genie/spells/upgrade-genie.md +415 -0
  134. package/.genie/spells/url-presentation-protocol.md +301 -0
  135. package/.genie/spells/wish-initiation.md +158 -0
  136. package/.genie/spells/wish-issue-linkage.md +410 -0
  137. package/.genie/spells/wish-lifecycle.md +100 -0
  138. package/.genie/state/provider-status.json +3 -0
  139. package/.genie/state/version.json +16 -0
  140. package/AGENTS.md +422 -0
  141. package/CLAUDE.md +1 -0
  142. package/LICENSE +21 -0
  143. package/Makefile +235 -0
  144. package/README.md +323 -0
  145. package/bin/pglite-server.js +457 -0
  146. package/ecosystem.config.cjs +23 -0
  147. package/examples/multi-tenant-demo.js +104 -0
  148. package/package.json +47 -0
  149. package/src/detector.js +105 -0
  150. package/src/index.js +177 -0
  151. package/src/pool.js +320 -0
  152. package/src/ports.js +114 -0
  153. package/src/protocol.js +216 -0
  154. package/src/registry.js +134 -0
  155. package/src/router.js +289 -0
  156. package/src/server.js +265 -0
  157. package/tests/benchmarks/runner.js +489 -0
  158. package/tests/multi-tenant.test.js +201 -0
@@ -0,0 +1,457 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { fileURLToPath } from 'url';
4
+ import path from 'path';
5
+ import {
6
+ startServer,
7
+ stopServer,
8
+ list,
9
+ findByDataDir,
10
+ findByPort,
11
+ portInfo,
12
+ cleanup,
13
+ startMultiTenantServer
14
+ } from '../src/index.js';
15
+
16
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
17
+
18
+ // Global error handlers
19
+ process.on('unhandledRejection', (reason, promise) => {
20
+ // ExitStatus errors are expected from PGlite WASM cleanup - ignore them
21
+ if (reason && reason.name === 'ExitStatus') {
22
+ return;
23
+ }
24
+ console.error('āŒ Unhandled Promise Rejection:', reason);
25
+ // Don't exit - log and continue (PM2 will handle restarts if needed)
26
+ });
27
+
28
+ process.on('uncaughtException', (error) => {
29
+ console.error('āŒ Uncaught Exception:', error);
30
+ process.exit(1);
31
+ });
32
+
33
+ // Parse CLI arguments
34
+ const args = process.argv.slice(2);
35
+
36
+ // Default to router mode if first arg is a flag (e.g., --port) or no args
37
+ let command = args[0];
38
+ if (command?.startsWith('--') || command === undefined) {
39
+ command = 'router';
40
+ // Don't modify args - router will parse them
41
+ }
42
+
43
+ /**
44
+ * Print usage help
45
+ */
46
+ function printHelp() {
47
+ console.log(`
48
+ ╔═══════════════════════════════════════════════════════════════════╗
49
+ ā•‘ pgserve - Multi-Tenant PostgreSQL Router using PGlite ā•‘
50
+ ā•šā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•
51
+
52
+ USAGE:
53
+ pgserve <command> [options]
54
+
55
+ COMMANDS:
56
+ šŸš€ MULTI-TENANT MODE (Recommended):
57
+
58
+ router Start multi-tenant router (single port, auto-provision)
59
+ --port <number> PostgreSQL port (default: 8432)
60
+ --dir <path> Base directory for databases (default: ./data)
61
+ --max <number> Max concurrent databases (default: 100)
62
+ --log <level> Log level: error, warn, info, debug (default: info)
63
+ --memory Use in-memory databases (ephemeral, for testing)
64
+ --no-provision Disable auto-provisioning
65
+
66
+ šŸ“¦ LEGACY MODE (Single instance):
67
+
68
+ start <dataDir> Start server for specific data directory
69
+ --port <number> Use specific port (default: auto-allocate 12000-12999)
70
+ --log <level> Log level (default: info)
71
+
72
+ stop <dataDir> Stop server by data directory
73
+ stop --port <number> Stop server by port
74
+ stop --all Stop all running instances
75
+
76
+ list List all running instances
77
+
78
+ url <dataDir> Get connection URL for instance
79
+
80
+ health <dataDir> Check health of instance
81
+ health --port <number> Check health by port
82
+
83
+ cleanup Remove stale instances from registry
84
+
85
+ info Show port range and system info
86
+
87
+ help Show this help message
88
+
89
+ EXAMPLES:
90
+ šŸš€ Multi-tenant mode (RECOMMENDED):
91
+
92
+ # Start router on default port 8432
93
+ pgserve router
94
+
95
+ # Start on custom port with custom data directory
96
+ pgserve router --port 8433 --dir /var/lib/pglite
97
+
98
+ # Connect clients:
99
+ # postgresql://localhost:8432/user123 → auto-creates ./data/user123/
100
+ # postgresql://localhost:8432/app456 → auto-creates ./data/app456/
101
+
102
+ šŸ“¦ Legacy mode:
103
+
104
+ # Start single instance
105
+ pgserve start ./data/my-db --port 12000
106
+
107
+ # List instances
108
+ pgserve list
109
+
110
+ # Stop all
111
+ pgserve stop --all
112
+
113
+ `);
114
+ }
115
+
116
+ /**
117
+ * Format uptime
118
+ */
119
+ function formatUptime(started) {
120
+ const now = new Date();
121
+ const start = new Date(started);
122
+ const diff = now - start;
123
+
124
+ const hours = Math.floor(diff / (1000 * 60 * 60));
125
+ const minutes = Math.floor((diff % (1000 * 60 * 60)) / (1000 * 60));
126
+
127
+ if (hours > 0) {
128
+ return `${hours}h ${minutes}m`;
129
+ }
130
+ return `${minutes}m`;
131
+ }
132
+
133
+ /**
134
+ * Command: start
135
+ */
136
+ async function cmdStart() {
137
+ const dataDir = args[1];
138
+
139
+ if (!dataDir) {
140
+ console.error('āŒ Error: Data directory required');
141
+ console.log('Usage: pglite-server start <dataDir> [--port <number>]');
142
+ process.exit(1);
143
+ }
144
+
145
+ const portIndex = args.indexOf('--port');
146
+ const port = portIndex >= 0 ? parseInt(args[portIndex + 1], 10) : null;
147
+
148
+ const logIndex = args.indexOf('--log');
149
+ const logLevel = logIndex >= 0 ? args[logIndex + 1] : 'info';
150
+
151
+ try {
152
+ const instance = await startServer({ dataDir, port, logLevel });
153
+
154
+ console.log(`\nāœ… Server started successfully`);
155
+ console.log(`šŸ“ Connection: ${instance.connectionUrl}`);
156
+ console.log(`šŸ“ Data: ${instance.dataDir}`);
157
+ console.log(`šŸ”Œ Port: ${instance.port}`);
158
+ console.log(`šŸ†” PID: ${instance.pid}`);
159
+ console.log(`\nPress Ctrl+C to stop\n`);
160
+
161
+ // Keep process alive
162
+ await new Promise(() => {});
163
+ } catch (error) {
164
+ console.error(`āŒ Failed to start server: ${error.message}`);
165
+ process.exit(1);
166
+ }
167
+ }
168
+
169
+ /**
170
+ * Command: stop
171
+ */
172
+ async function cmdStop() {
173
+ const stopAll = args.includes('--all');
174
+
175
+ if (stopAll) {
176
+ const instances = list();
177
+
178
+ if (instances.length === 0) {
179
+ console.log('ā„¹ļø No running instances');
180
+ return;
181
+ }
182
+
183
+ console.log(`šŸ›‘ Stopping ${instances.length} instances...`);
184
+
185
+ for (const instance of instances) {
186
+ try {
187
+ await stopServer({ dataDir: instance.dataDir });
188
+ } catch (error) {
189
+ console.error(`āš ļø Failed to stop ${instance.dataDir}: ${error.message}`);
190
+ }
191
+ }
192
+
193
+ console.log(`āœ… Stopped ${instances.length} instances`);
194
+ return;
195
+ }
196
+
197
+ const portIndex = args.indexOf('--port');
198
+
199
+ if (portIndex >= 0) {
200
+ const port = parseInt(args[portIndex + 1], 10);
201
+ try {
202
+ await stopServer({ port });
203
+ } catch (error) {
204
+ console.error(`āŒ ${error.message}`);
205
+ process.exit(1);
206
+ }
207
+ return;
208
+ }
209
+
210
+ const dataDir = args[1];
211
+
212
+ if (!dataDir) {
213
+ console.error('āŒ Error: Data directory or --port required');
214
+ console.log('Usage: pglite-server stop <dataDir> | --port <number> | --all');
215
+ process.exit(1);
216
+ }
217
+
218
+ try {
219
+ await stopServer({ dataDir });
220
+ } catch (error) {
221
+ console.error(`āŒ ${error.message}`);
222
+ process.exit(1);
223
+ }
224
+ }
225
+
226
+ /**
227
+ * Command: list
228
+ */
229
+ function cmdList() {
230
+ const instances = list();
231
+
232
+ if (instances.length === 0) {
233
+ console.log('ā„¹ļø No running instances');
234
+ return;
235
+ }
236
+
237
+ console.log('\nActive Instances:');
238
+ console.log('ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¬ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¬ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¬ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”');
239
+ console.log('│ Port │ Data Directory │ PID │ Uptime │');
240
+ console.log('ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤');
241
+
242
+ for (const instance of instances) {
243
+ const dataDir = instance.dataDir.length > 39
244
+ ? '...' + instance.dataDir.slice(-36)
245
+ : instance.dataDir;
246
+
247
+ console.log(
248
+ `│ ${String(instance.port).padEnd(6)} │ ${dataDir.padEnd(43)} │ ${String(instance.pid).padEnd(6)} │ ${formatUptime(instance.started).padEnd(11)} │`
249
+ );
250
+ }
251
+
252
+ console.log('ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”“ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”“ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”“ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜');
253
+ console.log(`\nTotal: ${instances.length} instances`);
254
+
255
+ const info = portInfo();
256
+ console.log(`Port range: ${info.start}-${info.end} (${info.used}/${info.total} used)\n`);
257
+ }
258
+
259
+ /**
260
+ * Command: url
261
+ */
262
+ function cmdUrl() {
263
+ const dataDir = args[1];
264
+
265
+ if (!dataDir) {
266
+ console.error('āŒ Error: Data directory required');
267
+ console.log('Usage: pglite-server url <dataDir>');
268
+ process.exit(1);
269
+ }
270
+
271
+ const instance = findByDataDir(dataDir);
272
+
273
+ if (!instance) {
274
+ console.error(`āŒ No running instance found for ${dataDir}`);
275
+ process.exit(1);
276
+ }
277
+
278
+ console.log(`postgresql://localhost:${instance.port}`);
279
+ }
280
+
281
+ /**
282
+ * Command: health
283
+ */
284
+ function cmdHealth() {
285
+ const portIndex = args.indexOf('--port');
286
+ let instance;
287
+
288
+ if (portIndex >= 0) {
289
+ const port = parseInt(args[portIndex + 1], 10);
290
+ instance = findByPort(port);
291
+ } else {
292
+ const dataDir = args[1];
293
+ if (!dataDir) {
294
+ console.error('āŒ Error: Data directory or --port required');
295
+ console.log('Usage: pglite-server health <dataDir> | --port <number>');
296
+ process.exit(1);
297
+ }
298
+ instance = findByDataDir(dataDir);
299
+ }
300
+
301
+ if (!instance) {
302
+ console.error('āŒ No running instance found');
303
+ process.exit(1);
304
+ }
305
+
306
+ console.log(`\nāœ… Instance healthy`);
307
+ console.log(`šŸ“ URL: postgresql://localhost:${instance.port}`);
308
+ console.log(`šŸ“ Data: ${instance.dataDir}`);
309
+ console.log(`šŸ”Œ Port: ${instance.port}`);
310
+ console.log(`šŸ†” PID: ${instance.pid}`);
311
+ console.log(`ā±ļø Uptime: ${formatUptime(instance.started)}`);
312
+ console.log(`šŸ“Š Version: ${instance.version}\n`);
313
+ }
314
+
315
+ /**
316
+ * Command: info
317
+ */
318
+ function cmdInfo() {
319
+ const info = portInfo();
320
+
321
+ console.log('\nšŸ“Š Port Range Information:');
322
+ console.log(` Range: ${info.start}-${info.end}`);
323
+ console.log(` Total: ${info.total} ports`);
324
+ console.log(` Used: ${info.used} ports`);
325
+ console.log(` Available: ${info.available} ports`);
326
+
327
+ if (info.usedPorts.length > 0) {
328
+ console.log(` Active ports: ${info.usedPorts.join(', ')}`);
329
+ }
330
+
331
+ console.log('');
332
+ }
333
+
334
+ /**
335
+ * Command: cleanup
336
+ */
337
+ function cmdCleanup() {
338
+ const cleaned = cleanup();
339
+
340
+ if (cleaned === 0) {
341
+ console.log('āœ… No stale instances to clean up');
342
+ } else {
343
+ console.log(`āœ… Cleaned up ${cleaned} stale instance${cleaned > 1 ? 's' : ''}`);
344
+ }
345
+ }
346
+
347
+ /**
348
+ * Command: router (multi-tenant mode)
349
+ */
350
+ async function cmdRouter() {
351
+ // Parse options
352
+ const portIndex = args.indexOf('--port');
353
+ const port = portIndex >= 0 ? parseInt(args[portIndex + 1], 10) : 8432;
354
+
355
+ const dirIndex = args.indexOf('--dir');
356
+ const dataDir = dirIndex >= 0 ? args[dirIndex + 1] : './data';
357
+
358
+ const maxIndex = args.indexOf('--max');
359
+ const maxInstances = maxIndex >= 0 ? parseInt(args[maxIndex + 1], 10) : 100;
360
+
361
+ const logIndex = args.indexOf('--log');
362
+ const logLevel = logIndex >= 0 ? args[logIndex + 1] : 'info';
363
+
364
+ const memoryMode = args.includes('--memory');
365
+ const autoProvision = !args.includes('--no-provision');
366
+
367
+ try {
368
+ console.log(`
369
+ ╔═══════════════════════════════════════════════════════════════════╗
370
+ ā•‘ Starting Multi-Tenant PostgreSQL Router ā•‘
371
+ ā•šā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•
372
+ `);
373
+
374
+ const router = await startMultiTenantServer({
375
+ port,
376
+ baseDir: memoryMode ? null : dataDir,
377
+ memoryMode,
378
+ maxInstances,
379
+ logLevel,
380
+ autoProvision
381
+ });
382
+
383
+ console.log(`
384
+ āœ… Multi-tenant router started successfully!
385
+
386
+ šŸ“ PostgreSQL endpoint: postgresql://localhost:${port}/<database>
387
+ šŸ“ Data directory: ${memoryMode ? '(in-memory)' : dataDir}
388
+ šŸŽÆ Auto-provision: ${autoProvision ? 'enabled' : 'disabled'}
389
+ šŸ’¾ Mode: ${memoryMode ? 'In-memory (ephemeral)' : 'Persistent'}
390
+ šŸ“Š Max instances: ${maxInstances}
391
+
392
+ šŸ’” Examples:
393
+ postgresql://localhost:${port}/user123 → ${memoryMode ? 'Creates in-memory database' : `Creates ${dataDir}/user123/`}
394
+ postgresql://localhost:${port}/app456 → ${memoryMode ? 'Creates in-memory database' : `Creates ${dataDir}/app456/`}
395
+
396
+ Press Ctrl+C to stop
397
+ `);
398
+
399
+ // Keep process alive
400
+ await new Promise(() => {});
401
+ } catch (error) {
402
+ console.error(`āŒ Failed to start router: ${error.message}`);
403
+ process.exit(1);
404
+ }
405
+ }
406
+
407
+ // Main CLI router
408
+ async function main() {
409
+ switch (command) {
410
+ case 'router':
411
+ await cmdRouter();
412
+ break;
413
+
414
+ case 'start':
415
+ await cmdStart();
416
+ break;
417
+
418
+ case 'stop':
419
+ await cmdStop();
420
+ break;
421
+
422
+ case 'list':
423
+ cmdList();
424
+ break;
425
+
426
+ case 'url':
427
+ cmdUrl();
428
+ break;
429
+
430
+ case 'health':
431
+ cmdHealth();
432
+ break;
433
+
434
+ case 'info':
435
+ cmdInfo();
436
+ break;
437
+
438
+ case 'cleanup':
439
+ cmdCleanup();
440
+ break;
441
+
442
+ case 'help':
443
+ case undefined:
444
+ printHelp();
445
+ break;
446
+
447
+ default:
448
+ console.error(`āŒ Unknown command: ${command}`);
449
+ printHelp();
450
+ process.exit(1);
451
+ }
452
+ }
453
+
454
+ main().catch((error) => {
455
+ console.error(`āŒ Error: ${error.message}`);
456
+ process.exit(1);
457
+ });
@@ -0,0 +1,23 @@
1
+ module.exports = {
2
+ apps: [
3
+ {
4
+ name: 'PGlite Local Server',
5
+ script: './bin/pglite-server.js',
6
+ args: 'start ./data/genieos-local --port 12000 --log info',
7
+ cwd: '/home/namastex/dev/pglite-embedded-server',
8
+ interpreter: 'node',
9
+ instances: 1,
10
+ autorestart: true,
11
+ watch: false,
12
+ max_memory_restart: '512M',
13
+ env: {
14
+ NODE_ENV: 'production'
15
+ },
16
+ error_file: '/home/namastex/logs/pglite-server-error.log',
17
+ out_file: '/home/namastex/logs/pglite-server-out.log',
18
+ log_date_format: 'YYYY-MM-DD HH:mm:ss Z',
19
+ merge_logs: true,
20
+ time: true
21
+ }
22
+ ]
23
+ };
@@ -0,0 +1,104 @@
1
+ /**
2
+ * Multi-Tenant Router Demo
3
+ *
4
+ * Shows how to use the new multi-tenant architecture
5
+ */
6
+
7
+ import { startMultiTenantServer } from '../src/index.js';
8
+ import pg from 'pg';
9
+
10
+ const { Client } = pg;
11
+
12
+ async function demo() {
13
+ console.log('šŸš€ Starting multi-tenant router demo...\n');
14
+
15
+ // Start multi-tenant router
16
+ const router = await startMultiTenantServer({
17
+ port: 15432,
18
+ baseDir: './demo-data',
19
+ logLevel: 'info'
20
+ });
21
+
22
+ console.log('\nšŸ“Š Initial stats:');
23
+ console.log(JSON.stringify(router.getStats(), null, 2));
24
+
25
+ // Connect to database "user123" (auto-created)
26
+ console.log('\nšŸ“„ Connecting to database: user123');
27
+ const client1 = new Client({
28
+ host: '127.0.0.1',
29
+ port: 15432,
30
+ database: 'user123'
31
+ });
32
+
33
+ await client1.connect();
34
+ console.log('āœ… Connected to user123');
35
+
36
+ // Create table and insert data
37
+ await client1.query('CREATE TABLE users (id SERIAL PRIMARY KEY, name TEXT)');
38
+ await client1.query("INSERT INTO users (name) VALUES ('Alice'), ('Bob')");
39
+
40
+ const result1 = await client1.query('SELECT * FROM users');
41
+ console.log('šŸ“‹ user123 data:', result1.rows);
42
+
43
+ await client1.end();
44
+ console.log('šŸ”Œ Disconnected from user123');
45
+
46
+ // Connect to database "app456" (auto-created)
47
+ console.log('\nšŸ“„ Connecting to database: app456');
48
+ const client2 = new Client({
49
+ host: '127.0.0.1',
50
+ port: 15432,
51
+ database: 'app456'
52
+ });
53
+
54
+ await client2.connect();
55
+ console.log('āœ… Connected to app456');
56
+
57
+ // Different schema in different database
58
+ await client2.query('CREATE TABLE posts (id SERIAL PRIMARY KEY, title TEXT)');
59
+ await client2.query("INSERT INTO posts (title) VALUES ('Hello World'), ('Multi-tenant magic')");
60
+
61
+ const result2 = await client2.query('SELECT * FROM posts');
62
+ console.log('šŸ“‹ app456 data:', result2.rows);
63
+
64
+ await client2.end();
65
+ console.log('šŸ”Œ Disconnected from app456');
66
+
67
+ // Show final stats
68
+ console.log('\nšŸ“Š Final stats:');
69
+ console.log(JSON.stringify(router.getStats(), null, 2));
70
+
71
+ console.log('\nšŸ“‹ All databases:');
72
+ console.log(JSON.stringify(router.listDatabases(), null, 2));
73
+
74
+ // Reconnect to user123 to verify data persists
75
+ console.log('\nšŸ”„ Reconnecting to user123 to verify data persists...');
76
+ const client1Again = new Client({
77
+ host: '127.0.0.1',
78
+ port: 15432,
79
+ database: 'user123'
80
+ });
81
+
82
+ await client1Again.connect();
83
+ const persistedData = await client1Again.query('SELECT * FROM users');
84
+ console.log('āœ… Persisted data in user123:', persistedData.rows);
85
+
86
+ await client1Again.end();
87
+
88
+ // Stop router
89
+ console.log('\nšŸ›‘ Stopping router...');
90
+ await router.stop();
91
+
92
+ console.log('\nāœ… Demo complete!');
93
+ console.log('\nšŸŽÆ Key achievements:');
94
+ console.log(' • Single port (15432) handled multiple databases');
95
+ console.log(' • Auto-provisioned user123 and app456');
96
+ console.log(' • Data isolated between databases');
97
+ console.log(' • Data persisted across reconnections');
98
+ console.log(' • Zero configuration required!');
99
+ }
100
+
101
+ demo().catch((error) => {
102
+ console.error('āŒ Demo failed:', error);
103
+ process.exit(1);
104
+ });
package/package.json ADDED
@@ -0,0 +1,47 @@
1
+ {
2
+ "name": "pgserve",
3
+ "version": "0.1.1",
4
+ "description": "Multi-instance PostgreSQL embedded server using PGlite - zero config, auto-port allocation, perfect for development and embedded apps",
5
+ "main": "src/index.js",
6
+ "type": "module",
7
+ "bin": {
8
+ "pgserve": "./bin/pglite-server.js"
9
+ },
10
+ "scripts": {
11
+ "bench": "node tests/benchmarks/runner.js",
12
+ "test": "node --test tests/**/*.test.js"
13
+ },
14
+ "keywords": [
15
+ "pglite",
16
+ "postgresql",
17
+ "embedded",
18
+ "database",
19
+ "wasm",
20
+ "electron",
21
+ "development",
22
+ "multi-instance"
23
+ ],
24
+ "author": "Namastex Labs <labs@namastex.com>",
25
+ "license": "MIT",
26
+ "repository": {
27
+ "type": "git",
28
+ "url": "https://github.com/namastexlabs/pglite-embedded-server.git"
29
+ },
30
+ "bugs": {
31
+ "url": "https://github.com/namastexlabs/pglite-embedded-server/issues"
32
+ },
33
+ "homepage": "https://github.com/namastexlabs/pglite-embedded-server#readme",
34
+ "dependencies": {
35
+ "@electric-sql/pglite": "^0.3.14",
36
+ "@electric-sql/pglite-socket": "^0.0.19",
37
+ "pino": "^10.1.0",
38
+ "pino-pretty": "^13.1.2"
39
+ },
40
+ "devDependencies": {
41
+ "better-sqlite3": "^11.10.0",
42
+ "pg": "^8.16.3"
43
+ },
44
+ "engines": {
45
+ "node": ">=18.0.0"
46
+ }
47
+ }