pgsqlio 0.1.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.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/app.ts","../src/commands/dump.ts","../src/utils.ts","../src/tui/shared.ts","../src/tui/branding.ts","../src/commands/restore.ts","../src/commands/cleanup.ts","../src/commands/drop-databases.ts"],"sourcesContent":["import { createCliRenderer } from \"@opentui/core\";\nimport { runDumpFlow } from \"./commands/dump.js\";\nimport { runRestoreFlow } from \"./commands/restore.js\";\nimport { runCleanupFlow } from \"./commands/cleanup.js\";\nimport { runDropDatabasesFlow } from \"./commands/drop-databases.js\";\nimport { mountShell } from \"./tui/branding.js\";\nimport { BACK, promptSelect } from \"./tui/shared.js\";\n\nexport async function runApp(): Promise<void> {\n const renderer = await createCliRenderer({\n exitOnCtrlC: true,\n backgroundColor: \"#0C0C0C\",\n });\n\n renderer.start();\n mountShell(renderer);\n\n try {\n while (true) {\n const action = await promptSelect(renderer, {\n title: \"pgsqlio\",\n message: \"What do you want to do?\",\n choices: [\n {\n name: \"Dump\",\n description: \"Backup one or more databases\",\n value: \"dump\",\n },\n {\n name: \"Restore\",\n description: \"Import a .sql backup into a database\",\n value: \"restore\",\n },\n {\n name: \"Cleanup\",\n description: \"Drop and recreate the public schema\",\n value: \"cleanup\",\n },\n {\n name: \"Drop databases\",\n description: \"Select and permanently drop databases\",\n value: \"drop-databases\",\n },\n {\n name: \"Quit\",\n description: \"Exit pgsqlio\",\n value: \"quit\",\n },\n ],\n });\n\n if (action === BACK || action === \"quit\") break;\n\n if (action === \"dump\") await runDumpFlow(renderer);\n else if (action === \"restore\") await runRestoreFlow(renderer);\n else if (action === \"cleanup\") await runCleanupFlow(renderer);\n else if (action === \"drop-databases\") await runDropDatabasesFlow(renderer);\n }\n } finally {\n renderer.destroy();\n }\n}\n","import { createWriteStream, unlinkSync, type WriteStream } from \"node:fs\";\nimport { spawn } from \"node:child_process\";\nimport { finished, pipeline } from \"node:stream/promises\";\nimport {\n BoxRenderable,\n TextRenderable,\n type CliRenderer,\n type KeyEvent,\n} from \"@opentui/core\";\nimport { SpinnerRenderable } from \"opentui-spinner\";\nimport { listDatabases, replaceDbInUrl, requireBin, timestamp } from \"../utils.js\";\nimport {\n BACK,\n clearContent,\n promptDbUrl,\n promptMultiSelect,\n promptSelect,\n type FlowResult,\n} from \"../tui/shared.js\";\n\ntype OutputMode = \"separate\" | \"single\";\n\nconst PG_DUMP_ARGS = [\"--clean\", \"--if-exists\"] as const;\n\nfunction quoteIdent(name: string): string {\n return `\"${name.replace(/\"/g, '\"\"')}\"`;\n}\n\nfunction sqlString(name: string): string {\n return `'${name.replace(/'/g, \"''\")}'`;\n}\n\nasync function waitExit(child: ReturnType<typeof spawn>): Promise<number | null> {\n if (child.exitCode !== null) return child.exitCode;\n return new Promise((resolve) => child.once(\"close\", resolve));\n}\n\nasync function dumpOne(url: string, outFile: string): Promise<boolean> {\n const child = spawn(\"pg_dump\", [...PG_DUMP_ARGS, url], {\n stdio: [\"ignore\", \"pipe\", \"inherit\"],\n });\n const out = createWriteStream(outFile);\n\n try {\n await pipeline(child.stdout!, out);\n } catch {\n return false;\n }\n\n return (await waitExit(child)) === 0;\n}\n\n/** Append one database dump into an open stream (does not end the stream). */\nasync function dumpAppend(\n url: string,\n out: WriteStream,\n dbName: string,\n): Promise<boolean> {\n // Create DB if missing, then connect — required for restore onto empty servers\n const header =\n `\\n--\\n-- Database: ${dbName}\\n--\\n` +\n `\\\\connect postgres\\n` +\n `SELECT format('CREATE DATABASE %I', ${sqlString(dbName)})\\n` +\n `WHERE NOT EXISTS (SELECT FROM pg_database WHERE datname = ${sqlString(dbName)})\\\\gexec\\n` +\n `\\\\connect ${quoteIdent(dbName)}\\n\\n`;\n\n await new Promise<void>((resolve, reject) => {\n out.write(header, (err) => (err ? reject(err) : resolve()));\n });\n\n const child = spawn(\"pg_dump\", [...PG_DUMP_ARGS, url], {\n stdio: [\"ignore\", \"pipe\", \"inherit\"],\n });\n child.stdout!.pipe(out, { end: false });\n\n try {\n await finished(child.stdout!);\n } catch {\n return false;\n }\n\n return (await waitExit(child)) === 0;\n}\n\nasync function runProgress(\n renderer: CliRenderer,\n dbUrl: string,\n selected: string[],\n outputMode: OutputMode,\n): Promise<number> {\n const content = clearContent(renderer);\n\n const ts = timestamp();\n let failed = 0;\n const combinedFile =\n outputMode === \"single\" ? `backup_combined_${ts}.sql` : null;\n\n const panel = new BoxRenderable(renderer, {\n id: \"progress-panel\",\n border: true,\n borderColor: \"#555555\",\n title:\n outputMode === \"single\"\n ? ` dumping ${selected.length} database(s) → one file `\n : ` dumping ${selected.length} database(s) `,\n flexDirection: \"column\",\n padding: 1,\n width: \"100%\",\n flexGrow: 1,\n });\n\n const status = new TextRenderable(renderer, {\n id: \"progress-status\",\n content: combinedFile ? `Writing ${combinedFile}…` : \"Starting…\",\n fg: \"#AAAAAA\",\n marginBottom: 1,\n });\n panel.add(status);\n\n type Row = {\n spinner: SpinnerRenderable;\n label: TextRenderable;\n };\n\n const rows = new Map<string, Row>();\n\n for (const db of selected) {\n const box = new BoxRenderable(renderer, {\n id: `row-${db}`,\n flexDirection: \"row\",\n alignItems: \"center\",\n height: 1,\n width: \"100%\",\n });\n const spinner = new SpinnerRenderable(renderer, {\n id: `spin-${db}`,\n name: \"dots\",\n color: \"#88CCFF\",\n autoplay: false,\n });\n const label = new TextRenderable(renderer, {\n id: `label-${db}`,\n content: ` ${db} pending`,\n fg: \"#888888\",\n marginLeft: 1,\n });\n box.add(spinner);\n box.add(label);\n panel.add(box);\n rows.set(db, { spinner, label });\n }\n\n content.add(panel);\n\n const combined = combinedFile ? createWriteStream(combinedFile) : null;\n if (combined) {\n combined.write(\n `-- pgsqlio combined dump\\n-- databases: ${selected.join(\", \")}\\n-- created: ${ts}\\n`,\n );\n }\n\n for (let i = 0; i < selected.length; i++) {\n const db = selected[i]!;\n const url = replaceDbInUrl(dbUrl, db);\n const row = rows.get(db)!;\n const outLabel = combinedFile ?? `backup_${db}_${ts}.sql`;\n\n status.content = `Dumping ${i + 1}/${selected.length}: ${db}`;\n row.label.content = ` ${db} → ${outLabel}`;\n row.label.fg = \"#FFFFFF\";\n row.spinner.start();\n\n const ok = combined\n ? await dumpAppend(url, combined, db)\n : await dumpOne(url, outLabel);\n\n row.spinner.stop();\n row.spinner.visible = false;\n\n if (ok) {\n row.label.content = `✓ ${db} → ${outLabel}`;\n row.label.fg = \"#66DD88\";\n } else {\n row.label.content = `✗ ${db} failed`;\n row.label.fg = \"#FF6666\";\n failed += 1;\n if (!combined) {\n try {\n unlinkSync(outLabel);\n } catch {\n /* ignore */\n }\n }\n }\n }\n\n if (combined) {\n await new Promise<void>((resolve, reject) => {\n combined.end(() => resolve());\n combined.on(\"error\", reject);\n });\n if (failed > 0) {\n try {\n unlinkSync(combinedFile!);\n } catch {\n /* ignore */\n }\n }\n }\n\n status.content =\n failed === 0\n ? combinedFile\n ? `Done — ${selected.length} database(s) → ${combinedFile}`\n : `Done — ${selected.length} database(s) dumped`\n : `Finished with ${failed} failure(s)`;\n status.fg = failed === 0 ? \"#66DD88\" : \"#FFAA66\";\n\n panel.add(\n new TextRenderable(renderer, {\n id: \"progress-done\",\n content: \"Press Enter to continue\",\n fg: \"#888888\",\n marginTop: 1,\n }),\n );\n\n await new Promise<void>((resolve) => {\n const onKey = (key: KeyEvent) => {\n if (key.name === \"return\" || key.name === \"enter\" || key.name === \"q\") {\n renderer.keyInput.off(\"keypress\", onKey);\n resolve();\n }\n };\n renderer.keyInput.on(\"keypress\", onKey);\n });\n\n return failed;\n}\n\nexport async function runDumpFlow(renderer: CliRenderer): Promise<FlowResult> {\n requireBin(\"psql\");\n requireBin(\"pg_dump\");\n\n let dbUrl: string;\n try {\n dbUrl = await promptDbUrl(renderer, \"pgsqlio · dump\");\n } catch {\n return \"back\";\n }\n\n const content = clearContent(renderer);\n const loading = new BoxRenderable(renderer, {\n id: \"loading\",\n flexDirection: \"row\",\n alignItems: \"center\",\n padding: 1,\n flexGrow: 1,\n });\n const spin = new SpinnerRenderable(renderer, {\n id: \"loading-spin\",\n name: \"dots\",\n color: \"#88CCFF\",\n });\n loading.add(spin);\n loading.add(\n new TextRenderable(renderer, {\n id: \"loading-text\",\n content: \" Fetching database list…\",\n fg: \"#CCCCCC\",\n marginLeft: 1,\n }),\n );\n content.add(loading);\n\n const listed = listDatabases(dbUrl);\n spin.stop();\n const databases = listed.databases;\n\n if (!listed.ok || databases.length === 0) {\n const failContent = clearContent(renderer);\n failContent.add(\n new TextRenderable(renderer, {\n content: listed.ok\n ? \"❌ No databases found\"\n : `❌ Connection failed: ${listed.error ?? \"unknown error\"}`,\n fg: \"#FF6666\",\n padding: 1,\n }),\n );\n await new Promise((r) => setTimeout(r, 2000));\n return \"fail\";\n }\n\n let selected: string[] | undefined;\n let outputMode: OutputMode = \"separate\";\n\n while (!selected) {\n const mode = await promptSelect(renderer, {\n title: \"pgsqlio · dump\",\n message: \"What do you want to dump?\",\n allowBack: true,\n choices: [\n {\n name: `All databases (${databases.length})`,\n description: \"Dump every non-template database\",\n value: \"all\",\n },\n {\n name: \"Select databases\",\n description: \"Pick databases with checkboxes\",\n value: \"select\",\n },\n ],\n });\n\n if (mode === BACK) return \"back\";\n\n if (mode === \"all\") {\n selected = [...databases];\n } else {\n const picks = await promptMultiSelect(renderer, {\n title: \"select databases\",\n items: databases,\n });\n if (picks === BACK) continue;\n if (picks.length === 0) continue;\n selected = picks;\n }\n\n if (selected.length > 1) {\n const fileMode = await promptSelect(renderer, {\n title: \"pgsqlio · dump\",\n message: `How should ${selected.length} databases be written?`,\n allowBack: true,\n choices: [\n {\n name: \"Separate files\",\n description: \"One backup_<db>_….sql file per database\",\n value: \"separate\" as const,\n },\n {\n name: \"Single file\",\n description: \"One backup_combined_….sql with \\\\connect between DBs\",\n value: \"single\" as const,\n },\n ],\n });\n\n if (fileMode === BACK) {\n selected = undefined;\n continue;\n }\n outputMode = fileMode;\n } else {\n outputMode = \"separate\";\n }\n }\n\n const failed = await runProgress(renderer, dbUrl, selected, outputMode);\n return failed === 0 ? \"ok\" : \"fail\";\n}\n","import { spawnSync } from \"node:child_process\";\n\nexport function requireBin(name: string): void {\n const result = spawnSync(name, [\"--version\"], { encoding: \"utf8\" });\n if (result.error || result.status !== 0) {\n console.error(`❌ Missing required binary: ${name} (install PostgreSQL client tools)`);\n process.exit(1);\n }\n}\n\nexport function run(\n cmd: string,\n args: string[],\n options?: { input?: string },\n): { ok: boolean; output: string } {\n const result = spawnSync(cmd, args, {\n encoding: \"utf8\",\n input: options?.input,\n stdio: options?.input !== undefined ? [\"pipe\", \"pipe\", \"pipe\"] : [\"ignore\", \"pipe\", \"pipe\"],\n });\n const output = [result.stdout, result.stderr].filter(Boolean).join(\"\\n\").trim();\n return { ok: result.status === 0, output };\n}\n\nexport function quoteIdent(name: string): string {\n return `\"${name.replace(/\"/g, '\"\"')}\"`;\n}\n\n/** Create database if it does not exist (connect via adminUrl, usually …/postgres). */\nexport function ensureDatabase(adminUrl: string, name: string): { ok: boolean; output: string } {\n const lit = name.replace(/'/g, \"''\");\n const { ok: checkOk, stdout } = runCapture(\"psql\", [\n adminUrl,\n \"-Atc\",\n `SELECT 1 FROM pg_database WHERE datname = '${lit}'`,\n ]);\n if (checkOk && stdout.trim() === \"1\") {\n return { ok: true, output: \"\" };\n }\n return run(\"psql\", [adminUrl, \"-v\", \"ON_ERROR_STOP=1\", \"-c\", `CREATE DATABASE ${quoteIdent(name)}`]);\n}\n\n/** Reset public schema so a plain dump can restore without \"already exists\" errors. */\nexport function wipePublicSchema(dbUrl: string): { ok: boolean; output: string } {\n return run(\"psql\", [\n dbUrl,\n \"-v\",\n \"ON_ERROR_STOP=1\",\n \"-c\",\n \"DROP SCHEMA IF EXISTS public CASCADE; CREATE SCHEMA public; GRANT ALL ON SCHEMA public TO public; GRANT ALL ON SCHEMA public TO CURRENT_USER;\",\n ]);\n}\n\n/** Parse \\\\connect targets from a dump (for combined backups). */\nexport function extractConnectTargets(sql: string): string[] {\n const found = new Set<string>();\n for (const m of sql.matchAll(/\\\\connect\\s+\"((?:\\\\.|[^\"\\\\])*)\"/gi)) {\n found.add(m[1]!.replace(/\"\"/g, '\"'));\n }\n for (const m of sql.matchAll(/\\\\connect\\s+([^\\s\"]+)/gi)) {\n const name = m[1]!.replace(/;$/, \"\");\n if (name.toLowerCase() !== \"postgres\") found.add(name);\n }\n found.delete(\"postgres\");\n return [...found];\n}\n\nexport function runCapture(\n cmd: string,\n args: string[],\n): { ok: boolean; stdout: string; stderr: string } {\n const result = spawnSync(cmd, args, { encoding: \"utf8\" });\n return {\n ok: result.status === 0,\n stdout: (result.stdout ?? \"\").trim(),\n stderr: (result.stderr ?? \"\").trim(),\n };\n}\n\n/** Ensure URL has a database path (defaults to postgres). */\nexport function normalizeConnUrl(url: string): string {\n const [base, query] = url.split(\"?\");\n let next = base ?? url;\n if (/^postgres(?:ql)?:\\/\\/[^/]+$/i.test(next)) {\n next = `${next}/postgres`;\n } else if (/^postgres(?:ql)?:\\/\\/[^/]+\\/$/i.test(next)) {\n next = `${next}postgres`;\n }\n return query !== undefined ? `${next}?${query}` : next;\n}\n\nexport function replaceDbInUrl(url: string, newDb: string): string {\n const normalized = normalizeConnUrl(url);\n const [base, query] = normalized.split(\"?\");\n const match = (base ?? normalized).match(/^(postgres(?:ql)?:\\/\\/[^/]+)/i);\n const authority = match?.[1] ?? (base ?? normalized).replace(/\\/[^/]*$/, \"\");\n const replaced = `${authority}/${newDb}`;\n return query !== undefined ? `${replaced}?${query}` : replaced;\n}\n\nexport function timestamp(): string {\n const d = new Date();\n const pad = (n: number) => String(n).padStart(2, \"0\");\n return (\n `${d.getFullYear()}${pad(d.getMonth() + 1)}${pad(d.getDate())}_` +\n `${pad(d.getHours())}${pad(d.getMinutes())}${pad(d.getSeconds())}`\n );\n}\n\nexport function listDatabases(dbUrl: string): { ok: boolean; databases: string[]; error?: string } {\n const url = normalizeConnUrl(dbUrl);\n const { ok, stdout, stderr } = runCapture(\"psql\", [\n url,\n \"-Atc\",\n \"SELECT datname FROM pg_database WHERE datistemplate = false ORDER BY datname;\",\n ]);\n if (!ok) {\n return { ok: false, databases: [], error: stderr || stdout || \"Connection failed\" };\n }\n return { ok: true, databases: stdout.split(\"\\n\").filter(Boolean) };\n}\n\nexport function listTables(\n dbUrl: string,\n schema = \"public\",\n): { ok: boolean; tables: string[]; error?: string } {\n const url = normalizeConnUrl(dbUrl);\n const lit = schema.replace(/'/g, \"''\");\n const { ok, stdout, stderr } = runCapture(\"psql\", [\n url,\n \"-Atc\",\n `SELECT tablename FROM pg_tables WHERE schemaname = '${lit}' ORDER BY tablename;`,\n ]);\n if (!ok) {\n return { ok: false, tables: [], error: stderr || stdout || \"Connection failed\" };\n }\n return { ok: true, tables: stdout.split(\"\\n\").filter(Boolean) };\n}\n\nexport function dropTables(\n dbUrl: string,\n tables: string[],\n schema = \"public\",\n): { ok: boolean; output: string } {\n if (tables.length === 0) return { ok: true, output: \"\" };\n const qualified = tables\n .map((t) => `${quoteIdent(schema)}.${quoteIdent(t)}`)\n .join(\", \");\n return run(\"psql\", [\n dbUrl,\n \"-v\",\n \"ON_ERROR_STOP=1\",\n \"-c\",\n `DROP TABLE IF EXISTS ${qualified} CASCADE;`,\n ]);\n}\n\n/** Drop databases (connects via /postgres). Terminates backends when possible. */\nexport function dropDatabases(\n connUrl: string,\n names: string[],\n): { ok: boolean; output: string } {\n const adminUrl = replaceDbInUrl(normalizeConnUrl(connUrl), \"postgres\");\n const logs: string[] = [];\n\n for (const name of names) {\n if (name === \"postgres\" || name.startsWith(\"template\")) {\n return {\n ok: false,\n output: `Refusing to drop protected database: ${name}`,\n };\n }\n\n const lit = name.replace(/'/g, \"''\");\n // Kick connections so DROP DATABASE can proceed\n const terminate = run(\"psql\", [\n adminUrl,\n \"-c\",\n `SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = '${lit}' AND pid <> pg_backend_pid();`,\n ]);\n if (terminate.output) logs.push(terminate.output);\n\n const dropped = run(\"psql\", [\n adminUrl,\n \"-v\",\n \"ON_ERROR_STOP=1\",\n \"-c\",\n `DROP DATABASE IF EXISTS ${quoteIdent(name)};`,\n ]);\n if (dropped.output) logs.push(dropped.output);\n if (!dropped.ok) {\n return {\n ok: false,\n output: [`Failed dropping \"${name}\"`, ...logs].filter(Boolean).join(\"\\n\"),\n };\n }\n }\n\n return { ok: true, output: logs.join(\"\\n\").trim() };\n}\n\nexport function printUsage(bin = \"pgsqlio\"): void {\n console.log(`Usage:\n ${bin}\n\nInteractive menu: Dump · Restore · Cleanup · Drop databases`);\n}\n","import {\n BoxRenderable,\n InputRenderable,\n InputRenderableEvents,\n SelectRenderable,\n SelectRenderableEvents,\n TextRenderable,\n type CliRenderer,\n type KeyEvent,\n type SelectOption,\n} from \"@opentui/core\";\nimport { SpinnerRenderable } from \"opentui-spinner\";\nimport { getContent } from \"./branding.js\";\nimport { normalizeConnUrl } from \"../utils.js\";\n\nexport type FlowResult = \"ok\" | \"fail\" | \"back\";\nexport const BACK = \"__back__\" as const;\n\n/** Clear only the content region — sticky branding stays. */\nexport function clearContent(renderer: CliRenderer): BoxRenderable {\n const content = getContent(renderer);\n for (const child of content.getChildren()) {\n content.remove(child.id);\n }\n return content;\n}\n\n/** @deprecated use clearContent — kept as alias for call sites */\nexport const clearRoot = clearContent;\n\nexport function looksLikePgUrl(value: string): boolean {\n return /^(postgres(ql)?:\\/\\/)/i.test(value.trim());\n}\n\n/** Checkbox multi-select with ← Back. Returns BACK or selected item ids. */\nexport function promptMultiSelect(\n renderer: CliRenderer,\n options: {\n title: string;\n items: string[];\n emptyMessage?: string;\n },\n): Promise<string[] | typeof BACK> {\n return new Promise((resolve) => {\n const { items } = options;\n if (items.length === 0) {\n resolve([]);\n return;\n }\n\n const content = clearContent(renderer);\n const choices = [BACK, ...items];\n const checked = new Set<string>();\n let cursor = 1;\n\n const panel = new BoxRenderable(renderer, {\n id: \"pick-panel\",\n border: true,\n borderColor: \"#555555\",\n title: ` ${options.title} `,\n flexDirection: \"column\",\n padding: 1,\n width: \"100%\",\n flexGrow: 1,\n });\n\n panel.add(\n new TextRenderable(renderer, {\n id: \"pick-hint\",\n content: \"Space toggle · a all · Enter confirm · Esc back\",\n fg: \"#888888\",\n marginBottom: 1,\n }),\n );\n\n const list = new BoxRenderable(renderer, {\n id: \"pick-list\",\n flexDirection: \"column\",\n flexGrow: 1,\n width: \"100%\",\n });\n\n const lines: TextRenderable[] = choices.map((_, i) => {\n const line = new TextRenderable(renderer, {\n id: `pick-line-${i}`,\n content: \"\",\n fg: \"#CCCCCC\",\n });\n list.add(line);\n return line;\n });\n\n panel.add(list);\n content.add(panel);\n\n const redraw = () => {\n for (let i = 0; i < choices.length; i++) {\n const id = choices[i]!;\n const isBack = id === BACK;\n const marker = isBack ? \" \" : checked.has(id) ? \"[x]\" : \"[ ]\";\n const pointer = i === cursor ? \"▶\" : \" \";\n const label = isBack ? \"← Back\" : id;\n lines[i]!.content = `${pointer} ${marker} ${label}`;\n lines[i]!.fg = i === cursor ? \"#FFFF66\" : isBack ? \"#88AADD\" : \"#CCCCCC\";\n }\n };\n\n redraw();\n\n const onKey = (key: KeyEvent) => {\n if (key.name === \"up\" || key.name === \"k\") {\n cursor = Math.max(0, cursor - 1);\n redraw();\n return;\n }\n if (key.name === \"down\" || key.name === \"j\") {\n cursor = Math.min(choices.length - 1, cursor + 1);\n redraw();\n return;\n }\n if (key.name === \"space\") {\n const id = choices[cursor]!;\n if (id === BACK) {\n cleanup();\n resolve(BACK);\n return;\n }\n if (checked.has(id)) checked.delete(id);\n else checked.add(id);\n redraw();\n return;\n }\n if (key.name === \"a\") {\n if (checked.size === items.length) checked.clear();\n else items.forEach((item) => checked.add(item));\n redraw();\n return;\n }\n if (key.name === \"escape\") {\n cleanup();\n resolve(BACK);\n return;\n }\n if (key.name === \"return\" || key.name === \"enter\") {\n const id = choices[cursor]!;\n if (id === BACK) {\n cleanup();\n resolve(BACK);\n return;\n }\n if (checked.size === 0) return;\n cleanup();\n resolve([...checked]);\n }\n };\n\n const cleanup = () => {\n renderer.keyInput.off(\"keypress\", onKey);\n };\n\n renderer.keyInput.on(\"keypress\", onKey);\n });\n}\n\nexport function promptSelect<T extends string>(\n renderer: CliRenderer,\n options: {\n title: string;\n message: string;\n hint?: string;\n choices: Array<{ name: string; description: string; value: T }>;\n allowBack?: boolean;\n },\n): Promise<T | typeof BACK> {\n return new Promise((resolve, reject) => {\n const content = clearContent(renderer);\n\n const panel = new BoxRenderable(renderer, {\n id: \"select-panel\",\n border: true,\n borderColor: \"#555555\",\n title: ` ${options.title} `,\n flexDirection: \"column\",\n padding: 1,\n width: \"100%\",\n flexGrow: 1,\n });\n\n panel.add(\n new TextRenderable(renderer, {\n id: \"select-message\",\n content: options.message,\n fg: \"#FFFFFF\",\n marginBottom: 1,\n }),\n );\n\n const menu = new SelectRenderable(renderer, {\n id: \"select-menu\",\n width: \"100%\",\n height: Math.max(4, options.choices.length * 2 + 1),\n showDescription: true,\n showScrollIndicator: false,\n options: options.choices.map(\n (c): SelectOption => ({\n name: c.name,\n description: c.description,\n value: c.value,\n }),\n ),\n });\n\n panel.add(menu);\n panel.add(\n new TextRenderable(renderer, {\n id: \"select-hint\",\n content:\n options.hint ??\n (options.allowBack\n ? \"↑/↓ move · Enter confirm · Esc back\"\n : \"↑/↓ move · Enter confirm · Ctrl+C quit\"),\n fg: \"#888888\",\n marginTop: 1,\n }),\n );\n\n content.add(panel);\n menu.focus();\n\n const onSelect = (_index: number, option: SelectOption) => {\n cleanup();\n resolve(option.value as T);\n };\n\n const onKey = (key: KeyEvent) => {\n if (key.name === \"escape\") {\n cleanup();\n if (options.allowBack) resolve(BACK);\n else reject(new Error(\"cancelled\"));\n }\n };\n\n const cleanup = () => {\n menu.off(SelectRenderableEvents.ITEM_SELECTED, onSelect);\n renderer.keyInput.off(\"keypress\", onKey);\n };\n\n menu.on(SelectRenderableEvents.ITEM_SELECTED, onSelect);\n renderer.keyInput.on(\"keypress\", onKey);\n });\n}\n\nexport function promptText(\n renderer: CliRenderer,\n options: {\n title: string;\n label: string;\n placeholder?: string;\n initial?: string;\n hint?: string;\n maxLength?: number;\n validate?: (value: string) => string | null;\n },\n): Promise<string> {\n return new Promise((resolve, reject) => {\n const content = clearContent(renderer);\n\n const panel = new BoxRenderable(renderer, {\n id: \"text-panel\",\n border: true,\n borderColor: \"#555555\",\n title: ` ${options.title} `,\n flexDirection: \"column\",\n padding: 1,\n width: \"100%\",\n flexGrow: 1,\n gap: 1,\n });\n\n panel.add(\n new TextRenderable(renderer, {\n id: \"text-label\",\n content: options.label,\n fg: \"#FFFFFF\",\n }),\n );\n\n const error = new TextRenderable(renderer, {\n id: \"text-error\",\n content: \"\",\n fg: \"#FF6666\",\n height: 1,\n });\n\n const input = new InputRenderable(renderer, {\n id: \"text-input\",\n width: \"100%\",\n height: 1,\n placeholder: options.placeholder ?? \"\",\n value: options.initial?.trim() ?? \"\",\n maxLength: options.maxLength ?? 2000,\n backgroundColor: \"#1A1A1A\",\n focusedBackgroundColor: \"#2A2A2A\",\n textColor: \"#FFFFFF\",\n cursorColor: \"#88CCFF\",\n });\n\n panel.add(input);\n panel.add(error);\n panel.add(\n new TextRenderable(renderer, {\n id: \"text-hint\",\n content: options.hint ?? \"Enter confirm · Esc back\",\n fg: \"#888888\",\n }),\n );\n\n content.add(panel);\n input.focus();\n\n const submit = (value: string) => {\n const trimmed = value.trim();\n if (!trimmed) {\n error.content = \"Value is required\";\n return;\n }\n if (options.validate) {\n const msg = options.validate(trimmed);\n if (msg) {\n error.content = msg;\n return;\n }\n }\n cleanup();\n resolve(trimmed);\n };\n\n const onEnter = (value: string) => submit(value);\n const onKey = (key: KeyEvent) => {\n if (key.name === \"escape\") {\n cleanup();\n reject(new Error(\"cancelled\"));\n }\n };\n\n const cleanup = () => {\n input.off(InputRenderableEvents.ENTER, onEnter);\n renderer.keyInput.off(\"keypress\", onKey);\n };\n\n input.on(InputRenderableEvents.ENTER, onEnter);\n renderer.keyInput.on(\"keypress\", onKey);\n });\n}\n\nexport function promptDbUrl(\n renderer: CliRenderer,\n title: string,\n initial?: string,\n): Promise<string> {\n return promptText(renderer, {\n title,\n label: \"PostgreSQL connection URL\",\n placeholder: \"postgresql://user:password@host:5432\",\n initial,\n hint: \"Enter confirm · Esc back · database name optional (you can pick next)\",\n validate: (value) =>\n looksLikePgUrl(value)\n ? null\n : \"URL must start with postgresql:// or postgres://\",\n }).then(normalizeConnUrl);\n}\n\nexport async function waitForEnter(\n renderer: CliRenderer,\n message = \"Press Enter to continue\",\n): Promise<void> {\n const content = getContent(renderer);\n const hint = new TextRenderable(renderer, {\n id: `wait-enter-${Date.now()}`,\n content: message,\n fg: \"#888888\",\n marginTop: 1,\n });\n\n const children = content.getChildren();\n const last = children[children.length - 1];\n if (last) {\n try {\n (last as BoxRenderable).add(hint);\n } catch {\n content.add(hint);\n }\n } else {\n content.add(hint);\n }\n\n await new Promise<void>((resolve) => {\n const onKey = (key: KeyEvent) => {\n if (key.name === \"return\" || key.name === \"enter\" || key.name === \"q\") {\n renderer.keyInput.off(\"keypress\", onKey);\n resolve();\n }\n };\n renderer.keyInput.on(\"keypress\", onKey);\n });\n}\n\nexport async function showStatus(\n renderer: CliRenderer,\n options: {\n title: string;\n message: string;\n ok: boolean;\n detail?: string;\n },\n): Promise<void> {\n const content = clearContent(renderer);\n const panel = new BoxRenderable(renderer, {\n id: \"status-panel\",\n border: true,\n borderColor: \"#555555\",\n title: ` ${options.title} `,\n flexDirection: \"column\",\n padding: 1,\n width: \"100%\",\n flexGrow: 1,\n });\n panel.add(\n new TextRenderable(renderer, {\n content: options.message,\n fg: options.ok ? \"#66DD88\" : \"#FF6666\",\n marginBottom: options.detail ? 1 : 0,\n }),\n );\n if (options.detail) {\n const lines = options.detail.split(\"\\n\").filter(Boolean).slice(-8);\n for (const [i, line] of lines.entries()) {\n panel.add(\n new TextRenderable(renderer, {\n id: `status-detail-${i}`,\n content: line.length > 100 ? `${line.slice(0, 97)}…` : line,\n fg: \"#A1A1AA\",\n }),\n );\n }\n }\n content.add(panel);\n await waitForEnter(renderer);\n}\n\nexport async function withSpinner<T>(\n renderer: CliRenderer,\n title: string,\n label: string,\n work: () => Promise<T> | T,\n): Promise<T> {\n const content = clearContent(renderer);\n const panel = new BoxRenderable(renderer, {\n id: \"spinner-panel\",\n border: true,\n borderColor: \"#555555\",\n title: ` ${title} `,\n flexDirection: \"row\",\n alignItems: \"center\",\n padding: 1,\n width: \"100%\",\n flexGrow: 1,\n });\n const spin = new SpinnerRenderable(renderer, {\n id: \"work-spin\",\n name: \"dots\",\n color: \"#88CCFF\",\n });\n panel.add(spin);\n panel.add(\n new TextRenderable(renderer, {\n content: ` ${label}`,\n fg: \"#CCCCCC\",\n marginLeft: 1,\n }),\n );\n content.add(panel);\n try {\n return await work();\n } finally {\n spin.stop();\n }\n}\n","import {\n ASCIIFontRenderable,\n BoxRenderable,\n TextRenderable,\n type CliRenderer,\n} from \"@opentui/core\";\n\nexport const SHELL_ID = \"app-shell\";\nexport const CONTENT_ID = \"app-content\";\nconst VERSION = \"0.1.0\";\n\nfunction createHeader(renderer: CliRenderer): BoxRenderable {\n const header = new BoxRenderable(renderer, {\n id: \"app-header\",\n width: \"100%\",\n flexDirection: \"column\",\n flexShrink: 0,\n });\n\n const logo = new BoxRenderable(renderer, {\n id: \"branding-logo\",\n position: \"relative\",\n marginBottom: 0,\n });\n\n logo.add(\n new ASCIIFontRenderable(renderer, {\n id: \"branding-shadow\",\n text: \"pgsqlio\",\n font: \"block\",\n color: \"#1A2228\",\n position: \"absolute\",\n left: 1,\n top: 1,\n zIndex: 0,\n }),\n );\n\n logo.add(\n new ASCIIFontRenderable(renderer, {\n id: \"branding-wordmark\",\n text: \"pgsqlio\",\n font: \"block\",\n color: [\"#2DD4BF\", \"#67E8F9\", \"#E0F2FE\"],\n zIndex: 1,\n }),\n );\n\n header.add(logo);\n header.add(\n new TextRenderable(renderer, {\n id: \"branding-tagline\",\n content: `PostgreSQL dump · restore · cleanup · v${VERSION}`,\n fg: \"#3F3F46\",\n marginTop: 1,\n }),\n );\n\n return header;\n}\n\n/** Mount sticky branding + content region. Call once at app start. */\nexport function mountShell(renderer: CliRenderer): BoxRenderable {\n for (const child of renderer.root.getChildren()) {\n renderer.root.remove(child.id);\n }\n\n const shell = new BoxRenderable(renderer, {\n id: SHELL_ID,\n width: \"100%\",\n height: \"100%\",\n flexDirection: \"column\",\n backgroundColor: \"#0C0C0C\",\n padding: 1,\n });\n\n shell.add(createHeader(renderer));\n\n const content = new BoxRenderable(renderer, {\n id: CONTENT_ID,\n width: \"100%\",\n flexGrow: 1,\n flexShrink: 1,\n flexDirection: \"column\",\n marginTop: 1,\n });\n shell.add(content);\n renderer.root.add(shell);\n return content;\n}\n\nexport function getContent(renderer: CliRenderer): BoxRenderable {\n const shell = renderer.root.getChildren().find((c) => c.id === SHELL_ID) as\n | BoxRenderable\n | undefined;\n if (!shell) {\n return mountShell(renderer);\n }\n const content = shell.getChildren().find((c) => c.id === CONTENT_ID) as\n | BoxRenderable\n | undefined;\n if (!content) {\n return mountShell(renderer);\n }\n return content;\n}\n","import { existsSync, readFileSync } from \"node:fs\";\nimport type { CliRenderer } from \"@opentui/core\";\nimport {\n ensureDatabase,\n extractConnectTargets,\n replaceDbInUrl,\n requireBin,\n run,\n wipePublicSchema,\n} from \"../utils.js\";\nimport {\n promptDbUrl,\n promptSelect,\n promptText,\n showStatus,\n withSpinner,\n BACK,\n type FlowResult,\n} from \"../tui/shared.js\";\n\nexport async function runRestoreFlow(renderer: CliRenderer): Promise<FlowResult> {\n requireBin(\"psql\");\n\n let dbUrl: string;\n try {\n dbUrl = await promptDbUrl(renderer, \"pgsqlio · restore\");\n } catch {\n return \"back\";\n }\n\n let file: string;\n try {\n file = await promptText(renderer, {\n title: \"pgsqlio · restore\",\n label: \"SQL backup file path\",\n placeholder: \"backup_mydb_20250924_121530.sql\",\n validate: (value) =>\n existsSync(value) ? null : \"File not found — check the path\",\n });\n } catch {\n return \"back\";\n }\n\n let sql = \"\";\n try {\n sql = readFileSync(file, \"utf8\");\n } catch {\n await showStatus(renderer, {\n title: \"pgsqlio · restore\",\n message: \"❌ Could not read backup file\",\n ok: false,\n });\n return \"fail\";\n }\n\n const targets = extractConnectTargets(sql);\n const isCombined = targets.length > 0;\n const adminUrl = replaceDbInUrl(dbUrl, \"postgres\");\n\n const confirm = await promptSelect(renderer, {\n title: \"pgsqlio · restore\",\n message: isCombined\n ? `Restore ${targets.length} database(s) from ${file}?`\n : `Restore into this database from ${file}?`,\n allowBack: true,\n choices: [\n {\n name: \"Wipe schemas, then restore\",\n description:\n \"DROP SCHEMA public CASCADE on target DB(s) first (fixes “already exists”)\",\n value: \"wipe\",\n },\n {\n name: \"Restore as-is\",\n description: \"Do not wipe — fails if objects already exist\",\n value: \"asis\",\n },\n { name: \"Cancel\", description: \"Return to main menu\", value: \"no\" },\n ],\n });\n\n if (confirm === BACK || confirm === \"no\") return \"back\";\n const wipeFirst = confirm === \"wipe\";\n\n const result = await withSpinner(\n renderer,\n \"pgsqlio · restore\",\n wipeFirst ? \"Wiping + restoring…\" : \"Restoring…\",\n () => {\n const logs: string[] = [];\n\n if (isCombined) {\n for (const db of targets) {\n const created = ensureDatabase(adminUrl, db);\n if (!created.ok) {\n return {\n ok: false,\n output: [`Failed creating database \"${db}\"`, created.output]\n .filter(Boolean)\n .join(\"\\n\"),\n };\n }\n if (created.output) logs.push(created.output);\n\n if (wipeFirst) {\n const wiped = wipePublicSchema(replaceDbInUrl(adminUrl, db));\n if (!wiped.ok) {\n return {\n ok: false,\n output: [`Failed wiping database \"${db}\"`, wiped.output]\n .filter(Boolean)\n .join(\"\\n\"),\n };\n }\n if (wiped.output) logs.push(wiped.output);\n }\n }\n } else if (wipeFirst) {\n const wiped = wipePublicSchema(dbUrl);\n if (!wiped.ok) {\n return {\n ok: false,\n output: [\"Failed wiping target database\", wiped.output]\n .filter(Boolean)\n .join(\"\\n\"),\n };\n }\n if (wiped.output) logs.push(wiped.output);\n }\n\n // Combined dumps switch DB via \\\\connect — start on postgres so meta-commands work\n const restoreUrl = isCombined ? adminUrl : dbUrl;\n const restored = run(\"psql\", [\n restoreUrl,\n \"-v\",\n \"ON_ERROR_STOP=1\",\n \"-f\",\n file,\n ]);\n if (restored.output) logs.push(restored.output);\n return {\n ok: restored.ok,\n output: logs.join(\"\\n\").trim(),\n };\n },\n );\n\n await showStatus(renderer, {\n title: \"pgsqlio · restore\",\n message: result.ok ? \"✅ Restore successful\" : \"❌ Restore failed\",\n ok: result.ok,\n detail: result.ok ? undefined : result.output || undefined,\n });\n\n return result.ok ? \"ok\" : \"fail\";\n}\n","import type { CliRenderer } from \"@opentui/core\";\nimport { requireBin, run } from \"../utils.js\";\nimport {\n BACK,\n promptDbUrl,\n promptSelect,\n showStatus,\n withSpinner,\n type FlowResult,\n} from \"../tui/shared.js\";\n\nexport async function runCleanupFlow(renderer: CliRenderer): Promise<FlowResult> {\n requireBin(\"psql\");\n\n let dbUrl: string;\n try {\n dbUrl = await promptDbUrl(renderer, \"pgsqlio · cleanup\");\n } catch {\n return \"back\";\n }\n\n const confirm = await promptSelect(renderer, {\n title: \"pgsqlio · cleanup\",\n message: \"Drop and recreate the public schema? This is destructive.\",\n allowBack: true,\n choices: [\n {\n name: \"Yes, clean database\",\n description: \"DROP SCHEMA public CASCADE; CREATE SCHEMA public;\",\n value: \"yes\",\n },\n { name: \"Cancel\", description: \"Return to main menu\", value: \"no\" },\n ],\n });\n\n if (confirm === BACK || confirm === \"no\") return \"back\";\n\n const result = await withSpinner(renderer, \"pgsqlio · cleanup\", \"Cleaning…\", () =>\n run(\"psql\", [\n dbUrl,\n \"-v\",\n \"ON_ERROR_STOP=1\",\n \"-c\",\n \"DROP SCHEMA public CASCADE; CREATE SCHEMA public;\",\n ]),\n );\n\n await showStatus(renderer, {\n title: \"pgsqlio · cleanup\",\n message: result.ok\n ? \"✅ Cleanup successful (database is empty)\"\n : \"❌ Cleanup failed\",\n ok: result.ok,\n detail: result.ok ? undefined : result.output || undefined,\n });\n\n return result.ok ? \"ok\" : \"fail\";\n}\n","import {\n BoxRenderable,\n TextRenderable,\n type CliRenderer,\n} from \"@opentui/core\";\nimport { SpinnerRenderable } from \"opentui-spinner\";\nimport {\n dropDatabases,\n listDatabases,\n normalizeConnUrl,\n requireBin,\n} from \"../utils.js\";\nimport {\n BACK,\n clearContent,\n promptDbUrl,\n promptMultiSelect,\n promptSelect,\n showStatus,\n withSpinner,\n type FlowResult,\n} from \"../tui/shared.js\";\n\nexport async function runDropDatabasesFlow(\n renderer: CliRenderer,\n): Promise<FlowResult> {\n requireBin(\"psql\");\n\n let connUrl: string;\n try {\n connUrl = await promptDbUrl(renderer, \"pgsqlio · drop databases\");\n } catch {\n return \"back\";\n }\n connUrl = normalizeConnUrl(connUrl);\n\n const loadingWrap = clearContent(renderer);\n const spin = new SpinnerRenderable(renderer, {\n id: \"db-spin\",\n name: \"dots\",\n color: \"#88CCFF\",\n });\n const loading = new BoxRenderable(renderer, {\n id: \"db-loading\",\n flexDirection: \"row\",\n alignItems: \"center\",\n padding: 1,\n flexGrow: 1,\n });\n loading.add(spin);\n loading.add(\n new TextRenderable(renderer, {\n content: \" Fetching databases…\",\n fg: \"#CCCCCC\",\n marginLeft: 1,\n }),\n );\n loadingWrap.add(loading);\n\n const listed = listDatabases(connUrl);\n spin.stop();\n\n if (!listed.ok) {\n await showStatus(renderer, {\n title: \"pgsqlio · drop databases\",\n message: \"❌ Could not list databases\",\n ok: false,\n detail: listed.error,\n });\n return \"fail\";\n }\n\n // Never offer to drop postgres / template DBs from this UI\n const droppable = listed.databases.filter(\n (name) => name !== \"postgres\" && !name.startsWith(\"template\"),\n );\n\n if (droppable.length === 0) {\n await showStatus(renderer, {\n title: \"pgsqlio · drop databases\",\n message: \"No droppable databases found\",\n ok: false,\n detail: \"postgres / template* are protected\",\n });\n return \"fail\";\n }\n\n const selected = await promptMultiSelect(renderer, {\n title: \"select databases to drop\",\n items: droppable,\n });\n\n if (selected === BACK) return \"back\";\n if (selected.length === 0) return \"back\";\n\n const confirm = await promptSelect(renderer, {\n title: \"pgsqlio · drop databases\",\n message: `Permanently drop ${selected.length} database(s)? This cannot be undone.`,\n allowBack: true,\n choices: [\n {\n name: \"Yes, drop selected databases\",\n description:\n selected.slice(0, 5).join(\", \") + (selected.length > 5 ? \"…\" : \"\"),\n value: \"yes\",\n },\n { name: \"Cancel\", description: \"Return to main menu\", value: \"no\" },\n ],\n });\n\n if (confirm === BACK || confirm === \"no\") return \"back\";\n\n const result = await withSpinner(\n renderer,\n \"pgsqlio · drop databases\",\n `Dropping ${selected.length} database(s)…`,\n () => dropDatabases(connUrl, selected),\n );\n\n await showStatus(renderer, {\n title: \"pgsqlio · drop databases\",\n message: result.ok\n ? `✅ Dropped ${selected.length} database(s)`\n : \"❌ Drop databases failed\",\n ok: result.ok,\n detail: result.ok ? selected.join(\", \") : result.output || undefined,\n });\n\n return result.ok ? \"ok\" : \"fail\";\n}\n"],"mappings":"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","names":["BoxRenderable","TextRenderable","SpinnerRenderable","BoxRenderable","TextRenderable","BoxRenderable","TextRenderable","quoteIdent","BoxRenderable","TextRenderable","SpinnerRenderable","BoxRenderable","TextRenderable","SpinnerRenderable","SpinnerRenderable","BoxRenderable","TextRenderable"]}
package/dist/bin.js ADDED
@@ -0,0 +1,44 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/bin.ts
4
+ import { spawn } from "child_process";
5
+ import { existsSync } from "fs";
6
+ import { dirname, join } from "path";
7
+ import { fileURLToPath } from "url";
8
+ var here = dirname(fileURLToPath(import.meta.url));
9
+ var cli = join(here, "cli.js");
10
+ var args = process.argv.slice(2);
11
+ function run(command, commandArgs) {
12
+ const child = spawn(command, commandArgs, {
13
+ stdio: "inherit",
14
+ env: process.env
15
+ });
16
+ child.on("exit", (code, signal) => {
17
+ if (signal) process.kill(process.pid, signal);
18
+ process.exit(code ?? 1);
19
+ });
20
+ }
21
+ if (!existsSync(cli)) {
22
+ console.error("pgsqlio: missing dist/cli.js \u2014 run npm run build");
23
+ process.exit(1);
24
+ }
25
+ var probe = spawn("bun", ["--version"], { stdio: "ignore" });
26
+ probe.on("error", () => {
27
+ console.error(`pgsqlio: Bun is required (Node ${process.versions.node} cannot load OpenTUI).
28
+
29
+ Install Bun: https://bun.sh
30
+ Then run: bun dist/cli.js`);
31
+ process.exit(1);
32
+ });
33
+ probe.on("exit", (code) => {
34
+ if (code === 0) {
35
+ run("bun", [cli, ...args]);
36
+ return;
37
+ }
38
+ console.error(`pgsqlio: Bun is required (Node ${process.versions.node} cannot load OpenTUI).
39
+
40
+ Install Bun: https://bun.sh
41
+ Then run: bun dist/cli.js`);
42
+ process.exit(1);
43
+ });
44
+ //# sourceMappingURL=bin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/bin.ts"],"sourcesContent":["import { spawn } from \"node:child_process\";\nimport { existsSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\nconst here = dirname(fileURLToPath(import.meta.url));\nconst cli = join(here, \"cli.js\");\nconst args = process.argv.slice(2);\n\nfunction run(command: string, commandArgs: string[]): void {\n const child = spawn(command, commandArgs, {\n stdio: \"inherit\",\n env: process.env,\n });\n child.on(\"exit\", (code, signal) => {\n if (signal) process.kill(process.pid, signal);\n process.exit(code ?? 1);\n });\n}\n\nif (!existsSync(cli)) {\n console.error(\"pgsqlio: missing dist/cli.js — run npm run build\");\n process.exit(1);\n}\n\nconst probe = spawn(\"bun\", [\"--version\"], { stdio: \"ignore\" });\n\nprobe.on(\"error\", () => {\n console.error(`pgsqlio: Bun is required (Node ${process.versions.node} cannot load OpenTUI).\n\nInstall Bun: https://bun.sh\nThen run: bun dist/cli.js`);\n process.exit(1);\n});\n\nprobe.on(\"exit\", (code) => {\n if (code === 0) {\n run(\"bun\", [cli, ...args]);\n return;\n }\n console.error(`pgsqlio: Bun is required (Node ${process.versions.node} cannot load OpenTUI).\n\nInstall Bun: https://bun.sh\nThen run: bun dist/cli.js`);\n process.exit(1);\n});\n"],"mappings":";;;AAAA,SAAS,aAAa;AACtB,SAAS,kBAAkB;AAC3B,SAAS,SAAS,YAAY;AAC9B,SAAS,qBAAqB;AAE9B,IAAM,OAAO,QAAQ,cAAc,YAAY,GAAG,CAAC;AACnD,IAAM,MAAM,KAAK,MAAM,QAAQ;AAC/B,IAAM,OAAO,QAAQ,KAAK,MAAM,CAAC;AAEjC,SAAS,IAAI,SAAiB,aAA6B;AACzD,QAAM,QAAQ,MAAM,SAAS,aAAa;AAAA,IACxC,OAAO;AAAA,IACP,KAAK,QAAQ;AAAA,EACf,CAAC;AACD,QAAM,GAAG,QAAQ,CAAC,MAAM,WAAW;AACjC,QAAI,OAAQ,SAAQ,KAAK,QAAQ,KAAK,MAAM;AAC5C,YAAQ,KAAK,QAAQ,CAAC;AAAA,EACxB,CAAC;AACH;AAEA,IAAI,CAAC,WAAW,GAAG,GAAG;AACpB,UAAQ,MAAM,uDAAkD;AAChE,UAAQ,KAAK,CAAC;AAChB;AAEA,IAAM,QAAQ,MAAM,OAAO,CAAC,WAAW,GAAG,EAAE,OAAO,SAAS,CAAC;AAE7D,MAAM,GAAG,SAAS,MAAM;AACtB,UAAQ,MAAM,kCAAkC,QAAQ,SAAS,IAAI;AAAA;AAAA;AAAA,0BAG7C;AACxB,UAAQ,KAAK,CAAC;AAChB,CAAC;AAED,MAAM,GAAG,QAAQ,CAAC,SAAS;AACzB,MAAI,SAAS,GAAG;AACd,QAAI,OAAO,CAAC,KAAK,GAAG,IAAI,CAAC;AACzB;AAAA,EACF;AACA,UAAQ,MAAM,kCAAkC,QAAQ,SAAS,IAAI;AAAA;AAAA;AAAA,0BAG7C;AACxB,UAAQ,KAAK,CAAC;AAChB,CAAC;","names":[]}
package/dist/cli.js ADDED
@@ -0,0 +1,32 @@
1
+ // src/cli.ts
2
+ function isBun() {
3
+ return typeof globalThis.Bun !== "undefined";
4
+ }
5
+ function nodeSupportsFfi() {
6
+ const major = Number(process.versions.node?.split(".")[0] ?? 0);
7
+ return major >= 26;
8
+ }
9
+ function printRuntimeHelp() {
10
+ console.error(`pgsqlio needs Bun (\u22651.3) or Node.js (\u226526.4 with --experimental-ffi).
11
+
12
+ You're on Node ${process.versions.node ?? "unknown"}.
13
+
14
+ Run with Bun instead:
15
+ bun dist/cli.js
16
+ bun src/cli.ts
17
+
18
+ Install Bun: https://bun.sh`);
19
+ }
20
+ async function main() {
21
+ if (!isBun() && !nodeSupportsFfi()) {
22
+ printRuntimeHelp();
23
+ process.exit(1);
24
+ }
25
+ const { runApp } = await import("./app-74TNCHFZ.js");
26
+ await runApp();
27
+ }
28
+ main().catch((err) => {
29
+ console.error(err);
30
+ process.exit(1);
31
+ });
32
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cli.ts"],"sourcesContent":["function isBun(): boolean {\n return typeof (globalThis as { Bun?: unknown }).Bun !== \"undefined\";\n}\n\nfunction nodeSupportsFfi(): boolean {\n const major = Number(process.versions.node?.split(\".\")[0] ?? 0);\n // OpenTUI requires Node >= 26.4 with node:ffi\n return major >= 26;\n}\n\nfunction printRuntimeHelp(): void {\n console.error(`pgsqlio needs Bun (≥1.3) or Node.js (≥26.4 with --experimental-ffi).\n\nYou're on Node ${process.versions.node ?? \"unknown\"}.\n\nRun with Bun instead:\n bun dist/cli.js\n bun src/cli.ts\n\nInstall Bun: https://bun.sh`);\n}\n\nasync function main(): Promise<void> {\n if (!isBun() && !nodeSupportsFfi()) {\n printRuntimeHelp();\n process.exit(1);\n }\n\n const { runApp } = await import(\"./app.js\");\n await runApp();\n}\n\nmain().catch((err: unknown) => {\n console.error(err);\n process.exit(1);\n});\n"],"mappings":";AAAA,SAAS,QAAiB;AACxB,SAAO,OAAQ,WAAiC,QAAQ;AAC1D;AAEA,SAAS,kBAA2B;AAClC,QAAM,QAAQ,OAAO,QAAQ,SAAS,MAAM,MAAM,GAAG,EAAE,CAAC,KAAK,CAAC;AAE9D,SAAO,SAAS;AAClB;AAEA,SAAS,mBAAyB;AAChC,UAAQ,MAAM;AAAA;AAAA,iBAEC,QAAQ,SAAS,QAAQ,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,4BAMvB;AAC5B;AAEA,eAAe,OAAsB;AACnC,MAAI,CAAC,MAAM,KAAK,CAAC,gBAAgB,GAAG;AAClC,qBAAiB;AACjB,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,EAAE,OAAO,IAAI,MAAM,OAAO,mBAAU;AAC1C,QAAM,OAAO;AACf;AAEA,KAAK,EAAE,MAAM,CAAC,QAAiB;AAC7B,UAAQ,MAAM,GAAG;AACjB,UAAQ,KAAK,CAAC;AAChB,CAAC;","names":[]}
package/package.json ADDED
@@ -0,0 +1,47 @@
1
+ {
2
+ "name": "pgsqlio",
3
+ "version": "0.1.0",
4
+ "description": "Interactive PostgreSQL dump, restore, and cleanup CLI (OpenTUI)",
5
+ "type": "module",
6
+ "bin": {
7
+ "pgsqlio": "./dist/bin.js"
8
+ },
9
+ "files": [
10
+ "dist"
11
+ ],
12
+ "scripts": {
13
+ "build": "tsup",
14
+ "dev": "bun src/cli.ts",
15
+ "start": "bun dist/cli.js",
16
+ "prepublishOnly": "npm run build"
17
+ },
18
+ "engines": {
19
+ "bun": ">=1.3.0",
20
+ "node": ">=26.4.0"
21
+ },
22
+ "keywords": [
23
+ "postgresql",
24
+ "postgres",
25
+ "pg_dump",
26
+ "backup",
27
+ "restore",
28
+ "cli",
29
+ "opentui",
30
+ "tui"
31
+ ],
32
+ "license": "MIT",
33
+ "repository": {
34
+ "type": "git",
35
+ "url": "git+https://github.com/kingRayhan/pg_backup.git"
36
+ },
37
+ "devDependencies": {
38
+ "@types/node": "^22.20.2",
39
+ "tsup": "^8.4.0",
40
+ "tsx": "^4.19.3",
41
+ "typescript": "^5.8.2"
42
+ },
43
+ "dependencies": {
44
+ "@opentui/core": "0.3.4",
45
+ "opentui-spinner": "0.0.7"
46
+ }
47
+ }
package/readme.md ADDED
@@ -0,0 +1,30 @@
1
+ # pgsqlio
2
+
3
+ Interactive PostgreSQL dump, restore, and cleanup CLI — built with [OpenTUI](https://opentui.com).
4
+
5
+ Requires:
6
+ - [Bun](https://bun.sh) ≥ 1.3 (recommended) **or** Node.js ≥ 26.4 with `--experimental-ffi`
7
+ - [PostgreSQL client tools](https://www.postgresql.org/download/) (`pg_dump`, `psql`)
8
+
9
+ ## Run
10
+
11
+ ```bash
12
+ bun dist/cli.js
13
+ # or during development:
14
+ bun src/cli.ts
15
+ ```
16
+
17
+ Do **not** use `node dist/cli.js` on Node &lt; 26 — OpenTUI needs Bun (or Node ≥ 26.4 with FFI).
18
+
19
+ Opens a menu:
20
+
21
+ 1. **Dump** — enter URL → all or select databases → progress spinners
22
+ 2. **Restore** — enter URL + `.sql` path → confirm
23
+ 3. **Cleanup** — enter URL → confirm destructive reset
24
+ 4. **Quit**
25
+
26
+ ## Notes
27
+
28
+ - The DB user needs sufficient privileges.
29
+ - Dump backups are written as `backup_<dbname>_YYYYMMDD_HHMMSS.sql`.
30
+ - Esc goes back; Ctrl+C quits.