pgserve 2.1.2 → 2.2.0
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/CHANGELOG.md +86 -0
- package/README.md +105 -1
- package/bin/autopg-wrapper.cjs +16 -0
- package/bin/pgserve-wrapper.cjs +31 -6
- package/bin/postgres-server.js +80 -7
- package/console/README.md +131 -0
- package/console/api.js +173 -0
- package/console/app.jsx +483 -0
- package/console/colors_and_type.css +227 -0
- package/console/components.jsx +167 -0
- package/console/console.css +1666 -0
- package/console/data.jsx +350 -0
- package/console/index.html +31 -0
- package/console/screens/databases.jsx +5 -0
- package/console/screens/health.jsx +5 -0
- package/console/screens/ingress.jsx +5 -0
- package/console/screens/optimizer.jsx +5 -0
- package/console/screens/rlm-sim.jsx +5 -0
- package/console/screens/rlm-trace.jsx +5 -0
- package/console/screens/security.jsx +5 -0
- package/console/screens/settings.jsx +611 -0
- package/console/screens/sql.jsx +5 -0
- package/console/screens/sync.jsx +5 -0
- package/console/screens/tables.jsx +5 -0
- package/console/tweaks-panel.jsx +425 -0
- package/package.json +11 -1
- package/src/cli-config.cjs +310 -0
- package/src/cli-install.cjs +98 -11
- package/src/cli-restart.cjs +228 -0
- package/src/cli-ui.cjs +580 -0
- package/src/cluster.js +43 -38
- package/src/postgres.js +141 -19
- package/src/settings-loader.cjs +235 -0
- package/src/settings-migrate.cjs +212 -0
- package/src/settings-pg-args.cjs +146 -0
- package/src/settings-schema.cjs +422 -0
- package/src/settings-validator.cjs +416 -0
- package/src/settings-writer.cjs +288 -0
- package/.claude/context/windows-debug.md +0 -119
- package/.genie/AGENTS.md +0 -15
- package/.genie/agents/README.md +0 -110
- package/.genie/agents/analyze.md +0 -176
- package/.genie/agents/forge.md +0 -290
- package/.genie/agents/garbage-cleaner.md +0 -324
- package/.genie/agents/garbage-collector.md +0 -596
- package/.genie/agents/github-issue-gc.md +0 -618
- package/.genie/agents/review.md +0 -380
- package/.genie/agents/semantic-analyzer/find-duplicates.md +0 -90
- package/.genie/agents/semantic-analyzer/find-orphans.md +0 -99
- package/.genie/agents/semantic-analyzer.md +0 -101
- package/.genie/agents/update.md +0 -182
- package/.genie/agents/wish.md +0 -357
- package/.genie/brainstorms/pgserve-v2/DESIGN.md +0 -174
- package/.genie/code/AGENTS.md +0 -694
- package/.genie/code/agents/audit/risk.md +0 -173
- package/.genie/code/agents/audit/security.md +0 -189
- package/.genie/code/agents/audit.md +0 -145
- package/.genie/code/agents/challenge.md +0 -230
- package/.genie/code/agents/change-reviewer.md +0 -295
- package/.genie/code/agents/code-garbage-collector.md +0 -425
- package/.genie/code/agents/code-quality.md +0 -410
- package/.genie/code/agents/commit-suggester.md +0 -255
- package/.genie/code/agents/commit.md +0 -124
- package/.genie/code/agents/consensus.md +0 -204
- package/.genie/code/agents/daily-standup.md +0 -722
- package/.genie/code/agents/docgen.md +0 -48
- package/.genie/code/agents/explore.md +0 -79
- package/.genie/code/agents/fix.md +0 -100
- package/.genie/code/agents/git/commit-advisory.md +0 -219
- package/.genie/code/agents/git/workflows/issue.md +0 -244
- package/.genie/code/agents/git/workflows/pr.md +0 -179
- package/.genie/code/agents/git/workflows/release.md +0 -460
- package/.genie/code/agents/git/workflows/report.md +0 -342
- package/.genie/code/agents/git.md +0 -432
- package/.genie/code/agents/implementor.md +0 -161
- package/.genie/code/agents/install.md +0 -515
- package/.genie/code/agents/issue-creator.md +0 -344
- package/.genie/code/agents/polish.md +0 -116
- package/.genie/code/agents/qa.md +0 -653
- package/.genie/code/agents/refactor.md +0 -294
- package/.genie/code/agents/release.md +0 -1129
- package/.genie/code/agents/roadmap.md +0 -885
- package/.genie/code/agents/tests.md +0 -557
- package/.genie/code/agents/tracer.md +0 -50
- package/.genie/code/agents/update/upstream-update.md +0 -85
- package/.genie/code/agents/update/versions/generic-update.md +0 -305
- package/.genie/code/agents/vibe.md +0 -1317
- package/.genie/code/spells/agent-configuration.md +0 -58
- package/.genie/code/spells/automated-rc-publishing.md +0 -106
- package/.genie/code/spells/branch-tracker-guidance.md +0 -28
- package/.genie/code/spells/debug.md +0 -320
- package/.genie/code/spells/emoji-naming-convention.md +0 -303
- package/.genie/code/spells/evidence-storage.md +0 -26
- package/.genie/code/spells/file-naming-rules.md +0 -35
- package/.genie/code/spells/forge-code-blueprints.md +0 -195
- package/.genie/code/spells/genie-integration.md +0 -153
- package/.genie/code/spells/publishing-protocol.md +0 -61
- package/.genie/code/spells/team-consultation-protocol.md +0 -284
- package/.genie/code/spells/tool-requirements.md +0 -20
- package/.genie/code/spells/triad-maintenance-protocol.md +0 -154
- package/.genie/code/teams/tech-council/council.md +0 -328
- package/.genie/code/teams/tech-council/jt.md +0 -352
- package/.genie/code/teams/tech-council/nayr.md +0 -305
- package/.genie/code/teams/tech-council/oettam.md +0 -375
- package/.genie/neurons/README.md +0 -193
- package/.genie/neurons/forge.md +0 -106
- package/.genie/neurons/genie.md +0 -63
- package/.genie/neurons/review.md +0 -106
- package/.genie/neurons/wish.md +0 -104
- package/.genie/product/README.md +0 -20
- package/.genie/product/cli-automation.md +0 -359
- package/.genie/product/environment.md +0 -60
- package/.genie/product/mission.md +0 -60
- package/.genie/product/roadmap.md +0 -44
- package/.genie/product/tech-stack.md +0 -34
- package/.genie/product/templates/context-template.md +0 -218
- package/.genie/product/templates/qa-done-report-template.md +0 -68
- package/.genie/product/templates/review-report-template.md +0 -89
- package/.genie/product/templates/wish-template.md +0 -120
- package/.genie/scripts/helpers/analyze-commit.js +0 -195
- package/.genie/scripts/helpers/bullet-counter.js +0 -194
- package/.genie/scripts/helpers/bullet-find.js +0 -289
- package/.genie/scripts/helpers/bullet-id.js +0 -244
- package/.genie/scripts/helpers/check-secrets.js +0 -237
- package/.genie/scripts/helpers/count-tokens.js +0 -200
- package/.genie/scripts/helpers/create-frontmatter.js +0 -456
- package/.genie/scripts/helpers/detect-markers.js +0 -293
- package/.genie/scripts/helpers/detect-todos.js +0 -267
- package/.genie/scripts/helpers/detect-unlabeled-blocks.js +0 -135
- package/.genie/scripts/helpers/embeddings.js +0 -344
- package/.genie/scripts/helpers/find-empty-sections.js +0 -158
- package/.genie/scripts/helpers/index.js +0 -319
- package/.genie/scripts/helpers/validate-frontmatter.js +0 -578
- package/.genie/scripts/helpers/validate-links.js +0 -207
- package/.genie/scripts/helpers/validate-paths.js +0 -373
- package/.genie/spells/README.md +0 -9
- package/.genie/spells/ace-protocol.md +0 -118
- package/.genie/spells/ask-one-at-a-time.md +0 -175
- package/.genie/spells/backup-analyzer.md +0 -542
- package/.genie/spells/blocker.md +0 -12
- package/.genie/spells/break-things-move-fast.md +0 -56
- package/.genie/spells/context-candidates.md +0 -72
- package/.genie/spells/context-critic.md +0 -51
- package/.genie/spells/defer-to-expertise.md +0 -278
- package/.genie/spells/delegate-dont-do.md +0 -292
- package/.genie/spells/error-investigation-protocol.md +0 -328
- package/.genie/spells/evidence-based-completion.md +0 -273
- package/.genie/spells/experiment.md +0 -65
- package/.genie/spells/file-creation-protocol.md +0 -229
- package/.genie/spells/forge-integration.md +0 -281
- package/.genie/spells/forge-orchestration.md +0 -514
- package/.genie/spells/gather-context.md +0 -18
- package/.genie/spells/global-health-check.md +0 -34
- package/.genie/spells/global-noop-roundtrip.md +0 -25
- package/.genie/spells/install-genie.md +0 -1232
- package/.genie/spells/install.md +0 -82
- package/.genie/spells/investigate-before-commit.md +0 -112
- package/.genie/spells/know-yourself.md +0 -288
- package/.genie/spells/learn.md +0 -828
- package/.genie/spells/mcp-diagnostic-protocol.md +0 -246
- package/.genie/spells/mcp-first.md +0 -124
- package/.genie/spells/multi-step-execution.md +0 -67
- package/.genie/spells/orchestration-boundary-protocol.md +0 -256
- package/.genie/spells/orchestrator-not-implementor.md +0 -189
- package/.genie/spells/prompt.md +0 -746
- package/.genie/spells/reflect.md +0 -404
- package/.genie/spells/routing-decision-matrix.md +0 -368
- package/.genie/spells/run-in-parallel.md +0 -12
- package/.genie/spells/session-state-updater-example.md +0 -196
- package/.genie/spells/session-state-updater.md +0 -220
- package/.genie/spells/track-long-running-tasks.md +0 -133
- package/.genie/spells/troubleshoot-infrastructure.md +0 -176
- package/.genie/spells/upgrade-genie.md +0 -415
- package/.genie/spells/url-presentation-protocol.md +0 -301
- package/.genie/spells/wish-initiation.md +0 -158
- package/.genie/spells/wish-issue-linkage.md +0 -410
- package/.genie/spells/wish-lifecycle.md +0 -100
- package/.genie/state/provider-status.json +0 -3
- package/.genie/state/version.json +0 -16
- package/.genie/wishes/canonical-pgserve-pm2-supervision/WISH.md +0 -290
- package/.genie/wishes/pgserve-v2/BRIEF-from-genie-pgserve.md +0 -99
- package/.genie/wishes/pgserve-v2/WISH.md +0 -442
- package/.genie/wishes/release-system-genie-pattern/WISH.md +0 -268
- package/.genie/wishes/release-system-genie-pattern/validation.md +0 -205
- package/.gitguardian.yaml +0 -29
- package/.gitguardianignore +0 -16
- package/.github/workflows/ci.yml +0 -122
- package/.github/workflows/release.yml +0 -289
- package/.github/workflows/version.yml +0 -228
- package/.husky/pre-commit +0 -2
- package/AGENTS.md +0 -433
- package/CLAUDE.md +0 -1
- package/Makefile +0 -285
- package/assets/icon.ico +0 -0
- package/bun.lock +0 -435
- package/bunfig.toml +0 -28
- package/ecosystem.config.cjs +0 -23
- package/eslint.config.js +0 -63
- package/examples/multi-tenant-demo.js +0 -104
- package/install.sh +0 -123
- package/knip.json +0 -9
- package/scripts/test-bun-self-heal.sh +0 -163
- package/scripts/test-npx.sh +0 -60
- package/tests/audit.test.js +0 -189
- package/tests/backpressure.test.js +0 -167
- package/tests/benchmarks/runner.js +0 -1197
- package/tests/benchmarks/vector-generator.js +0 -368
- package/tests/cli-install.test.js +0 -322
- package/tests/control-db.test.js +0 -285
- package/tests/daemon-control.test.js +0 -171
- package/tests/daemon-fingerprint-integration.test.js +0 -111
- package/tests/daemon-pr24-regression.test.js +0 -198
- package/tests/fingerprint.test.js +0 -263
- package/tests/fixtures/240-orphan-seed.sql +0 -30
- package/tests/multi-tenant.test.js +0 -374
- package/tests/orphan-cleanup.test.js +0 -390
- package/tests/pg-version-regex.test.js +0 -129
- package/tests/quick-bench.js +0 -135
- package/tests/router-handshake-retry.test.js +0 -119
- package/tests/router-handshake-watchdog.test.js +0 -110
- package/tests/sdk.test.js +0 -71
- package/tests/stale-postmaster-pid.test.js +0 -85
- package/tests/stress-test.js +0 -439
- package/tests/sync-perf-test.js +0 -150
- package/tests/tcp-listen.test.js +0 -368
- package/tests/tenancy.test.js +0 -403
- package/tests/wrapper-supervision.test.js +0 -107
|
@@ -1,104 +0,0 @@
|
|
|
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/install.sh
DELETED
|
@@ -1,123 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# ============================================================================
|
|
3
|
-
# pgserve — Canonical PostgreSQL backbone installer
|
|
4
|
-
#
|
|
5
|
-
# Bootstraps a single shared pgserve instance under pm2 supervision. Used as
|
|
6
|
-
# a prerequisite by `omni/install.sh` and `genie/install.sh` so every
|
|
7
|
-
# automagik service on a host points at the same Postgres.
|
|
8
|
-
#
|
|
9
|
-
# Usage:
|
|
10
|
-
# curl -fsSL https://raw.githubusercontent.com/namastexlabs/pgserve/main/install.sh | bash
|
|
11
|
-
#
|
|
12
|
-
# With pinned version:
|
|
13
|
-
# PGSERVE_VERSION=^2.1.1 curl -fsSL .../install.sh | bash
|
|
14
|
-
#
|
|
15
|
-
# Local checkout:
|
|
16
|
-
# bash install.sh
|
|
17
|
-
#
|
|
18
|
-
# Idempotent — re-running is a no-op success when pgserve is already
|
|
19
|
-
# registered under pm2 with a healthy entry.
|
|
20
|
-
# ============================================================================
|
|
21
|
-
set -euo pipefail
|
|
22
|
-
|
|
23
|
-
PGSERVE_VERSION="${PGSERVE_VERSION:-^2.1.0}"
|
|
24
|
-
|
|
25
|
-
# Colors (no-op when stdout isn't a tty)
|
|
26
|
-
if [[ -t 1 ]]; then
|
|
27
|
-
RED='\033[0;31m'
|
|
28
|
-
GREEN='\033[0;32m'
|
|
29
|
-
YELLOW='\033[1;33m'
|
|
30
|
-
BLUE='\033[0;34m'
|
|
31
|
-
CYAN='\033[0;36m'
|
|
32
|
-
BOLD='\033[1m'
|
|
33
|
-
NC='\033[0m'
|
|
34
|
-
else
|
|
35
|
-
RED='' GREEN='' YELLOW='' BLUE='' CYAN='' BOLD='' NC=''
|
|
36
|
-
fi
|
|
37
|
-
|
|
38
|
-
info() { printf "${BLUE}ℹ${NC} %s\n" "$*"; }
|
|
39
|
-
ok() { printf "${GREEN}✓${NC} %s\n" "$*"; }
|
|
40
|
-
warn() { printf "${YELLOW}⚠${NC} %s\n" "$*"; }
|
|
41
|
-
fail() { printf "${RED}✗${NC} %s\n" "$*" >&2; exit 1; }
|
|
42
|
-
step() { printf "\n${BOLD}${CYAN}▸ %s${NC}\n" "$*"; }
|
|
43
|
-
|
|
44
|
-
has_cmd() { command -v "$1" >/dev/null 2>&1; }
|
|
45
|
-
|
|
46
|
-
# ============================================================================
|
|
47
|
-
# Prerequisites: bun + pm2
|
|
48
|
-
# ============================================================================
|
|
49
|
-
|
|
50
|
-
ensure_bun() {
|
|
51
|
-
if has_cmd bun; then
|
|
52
|
-
ok "bun $(bun --version 2>/dev/null || echo '?')"
|
|
53
|
-
return 0
|
|
54
|
-
fi
|
|
55
|
-
info "Installing bun (https://bun.sh)..."
|
|
56
|
-
curl -fsSL https://bun.sh/install | bash >/dev/null 2>&1 || fail "bun install failed — see https://bun.sh"
|
|
57
|
-
# Make bun available to the rest of this script without requiring a re-login.
|
|
58
|
-
export PATH="$HOME/.bun/bin:$PATH"
|
|
59
|
-
has_cmd bun || fail "bun installed but not on PATH — restart your shell and re-run."
|
|
60
|
-
ok "bun $(bun --version)"
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
ensure_pm2() {
|
|
64
|
-
if has_cmd pm2; then
|
|
65
|
-
ok "pm2 $(pm2 --version 2>/dev/null || echo '?')"
|
|
66
|
-
return 0
|
|
67
|
-
fi
|
|
68
|
-
info "Installing pm2 (process supervisor)..."
|
|
69
|
-
bun add -g pm2 >/dev/null 2>&1 || fail "pm2 install failed — try: bun add -g pm2"
|
|
70
|
-
has_cmd pm2 || fail "pm2 installed but not on PATH — restart your shell and re-run."
|
|
71
|
-
ok "pm2 installed"
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
# ============================================================================
|
|
75
|
-
# pgserve binary + pm2 registration
|
|
76
|
-
# ============================================================================
|
|
77
|
-
|
|
78
|
-
ensure_pgserve_binary() {
|
|
79
|
-
# Probe via `pgserve port` (real subcommand). `pgserve --version` doesn't
|
|
80
|
-
# exist in 2.1.x — using it would false-negative and trigger a redundant
|
|
81
|
-
# reinstall every time install.sh runs.
|
|
82
|
-
if has_cmd pgserve && pgserve port >/dev/null 2>&1; then
|
|
83
|
-
ok "pgserve binary present (port $(pgserve port 2>/dev/null))"
|
|
84
|
-
return 0
|
|
85
|
-
fi
|
|
86
|
-
info "Installing pgserve@${PGSERVE_VERSION} globally..."
|
|
87
|
-
bun add -g "pgserve@${PGSERVE_VERSION}" >/dev/null 2>&1 \
|
|
88
|
-
|| fail "pgserve install failed — try: bun add -g pgserve@${PGSERVE_VERSION}"
|
|
89
|
-
has_cmd pgserve || fail "pgserve installed but not on PATH — restart your shell and re-run."
|
|
90
|
-
ok "pgserve $(pgserve port 2>/dev/null || echo '?')"
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
register_pgserve_pm2() {
|
|
94
|
-
info "Registering pgserve under pm2 (idempotent)..."
|
|
95
|
-
# `pgserve install` prints its own success/already-installed line and exits
|
|
96
|
-
# 0 in both cases. We pipe stderr through so any pm2 errors surface to the
|
|
97
|
-
# operator (the pm2-6.x --min-uptime breakage we hit on 2026-04-30 was
|
|
98
|
-
# invisible because stderr was being captured).
|
|
99
|
-
pgserve install || fail "pgserve install failed — see ~/.pgserve/logs/pgserve-error.log"
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
# ============================================================================
|
|
103
|
-
# Main
|
|
104
|
-
# ============================================================================
|
|
105
|
-
|
|
106
|
-
main() {
|
|
107
|
-
step "Installing canonical pgserve"
|
|
108
|
-
ensure_bun
|
|
109
|
-
ensure_pm2
|
|
110
|
-
ensure_pgserve_binary
|
|
111
|
-
register_pgserve_pm2
|
|
112
|
-
|
|
113
|
-
echo ""
|
|
114
|
-
ok "Canonical pgserve ready"
|
|
115
|
-
info "URL: $(pgserve url 2>/dev/null || echo '<run: pgserve url>')"
|
|
116
|
-
info "Port: $(pgserve port 2>/dev/null || echo '?')"
|
|
117
|
-
info "Logs: ~/.pgserve/logs/"
|
|
118
|
-
echo ""
|
|
119
|
-
info "Other automagik services on this host (omni, genie, ...) will share this pgserve."
|
|
120
|
-
echo ""
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
main "$@"
|
package/knip.json
DELETED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"$schema": "https://unpkg.com/knip@5/schema.json",
|
|
3
|
-
"entry": ["src/index.js", "bin/postgres-server.js", "bin/pgserve-wrapper.cjs"],
|
|
4
|
-
"project": ["src/**/*.js", "bin/**/*.js", "bin/**/*.cjs"],
|
|
5
|
-
"ignore": ["tests/**", "helpers/**", "scripts/**"],
|
|
6
|
-
"ignoreBinaries": ["scripts/test-npx.sh", "scripts/test-bun-self-heal.sh", "make"],
|
|
7
|
-
"ignoreDependencies": ["bun"],
|
|
8
|
-
"ignoreExportsUsedInFile": true
|
|
9
|
-
}
|
|
@@ -1,163 +0,0 @@
|
|
|
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 postgres-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 postgres-server so we can detect a successful spawn without needing
|
|
41
|
-
# postgres binaries in the fixture.
|
|
42
|
-
cat > "$FIXTURE/node_modules/pgserve/bin/postgres-server.js" <<'EOF'
|
|
43
|
-
console.log("postgres-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 "postgres-server-spawned"; then
|
|
101
|
-
echo "✗ postgres-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 postgres-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 "postgres-server-spawned"; then
|
|
126
|
-
echo "✗ postgres-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 postgres-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 ==="
|
package/scripts/test-npx.sh
DELETED
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
# Test that the package works with npx (simulates fresh user install)
|
|
3
|
-
# This catches path resolution issues that static analysis can't detect
|
|
4
|
-
|
|
5
|
-
set -e
|
|
6
|
-
|
|
7
|
-
echo "=== Testing npx compatibility ==="
|
|
8
|
-
|
|
9
|
-
# Create temp directory
|
|
10
|
-
TEST_DIR=$(mktemp -d)
|
|
11
|
-
trap "rm -rf $TEST_DIR" EXIT
|
|
12
|
-
|
|
13
|
-
# Pack the current package
|
|
14
|
-
echo "Packing package..."
|
|
15
|
-
PACK_OUTPUT=$(npm pack --pack-destination "$TEST_DIR" 2>&1)
|
|
16
|
-
PACK_FILE=$(echo "$PACK_OUTPUT" | grep -E '\.tgz$' | tail -1)
|
|
17
|
-
|
|
18
|
-
# If npm pack fails, exit with an error
|
|
19
|
-
if [ -z "$PACK_FILE" ] || [ ! -f "$TEST_DIR/$PACK_FILE" ]; then
|
|
20
|
-
echo "✗ Failed to pack package with npm"
|
|
21
|
-
echo "Pack output: $PACK_OUTPUT"
|
|
22
|
-
exit 1
|
|
23
|
-
fi
|
|
24
|
-
echo "Packed: $PACK_FILE"
|
|
25
|
-
|
|
26
|
-
# Install in isolated environment using npm
|
|
27
|
-
echo "Installing in isolated environment..."
|
|
28
|
-
cd "$TEST_DIR"
|
|
29
|
-
echo '{"name":"test-npx-install","private":true}' > package.json
|
|
30
|
-
npm install "./$PACK_FILE" > /dev/null 2>&1
|
|
31
|
-
|
|
32
|
-
# Test that it starts (with timeout)
|
|
33
|
-
echo "Testing server startup via npx..."
|
|
34
|
-
timeout 30 npx pgserve --no-cluster --port 15432 > output.log 2>&1 &
|
|
35
|
-
PID=$!
|
|
36
|
-
|
|
37
|
-
# Wait for ready signal (Server started successfully!)
|
|
38
|
-
for i in {1..60}; do
|
|
39
|
-
if grep -q "Server started successfully" output.log 2>/dev/null; then
|
|
40
|
-
echo "✓ Server started successfully via npx"
|
|
41
|
-
kill $PID 2>/dev/null || true
|
|
42
|
-
wait $PID 2>/dev/null || true
|
|
43
|
-
echo "=== npx test PASSED ==="
|
|
44
|
-
exit 0
|
|
45
|
-
fi
|
|
46
|
-
if ! kill -0 $PID 2>/dev/null; then
|
|
47
|
-
echo "✗ Server exited unexpectedly"
|
|
48
|
-
cat output.log
|
|
49
|
-
echo "=== npx test FAILED ==="
|
|
50
|
-
exit 1
|
|
51
|
-
fi
|
|
52
|
-
sleep 0.5
|
|
53
|
-
done
|
|
54
|
-
|
|
55
|
-
# Timeout
|
|
56
|
-
kill $PID 2>/dev/null || true
|
|
57
|
-
echo "✗ Server did not start within timeout"
|
|
58
|
-
cat output.log
|
|
59
|
-
echo "=== npx test FAILED ==="
|
|
60
|
-
exit 1
|
package/tests/audit.test.js
DELETED
|
@@ -1,189 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tests for src/audit.js — JSONL writer with rotation + syslog target.
|
|
3
|
-
*
|
|
4
|
-
* Tests use temp dirs under /tmp; nothing touches the user's real
|
|
5
|
-
* `~/.pgserve/audit.log`. The syslog test stubs `logger` via PATH so we
|
|
6
|
-
* don't depend on (or pollute) the host's syslog daemon.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import { test, expect, beforeEach, afterEach } from 'bun:test';
|
|
10
|
-
import fs from 'fs';
|
|
11
|
-
import os from 'os';
|
|
12
|
-
import path from 'path';
|
|
13
|
-
import {
|
|
14
|
-
audit,
|
|
15
|
-
configureAudit,
|
|
16
|
-
readAuditTarget,
|
|
17
|
-
AUDIT_EVENTS,
|
|
18
|
-
_internals,
|
|
19
|
-
} from '../src/audit.js';
|
|
20
|
-
|
|
21
|
-
let scratchDir;
|
|
22
|
-
|
|
23
|
-
beforeEach(() => {
|
|
24
|
-
scratchDir = fs.mkdtempSync(path.join(os.tmpdir(), 'pgserve-audit-test-'));
|
|
25
|
-
configureAudit({
|
|
26
|
-
logFile: path.join(scratchDir, 'audit.log'),
|
|
27
|
-
target: 'file',
|
|
28
|
-
});
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
afterEach(() => {
|
|
32
|
-
try {
|
|
33
|
-
fs.rmSync(scratchDir, { recursive: true, force: true });
|
|
34
|
-
} catch { /* noop */ }
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
test('audit() appends a JSON line per event', () => {
|
|
38
|
-
audit(AUDIT_EVENTS.DB_CREATED, { fingerprint: 'abc123def456', db: 'app_demo_abc123def456' });
|
|
39
|
-
audit(AUDIT_EVENTS.CONNECTION_ROUTED, { fingerprint: 'abc123def456', peer_pid: 1234 });
|
|
40
|
-
|
|
41
|
-
const logFile = path.join(scratchDir, 'audit.log');
|
|
42
|
-
const lines = fs.readFileSync(logFile, 'utf8').trim().split('\n');
|
|
43
|
-
expect(lines.length).toBe(2);
|
|
44
|
-
const r1 = JSON.parse(lines[0]);
|
|
45
|
-
expect(r1.event).toBe('db_created');
|
|
46
|
-
expect(r1.fingerprint).toBe('abc123def456');
|
|
47
|
-
expect(typeof r1.ts).toBe('string');
|
|
48
|
-
expect(new Date(r1.ts).toString()).not.toBe('Invalid Date');
|
|
49
|
-
|
|
50
|
-
const r2 = JSON.parse(lines[1]);
|
|
51
|
-
expect(r2.event).toBe('connection_routed');
|
|
52
|
-
expect(r2.peer_pid).toBe(1234);
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
test('audit() refuses unknown events', () => {
|
|
56
|
-
expect(() => audit('definitely_not_a_real_event', {})).toThrow(/unknown event/);
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
test('audit() creates the parent directory if missing', () => {
|
|
60
|
-
const nested = path.join(scratchDir, 'nested', 'sub', 'audit.log');
|
|
61
|
-
audit(AUDIT_EVENTS.DB_CREATED, { fingerprint: 'a'.repeat(12) }, { logFile: nested });
|
|
62
|
-
expect(fs.existsSync(nested)).toBe(true);
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
test('all v2.0 event names are exported (incl. Group 6 tcp_*)', () => {
|
|
66
|
-
expect(Object.values(AUDIT_EVENTS).sort()).toEqual([
|
|
67
|
-
'connection_denied_fingerprint_mismatch',
|
|
68
|
-
'connection_routed',
|
|
69
|
-
'db_created',
|
|
70
|
-
'db_persist_honored',
|
|
71
|
-
'db_reaped_liveness',
|
|
72
|
-
'db_reaped_ttl',
|
|
73
|
-
'enforcement_kill_switch_used',
|
|
74
|
-
'tcp_token_denied',
|
|
75
|
-
'tcp_token_issued',
|
|
76
|
-
'tcp_token_used',
|
|
77
|
-
]);
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
test('rotation kicks in once existing file crosses 50 MB', () => {
|
|
81
|
-
const logFile = path.join(scratchDir, 'audit.log');
|
|
82
|
-
// Use a sparse file to simulate a 50 MB log without writing 50 MB.
|
|
83
|
-
const fd = fs.openSync(logFile, 'w');
|
|
84
|
-
fs.ftruncateSync(fd, _internals.ROTATE_THRESHOLD_BYTES);
|
|
85
|
-
fs.closeSync(fd);
|
|
86
|
-
|
|
87
|
-
audit(AUDIT_EVENTS.DB_CREATED, { fingerprint: 'r'.repeat(12) });
|
|
88
|
-
|
|
89
|
-
// Original file rotated to .1, fresh file holds the new line.
|
|
90
|
-
expect(fs.existsSync(`${logFile}.1`)).toBe(true);
|
|
91
|
-
const fresh = fs.readFileSync(logFile, 'utf8');
|
|
92
|
-
expect(fresh.trim().split('\n').length).toBe(1);
|
|
93
|
-
expect(JSON.parse(fresh.trim()).event).toBe('db_created');
|
|
94
|
-
|
|
95
|
-
// The rotated file is the original 50 MB sparse file.
|
|
96
|
-
expect(fs.statSync(`${logFile}.1`).size).toBe(_internals.ROTATE_THRESHOLD_BYTES);
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
test('rotation cascades up to KEEP files and drops the eldest', () => {
|
|
100
|
-
const logFile = path.join(scratchDir, 'audit.log');
|
|
101
|
-
// Pre-populate audit.log.1 ... audit.log.5 with distinct markers.
|
|
102
|
-
for (let i = 1; i <= _internals.ROTATE_KEEP; i++) {
|
|
103
|
-
fs.writeFileSync(`${logFile}.${i}`, `slot-${i}\n`);
|
|
104
|
-
}
|
|
105
|
-
// And the live audit.log just under threshold.
|
|
106
|
-
const fd = fs.openSync(logFile, 'w');
|
|
107
|
-
fs.ftruncateSync(fd, _internals.ROTATE_THRESHOLD_BYTES);
|
|
108
|
-
fs.closeSync(fd);
|
|
109
|
-
|
|
110
|
-
audit(AUDIT_EVENTS.DB_CREATED, { fingerprint: 'q'.repeat(12) });
|
|
111
|
-
|
|
112
|
-
// .5 (was "slot-5") dropped; .4 → .5; .3 → .4; .2 → .3; .1 → .2; live → .1.
|
|
113
|
-
expect(fs.readFileSync(`${logFile}.5`, 'utf8').trim()).toBe('slot-4');
|
|
114
|
-
expect(fs.readFileSync(`${logFile}.4`, 'utf8').trim()).toBe('slot-3');
|
|
115
|
-
expect(fs.readFileSync(`${logFile}.3`, 'utf8').trim()).toBe('slot-2');
|
|
116
|
-
expect(fs.readFileSync(`${logFile}.2`, 'utf8').trim()).toBe('slot-1');
|
|
117
|
-
expect(fs.statSync(`${logFile}.1`).size).toBe(_internals.ROTATE_THRESHOLD_BYTES);
|
|
118
|
-
});
|
|
119
|
-
|
|
120
|
-
test('audit({target:"syslog"}) spawns logger -t pgserve-audit', async () => {
|
|
121
|
-
// Stub `logger` by prepending a temp shim to PATH.
|
|
122
|
-
const shimDir = path.join(scratchDir, 'shim');
|
|
123
|
-
fs.mkdirSync(shimDir, { recursive: true });
|
|
124
|
-
const marker = path.join(scratchDir, 'logger-calls.txt');
|
|
125
|
-
const shimPath = path.join(shimDir, 'logger');
|
|
126
|
-
fs.writeFileSync(
|
|
127
|
-
shimPath,
|
|
128
|
-
`#!/usr/bin/env bash
|
|
129
|
-
# Capture argv to a marker file so the test can verify the spawn.
|
|
130
|
-
printf '%s\\n' "$*" >> "${marker}"
|
|
131
|
-
`,
|
|
132
|
-
{ mode: 0o755 },
|
|
133
|
-
);
|
|
134
|
-
|
|
135
|
-
const oldPath = process.env.PATH;
|
|
136
|
-
process.env.PATH = `${shimDir}:${oldPath}`;
|
|
137
|
-
try {
|
|
138
|
-
audit(
|
|
139
|
-
AUDIT_EVENTS.CONNECTION_ROUTED,
|
|
140
|
-
{ fingerprint: 's'.repeat(12) },
|
|
141
|
-
{ target: 'syslog' },
|
|
142
|
-
);
|
|
143
|
-
// logger is spawned async; poll briefly for the marker.
|
|
144
|
-
const deadline = Date.now() + 2000;
|
|
145
|
-
while (!fs.existsSync(marker) && Date.now() < deadline) {
|
|
146
|
-
await new Promise(r => setTimeout(r, 25));
|
|
147
|
-
}
|
|
148
|
-
expect(fs.existsSync(marker)).toBe(true);
|
|
149
|
-
const contents = fs.readFileSync(marker, 'utf8');
|
|
150
|
-
expect(contents).toContain('-t pgserve-audit');
|
|
151
|
-
expect(contents).toContain('"event":"connection_routed"');
|
|
152
|
-
} finally {
|
|
153
|
-
process.env.PATH = oldPath;
|
|
154
|
-
}
|
|
155
|
-
});
|
|
156
|
-
|
|
157
|
-
test('audit({target:"syslog"}) swallows missing logger binary', () => {
|
|
158
|
-
// Point PATH at an empty dir → `logger` cannot be found → no throw.
|
|
159
|
-
const empty = path.join(scratchDir, 'empty');
|
|
160
|
-
fs.mkdirSync(empty);
|
|
161
|
-
const oldPath = process.env.PATH;
|
|
162
|
-
process.env.PATH = empty;
|
|
163
|
-
try {
|
|
164
|
-
expect(() =>
|
|
165
|
-
audit(
|
|
166
|
-
AUDIT_EVENTS.CONNECTION_ROUTED,
|
|
167
|
-
{ fingerprint: 'z'.repeat(12) },
|
|
168
|
-
{ target: 'syslog' },
|
|
169
|
-
),
|
|
170
|
-
).not.toThrow();
|
|
171
|
-
} finally {
|
|
172
|
-
process.env.PATH = oldPath;
|
|
173
|
-
}
|
|
174
|
-
});
|
|
175
|
-
|
|
176
|
-
test('readAuditTarget reads pgserve.audit.target from package.json', () => {
|
|
177
|
-
const pkgFile = path.join(scratchDir, 'package.json');
|
|
178
|
-
fs.writeFileSync(
|
|
179
|
-
pkgFile,
|
|
180
|
-
JSON.stringify({ name: 'demo', pgserve: { audit: { target: 'syslog' } } }),
|
|
181
|
-
);
|
|
182
|
-
expect(readAuditTarget(pkgFile)).toBe('syslog');
|
|
183
|
-
|
|
184
|
-
fs.writeFileSync(pkgFile, JSON.stringify({ name: 'demo' }));
|
|
185
|
-
expect(readAuditTarget(pkgFile)).toBe('file');
|
|
186
|
-
|
|
187
|
-
// Missing file → file (default).
|
|
188
|
-
expect(readAuditTarget(path.join(scratchDir, 'missing.json'))).toBe('file');
|
|
189
|
-
});
|