pgserve 1.1.9 → 1.1.10
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/.genie/AGENTS.md +2 -0
- package/.genie/code/AGENTS.md +2 -0
- package/AGENTS.md +2 -0
- package/bin/pgserve-wrapper.cjs +105 -0
- package/knip.json +1 -1
- package/package.json +3 -2
- package/scripts/test-bun-self-heal.sh +163 -0
package/.genie/AGENTS.md
CHANGED
|
@@ -5,6 +5,8 @@ description: Global agents (orchestration, QA, analysis, maintenance)
|
|
|
5
5
|
github_url: https://github.com/namastexlabs/automagik-genie/tree/main/.genie
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
+
> **Shared rules in `~/.claude/rules/agent-bible.md`. Read it.**
|
|
9
|
+
|
|
8
10
|
# Base Genie Agents
|
|
9
11
|
|
|
10
12
|
**Global agents available across all collectives.**
|
package/.genie/code/AGENTS.md
CHANGED
package/AGENTS.md
CHANGED
package/bin/pgserve-wrapper.cjs
CHANGED
|
@@ -53,8 +53,113 @@ if (!bunPath) {
|
|
|
53
53
|
process.exit(1);
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
+
// Pre-flight health check: verify bun can actually execute.
|
|
57
|
+
//
|
|
58
|
+
// When pgserve is installed via `bun install` (as a global or transitive dep),
|
|
59
|
+
// the nested `bun` npm package's postinstall can be skipped, leaving
|
|
60
|
+
// `@oven/bun-<platform>/bin/bun` empty. The bun stub at `node_modules/bun/bin/bun`
|
|
61
|
+
// then exits instantly with:
|
|
62
|
+
// Error: Bun's postinstall script was not run.
|
|
63
|
+
//
|
|
64
|
+
// pglite-server.js's TCP readiness poll can't distinguish this from a slow
|
|
65
|
+
// startup, so users see a confusing 30s timeout. Detect the specific error
|
|
66
|
+
// here, attempt the documented self-heal once (`node install.js`), and retry.
|
|
67
|
+
// If self-heal also fails, surface the real error instead of hanging later.
|
|
68
|
+
ensureBunHealthy(bunPath);
|
|
69
|
+
|
|
56
70
|
const scriptPath = path.join(__dirname, 'pglite-server.js');
|
|
57
71
|
|
|
72
|
+
/**
|
|
73
|
+
* Verify the selected bun binary can execute. If it fails with the known
|
|
74
|
+
* "postinstall script was not run" signature, attempt a one-shot repair via
|
|
75
|
+
* the bun npm package's install.js. Throws (with a useful message) rather
|
|
76
|
+
* than letting pglite-server.js hang on the TCP readiness poll for 30s.
|
|
77
|
+
*/
|
|
78
|
+
function ensureBunHealthy(bunExe) {
|
|
79
|
+
const probe = probeBun(bunExe);
|
|
80
|
+
if (probe.ok) return;
|
|
81
|
+
|
|
82
|
+
// Only attempt self-heal for the specific postinstall-not-run failure.
|
|
83
|
+
// Any other failure (corrupt binary, unsupported glibc, etc.) is surfaced
|
|
84
|
+
// as-is rather than silently papered over.
|
|
85
|
+
if (!isPostinstallMissingError(probe.output)) {
|
|
86
|
+
console.error('Error: bun runtime at', bunExe, 'failed to execute:');
|
|
87
|
+
console.error(probe.output || '(no output)');
|
|
88
|
+
process.exit(1);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const installJs = findBunInstallJs(bunExe);
|
|
92
|
+
if (!installJs) {
|
|
93
|
+
console.error('Error: bun runtime at', bunExe, 'is missing its platform binary,');
|
|
94
|
+
console.error('and the recovery script (node_modules/bun/install.js) could not be located.');
|
|
95
|
+
console.error('');
|
|
96
|
+
console.error('Try reinstalling pgserve, or run the fix manually:');
|
|
97
|
+
console.error(' cd <node_modules>/bun && node install.js');
|
|
98
|
+
process.exit(1);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
console.error('[pgserve] bun runtime missing platform binary; attempting self-heal...');
|
|
102
|
+
try {
|
|
103
|
+
execSync(`node ${JSON.stringify(installJs)}`, { stdio: 'inherit' });
|
|
104
|
+
} catch {
|
|
105
|
+
// fall through to second probe
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const second = probeBun(bunExe);
|
|
109
|
+
if (second.ok) {
|
|
110
|
+
console.error('[pgserve] bun runtime recovered.');
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
console.error('Error: bun runtime still broken after self-heal attempt.');
|
|
115
|
+
console.error(second.output || '(no output)');
|
|
116
|
+
console.error('');
|
|
117
|
+
console.error('Manual fix:');
|
|
118
|
+
console.error(` cd ${path.dirname(path.dirname(installJs))}/bun && node install.js`);
|
|
119
|
+
console.error('');
|
|
120
|
+
console.error('Upstream bug: https://github.com/namastexlabs/pgserve/issues/22');
|
|
121
|
+
process.exit(1);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function probeBun(bunExe) {
|
|
125
|
+
try {
|
|
126
|
+
const out = execSync(`${JSON.stringify(bunExe)} --version`, {
|
|
127
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
128
|
+
timeout: 10000,
|
|
129
|
+
encoding: 'utf8'
|
|
130
|
+
});
|
|
131
|
+
return { ok: true, output: out };
|
|
132
|
+
} catch (err) {
|
|
133
|
+
const output = [err.stderr, err.stdout, err.message]
|
|
134
|
+
.filter(Boolean).map(String).join('\n');
|
|
135
|
+
return { ok: false, output };
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function isPostinstallMissingError(output) {
|
|
140
|
+
return typeof output === 'string' &&
|
|
141
|
+
/Bun's postinstall script was not run/i.test(output);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function findBunInstallJs(bunExe) {
|
|
145
|
+
// Walk up from the bun binary toward a `bun` package dir containing install.js.
|
|
146
|
+
// Matches the wrapper's own location list - bun is always nested under a
|
|
147
|
+
// `bun` package directory (or its `bin/` subdir).
|
|
148
|
+
let cursor = path.dirname(path.resolve(bunExe));
|
|
149
|
+
for (let i = 0; i < 6; i++) {
|
|
150
|
+
const candidate = path.join(cursor, 'install.js');
|
|
151
|
+
if (fs.existsSync(candidate) && fs.existsSync(path.join(cursor, 'package.json'))) {
|
|
152
|
+
return candidate;
|
|
153
|
+
}
|
|
154
|
+
const nested = path.join(cursor, 'bun', 'install.js');
|
|
155
|
+
if (fs.existsSync(nested)) return nested;
|
|
156
|
+
const parent = path.dirname(cursor);
|
|
157
|
+
if (parent === cursor) break;
|
|
158
|
+
cursor = parent;
|
|
159
|
+
}
|
|
160
|
+
return null;
|
|
161
|
+
}
|
|
162
|
+
|
|
58
163
|
// Platform-specific spawning strategy:
|
|
59
164
|
// - Windows: Use pipes for explicit handle control (prevents EBUSY errors)
|
|
60
165
|
// - Unix: Use inherit for simplicity (works fine)
|
package/knip.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"entry": ["src/index.js", "bin/pglite-server.js", "bin/pgserve-wrapper.cjs"],
|
|
4
4
|
"project": ["src/**/*.js", "bin/**/*.js", "bin/**/*.cjs"],
|
|
5
5
|
"ignore": ["tests/**", "helpers/**", "scripts/**"],
|
|
6
|
-
"ignoreBinaries": ["scripts/test-npx.sh", "make"],
|
|
6
|
+
"ignoreBinaries": ["scripts/test-npx.sh", "scripts/test-bun-self-heal.sh", "make"],
|
|
7
7
|
"ignoreDependencies": ["bun"],
|
|
8
8
|
"ignoreExportsUsedInFile": true
|
|
9
9
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pgserve",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.10",
|
|
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",
|
|
@@ -19,7 +19,8 @@
|
|
|
19
19
|
"lint:fix": "eslint src/ bin/ --fix",
|
|
20
20
|
"deadcode": "knip",
|
|
21
21
|
"test:npx": "scripts/test-npx.sh",
|
|
22
|
-
"
|
|
22
|
+
"test:bun-self-heal": "scripts/test-bun-self-heal.sh",
|
|
23
|
+
"prepublishOnly": "npm run lint && npm run deadcode && npm run test:npx && npm run test:bun-self-heal",
|
|
23
24
|
"prepare": "husky"
|
|
24
25
|
},
|
|
25
26
|
"keywords": [
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Regression test for https://github.com/namastexlabs/pgserve/issues/22
|
|
3
|
+
#
|
|
4
|
+
# When pgserve is installed via `bun install`, the nested `bun` npm package's
|
|
5
|
+
# postinstall can be skipped, leaving @oven/bun-<platform>/bin/bun empty.
|
|
6
|
+
# The bun stub then refuses to run with "Bun's postinstall script was not run".
|
|
7
|
+
# pgserve-wrapper.cjs must detect this and self-heal via `node install.js`.
|
|
8
|
+
#
|
|
9
|
+
# This test stages a synthetic broken install tree, runs the wrapper, and
|
|
10
|
+
# asserts that it recovers and spawns pglite-server.
|
|
11
|
+
|
|
12
|
+
set -e
|
|
13
|
+
|
|
14
|
+
REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
|
15
|
+
WRAPPER="$REPO_ROOT/bin/pgserve-wrapper.cjs"
|
|
16
|
+
|
|
17
|
+
if [ ! -f "$WRAPPER" ]; then
|
|
18
|
+
echo "✗ wrapper not found: $WRAPPER"
|
|
19
|
+
exit 1
|
|
20
|
+
fi
|
|
21
|
+
|
|
22
|
+
# Use a real bun binary as the "recovered" payload so the healthy-path
|
|
23
|
+
# assertion is meaningful. Falls back to any bun on PATH.
|
|
24
|
+
REAL_BUN="${BUN_BIN:-$(command -v bun || true)}"
|
|
25
|
+
if [ -z "$REAL_BUN" ] || [ ! -x "$REAL_BUN" ]; then
|
|
26
|
+
echo "✗ bun runtime not found on PATH (set BUN_BIN to override)"
|
|
27
|
+
exit 1
|
|
28
|
+
fi
|
|
29
|
+
|
|
30
|
+
FIXTURE=$(mktemp -d)
|
|
31
|
+
trap "rm -rf $FIXTURE" EXIT
|
|
32
|
+
|
|
33
|
+
mkdir -p "$FIXTURE/node_modules/bun/bin"
|
|
34
|
+
mkdir -p "$FIXTURE/node_modules/@oven/bun-linux-x64/bin" # empty, simulating the bug
|
|
35
|
+
mkdir -p "$FIXTURE/node_modules/.bin"
|
|
36
|
+
mkdir -p "$FIXTURE/node_modules/pgserve/bin"
|
|
37
|
+
|
|
38
|
+
cp "$WRAPPER" "$FIXTURE/node_modules/pgserve/bin/pgserve-wrapper.cjs"
|
|
39
|
+
|
|
40
|
+
# Stub pglite-server so we can detect a successful spawn without needing
|
|
41
|
+
# postgres binaries in the fixture.
|
|
42
|
+
cat > "$FIXTURE/node_modules/pgserve/bin/pglite-server.js" <<'EOF'
|
|
43
|
+
console.log("pglite-server-spawned");
|
|
44
|
+
process.exit(0);
|
|
45
|
+
EOF
|
|
46
|
+
|
|
47
|
+
# Fake bun install.js: copies the real bun into the expected @oven location,
|
|
48
|
+
# mirroring what the real postinstall does.
|
|
49
|
+
cat > "$FIXTURE/node_modules/bun/install.js" <<EOF
|
|
50
|
+
const fs = require('fs');
|
|
51
|
+
const path = require('path');
|
|
52
|
+
const dst = path.resolve(__dirname, '..', '@oven', 'bun-linux-x64', 'bin', 'bun');
|
|
53
|
+
fs.mkdirSync(path.dirname(dst), { recursive: true });
|
|
54
|
+
fs.copyFileSync('$REAL_BUN', dst);
|
|
55
|
+
fs.chmodSync(dst, 0o755);
|
|
56
|
+
console.log('[test] install.js populated', dst);
|
|
57
|
+
EOF
|
|
58
|
+
echo '{"name":"bun","version":"1.3.12"}' > "$FIXTURE/node_modules/bun/package.json"
|
|
59
|
+
|
|
60
|
+
# Broken bun stub: prints the postinstall error unless the @oven binary exists.
|
|
61
|
+
cat > "$FIXTURE/node_modules/bun/bin/bun" <<'EOF'
|
|
62
|
+
#!/bin/sh
|
|
63
|
+
SELF=$(readlink -f "$0")
|
|
64
|
+
TARGET="$(dirname "$SELF")/../../@oven/bun-linux-x64/bin/bun"
|
|
65
|
+
if [ ! -x "$TARGET" ]; then
|
|
66
|
+
echo "Error: Bun's postinstall script was not run." >&2
|
|
67
|
+
echo "" >&2
|
|
68
|
+
echo "To fix this, run the postinstall script manually:" >&2
|
|
69
|
+
echo " cd node_modules/bun && node install.js" >&2
|
|
70
|
+
exit 1
|
|
71
|
+
fi
|
|
72
|
+
exec "$TARGET" "$@"
|
|
73
|
+
EOF
|
|
74
|
+
chmod +x "$FIXTURE/node_modules/bun/bin/bun"
|
|
75
|
+
|
|
76
|
+
ln -s ../bun/bin/bun "$FIXTURE/node_modules/.bin/bun"
|
|
77
|
+
|
|
78
|
+
echo "=== Testing self-heal on broken install ==="
|
|
79
|
+
OUTPUT=$(node "$FIXTURE/node_modules/pgserve/bin/pgserve-wrapper.cjs" 2>&1)
|
|
80
|
+
EXIT=$?
|
|
81
|
+
|
|
82
|
+
if [ $EXIT -ne 0 ]; then
|
|
83
|
+
echo "✗ wrapper exited non-zero: $EXIT"
|
|
84
|
+
echo "$OUTPUT"
|
|
85
|
+
exit 1
|
|
86
|
+
fi
|
|
87
|
+
|
|
88
|
+
if ! echo "$OUTPUT" | grep -q "attempting self-heal"; then
|
|
89
|
+
echo "✗ wrapper did not attempt self-heal"
|
|
90
|
+
echo "$OUTPUT"
|
|
91
|
+
exit 1
|
|
92
|
+
fi
|
|
93
|
+
|
|
94
|
+
if ! echo "$OUTPUT" | grep -q "bun runtime recovered"; then
|
|
95
|
+
echo "✗ wrapper did not report recovery"
|
|
96
|
+
echo "$OUTPUT"
|
|
97
|
+
exit 1
|
|
98
|
+
fi
|
|
99
|
+
|
|
100
|
+
if ! echo "$OUTPUT" | grep -q "pglite-server-spawned"; then
|
|
101
|
+
echo "✗ pglite-server was not spawned after self-heal"
|
|
102
|
+
echo "$OUTPUT"
|
|
103
|
+
exit 1
|
|
104
|
+
fi
|
|
105
|
+
|
|
106
|
+
echo "✓ self-heal path: wrapper detected, repaired, and spawned pglite-server"
|
|
107
|
+
|
|
108
|
+
echo ""
|
|
109
|
+
echo "=== Testing healthy path is unaffected ==="
|
|
110
|
+
OUTPUT=$(node "$FIXTURE/node_modules/pgserve/bin/pgserve-wrapper.cjs" 2>&1)
|
|
111
|
+
EXIT=$?
|
|
112
|
+
|
|
113
|
+
if [ $EXIT -ne 0 ]; then
|
|
114
|
+
echo "✗ wrapper exited non-zero on healthy path: $EXIT"
|
|
115
|
+
echo "$OUTPUT"
|
|
116
|
+
exit 1
|
|
117
|
+
fi
|
|
118
|
+
|
|
119
|
+
if echo "$OUTPUT" | grep -q "self-heal\|recovered"; then
|
|
120
|
+
echo "✗ wrapper logged self-heal messages on a healthy install"
|
|
121
|
+
echo "$OUTPUT"
|
|
122
|
+
exit 1
|
|
123
|
+
fi
|
|
124
|
+
|
|
125
|
+
if ! echo "$OUTPUT" | grep -q "pglite-server-spawned"; then
|
|
126
|
+
echo "✗ pglite-server was not spawned on healthy path"
|
|
127
|
+
echo "$OUTPUT"
|
|
128
|
+
exit 1
|
|
129
|
+
fi
|
|
130
|
+
|
|
131
|
+
echo "✓ healthy path: wrapper was silent and spawned pglite-server directly"
|
|
132
|
+
|
|
133
|
+
echo ""
|
|
134
|
+
echo "=== Testing non-postinstall errors surface raw ==="
|
|
135
|
+
# Replace stub with one that emits an unrelated error.
|
|
136
|
+
cat > "$FIXTURE/node_modules/bun/bin/bun" <<'EOF'
|
|
137
|
+
#!/bin/sh
|
|
138
|
+
echo "Error: GLIBC_2.99 not found (libc mismatch)" >&2
|
|
139
|
+
exit 127
|
|
140
|
+
EOF
|
|
141
|
+
chmod +x "$FIXTURE/node_modules/bun/bin/bun"
|
|
142
|
+
|
|
143
|
+
# Clear the @oven healed binary so the stub is what runs.
|
|
144
|
+
rm -f "$FIXTURE/node_modules/@oven/bun-linux-x64/bin/bun"
|
|
145
|
+
|
|
146
|
+
OUTPUT=$(node "$FIXTURE/node_modules/pgserve/bin/pgserve-wrapper.cjs" 2>&1 || true)
|
|
147
|
+
|
|
148
|
+
if echo "$OUTPUT" | grep -q "self-heal"; then
|
|
149
|
+
echo "✗ wrapper tried self-heal for a non-postinstall error"
|
|
150
|
+
echo "$OUTPUT"
|
|
151
|
+
exit 1
|
|
152
|
+
fi
|
|
153
|
+
|
|
154
|
+
if ! echo "$OUTPUT" | grep -q "GLIBC_2.99"; then
|
|
155
|
+
echo "✗ wrapper did not surface the real error message"
|
|
156
|
+
echo "$OUTPUT"
|
|
157
|
+
exit 1
|
|
158
|
+
fi
|
|
159
|
+
|
|
160
|
+
echo "✓ unrelated-error path: wrapper surfaced the raw error without self-heal"
|
|
161
|
+
|
|
162
|
+
echo ""
|
|
163
|
+
echo "=== bun self-heal test PASSED ==="
|