pgserve 1.1.3-rc.2 → 1.1.3-rc.4

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.
@@ -38,26 +38,33 @@ concurrency:
38
38
  jobs:
39
39
  build:
40
40
  name: Build ${{ matrix.platform }}
41
- runs-on: ubuntu-latest
41
+ runs-on: ${{ matrix.os }}
42
42
  strategy:
43
43
  fail-fast: false
44
44
  matrix:
45
45
  include:
46
+ # Linux/macOS: cross-compile on ubuntu (faster)
46
47
  - target: bun-linux-x64
47
48
  platform: linux-x64
48
49
  output: pgserve-linux-x64
50
+ os: ubuntu-latest
49
51
  - target: bun-linux-arm64
50
52
  platform: linux-arm64
51
53
  output: pgserve-linux-arm64
54
+ os: ubuntu-latest
52
55
  - target: bun-darwin-x64
53
56
  platform: darwin-x64
54
57
  output: pgserve-darwin-x64
58
+ os: ubuntu-latest
55
59
  - target: bun-darwin-arm64
56
60
  platform: darwin-arm64
57
61
  output: pgserve-darwin-arm64
62
+ os: ubuntu-latest
63
+ # Windows: native build required for --windows-icon
58
64
  - target: bun-windows-x64
59
65
  platform: windows-x64
60
66
  output: pgserve-windows-x64.exe
67
+ os: windows-latest
61
68
 
62
69
  steps:
63
70
  - name: Checkout
@@ -73,7 +80,18 @@ jobs:
73
80
  - name: Install dependencies
74
81
  run: bun install
75
82
 
83
+ # Windows: native build with custom icon
84
+ - name: Build for Windows (with icon)
85
+ if: matrix.platform == 'windows-x64'
86
+ run: |
87
+ mkdir -p dist
88
+ bun build --compile --windows-icon=assets/icon.ico bin/pglite-server.js --outfile dist/${{ matrix.output }}
89
+ ls -lh dist/
90
+ shell: bash
91
+
92
+ # Linux/macOS: cross-compile
76
93
  - name: Build for ${{ matrix.platform }}
94
+ if: matrix.platform != 'windows-x64'
77
95
  run: |
78
96
  mkdir -p dist
79
97
  bun build --compile --target=${{ matrix.target }} bin/pglite-server.js --outfile dist/${{ matrix.output }}
package/README.md CHANGED
@@ -99,6 +99,17 @@ npm install pgserve
99
99
 
100
100
  > PostgreSQL binaries are automatically downloaded on first run (~100MB).
101
101
 
102
+ ### Windows
103
+
104
+ Download `pgserve-windows-x64.exe` from [GitHub Releases](https://github.com/namastexlabs/pgserve/releases).
105
+
106
+ Double-click to run, or use CLI:
107
+
108
+ ```cmd
109
+ pgserve-windows-x64.exe --port 5432
110
+ pgserve-windows-x64.exe --data C:\pgserve-data
111
+ ```
112
+
102
113
  <br>
103
114
 
104
115
  ## CLI Reference
Binary file
@@ -55,63 +55,61 @@ if (!bunPath) {
55
55
 
56
56
  const scriptPath = path.join(__dirname, 'pglite-server.js');
57
57
 
58
- // Spawn bun with the actual script, inherit all stdio
59
- // IMPORTANT: Do NOT use detached mode - wrapper must wait for child to fully terminate
60
- // Using detached with stdio:'inherit' causes file handle inheritance issues on Windows
61
- const child = spawn(bunPath, [scriptPath, ...process.argv.slice(2)], {
62
- stdio: 'inherit',
63
- windowsHide: true
64
- });
65
-
66
- child.on('error', (err) => {
67
- console.error('Failed to start pgserve:', err.message);
68
- process.exit(1);
69
- });
58
+ // Platform-specific spawning strategy:
59
+ // - Windows: Use pipes for explicit handle control (prevents EBUSY errors)
60
+ // - Unix: Use inherit for simplicity (works fine)
70
61
 
71
- // Safety timeout: force exit if 'close' event never fires after 'exit'
72
- let forceExitTimeout = null;
62
+ if (isWindows) {
63
+ // WINDOWS PATH: Explicit pipe control to prevent EBUSY errors
64
+ // Using stdio: 'inherit' causes file handle inheritance that we cannot release,
65
+ // leading to npm cleanup failures. With pipes, we control when handles are destroyed.
73
66
 
74
- child.on('exit', () => {
75
- // Give 5 seconds for 'close' event after 'exit'
76
- forceExitTimeout = setTimeout(() => {
77
- console.error('Warning: Child process did not close cleanly, forcing exit');
78
- process.exit(1);
79
- }, 5000);
80
- });
81
-
82
- // Use 'close' event instead of 'exit' - fires AFTER all stdio streams are closed
83
- // This is critical for Windows where file handles may remain locked after 'exit' fires
84
- child.on('close', (code, signal) => {
85
- // Clear the safety timeout
86
- if (forceExitTimeout) {
87
- clearTimeout(forceExitTimeout);
88
- }
67
+ const child = spawn(bunPath, [scriptPath, ...process.argv.slice(2)], {
68
+ stdio: ['pipe', 'pipe', 'pipe'],
69
+ windowsHide: true
70
+ });
89
71
 
90
- // On Windows, use SYNCHRONOUS delay to ensure all file handles are released
91
- // This prevents EBUSY errors when npx tries to clean up the cache
92
- // NOTE: async/await does NOT work in EventEmitter callbacks - Node ignores the Promise
93
- if (isWindows) {
94
- const delay = 200; // ms - enough for Windows kernel to release handles
95
- const start = Date.now();
96
- while (Date.now() - start < delay) {
97
- // Synchronous busy-wait - actually blocks unlike async setTimeout
98
- }
72
+ // Manually pipe stdio - we now control the handles
73
+ // Handle stdin errors gracefully (may not be connected in some environments)
74
+ process.stdin.on('error', () => {});
75
+ child.stdin.on('error', () => {});
76
+
77
+ // Only pipe stdin if it's readable
78
+ if (process.stdin.readable) {
79
+ process.stdin.pipe(child.stdin);
99
80
  }
81
+ child.stdout.pipe(process.stdout);
82
+ child.stderr.pipe(process.stderr);
100
83
 
101
- if (signal) {
102
- // On Windows, can't reliably re-raise Unix signals
103
- if (isWindows) {
104
- process.exit(1);
105
- } else {
106
- process.kill(process.pid, signal);
84
+ child.on('error', (err) => {
85
+ console.error('Failed to start pgserve:', err.message);
86
+ process.exit(1);
87
+ });
88
+
89
+ child.on('close', (code, signal) => {
90
+ // CRITICAL: Explicitly destroy ALL streams to release file handles
91
+ // This must happen BEFORE process.exit() to prevent EBUSY
92
+ try {
93
+ if (process.stdin.readable) {
94
+ process.stdin.unpipe(child.stdin);
95
+ }
96
+ child.stdin.destroy();
97
+ child.stdout.destroy();
98
+ child.stderr.destroy();
99
+ } catch {
100
+ // Ignore stream destruction errors
107
101
  }
108
- } else {
109
- process.exit(code ?? 0);
110
- }
111
- });
112
102
 
113
- // Platform-specific signal handling
114
- if (isWindows) {
103
+ // Remove all listeners to prevent memory leaks
104
+ child.removeAllListeners();
105
+
106
+ // Use setImmediate to ensure stream destruction completes before exit
107
+ // This gives the event loop one tick to process pending I/O cleanup
108
+ setImmediate(() => {
109
+ process.exit(signal ? 1 : (code ?? 0));
110
+ });
111
+ });
112
+
115
113
  // Windows: use taskkill for reliable process termination
116
114
  // process.kill(pid, 'SIGINT') does NOT work properly on Windows
117
115
  process.on('SIGINT', () => {
@@ -139,13 +137,37 @@ if (isWindows) {
139
137
  rl.on('SIGINT', () => {
140
138
  process.emit('SIGINT');
141
139
  });
140
+ // Clean up readline on close
141
+ child.on('close', () => {
142
+ rl.close();
143
+ });
142
144
  }
145
+
143
146
  } else {
147
+ // UNIX PATH: Simple stdio inheritance (works fine, no EBUSY issues)
148
+ const child = spawn(bunPath, [scriptPath, ...process.argv.slice(2)], {
149
+ stdio: 'inherit',
150
+ windowsHide: true
151
+ });
152
+
153
+ child.on('error', (err) => {
154
+ console.error('Failed to start pgserve:', err.message);
155
+ process.exit(1);
156
+ });
157
+
158
+ child.on('close', (code, signal) => {
159
+ if (signal) {
160
+ process.kill(process.pid, signal);
161
+ } else {
162
+ process.exit(code ?? 0);
163
+ }
164
+ });
165
+
144
166
  // Unix: forward signals to child process normally
145
- ['SIGINT', 'SIGTERM', 'SIGHUP'].forEach(signal => {
146
- process.on(signal, () => {
167
+ ['SIGINT', 'SIGTERM', 'SIGHUP'].forEach(sig => {
168
+ process.on(sig, () => {
147
169
  if (child.pid) {
148
- process.kill(child.pid, signal);
170
+ process.kill(child.pid, sig);
149
171
  }
150
172
  });
151
173
  });
package/bun.lock CHANGED
@@ -20,7 +20,7 @@
20
20
  "@embedded-postgres/darwin-arm64": "17.7.0-beta.15",
21
21
  "@embedded-postgres/darwin-x64": "17.7.0-beta.15",
22
22
  "@embedded-postgres/linux-x64": "17.7.0-beta.15",
23
- "@embedded-postgres/win32-x64": "17.7.0-beta.15",
23
+ "@embedded-postgres/windows-x64": "17.7.0-beta.15",
24
24
  },
25
25
  },
26
26
  },
@@ -33,6 +33,8 @@
33
33
 
34
34
  "@embedded-postgres/linux-x64": ["@embedded-postgres/linux-x64@17.7.0-beta.15", "", { "os": "linux", "cpu": "x64" }, "sha512-HeaxSHsw6ccVh8l5iC4OgXqvaaCGWnnZR9CpgNgrAfnKPPGiEhUPBmO2XhEsFQIhc+ad/+36h0NTvKo4bdi40w=="],
35
35
 
36
+ "@embedded-postgres/windows-x64": ["@embedded-postgres/windows-x64@17.7.0-beta.15", "", { "os": "win32", "cpu": "x64" }, "sha512-Oq11yyKxISjefuYdKljcp3Q+uxx237zn9YpP9hO43+6Feorq7USuMIDqk5ofLSQ30FAnVyTqaIQK8ZIjW+tQXQ=="],
37
+
36
38
  "@emnapi/core": ["@emnapi/core@1.7.1", "", { "dependencies": { "@emnapi/wasi-threads": "1.1.0", "tslib": "^2.4.0" } }, "sha512-o1uhUASyo921r2XtHYOHy7gdkGLge8ghBEQHMWmyJFoXlpU58kIrhhN3w26lpQb6dspetweapMn2CSNwQ8I4wg=="],
37
39
 
38
40
  "@emnapi/runtime": ["@emnapi/runtime@1.7.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-PVtJr5CmLwYAU9PZDMITZoR5iAOShYREoR45EyyLrbntV50mdePTgUn4AmOw90Ifcj+x2kRjdzr1HP3RrNiHGA=="],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pgserve",
3
- "version": "1.1.3-rc.2",
3
+ "version": "1.1.3-rc.4",
4
4
  "description": "Embedded PostgreSQL server with true concurrent connections - zero config, auto-provision databases",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
@@ -44,7 +44,7 @@
44
44
  "@embedded-postgres/darwin-arm64": "17.7.0-beta.15",
45
45
  "@embedded-postgres/darwin-x64": "17.7.0-beta.15",
46
46
  "@embedded-postgres/linux-x64": "17.7.0-beta.15",
47
- "@embedded-postgres/win32-x64": "17.7.0-beta.15"
47
+ "@embedded-postgres/windows-x64": "17.7.0-beta.15"
48
48
  },
49
49
  "devDependencies": {
50
50
  "@electric-sql/pglite": "^0.2.17",
package/src/postgres.js CHANGED
@@ -106,7 +106,7 @@ function getBinaryPaths() {
106
106
  } else if (platform === 'darwin' && arch === 'x64') {
107
107
  pkgName = '@embedded-postgres/darwin-x64';
108
108
  } else if (platform === 'win32' && arch === 'x64') {
109
- pkgName = '@embedded-postgres/win32-x64';
109
+ pkgName = '@embedded-postgres/windows-x64';
110
110
  } else {
111
111
  throw new Error(`Unsupported platform: ${platform}-${arch}`);
112
112
  }