@coffre/cli 0.1.13 → 0.1.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{connect-BXDkiWOL.js → connect-k2qEwjqZ.js} +1 -1
- package/dist/database-url-CJ9yqoi_.js +213 -0
- package/dist/deployment-S8za5nUu.js +1369 -0
- package/dist/init-mO8V087p.js +71 -0
- package/dist/{keys-BCKUEb-Y.js → keys-DDdMwm0N.js} +2 -352
- package/dist/main.js +121 -2
- package/dist/migrate-ERNNgYO0.js +163 -0
- package/dist/{setup-BMmFAR3N.js → setup-qGoLPykX.js} +17 -1810
- package/dist/templates/node/README.md +4 -2
- package/dist/templates/node/package.json +3 -3
- package/dist/templates/workers/README.md +4 -2
- package/dist/templates/workers/package.json +4 -4
- package/dist/tty-BDa14JX2.js +283 -0
- package/dist/update-DwGbG-eM.js +263 -0
- package/dist/version-CPk6zTTK.js +317 -0
- package/package.json +4 -4
- package/dist/{connect-tekAM4hC.js → connect-C4wevMJ5.js} +34 -34
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
import { d as style, g as yes, h as wrap, m as width, n as edit, p as truncate, s as readKeys, t as Cancelled } from "./tty-BDa14JX2.js";
|
|
2
|
+
import { readFileSync } from "node:fs";
|
|
3
|
+
//#region src/steps.ts
|
|
4
|
+
const SPINNER = [
|
|
5
|
+
"⠋",
|
|
6
|
+
"⠙",
|
|
7
|
+
"⠹",
|
|
8
|
+
"⠸",
|
|
9
|
+
"⠼",
|
|
10
|
+
"⠴",
|
|
11
|
+
"⠦",
|
|
12
|
+
"⠧",
|
|
13
|
+
"⠇",
|
|
14
|
+
"⠏"
|
|
15
|
+
];
|
|
16
|
+
const FRAME_MS = 80;
|
|
17
|
+
/** A step failed, and its line says why: nothing more to show. */
|
|
18
|
+
var StepFailed = class extends Error {};
|
|
19
|
+
var Steps = class {
|
|
20
|
+
#out;
|
|
21
|
+
#s;
|
|
22
|
+
#steps;
|
|
23
|
+
#keys;
|
|
24
|
+
#describe;
|
|
25
|
+
#frame = 0;
|
|
26
|
+
#drawn = 0;
|
|
27
|
+
/** The first step still redrawn: those above it have settled, and been printed for good. */
|
|
28
|
+
#from = 0;
|
|
29
|
+
#timer = null;
|
|
30
|
+
/** Whether the failed step has been printed, where nothing is redrawn. */
|
|
31
|
+
#reported = false;
|
|
32
|
+
#restoreCursor = () => this.#out.write("\x1B[?25h");
|
|
33
|
+
/** Ctrl-C while a step runs, the terminal in its usual mode: the cursor back, then out, with nothing more to show. */
|
|
34
|
+
#interrupt = () => {
|
|
35
|
+
this.#stop();
|
|
36
|
+
this.end();
|
|
37
|
+
this.#out.write("\n");
|
|
38
|
+
process.exit(130);
|
|
39
|
+
};
|
|
40
|
+
/**
|
|
41
|
+
* `titles`, what each step does while it runs; `keys`, the keyboard for
|
|
42
|
+
* questions; `describe`, an error as it may be shown, a secret it quotes
|
|
43
|
+
* taken out.
|
|
44
|
+
*/
|
|
45
|
+
constructor(out, titles, keys, describe) {
|
|
46
|
+
this.#out = out;
|
|
47
|
+
this.#s = style(out);
|
|
48
|
+
this.#keys = keys;
|
|
49
|
+
this.#describe = describe;
|
|
50
|
+
this.#steps = titles.map((text) => ({
|
|
51
|
+
text,
|
|
52
|
+
status: "pending",
|
|
53
|
+
details: [],
|
|
54
|
+
under: [],
|
|
55
|
+
input: null,
|
|
56
|
+
choice: null
|
|
57
|
+
}));
|
|
58
|
+
if (this.#s.ansi) {
|
|
59
|
+
out.write("\x1B[?25l");
|
|
60
|
+
process.once("exit", this.#restoreCursor);
|
|
61
|
+
process.on("SIGINT", this.#interrupt);
|
|
62
|
+
this.#draw();
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Run step `i`: its line is the outcome `work` returns, or the error it
|
|
67
|
+
* throws, which comes out as StepFailed, unless it is Cancelled.
|
|
68
|
+
*/
|
|
69
|
+
async run(i, work) {
|
|
70
|
+
const state = this.#steps[i];
|
|
71
|
+
state.status = "running";
|
|
72
|
+
this.#tick();
|
|
73
|
+
const step = {
|
|
74
|
+
note: (text) => {
|
|
75
|
+
state.text = text;
|
|
76
|
+
this.#draw();
|
|
77
|
+
},
|
|
78
|
+
under: (lines) => {
|
|
79
|
+
state.under = lines;
|
|
80
|
+
this.#draw();
|
|
81
|
+
},
|
|
82
|
+
ask: (question) => this.#ask(state, question),
|
|
83
|
+
choose: (question, options) => this.#choose(state, question, options),
|
|
84
|
+
paste: (prompt, accept, signal) => this.#paste(state, prompt, accept, signal)
|
|
85
|
+
};
|
|
86
|
+
try {
|
|
87
|
+
const outcome = await work(step);
|
|
88
|
+
Object.assign(state, typeof outcome === "string" ? {
|
|
89
|
+
text: outcome,
|
|
90
|
+
details: []
|
|
91
|
+
} : outcome, {
|
|
92
|
+
status: "done",
|
|
93
|
+
under: [],
|
|
94
|
+
input: null
|
|
95
|
+
});
|
|
96
|
+
} catch (error) {
|
|
97
|
+
Object.assign(state, {
|
|
98
|
+
under: [],
|
|
99
|
+
input: null,
|
|
100
|
+
choice: null
|
|
101
|
+
});
|
|
102
|
+
state.status = "failed";
|
|
103
|
+
state.details = error instanceof Cancelled ? [] : [this.#describe(error)];
|
|
104
|
+
if (error instanceof Cancelled) state.text = `${state.text}: cancelled`;
|
|
105
|
+
this.#stop();
|
|
106
|
+
throw error instanceof Cancelled ? error : new StepFailed(state.details[0]);
|
|
107
|
+
}
|
|
108
|
+
this.#stop();
|
|
109
|
+
if (!this.#s.ansi) this.#out.write(`✓ ${state.text}\n${state.details.map((line) => ` ${line}\n`).join("")}`);
|
|
110
|
+
else if (this.#steps.every((each) => each.status === "done")) this.end();
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Lines that stay, such as an address to open, printed whole, so that a
|
|
114
|
+
* terminal can wrap them, and they can be copied or clicked. They go
|
|
115
|
+
* under the steps done, above the one running: those done are printed
|
|
116
|
+
* for good first, and only the rest is redrawn from then on.
|
|
117
|
+
*/
|
|
118
|
+
print(text) {
|
|
119
|
+
if (!this.#s.ansi) {
|
|
120
|
+
this.#out.write(`${text}\n`);
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
const running = this.#steps.findIndex((each, i) => i >= this.#from && each.status !== "done");
|
|
124
|
+
const settled = this.#lines(this.#from, running === -1 ? this.#steps.length : running);
|
|
125
|
+
this.#from = running === -1 ? this.#steps.length : running;
|
|
126
|
+
const up = this.#drawn > 0 ? `\x1b[${this.#drawn}F` : "";
|
|
127
|
+
this.#out.write(`${up}\x1b[J${settled.map((line) => `${line}\n`).join("")}${text}\n`);
|
|
128
|
+
this.#drawn = 0;
|
|
129
|
+
this.#draw();
|
|
130
|
+
}
|
|
131
|
+
/** A remark that stays, dim, wrapped to the terminal's width. */
|
|
132
|
+
aside(text) {
|
|
133
|
+
this.print(this.#s.dim(wrap(text, Math.max(20, (this.#out.columns ?? 80) - 3)).map((line) => ` ${line}`).join("\n")));
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* An address to open, after what it is for: on one line when both fit;
|
|
137
|
+
* otherwise the address on a line of its own, from the first column, so
|
|
138
|
+
* that the terminal wraps it in one piece, to copy whole.
|
|
139
|
+
*/
|
|
140
|
+
link(label, address) {
|
|
141
|
+
const fits = width(` ${label} ${address}`) < (this.#out.columns ?? 80);
|
|
142
|
+
this.print(fits ? ` ${this.#s.dim(label)} ${address}` : ` ${this.#s.dim(label)}\n${address}`);
|
|
143
|
+
}
|
|
144
|
+
/** Leave the list as it stands, the cursor below it. */
|
|
145
|
+
end() {
|
|
146
|
+
this.#stop();
|
|
147
|
+
this.#release();
|
|
148
|
+
}
|
|
149
|
+
/** The cursor back, and Ctrl-C the terminal's again. */
|
|
150
|
+
#release() {
|
|
151
|
+
if (this.#s.ansi) {
|
|
152
|
+
this.#restoreCursor();
|
|
153
|
+
process.off("exit", this.#restoreCursor);
|
|
154
|
+
process.off("SIGINT", this.#interrupt);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
async #ask(state, question) {
|
|
158
|
+
const keys = this.#keys();
|
|
159
|
+
if (keys === null) return false;
|
|
160
|
+
this.#stop();
|
|
161
|
+
const before = state.text;
|
|
162
|
+
state.text = `${question} ${this.#s.dim("y/N")}`;
|
|
163
|
+
state.status = "asking";
|
|
164
|
+
this.#draw();
|
|
165
|
+
try {
|
|
166
|
+
return await yes(keys);
|
|
167
|
+
} finally {
|
|
168
|
+
state.text = before;
|
|
169
|
+
state.status = "running";
|
|
170
|
+
this.#tick();
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
async #choose(state, question, options) {
|
|
174
|
+
const keys = this.#keys();
|
|
175
|
+
if (keys === null) return 0;
|
|
176
|
+
this.#stop();
|
|
177
|
+
const before = state.text;
|
|
178
|
+
Object.assign(state, {
|
|
179
|
+
text: `${question} ${this.#s.dim("↑↓ then Enter")}`,
|
|
180
|
+
status: "asking",
|
|
181
|
+
choice: {
|
|
182
|
+
options,
|
|
183
|
+
at: 0
|
|
184
|
+
}
|
|
185
|
+
});
|
|
186
|
+
let cancelled = false;
|
|
187
|
+
try {
|
|
188
|
+
const chosen = readKeys(keys, (key) => {
|
|
189
|
+
const choice = state.choice;
|
|
190
|
+
if (key.ctrl && key.name === "c") cancelled = true;
|
|
191
|
+
else if (key.name === "return" || key.name === "enter") return true;
|
|
192
|
+
else if (key.name === "up" || key.name === "k") choice.at = (choice.at - 1 + options.length) % options.length;
|
|
193
|
+
else if (key.name === "down" || key.name === "j" || key.name === "tab") choice.at = (choice.at + 1) % options.length;
|
|
194
|
+
if (cancelled) return true;
|
|
195
|
+
this.#draw();
|
|
196
|
+
return false;
|
|
197
|
+
});
|
|
198
|
+
this.#draw();
|
|
199
|
+
await chosen;
|
|
200
|
+
if (cancelled) throw new Cancelled();
|
|
201
|
+
return state.choice.at;
|
|
202
|
+
} finally {
|
|
203
|
+
Object.assign(state, {
|
|
204
|
+
text: before,
|
|
205
|
+
status: "running",
|
|
206
|
+
choice: null
|
|
207
|
+
});
|
|
208
|
+
this.#tick();
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
async #paste(state, prompt, accept, signal) {
|
|
212
|
+
const keys = this.#keys();
|
|
213
|
+
if (keys === null) {
|
|
214
|
+
await new Promise((resolve) => signal.aborted ? resolve() : signal.addEventListener("abort", () => resolve()));
|
|
215
|
+
return null;
|
|
216
|
+
}
|
|
217
|
+
state.input = {
|
|
218
|
+
prompt,
|
|
219
|
+
typed: "",
|
|
220
|
+
error: null
|
|
221
|
+
};
|
|
222
|
+
let taken = null;
|
|
223
|
+
let cancelled = false;
|
|
224
|
+
try {
|
|
225
|
+
const pasted = readKeys(keys, async (key, sequence) => {
|
|
226
|
+
const input = state.input;
|
|
227
|
+
const next = edit(input.typed, key, sequence);
|
|
228
|
+
if (next === "cancel") cancelled = true;
|
|
229
|
+
else if (next === "submit" && input.typed.trim() !== "") {
|
|
230
|
+
const text = input.typed.trim();
|
|
231
|
+
input.typed = "";
|
|
232
|
+
input.error = await accept(text);
|
|
233
|
+
if (input.error === null) taken = text;
|
|
234
|
+
} else if (next !== "submit") {
|
|
235
|
+
input.typed = next;
|
|
236
|
+
input.error = null;
|
|
237
|
+
}
|
|
238
|
+
this.#draw();
|
|
239
|
+
return cancelled || taken !== null;
|
|
240
|
+
}, signal);
|
|
241
|
+
this.#draw();
|
|
242
|
+
await pasted;
|
|
243
|
+
} finally {
|
|
244
|
+
state.input = null;
|
|
245
|
+
this.#draw();
|
|
246
|
+
}
|
|
247
|
+
if (cancelled) throw new Cancelled();
|
|
248
|
+
return taken;
|
|
249
|
+
}
|
|
250
|
+
#tick() {
|
|
251
|
+
this.#draw();
|
|
252
|
+
if (this.#s.ansi && this.#timer === null) this.#timer = setInterval(() => {
|
|
253
|
+
this.#frame = (this.#frame + 1) % SPINNER.length;
|
|
254
|
+
this.#draw();
|
|
255
|
+
}, FRAME_MS).unref();
|
|
256
|
+
}
|
|
257
|
+
#stop() {
|
|
258
|
+
if (this.#timer !== null) clearInterval(this.#timer);
|
|
259
|
+
this.#timer = null;
|
|
260
|
+
this.#draw();
|
|
261
|
+
if (!this.#s.ansi) {
|
|
262
|
+
const failed = this.#steps.find((each) => each.status === "failed");
|
|
263
|
+
if (failed !== void 0 && !this.#reported) {
|
|
264
|
+
this.#reported = true;
|
|
265
|
+
this.#out.write(`✗ ${failed.text}\n${failed.details.map((line) => ` ${line}\n`).join("")}`);
|
|
266
|
+
}
|
|
267
|
+
} else if (this.#steps.some((each) => each.status === "failed")) this.#release();
|
|
268
|
+
}
|
|
269
|
+
#draw() {
|
|
270
|
+
if (!this.#s.ansi) return;
|
|
271
|
+
const lines = this.#lines(this.#from, this.#steps.length);
|
|
272
|
+
const up = this.#drawn > 0 ? `\x1b[${this.#drawn}F` : "";
|
|
273
|
+
this.#out.write(`${up}${lines.map((line) => `${line}\x1b[K\n`).join("")}\x1b[J`);
|
|
274
|
+
this.#drawn = lines.length;
|
|
275
|
+
}
|
|
276
|
+
/** Steps `from` to `to`, as lines cut to the terminal's width. */
|
|
277
|
+
#lines(from, to) {
|
|
278
|
+
const s = this.#s;
|
|
279
|
+
const columns = Math.max(20, (this.#out.columns ?? 80) - 1);
|
|
280
|
+
return this.#steps.slice(from, to).flatMap(({ text, status, details, under, input, choice }) => {
|
|
281
|
+
const icon = {
|
|
282
|
+
pending: s.dim("·"),
|
|
283
|
+
running: s.accent(SPINNER[this.#frame]),
|
|
284
|
+
asking: s.accent("?"),
|
|
285
|
+
done: s.green("✓"),
|
|
286
|
+
failed: s.red("✗")
|
|
287
|
+
}[status];
|
|
288
|
+
const line = status === "pending" ? s.dim(text) : status === "failed" ? s.red(text) : text;
|
|
289
|
+
const below = status === "failed" ? details.flatMap((detail) => wrap(detail, columns - 4)) : details.map(s.dim);
|
|
290
|
+
const live = [
|
|
291
|
+
...under.map((text) => ` ${s.dim(text)}`),
|
|
292
|
+
...choice === null ? [] : choice.options.map((option, i) => i === choice.at ? ` ${s.accent("›")} ${s.accent(option)}` : ` ${option}`),
|
|
293
|
+
...input === null ? [] : [
|
|
294
|
+
` ${input.prompt}`,
|
|
295
|
+
` ${s.accent("›")} ${"•".repeat(input.typed.length)}`,
|
|
296
|
+
...input.error === null ? [] : [` ${s.red(input.error)}`]
|
|
297
|
+
]
|
|
298
|
+
];
|
|
299
|
+
return [
|
|
300
|
+
` ${icon} ${line}`,
|
|
301
|
+
...below.map((detail) => ` ${detail}`),
|
|
302
|
+
...live
|
|
303
|
+
];
|
|
304
|
+
}).map((line) => truncate(line, columns));
|
|
305
|
+
}
|
|
306
|
+
};
|
|
307
|
+
//#endregion
|
|
308
|
+
//#region src/version.ts
|
|
309
|
+
/**
|
|
310
|
+
* This CLI's version: the schema it migrates to, the version of coffre a
|
|
311
|
+
* deployment it makes runs, and the one `coffre migrate` expects to find.
|
|
312
|
+
*/
|
|
313
|
+
function cliVersion() {
|
|
314
|
+
return JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8")).version;
|
|
315
|
+
}
|
|
316
|
+
//#endregion
|
|
317
|
+
export { StepFailed as n, Steps as r, cliVersion as t };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@coffre/cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.14",
|
|
4
4
|
"description": "The coffre command line: secrets in your shell, and `coffre init` for a new deployment.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -28,9 +28,9 @@
|
|
|
28
28
|
"pg": "8.16.3",
|
|
29
29
|
"tsdown": "0.23.0",
|
|
30
30
|
"typescript": "5.9.3",
|
|
31
|
-
"@coffre/
|
|
32
|
-
"@coffre/
|
|
33
|
-
"@coffre/
|
|
31
|
+
"@coffre/client": "0.1.14",
|
|
32
|
+
"@coffre/db": "0.1.14",
|
|
33
|
+
"@coffre/core": "0.1.14"
|
|
34
34
|
},
|
|
35
35
|
"scripts": {
|
|
36
36
|
"build": "tsdown && node scripts/copy-templates.ts && node scripts/copy-migrations.ts",
|
|
@@ -4,8 +4,8 @@ import { D as customType, O as boolean, V as SQL, W as sql, b as text, ct as is,
|
|
|
4
4
|
import { r as drizzle } from "./node-postgres-BHgGDfvg.js";
|
|
5
5
|
import { a as trackCommits, i as relationsOf, n as asPostgresDatabase, r as ACTIVE_SUBJECT } from "./portable-9wWdi5e1.js";
|
|
6
6
|
import { resolve } from "node:path";
|
|
7
|
-
import { AsyncLocalStorage } from "node:async_hooks";
|
|
8
7
|
import { checkServerIdentity } from "node:tls";
|
|
8
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
9
9
|
//#region ../../node_modules/.pnpm/drizzle-orm@0.45.2_@cloudflare+workers-types@5.20260920.1_@libsql+client@0.18.0_@types+pg@8.20.0_pg@8.16.3/node_modules/drizzle-orm/pg-core/checks.js
|
|
10
10
|
var CheckBuilder = class {
|
|
11
11
|
constructor(name, value) {
|
|
@@ -126,6 +126,39 @@ function uniqueIndex(name) {
|
|
|
126
126
|
return new IndexBuilderOn(true, name);
|
|
127
127
|
}
|
|
128
128
|
//#endregion
|
|
129
|
+
//#region ../../node_modules/.pnpm/pg-connection-string@2.14.0/node_modules/pg-connection-string/esm/index.mjs
|
|
130
|
+
var import_pg_connection_string = /* @__PURE__ */ __toESM(require_pg_connection_string(), 1);
|
|
131
|
+
import_pg_connection_string.default.parse;
|
|
132
|
+
import_pg_connection_string.default.parse;
|
|
133
|
+
import_pg_connection_string.default.toClientConfig;
|
|
134
|
+
const parseIntoClientConfig = import_pg_connection_string.default.parseIntoClientConfig;
|
|
135
|
+
//#endregion
|
|
136
|
+
//#region ../db/dist/postgres-Bmar8y0q.js
|
|
137
|
+
/** Keep libpq's system-root shortcut from being read as a certificate filename by pg. */
|
|
138
|
+
function postgresConnection(url) {
|
|
139
|
+
let parsed;
|
|
140
|
+
try {
|
|
141
|
+
parsed = new URL(url);
|
|
142
|
+
} catch {
|
|
143
|
+
throw new Error("invalid Postgres URL");
|
|
144
|
+
}
|
|
145
|
+
const parameters = parsed.searchParams;
|
|
146
|
+
if (parameters.getAll("sslrootcert").at(-1) !== "system") return { connectionString: url };
|
|
147
|
+
const mode = parameters.getAll("sslmode").at(-1);
|
|
148
|
+
if (mode !== void 0 && mode !== "verify-full") throw new Error("sslrootcert=system requires sslmode=verify-full");
|
|
149
|
+
parameters.delete("sslrootcert");
|
|
150
|
+
parameters.set("sslmode", "verify-full");
|
|
151
|
+
const config = parseIntoClientConfig(parsed.toString());
|
|
152
|
+
const host = config.host;
|
|
153
|
+
if (!host) throw new Error("sslrootcert=system requires a Postgres hostname");
|
|
154
|
+
config.ssl = {
|
|
155
|
+
...config.ssl,
|
|
156
|
+
rejectUnauthorized: true,
|
|
157
|
+
checkServerIdentity: (_hostname, certificate) => checkServerIdentity(host, certificate)
|
|
158
|
+
};
|
|
159
|
+
return config;
|
|
160
|
+
}
|
|
161
|
+
//#endregion
|
|
129
162
|
//#region ../db/dist/rolldown-runtime-D7D4PA-g.js
|
|
130
163
|
var __defProp = Object.defineProperty;
|
|
131
164
|
var __exportAll = (all, no_symbols) => {
|
|
@@ -562,39 +595,6 @@ function createDatabase(pool) {
|
|
|
562
595
|
return trackCommits(db);
|
|
563
596
|
}
|
|
564
597
|
//#endregion
|
|
565
|
-
//#region ../../node_modules/.pnpm/pg-connection-string@2.14.0/node_modules/pg-connection-string/esm/index.mjs
|
|
566
|
-
var import_pg_connection_string = /* @__PURE__ */ __toESM(require_pg_connection_string(), 1);
|
|
567
|
-
import_pg_connection_string.default.parse;
|
|
568
|
-
import_pg_connection_string.default.parse;
|
|
569
|
-
import_pg_connection_string.default.toClientConfig;
|
|
570
|
-
const parseIntoClientConfig = import_pg_connection_string.default.parseIntoClientConfig;
|
|
571
|
-
//#endregion
|
|
572
|
-
//#region ../db/dist/postgres-Bmar8y0q.js
|
|
573
|
-
/** Keep libpq's system-root shortcut from being read as a certificate filename by pg. */
|
|
574
|
-
function postgresConnection(url) {
|
|
575
|
-
let parsed;
|
|
576
|
-
try {
|
|
577
|
-
parsed = new URL(url);
|
|
578
|
-
} catch {
|
|
579
|
-
throw new Error("invalid Postgres URL");
|
|
580
|
-
}
|
|
581
|
-
const parameters = parsed.searchParams;
|
|
582
|
-
if (parameters.getAll("sslrootcert").at(-1) !== "system") return { connectionString: url };
|
|
583
|
-
const mode = parameters.getAll("sslmode").at(-1);
|
|
584
|
-
if (mode !== void 0 && mode !== "verify-full") throw new Error("sslrootcert=system requires sslmode=verify-full");
|
|
585
|
-
parameters.delete("sslrootcert");
|
|
586
|
-
parameters.set("sslmode", "verify-full");
|
|
587
|
-
const config = parseIntoClientConfig(parsed.toString());
|
|
588
|
-
const host = config.host;
|
|
589
|
-
if (!host) throw new Error("sslrootcert=system requires a Postgres hostname");
|
|
590
|
-
config.ssl = {
|
|
591
|
-
...config.ssl,
|
|
592
|
-
rejectUnauthorized: true,
|
|
593
|
-
checkServerIdentity: (_hostname, certificate) => checkServerIdentity(host, certificate)
|
|
594
|
-
};
|
|
595
|
-
return config;
|
|
596
|
-
}
|
|
597
|
-
//#endregion
|
|
598
598
|
//#region ../db/dist/connect.js
|
|
599
599
|
/**
|
|
600
600
|
* Open a database from its URL, on a Node server:
|