pgsqlio 0.1.2 → 0.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.
@@ -3,7 +3,7 @@ import { createCliRenderer } from "@opentui/core";
3
3
 
4
4
  // src/commands/dump.ts
5
5
  import { createWriteStream, unlinkSync } from "fs";
6
- import { spawn } from "child_process";
6
+ import { spawn as spawn2 } from "child_process";
7
7
  import { finished, pipeline } from "stream/promises";
8
8
  import {
9
9
  BoxRenderable as BoxRenderable3,
@@ -12,7 +12,7 @@ import {
12
12
  import { SpinnerRenderable as SpinnerRenderable2 } from "opentui-spinner";
13
13
 
14
14
  // src/utils.ts
15
- import { spawnSync } from "child_process";
15
+ import { spawn, spawnSync } from "child_process";
16
16
  function requireBin(name) {
17
17
  const result = spawnSync(name, ["--version"], { encoding: "utf8" });
18
18
  if (result.error || result.status !== 0) {
@@ -73,6 +73,25 @@ function runCapture(cmd, args) {
73
73
  stderr: (result.stderr ?? "").trim()
74
74
  };
75
75
  }
76
+ function runCaptureAsync(cmd, args) {
77
+ return new Promise((resolve) => {
78
+ const child = spawn(cmd, args, { stdio: ["ignore", "pipe", "pipe"] });
79
+ const out = [];
80
+ const err = [];
81
+ child.stdout.on("data", (chunk) => out.push(chunk));
82
+ child.stderr.on("data", (chunk) => err.push(chunk));
83
+ child.on("error", (error) => {
84
+ resolve({ ok: false, stdout: "", stderr: error.message });
85
+ });
86
+ child.on("close", (code) => {
87
+ resolve({
88
+ ok: code === 0,
89
+ stdout: Buffer.concat(out).toString("utf8").trim(),
90
+ stderr: Buffer.concat(err).toString("utf8").trim()
91
+ });
92
+ });
93
+ });
94
+ }
76
95
  function normalizeConnUrl(url) {
77
96
  const [base, query] = url.split("?");
78
97
  let next = base ?? url;
@@ -96,9 +115,21 @@ function timestamp() {
96
115
  const pad = (n) => String(n).padStart(2, "0");
97
116
  return `${d.getFullYear()}${pad(d.getMonth() + 1)}${pad(d.getDate())}_${pad(d.getHours())}${pad(d.getMinutes())}${pad(d.getSeconds())}`;
98
117
  }
99
- function listDatabases(dbUrl) {
118
+ async function pingConnection(dbUrl) {
100
119
  const url = normalizeConnUrl(dbUrl);
101
- const { ok, stdout, stderr } = runCapture("psql", [
120
+ const { ok, stdout, stderr } = await runCaptureAsync("psql", [
121
+ url,
122
+ "-Atc",
123
+ "SELECT 1"
124
+ ]);
125
+ if (!ok) {
126
+ return { ok: false, error: stderr || stdout || "Connection failed" };
127
+ }
128
+ return { ok: true };
129
+ }
130
+ async function listDatabases(dbUrl) {
131
+ const url = normalizeConnUrl(dbUrl);
132
+ const { ok, stdout, stderr } = await runCaptureAsync("psql", [
102
133
  url,
103
134
  "-Atc",
104
135
  "SELECT datname FROM pg_database WHERE datistemplate = false ORDER BY datname;"
@@ -431,6 +462,9 @@ function promptSelect(renderer, options) {
431
462
  renderer.keyInput.on("keypress", onKey);
432
463
  });
433
464
  }
465
+ function yieldFrame() {
466
+ return new Promise((resolve) => setImmediate(resolve));
467
+ }
434
468
  function promptText(renderer, options) {
435
469
  return new Promise((resolve, reject) => {
436
470
  const content = clearContent(renderer);
@@ -452,26 +486,47 @@ function promptText(renderer, options) {
452
486
  fg: "#FFFFFF"
453
487
  })
454
488
  );
455
- const error = new TextRenderable2(renderer, {
456
- id: "text-error",
457
- content: "",
458
- fg: "#FF6666",
459
- height: 1
489
+ const inputFrame = new BoxRenderable2(renderer, {
490
+ id: "text-input-frame",
491
+ width: "100%",
492
+ padding: 1,
493
+ backgroundColor: "#2A2A2A"
460
494
  });
461
495
  const input = new InputRenderable(renderer, {
462
496
  id: "text-input",
463
497
  width: "100%",
464
- height: 1,
465
498
  placeholder: options.placeholder ?? "",
466
499
  value: options.initial?.trim() ?? "",
467
500
  maxLength: options.maxLength ?? 2e3,
468
- backgroundColor: "#1A1A1A",
501
+ backgroundColor: "#2A2A2A",
469
502
  focusedBackgroundColor: "#2A2A2A",
470
503
  textColor: "#FFFFFF",
471
504
  cursorColor: "#88CCFF"
472
505
  });
473
- panel.add(input);
474
- panel.add(error);
506
+ inputFrame.add(input);
507
+ const statusRow = new BoxRenderable2(renderer, {
508
+ id: "text-status-row",
509
+ flexDirection: "row",
510
+ alignItems: "center",
511
+ width: "100%",
512
+ height: 1
513
+ });
514
+ const spin = new SpinnerRenderable(renderer, {
515
+ name: "dots",
516
+ color: "#88CCFF",
517
+ autoplay: false
518
+ });
519
+ spin.visible = false;
520
+ const statusText = new TextRenderable2(renderer, {
521
+ id: "text-status",
522
+ content: "",
523
+ fg: "#FF6666",
524
+ marginLeft: 1
525
+ });
526
+ statusRow.add(spin);
527
+ statusRow.add(statusText);
528
+ panel.add(inputFrame);
529
+ panel.add(statusRow);
475
530
  panel.add(
476
531
  new TextRenderable2(renderer, {
477
532
  id: "text-hint",
@@ -481,23 +536,68 @@ function promptText(renderer, options) {
481
536
  );
482
537
  content.add(panel);
483
538
  input.focus();
484
- const submit = (value) => {
539
+ let busy = false;
540
+ let cancelled = false;
541
+ const setBusy = (on, message = "") => {
542
+ busy = on;
543
+ if (on) {
544
+ spin.visible = true;
545
+ spin.start();
546
+ statusText.content = message;
547
+ statusText.fg = "#CCCCCC";
548
+ } else {
549
+ spin.stop();
550
+ spin.visible = false;
551
+ statusText.content = "";
552
+ }
553
+ };
554
+ const setError = (msg) => {
555
+ spin.stop();
556
+ spin.visible = false;
557
+ busy = false;
558
+ statusText.content = msg;
559
+ statusText.fg = "#FF6666";
560
+ input.focus();
561
+ };
562
+ const submit = async (value) => {
563
+ if (busy) return;
485
564
  const trimmed = value.trim();
486
565
  if (!trimmed) {
487
- error.content = "Value is required";
566
+ setError("Value is required");
488
567
  return;
489
568
  }
490
569
  if (options.validate) {
491
570
  const msg = options.validate(trimmed);
492
571
  if (msg) {
493
- error.content = msg;
572
+ setError(msg);
573
+ return;
574
+ }
575
+ }
576
+ if (options.verify) {
577
+ setBusy(true, options.busyMessage ?? "Working\u2026");
578
+ input.blur();
579
+ await yieldFrame();
580
+ try {
581
+ const msg = await options.verify(trimmed);
582
+ if (cancelled) return;
583
+ if (msg) {
584
+ setError(msg);
585
+ return;
586
+ }
587
+ } catch (err) {
588
+ if (cancelled) return;
589
+ setError(err instanceof Error ? err.message : "Failed");
494
590
  return;
495
591
  }
592
+ if (cancelled) return;
593
+ setBusy(false);
496
594
  }
497
595
  cleanup();
498
596
  resolve(trimmed);
499
597
  };
500
- const onEnter = (value) => submit(value);
598
+ const onEnter = (value) => {
599
+ void submit(value);
600
+ };
501
601
  const onKey = (key) => {
502
602
  if (key.name === "escape") {
503
603
  cleanup();
@@ -505,6 +605,9 @@ function promptText(renderer, options) {
505
605
  }
506
606
  };
507
607
  const cleanup = () => {
608
+ cancelled = true;
609
+ busy = false;
610
+ spin.stop();
508
611
  input.off(InputRenderableEvents.ENTER, onEnter);
509
612
  renderer.keyInput.off("keypress", onKey);
510
613
  };
@@ -512,16 +615,40 @@ function promptText(renderer, options) {
512
615
  renderer.keyInput.on("keypress", onKey);
513
616
  });
514
617
  }
515
- function promptDbUrl(renderer, title, initial) {
618
+ function promptDbUrl(renderer, title, options) {
619
+ const verify = options?.verify ?? (async (url) => {
620
+ const ping = await pingConnection(url);
621
+ return ping.ok ? null : ping.error ?? "Connection failed";
622
+ });
516
623
  return promptText(renderer, {
517
624
  title,
518
625
  label: "PostgreSQL connection URL",
519
626
  placeholder: "postgresql://user:password@host:5432",
520
- initial,
627
+ initial: options?.initial,
521
628
  hint: "Enter confirm \xB7 Esc back \xB7 database name optional (you can pick next)",
522
- validate: (value) => looksLikePgUrl(value) ? null : "URL must start with postgresql:// or postgres://"
629
+ busyMessage: options?.busyMessage ?? "Connecting\u2026",
630
+ validate: (value) => looksLikePgUrl(value) ? null : "URL must start with postgresql:// or postgres://",
631
+ verify: (value) => verify(normalizeConnUrl(value))
523
632
  }).then(normalizeConnUrl);
524
633
  }
634
+ async function promptDbUrlAndDatabases(renderer, title, options) {
635
+ let databases = [];
636
+ const url = await promptDbUrl(renderer, title, {
637
+ initial: options?.initial,
638
+ busyMessage: "Fetching databases\u2026",
639
+ verify: async (value) => {
640
+ const listed = await listDatabases(value);
641
+ if (!listed.ok) return listed.error ?? "Connection failed";
642
+ const next = options?.filter ? listed.databases.filter(options.filter) : listed.databases;
643
+ if (next.length === 0) {
644
+ return options?.emptyMessage ?? "No databases found";
645
+ }
646
+ databases = next;
647
+ return null;
648
+ }
649
+ });
650
+ return { url, databases };
651
+ }
525
652
  async function waitForEnter(renderer, message = "Press Enter to continue") {
526
653
  const content = getContent(renderer);
527
654
  const hint = new TextRenderable2(renderer, {
@@ -612,6 +739,7 @@ async function withSpinner(renderer, title, label, work) {
612
739
  })
613
740
  );
614
741
  content.add(panel);
742
+ await yieldFrame();
615
743
  try {
616
744
  return await work();
617
745
  } finally {
@@ -632,7 +760,7 @@ async function waitExit(child) {
632
760
  return new Promise((resolve) => child.once("close", resolve));
633
761
  }
634
762
  async function dumpOne(url, outFile) {
635
- const child = spawn("pg_dump", [...PG_DUMP_ARGS, url], {
763
+ const child = spawn2("pg_dump", [...PG_DUMP_ARGS, url], {
636
764
  stdio: ["ignore", "pipe", "inherit"]
637
765
  });
638
766
  const out = createWriteStream(outFile);
@@ -657,7 +785,7 @@ WHERE NOT EXISTS (SELECT FROM pg_database WHERE datname = ${sqlString(dbName)})\
657
785
  await new Promise((resolve, reject) => {
658
786
  out.write(header, (err) => err ? reject(err) : resolve());
659
787
  });
660
- const child = spawn("pg_dump", [...PG_DUMP_ARGS, url], {
788
+ const child = spawn2("pg_dump", [...PG_DUMP_ARGS, url], {
661
789
  stdio: ["ignore", "pipe", "inherit"]
662
790
  });
663
791
  child.stdout.pipe(out, { end: false });
@@ -790,49 +918,14 @@ async function runDumpFlow(renderer) {
790
918
  requireBin("psql");
791
919
  requireBin("pg_dump");
792
920
  let dbUrl;
921
+ let databases;
793
922
  try {
794
- dbUrl = await promptDbUrl(renderer, "pgsqlio \xB7 dump");
923
+ const listed = await promptDbUrlAndDatabases(renderer, "pgsqlio \xB7 dump");
924
+ dbUrl = listed.url;
925
+ databases = listed.databases;
795
926
  } catch {
796
927
  return "back";
797
928
  }
798
- const content = clearContent(renderer);
799
- const loading = new BoxRenderable3(renderer, {
800
- id: "loading",
801
- flexDirection: "row",
802
- alignItems: "center",
803
- padding: 1,
804
- flexGrow: 1
805
- });
806
- const spin = new SpinnerRenderable2(renderer, {
807
- id: "loading-spin",
808
- name: "dots",
809
- color: "#88CCFF"
810
- });
811
- loading.add(spin);
812
- loading.add(
813
- new TextRenderable3(renderer, {
814
- id: "loading-text",
815
- content: " Fetching database list\u2026",
816
- fg: "#CCCCCC",
817
- marginLeft: 1
818
- })
819
- );
820
- content.add(loading);
821
- const listed = listDatabases(dbUrl);
822
- spin.stop();
823
- const databases = listed.databases;
824
- if (!listed.ok || databases.length === 0) {
825
- const failContent = clearContent(renderer);
826
- failContent.add(
827
- new TextRenderable3(renderer, {
828
- content: listed.ok ? "\u274C No databases found" : `\u274C Connection failed: ${listed.error ?? "unknown error"}`,
829
- fg: "#FF6666",
830
- padding: 1
831
- })
832
- );
833
- await new Promise((r) => setTimeout(r, 2e3));
834
- return "fail";
835
- }
836
929
  let selected;
837
930
  let outputMode = "separate";
838
931
  while (!selected) {
@@ -1057,65 +1150,24 @@ async function runCleanupFlow(renderer) {
1057
1150
  }
1058
1151
 
1059
1152
  // src/commands/drop-databases.ts
1060
- import {
1061
- BoxRenderable as BoxRenderable4,
1062
- TextRenderable as TextRenderable4
1063
- } from "@opentui/core";
1064
- import { SpinnerRenderable as SpinnerRenderable3 } from "opentui-spinner";
1065
1153
  async function runDropDatabasesFlow(renderer) {
1066
1154
  requireBin("psql");
1067
1155
  let connUrl;
1156
+ let droppable;
1068
1157
  try {
1069
- connUrl = await promptDbUrl(renderer, "pgsqlio \xB7 drop databases");
1158
+ const listed = await promptDbUrlAndDatabases(
1159
+ renderer,
1160
+ "pgsqlio \xB7 drop databases",
1161
+ {
1162
+ filter: (name) => name !== "postgres" && !name.startsWith("template"),
1163
+ emptyMessage: "No droppable databases found (postgres / template* are protected)"
1164
+ }
1165
+ );
1166
+ connUrl = listed.url;
1167
+ droppable = listed.databases;
1070
1168
  } catch {
1071
1169
  return "back";
1072
1170
  }
1073
- connUrl = normalizeConnUrl(connUrl);
1074
- const loadingWrap = clearContent(renderer);
1075
- const spin = new SpinnerRenderable3(renderer, {
1076
- id: "db-spin",
1077
- name: "dots",
1078
- color: "#88CCFF"
1079
- });
1080
- const loading = new BoxRenderable4(renderer, {
1081
- id: "db-loading",
1082
- flexDirection: "row",
1083
- alignItems: "center",
1084
- padding: 1,
1085
- flexGrow: 1
1086
- });
1087
- loading.add(spin);
1088
- loading.add(
1089
- new TextRenderable4(renderer, {
1090
- content: " Fetching databases\u2026",
1091
- fg: "#CCCCCC",
1092
- marginLeft: 1
1093
- })
1094
- );
1095
- loadingWrap.add(loading);
1096
- const listed = listDatabases(connUrl);
1097
- spin.stop();
1098
- if (!listed.ok) {
1099
- await showStatus(renderer, {
1100
- title: "pgsqlio \xB7 drop databases",
1101
- message: "\u274C Could not list databases",
1102
- ok: false,
1103
- detail: listed.error
1104
- });
1105
- return "fail";
1106
- }
1107
- const droppable = listed.databases.filter(
1108
- (name) => name !== "postgres" && !name.startsWith("template")
1109
- );
1110
- if (droppable.length === 0) {
1111
- await showStatus(renderer, {
1112
- title: "pgsqlio \xB7 drop databases",
1113
- message: "No droppable databases found",
1114
- ok: false,
1115
- detail: "postgres / template* are protected"
1116
- });
1117
- return "fail";
1118
- }
1119
1171
  const selected = await promptMultiSelect(renderer, {
1120
1172
  title: "select databases to drop",
1121
1173
  items: droppable
@@ -1196,7 +1248,8 @@ async function runApp() {
1196
1248
  if (action === "dump") await runDumpFlow(renderer);
1197
1249
  else if (action === "restore") await runRestoreFlow(renderer);
1198
1250
  else if (action === "cleanup") await runCleanupFlow(renderer);
1199
- else if (action === "drop-databases") await runDropDatabasesFlow(renderer);
1251
+ else if (action === "drop-databases")
1252
+ await runDropDatabasesFlow(renderer);
1200
1253
  }
1201
1254
  } finally {
1202
1255
  renderer.destroy();
@@ -1205,4 +1258,4 @@ async function runApp() {
1205
1258
  export {
1206
1259
  runApp
1207
1260
  };
1208
- //# sourceMappingURL=app-74TNCHFZ.js.map
1261
+ //# sourceMappingURL=app-YGK2LA7L.js.map
@@ -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\")\n 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 { replaceDbInUrl, requireBin, timestamp } from \"../utils.js\";\nimport {\n BACK,\n clearContent,\n promptDbUrlAndDatabases,\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 let databases: string[];\n try {\n const listed = await promptDbUrlAndDatabases(renderer, \"pgsqlio · dump\");\n dbUrl = listed.url;\n databases = listed.databases;\n } catch {\n return \"back\";\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 { spawn, 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\nexport function runCaptureAsync(\n cmd: string,\n args: string[],\n): Promise<{ ok: boolean; stdout: string; stderr: string }> {\n return new Promise((resolve) => {\n const child = spawn(cmd, args, { stdio: [\"ignore\", \"pipe\", \"pipe\"] });\n const out: Buffer[] = [];\n const err: Buffer[] = [];\n child.stdout.on(\"data\", (chunk: Buffer) => out.push(chunk));\n child.stderr.on(\"data\", (chunk: Buffer) => err.push(chunk));\n child.on(\"error\", (error) => {\n resolve({ ok: false, stdout: \"\", stderr: error.message });\n });\n child.on(\"close\", (code) => {\n resolve({\n ok: code === 0,\n stdout: Buffer.concat(out).toString(\"utf8\").trim(),\n stderr: Buffer.concat(err).toString(\"utf8\").trim(),\n });\n });\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 async function pingConnection(\n dbUrl: string,\n): Promise<{ ok: boolean; error?: string }> {\n const url = normalizeConnUrl(dbUrl);\n const { ok, stdout, stderr } = await runCaptureAsync(\"psql\", [\n url,\n \"-Atc\",\n \"SELECT 1\",\n ]);\n if (!ok) {\n return { ok: false, error: stderr || stdout || \"Connection failed\" };\n }\n return { ok: true };\n}\n\nexport async function listDatabases(\n dbUrl: string,\n): Promise<{ ok: boolean; databases: string[]; error?: string }> {\n const url = normalizeConnUrl(dbUrl);\n const { ok, stdout, stderr } = await runCaptureAsync(\"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 { listDatabases, normalizeConnUrl, pingConnection } 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\nfunction yieldFrame(): Promise<void> {\n return new Promise((resolve) => setImmediate(resolve));\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 busyMessage?: string;\n validate?: (value: string) => string | null;\n verify?: (value: string) => Promise<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 inputFrame = new BoxRenderable(renderer, {\n id: \"text-input-frame\",\n width: \"100%\",\n padding: 1,\n backgroundColor: \"#2A2A2A\",\n });\n\n const input = new InputRenderable(renderer, {\n id: \"text-input\",\n width: \"100%\",\n placeholder: options.placeholder ?? \"\",\n value: options.initial?.trim() ?? \"\",\n maxLength: options.maxLength ?? 2000,\n backgroundColor: \"#2A2A2A\",\n focusedBackgroundColor: \"#2A2A2A\",\n textColor: \"#FFFFFF\",\n cursorColor: \"#88CCFF\",\n });\n\n inputFrame.add(input);\n\n const statusRow = new BoxRenderable(renderer, {\n id: \"text-status-row\",\n flexDirection: \"row\",\n alignItems: \"center\",\n width: \"100%\",\n height: 1,\n });\n const spin = new SpinnerRenderable(renderer, {\n name: \"dots\",\n color: \"#88CCFF\",\n autoplay: false,\n });\n spin.visible = false;\n const statusText = new TextRenderable(renderer, {\n id: \"text-status\",\n content: \"\",\n fg: \"#FF6666\",\n marginLeft: 1,\n });\n statusRow.add(spin);\n statusRow.add(statusText);\n\n panel.add(inputFrame);\n panel.add(statusRow);\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 let busy = false;\n let cancelled = false;\n\n const setBusy = (on: boolean, message = \"\") => {\n busy = on;\n if (on) {\n spin.visible = true;\n spin.start();\n statusText.content = message;\n statusText.fg = \"#CCCCCC\";\n } else {\n spin.stop();\n spin.visible = false;\n statusText.content = \"\";\n }\n };\n\n const setError = (msg: string) => {\n spin.stop();\n spin.visible = false;\n busy = false;\n statusText.content = msg;\n statusText.fg = \"#FF6666\";\n input.focus();\n };\n\n const submit = async (value: string) => {\n if (busy) return;\n const trimmed = value.trim();\n if (!trimmed) {\n setError(\"Value is required\");\n return;\n }\n if (options.validate) {\n const msg = options.validate(trimmed);\n if (msg) {\n setError(msg);\n return;\n }\n }\n if (options.verify) {\n setBusy(true, options.busyMessage ?? \"Working…\");\n input.blur();\n await yieldFrame();\n try {\n const msg = await options.verify(trimmed);\n if (cancelled) return;\n if (msg) {\n setError(msg);\n return;\n }\n } catch (err) {\n if (cancelled) return;\n setError(err instanceof Error ? err.message : \"Failed\");\n return;\n }\n if (cancelled) return;\n setBusy(false);\n }\n cleanup();\n resolve(trimmed);\n };\n\n const onEnter = (value: string) => {\n void submit(value);\n };\n const onKey = (key: KeyEvent) => {\n if (key.name === \"escape\") {\n cleanup();\n reject(new Error(\"cancelled\"));\n }\n };\n\n const cleanup = () => {\n cancelled = true;\n busy = false;\n spin.stop();\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 options?: {\n initial?: string;\n busyMessage?: string;\n verify?: (url: string) => Promise<string | null>;\n },\n): Promise<string> {\n const verify =\n options?.verify ??\n (async (url: string) => {\n const ping = await pingConnection(url);\n return ping.ok ? null : ping.error ?? \"Connection failed\";\n });\n\n return promptText(renderer, {\n title,\n label: \"PostgreSQL connection URL\",\n placeholder: \"postgresql://user:password@host:5432\",\n initial: options?.initial,\n hint: \"Enter confirm · Esc back · database name optional (you can pick next)\",\n busyMessage: options?.busyMessage ?? \"Connecting…\",\n validate: (value) =>\n looksLikePgUrl(value)\n ? null\n : \"URL must start with postgresql:// or postgres://\",\n verify: (value) => verify(normalizeConnUrl(value)),\n }).then(normalizeConnUrl);\n}\n\n/** Prompt for a URL and fetch the database list on the same screen. */\nexport async function promptDbUrlAndDatabases(\n renderer: CliRenderer,\n title: string,\n options?: {\n initial?: string;\n emptyMessage?: string;\n filter?: (name: string) => boolean;\n },\n): Promise<{ url: string; databases: string[] }> {\n let databases: string[] = [];\n const url = await promptDbUrl(renderer, title, {\n initial: options?.initial,\n busyMessage: \"Fetching databases…\",\n verify: async (value) => {\n const listed = await listDatabases(value);\n if (!listed.ok) return listed.error ?? \"Connection failed\";\n const next = options?.filter\n ? listed.databases.filter(options.filter)\n : listed.databases;\n if (next.length === 0) {\n return options?.emptyMessage ?? \"No databases found\";\n }\n databases = next;\n return null;\n },\n });\n return { url, databases };\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 await yieldFrame();\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 type { CliRenderer } from \"@opentui/core\";\nimport { dropDatabases, requireBin } from \"../utils.js\";\nimport {\n BACK,\n promptDbUrlAndDatabases,\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 let droppable: string[];\n try {\n const listed = await promptDbUrlAndDatabases(\n renderer,\n \"pgsqlio · drop databases\",\n {\n filter: (name) => name !== \"postgres\" && !name.startsWith(\"template\"),\n emptyMessage:\n \"No droppable databases found (postgres / template* are protected)\",\n },\n );\n connUrl = listed.url;\n droppable = listed.databases;\n } catch {\n return \"back\";\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":["spawn","BoxRenderable","TextRenderable","SpinnerRenderable","BoxRenderable","TextRenderable","BoxRenderable","TextRenderable","quoteIdent","spawn","BoxRenderable","TextRenderable","SpinnerRenderable"]}
package/dist/cli.js CHANGED
@@ -22,7 +22,7 @@ async function main() {
22
22
  printRuntimeHelp();
23
23
  process.exit(1);
24
24
  }
25
- const { runApp } = await import("./app-74TNCHFZ.js");
25
+ const { runApp } = await import("./app-YGK2LA7L.js");
26
26
  await runApp();
27
27
  }
28
28
  main().catch((err) => {
package/dist/cli.js.map CHANGED
@@ -1 +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":[]}
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\");\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,mBAAO;AACvC,QAAM,OAAO;AACf;AAEA,KAAK,EAAE,MAAM,CAAC,QAAiB;AAC7B,UAAQ,MAAM,GAAG;AACjB,UAAQ,KAAK,CAAC;AAChB,CAAC;","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pgsqlio",
3
- "version": "0.1.2",
3
+ "version": "0.2.0",
4
4
  "description": "Interactive PostgreSQL dump, restore, and cleanup CLI (OpenTUI)",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1 +0,0 @@
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"]}