neon 3.0.0 → 3.1.1
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/README.md +70 -5
- package/dist/_chunks/auth_selection-DGgq6ifc.js +83 -0
- package/dist/_chunks/cmd_pipeline-CUbBO9U_.js +2818 -0
- package/dist/_chunks/credentials-MYdHdKah.js +188 -0
- package/dist/_chunks/env-NbA61JR3.js +585 -0
- package/dist/_chunks/env_services-Tz9G4JeT.js +531 -0
- package/dist/_chunks/paths-DMq0Lt7a.js +151 -0
- package/dist/_chunks/profiles-Ir29rqns.js +217 -0
- package/dist/_chunks/psql-DWH-kc69.js +2169 -0
- package/dist/_chunks/rolldown-runtime-D7D4PA-g.js +13 -0
- package/dist/_chunks/secure_file-BucZj4yQ.js +39 -0
- package/dist/analytics.js +163 -207
- package/dist/api.js +815 -758
- package/dist/auth.js +121 -141
- package/dist/auth_context.js +39 -53
- package/dist/cli.js +4 -7
- package/dist/commands/api.js +220 -250
- package/dist/commands/api_keys.js +251 -314
- package/dist/commands/auth.js +283 -328
- package/dist/commands/bootstrap.js +372 -437
- package/dist/commands/branches.js +304 -455
- package/dist/commands/bucket.js +374 -514
- package/dist/commands/checkout.js +213 -298
- package/dist/commands/config.js +573 -690
- package/dist/commands/connection_string.js +137 -165
- package/dist/commands/data_api.js +238 -260
- package/dist/commands/databases.js +67 -76
- package/dist/commands/deploy.js +31 -25
- package/dist/commands/dev.js +639 -719
- package/dist/commands/diff.js +156 -200
- package/dist/commands/env.js +255 -305
- package/dist/commands/functions.js +275 -355
- package/dist/commands/index.js +70 -65
- package/dist/commands/init.js +84 -119
- package/dist/commands/inspect.js +55 -55
- package/dist/commands/ip_allow.js +88 -120
- package/dist/commands/link.js +874 -1019
- package/dist/commands/logs.js +291 -0
- package/dist/commands/neon_auth.js +725 -933
- package/dist/commands/operations.js +34 -25
- package/dist/commands/orgs.js +28 -18
- package/dist/commands/profile.js +615 -846
- package/dist/commands/projects.js +313 -373
- package/dist/commands/psql.js +60 -58
- package/dist/commands/roles.js +55 -58
- package/dist/commands/schema_diff.js +87 -131
- package/dist/commands/set_context.js +34 -26
- package/dist/commands/snapshots.js +288 -413
- package/dist/commands/status.js +41 -37
- package/dist/commands/user.js +21 -10
- package/dist/commands/vpc_endpoints.js +85 -113
- package/dist/config.js +7 -6
- package/dist/config_format.js +50 -66
- package/dist/config_template.js +128 -157
- package/dist/context.js +183 -235
- package/dist/current_branch_fast_path.js +40 -49
- package/dist/dev/env.js +2 -446
- package/dist/dev/functions.js +54 -68
- package/dist/dev/inputs.js +46 -58
- package/dist/dev/runtime.js +135 -164
- package/dist/dev/websocket.js +766 -959
- package/dist/env.js +27 -33
- package/dist/env_file.js +118 -132
- package/dist/env_services.js +2 -51
- package/dist/errors.js +57 -68
- package/dist/functions_api.js +45 -43
- package/dist/help.js +189 -140
- package/dist/index.js +182 -257
- package/dist/init/agents.js +137 -118
- package/dist/init/auth.js +58 -68
- package/dist/init/bootstrap.js +325 -396
- package/dist/init/build_config.js +4 -2
- package/dist/init/detect_agent.js +56 -101
- package/dist/init/editors.js +35 -52
- package/dist/init/enrich_output.js +51 -66
- package/dist/init/extension.js +134 -171
- package/dist/init/inspect.js +179 -266
- package/dist/init/interactive.js +510 -622
- package/dist/init/neonctl.js +117 -168
- package/dist/init/orchestrate.js +157 -173
- package/dist/init/phases/auth.js +188 -202
- package/dist/init/phases/cleanup.js +23 -23
- package/dist/init/phases/db.js +251 -277
- package/dist/init/phases/getting_started.js +213 -223
- package/dist/init/phases/mcp.js +174 -224
- package/dist/init/phases/migrations.js +247 -248
- package/dist/init/phases/neon_auth.js +114 -133
- package/dist/init/phases/setup.js +546 -703
- package/dist/init/phases/skills.js +75 -86
- package/dist/init/phases/status.js +72 -67
- package/dist/init/resolve_context.js +102 -99
- package/dist/init/route_command.js +91 -98
- package/dist/init/skills.js +174 -218
- package/dist/init/vsix.js +77 -99
- package/dist/log.js +17 -16
- package/dist/neon_services.js +104 -129
- package/dist/parameters.gen.js +481 -471
- package/dist/pkg.js +17 -19
- package/dist/profile_keys.js +44 -47
- package/dist/psql/cli.js +44 -47
- package/dist/psql/command/cmd_cond.js +231 -406
- package/dist/psql/command/cmd_connect.js +557 -764
- package/dist/psql/command/cmd_copy.js +728 -984
- package/dist/psql/command/cmd_describe.js +1499 -1688
- package/dist/psql/command/cmd_format.js +733 -905
- package/dist/psql/command/cmd_io.js +2 -2193
- package/dist/psql/command/cmd_lo.js +297 -359
- package/dist/psql/command/cmd_meta.js +727 -878
- package/dist/psql/command/cmd_misc.js +138 -172
- package/dist/psql/command/cmd_pipeline.js +2 -1148
- package/dist/psql/command/cmd_restrict.js +119 -155
- package/dist/psql/command/cmd_show.js +529 -688
- package/dist/psql/command/dispatch.js +260 -325
- package/dist/psql/command/inputQueue.js +35 -33
- package/dist/psql/command/shared.js +49 -63
- package/dist/psql/complete/filenames.js +90 -133
- package/dist/psql/complete/index.js +59 -97
- package/dist/psql/complete/matcher.js +236 -300
- package/dist/psql/complete/psqlVars.js +218 -223
- package/dist/psql/complete/queries.js +159 -177
- package/dist/psql/complete/rules.js +1493 -2299
- package/dist/psql/core/common.js +2 -1253
- package/dist/psql/core/help.js +456 -546
- package/dist/psql/core/mainloop.js +692 -1303
- package/dist/psql/core/prompt.js +391 -408
- package/dist/psql/core/settings.js +429 -644
- package/dist/psql/core/sqlHelp.js +480 -554
- package/dist/psql/core/startup.js +2 -846
- package/dist/psql/core/syncVars.js +67 -110
- package/dist/psql/core/variables.js +156 -278
- package/dist/psql/describe/formatters.js +884 -1285
- package/dist/psql/describe/processNamePattern.js +173 -260
- package/dist/psql/describe/queries.js +1368 -2403
- package/dist/psql/describe/versionGate.js +32 -41
- package/dist/psql/index.js +2 -2030
- package/dist/psql/io/history.js +232 -271
- package/dist/psql/io/input.js +103 -108
- package/dist/psql/io/lineEditor/buffer.js +238 -319
- package/dist/psql/io/lineEditor/complete.js +135 -213
- package/dist/psql/io/lineEditor/filename.js +139 -148
- package/dist/psql/io/lineEditor/index.js +653 -870
- package/dist/psql/io/lineEditor/keymap.js +544 -702
- package/dist/psql/io/lineEditor/vt100.js +294 -341
- package/dist/psql/io/pgpass.js +158 -187
- package/dist/psql/io/pgservice.js +146 -183
- package/dist/psql/io/psqlrc.js +328 -403
- package/dist/psql/print/aligned.js +1020 -1683
- package/dist/psql/print/asciidoc.js +180 -214
- package/dist/psql/print/crosstab.js +281 -442
- package/dist/psql/print/csv.js +48 -70
- package/dist/psql/print/html.js +195 -226
- package/dist/psql/print/json.js +75 -88
- package/dist/psql/print/latex.js +291 -364
- package/dist/psql/print/pager.js +171 -242
- package/dist/psql/print/troff.js +194 -226
- package/dist/psql/print/unaligned.js +69 -95
- package/dist/psql/print/units.js +167 -169
- package/dist/psql/scanner/slash.js +428 -483
- package/dist/psql/scanner/sql.js +445 -889
- package/dist/psql/scanner/stringutils.js +309 -379
- package/dist/psql/types/index.js +8 -7
- package/dist/psql/types/scanner.js +25 -22
- package/dist/psql/wire/connection.js +2042 -2803
- package/dist/psql/wire/copy.js +84 -100
- package/dist/psql/wire/notify.js +39 -59
- package/dist/psql/wire/pipeline.js +305 -518
- package/dist/psql/wire/protocol.js +349 -417
- package/dist/psql/wire/sasl.js +180 -265
- package/dist/psql/wire/tls.js +400 -561
- package/dist/storage_api.js +115 -129
- package/dist/test_utils/fixtures.js +94 -113
- package/dist/test_utils/oauth_server.js +10 -7
- package/dist/test_utils/project_dir.js +33 -0
- package/dist/utils/ai_gateway_notice.js +131 -162
- package/dist/utils/api_enums.js +21 -28
- package/dist/utils/auth.js +10 -4
- package/dist/utils/branch_notice.js +20 -19
- package/dist/utils/branch_picker.js +83 -89
- package/dist/utils/cli_name.js +15 -12
- package/dist/utils/compute_units.js +20 -27
- package/dist/utils/config_diff.js +127 -158
- package/dist/utils/enrichers.js +95 -148
- package/dist/utils/esbuild.js +130 -189
- package/dist/utils/flags.js +35 -47
- package/dist/utils/formats.js +8 -15
- package/dist/utils/git_diff.js +69 -80
- package/dist/utils/inspect_db.js +101 -143
- package/dist/utils/inspect_queries.js +179 -142
- package/dist/utils/middlewares.js +39 -45
- package/dist/utils/openapi.js +87 -99
- package/dist/utils/package_manager.js +312 -110
- package/dist/utils/point_in_time.js +49 -53
- package/dist/utils/psql.js +89 -106
- package/dist/utils/service_picker.js +55 -58
- package/dist/utils/string.js +5 -5
- package/dist/utils/ui.js +38 -55
- package/dist/utils/write_sync.js +26 -35
- package/dist/utils/zip.js +4 -3
- package/dist/writer.js +67 -87
- package/package.json +11 -6
- package/dist/_shared/auth_selection.js +0 -86
- package/dist/_shared/credentials.js +0 -209
- package/dist/_shared/env-core/env.js +0 -558
- package/dist/_shared/env-core/reuse-secrets.js +0 -223
- package/dist/_shared/paths.js +0 -148
- package/dist/_shared/profiles.js +0 -276
- package/dist/_shared/secure_file.js +0 -43
package/dist/utils/flags.js
CHANGED
|
@@ -1,52 +1,40 @@
|
|
|
1
|
+
//#region src/utils/flags.ts
|
|
1
2
|
/**
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
3
|
+
* Strict readers for flags whose value decides how much a credential can reach.
|
|
4
|
+
*
|
|
5
|
+
* These live apart from any one command because both `api-keys` and `profile create` mint
|
|
6
|
+
* keys, and a lenient reading of a scope flag has the same consequence in either: the flag
|
|
7
|
+
* reads as falsy, the scope check falls through, and an **account** key is minted instead of
|
|
8
|
+
* the narrow one that was asked for. Two copies of that check is one copy too many.
|
|
9
|
+
*/
|
|
9
10
|
/**
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
// `--no-x` is the negation form and yields `false`, so name it rather than telling
|
|
26
|
-
// the user their value was empty when they never gave one.
|
|
27
|
-
if (value === false) {
|
|
28
|
-
throw new Error(required
|
|
29
|
-
? `--no-${name} is not valid: --${name} is required.`
|
|
30
|
-
: `--no-${name} is not a valid way to skip --${name}. Omit the flag entirely.`);
|
|
31
|
-
}
|
|
32
|
-
if (typeof value !== "string" || value.trim() === "") {
|
|
33
|
-
throw new Error(required
|
|
34
|
-
? `--${name} needs a value.`
|
|
35
|
-
: `--${name} needs a value. Pass one, or omit the flag entirely.`);
|
|
36
|
-
}
|
|
37
|
-
return value;
|
|
11
|
+
* A flag is either absent, or exactly one non-empty string. Anything else is an error.
|
|
12
|
+
*
|
|
13
|
+
* - `--org-id ""` — an unset shell variable; empty string, which is falsy.
|
|
14
|
+
* - `--no-org-id` — yargs boolean negation; `false`, which is falsy.
|
|
15
|
+
* - `--org-id a --org-id b` — an array, which would reach the API as `a,b`.
|
|
16
|
+
*
|
|
17
|
+
* A misspelled flag never binds and cannot be seen here; `.strict()` rejects it. Anything
|
|
18
|
+
* after a `--` terminator is handled by {@link noPassthrough}.
|
|
19
|
+
*/
|
|
20
|
+
const single = (name, { required = false } = {}) => (value) => {
|
|
21
|
+
if (value === void 0) return void 0;
|
|
22
|
+
if (Array.isArray(value)) throw new Error(`--${name} was given more than once. Pass it at most once.`);
|
|
23
|
+
if (value === false) throw new Error(required ? `--no-${name} is not valid: --${name} is required.` : `--no-${name} is not a valid way to skip --${name}. Omit the flag entirely.`);
|
|
24
|
+
if (typeof value !== "string" || value.trim() === "") throw new Error(required ? `--${name} needs a value.` : `--${name} needs a value. Pass one, or omit the flag entirely.`);
|
|
25
|
+
return value;
|
|
38
26
|
};
|
|
39
27
|
/**
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
}
|
|
51
|
-
return true;
|
|
28
|
+
* Refuse arguments after a `--` terminator.
|
|
29
|
+
*
|
|
30
|
+
* The CLI sets `populate--`, so everything past `--` lands in `argv["--"]` where `.strict()`
|
|
31
|
+
* never looks — `create --name x -- --project-id p` would parse cleanly and mint an account
|
|
32
|
+
* key from a line that names the scope flag.
|
|
33
|
+
*/
|
|
34
|
+
const noPassthrough = (command) => (argv) => {
|
|
35
|
+
const rest = argv["--"];
|
|
36
|
+
if (Array.isArray(rest) && rest.length > 0) throw new Error(`${command} takes no arguments after \`--\`, and options placed there are ignored rather than applied. Remove the \`--\`.`);
|
|
37
|
+
return true;
|
|
52
38
|
};
|
|
39
|
+
//#endregion
|
|
40
|
+
export { noPassthrough, single };
|
package/dist/utils/formats.js
CHANGED
|
@@ -1,18 +1,11 @@
|
|
|
1
|
+
//#region src/utils/formats.ts
|
|
1
2
|
const HAIKU_REGEX = /^[a-z0-9]+-[a-z0-9]+-[a-z0-9]+$/;
|
|
2
|
-
|
|
3
|
+
const looksLikeBranchId = (branch) => branch.startsWith("br-") && HAIKU_REGEX.test(branch.substring(3));
|
|
3
4
|
const LSN_REGEX = /^[a-fA-F0-9]{1,8}\/[a-fA-F0-9]{1,8}$/;
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* @info
|
|
10
|
-
* Check for ISO 8601/RFC 3339 format patterns
|
|
11
|
-
* Must contain 'T' separator and end with 'Z' or timezone offset
|
|
12
|
-
*
|
|
13
|
-
* `Date.parse` aggressive parsing will attempt a date out of any string.
|
|
14
|
-
* so if a branch name has a number, `Date.parse` will return a valid timestamp.
|
|
15
|
-
*/
|
|
16
|
-
const iso8601Regex = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d{3})?([+-]\d{2}:\d{2}|Z)$/;
|
|
17
|
-
return iso8601Regex.test(timestamp);
|
|
5
|
+
const looksLikeLSN = (lsn) => LSN_REGEX.test(lsn);
|
|
6
|
+
const looksLikeTimestamp = (timestamp) => {
|
|
7
|
+
if (isNaN(Date.parse(timestamp))) return false;
|
|
8
|
+
return /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d{3})?([+-]\d{2}:\d{2}|Z)$/.test(timestamp);
|
|
18
9
|
};
|
|
10
|
+
//#endregion
|
|
11
|
+
export { looksLikeBranchId, looksLikeLSN, looksLikeTimestamp };
|
package/dist/utils/git_diff.js
CHANGED
|
@@ -1,90 +1,79 @@
|
|
|
1
1
|
import chalk from "chalk";
|
|
2
2
|
import { structuredPatch } from "diff";
|
|
3
|
+
//#region src/utils/git_diff.ts
|
|
3
4
|
/**
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
* Color palette for the rendered diff. When `color` is false every entry is the
|
|
6
|
+
* identity function, so the exact same layout is emitted without ANSI codes
|
|
7
|
+
* (for `--no-color`, non-TTY pipes, and stable test snapshots).
|
|
8
|
+
*/
|
|
8
9
|
const palette = (color) => {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
noNewline: (s) => chalk.dim(s),
|
|
29
|
-
};
|
|
10
|
+
const id = (s) => s;
|
|
11
|
+
if (!color) return {
|
|
12
|
+
header: id,
|
|
13
|
+
removedFile: id,
|
|
14
|
+
addedFile: id,
|
|
15
|
+
hunk: id,
|
|
16
|
+
added: id,
|
|
17
|
+
removed: id,
|
|
18
|
+
noNewline: id
|
|
19
|
+
};
|
|
20
|
+
return {
|
|
21
|
+
header: (s) => chalk.bold(s),
|
|
22
|
+
removedFile: (s) => chalk.bold.red(s),
|
|
23
|
+
addedFile: (s) => chalk.bold.green(s),
|
|
24
|
+
hunk: (s) => chalk.cyan(s),
|
|
25
|
+
added: (s) => chalk.green(s),
|
|
26
|
+
removed: (s) => chalk.red(s),
|
|
27
|
+
noNewline: (s) => chalk.dim(s)
|
|
28
|
+
};
|
|
30
29
|
};
|
|
31
30
|
const branchLabel = (schema) => `${schema.branchName} (${schema.branchId})`;
|
|
32
31
|
/**
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
}
|
|
66
|
-
else {
|
|
67
|
-
lines.push(line);
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
return {
|
|
72
|
-
database: diff.database,
|
|
73
|
-
hasChanges: true,
|
|
74
|
-
text: lines.join("\n"),
|
|
75
|
-
};
|
|
32
|
+
* Render a single database's schema comparison as a git-style unified diff.
|
|
33
|
+
*
|
|
34
|
+
* Pure: given the two schemas it computes hunks with `diff`'s `structuredPatch`
|
|
35
|
+
* and lays them out like `git diff` — a bold header, red `---` / green `+++`
|
|
36
|
+
* branch lines, cyan `@@` hunk headers, and green/red line bodies. Returns
|
|
37
|
+
* `hasChanges: false` with empty text when the two schemas are identical.
|
|
38
|
+
*/
|
|
39
|
+
const renderDatabaseSchemaDiff = (diff, opts) => {
|
|
40
|
+
const patch = structuredPatch(diff.database, diff.database, diff.before.sql, diff.after.sql, "", "", { context: 3 });
|
|
41
|
+
if (patch.hunks.length === 0) return {
|
|
42
|
+
database: diff.database,
|
|
43
|
+
hasChanges: false,
|
|
44
|
+
text: ""
|
|
45
|
+
};
|
|
46
|
+
const paint = palette(opts.color);
|
|
47
|
+
const lines = [
|
|
48
|
+
paint.header(`diff --neon database ${diff.database}`),
|
|
49
|
+
paint.removedFile(`--- ${branchLabel(diff.before)}`),
|
|
50
|
+
paint.addedFile(`+++ ${branchLabel(diff.after)}`)
|
|
51
|
+
];
|
|
52
|
+
for (const hunk of patch.hunks) {
|
|
53
|
+
lines.push(paint.hunk(`@@ -${hunk.oldStart},${hunk.oldLines} +${hunk.newStart},${hunk.newLines} @@`));
|
|
54
|
+
for (const line of hunk.lines) if (line.startsWith("+")) lines.push(paint.added(line));
|
|
55
|
+
else if (line.startsWith("-")) lines.push(paint.removed(line));
|
|
56
|
+
else if (line.startsWith("\\")) lines.push(paint.noNewline(line));
|
|
57
|
+
else lines.push(line);
|
|
58
|
+
}
|
|
59
|
+
return {
|
|
60
|
+
database: diff.database,
|
|
61
|
+
hasChanges: true,
|
|
62
|
+
text: lines.join("\n")
|
|
63
|
+
};
|
|
76
64
|
};
|
|
77
65
|
/**
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
};
|
|
66
|
+
* Render every database's diff into one report, keeping only databases whose
|
|
67
|
+
* schema actually changed. `hasChanges` is false when nothing changed across
|
|
68
|
+
* all databases, letting the caller print a single "no differences" note.
|
|
69
|
+
*/
|
|
70
|
+
const renderSchemaDiffReport = (diffs, opts) => {
|
|
71
|
+
const changed = diffs.map((diff) => renderDatabaseSchemaDiff(diff, opts)).filter((r) => r.hasChanges);
|
|
72
|
+
return {
|
|
73
|
+
hasChanges: changed.length > 0,
|
|
74
|
+
text: changed.map((r) => r.text).join("\n\n"),
|
|
75
|
+
changedDatabases: changed.map((r) => r.database)
|
|
76
|
+
};
|
|
90
77
|
};
|
|
78
|
+
//#endregion
|
|
79
|
+
export { renderDatabaseSchemaDiff, renderSchemaDiffReport };
|
package/dist/utils/inspect_db.js
CHANGED
|
@@ -1,153 +1,111 @@
|
|
|
1
|
-
import { parseConnectionUri } from "../psql/index.js";
|
|
2
|
-
import { PgConnection } from "../psql/wire/connection.js";
|
|
3
1
|
import { EndpointType } from "./api_enums.js";
|
|
4
2
|
import { branchIdFromProps } from "./enrichers.js";
|
|
5
3
|
import { parsePITBranch } from "./point_in_time.js";
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
4
|
+
import { PgConnection } from "../psql/wire/connection.js";
|
|
5
|
+
import { o as parseConnectionUri } from "../_chunks/psql-DWH-kc69.js";
|
|
6
|
+
//#region src/utils/inspect_db.ts
|
|
7
|
+
const SSL_MODES = [
|
|
8
|
+
"require",
|
|
9
|
+
"verify-ca",
|
|
10
|
+
"verify-full",
|
|
11
|
+
"omit"
|
|
11
12
|
];
|
|
12
13
|
/**
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
}
|
|
73
|
-
const connectionString = new URL(`postgresql://${host}`);
|
|
74
|
-
connectionString.pathname = database;
|
|
75
|
-
connectionString.username = role;
|
|
76
|
-
connectionString.password = password;
|
|
77
|
-
if (props.prisma) {
|
|
78
|
-
connectionString.searchParams.set("connect_timeout", "30");
|
|
79
|
-
if (props.pooled) {
|
|
80
|
-
connectionString.searchParams.set("pool_timeout", "30");
|
|
81
|
-
connectionString.searchParams.set("pgbouncer", "true");
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
const ssl = props.ssl ?? "require";
|
|
85
|
-
if (ssl !== "omit") {
|
|
86
|
-
connectionString.searchParams.set("sslmode", ssl);
|
|
87
|
-
connectionString.searchParams.set("channel_binding", "require");
|
|
88
|
-
}
|
|
89
|
-
if (parsedPIT.tag === "lsn") {
|
|
90
|
-
connectionString.searchParams.set("options", `neon_lsn:${parsedPIT.lsn}`);
|
|
91
|
-
}
|
|
92
|
-
else if (parsedPIT.tag === "timestamp") {
|
|
93
|
-
connectionString.searchParams.set("options", `neon_timestamp:${parsedPIT.timestamp}`);
|
|
94
|
-
}
|
|
95
|
-
return {
|
|
96
|
-
connectionUri: connectionString.toString(),
|
|
97
|
-
host,
|
|
98
|
-
role,
|
|
99
|
-
password,
|
|
100
|
-
database,
|
|
101
|
-
options: connectionString.searchParams.toString(),
|
|
102
|
-
};
|
|
14
|
+
* Resolve a branch's live Postgres connection details via the Neon API
|
|
15
|
+
* (endpoint → role → password → database → URL, honoring point-in-time,
|
|
16
|
+
* pooling, prisma tuning, and SSL mode). Mirrors how `connection-string`
|
|
17
|
+
* resolves a branch so `inspect db` connects the same way.
|
|
18
|
+
*/
|
|
19
|
+
const resolveConnectionUri = async (props) => {
|
|
20
|
+
const projectId = props.projectId;
|
|
21
|
+
const parsedPIT = props.branch ? parsePITBranch(props.branch) : {
|
|
22
|
+
tag: "head",
|
|
23
|
+
branch: ""
|
|
24
|
+
};
|
|
25
|
+
if (props.branch) props.branch = parsedPIT.branch;
|
|
26
|
+
const branchId = await branchIdFromProps(props);
|
|
27
|
+
const { data: { endpoints } } = await props.apiClient.listProjectBranchEndpoints(projectId, branchId);
|
|
28
|
+
const matchEndpointType = props.endpointType ?? EndpointType.ReadWrite;
|
|
29
|
+
let endpoint = endpoints.find((e) => e.type === matchEndpointType);
|
|
30
|
+
if (!endpoint && props.endpointType == null) endpoint = endpoints[0];
|
|
31
|
+
if (!endpoint) throw new Error(`No ${props.endpointType ?? ""} endpoint found for the branch: ${branchId}`);
|
|
32
|
+
const role = props.roleName || await props.apiClient.listProjectBranchRoles(projectId, branchId).then(({ data }) => {
|
|
33
|
+
if (data.roles.length === 0) throw new Error(`No roles found for the branch: ${branchId}`);
|
|
34
|
+
if (data.roles.length === 1) return data.roles[0].name;
|
|
35
|
+
throw new Error(`Multiple roles found for the branch, please provide one with the --role-name option: ${data.roles.map((r) => r.name).join(", ")}`);
|
|
36
|
+
});
|
|
37
|
+
const { data: { databases: branchDatabases } } = await props.apiClient.listProjectBranchDatabases(projectId, branchId);
|
|
38
|
+
const database = props.databaseName || (() => {
|
|
39
|
+
if (branchDatabases.length === 0) throw new Error(`No databases found for the branch: ${branchId}`);
|
|
40
|
+
if (branchDatabases.length === 1) return branchDatabases[0].name;
|
|
41
|
+
throw new Error(`Multiple databases found for the branch, please provide one with the --database-name option: ${branchDatabases.map((d) => d.name).join(", ")}`);
|
|
42
|
+
})();
|
|
43
|
+
if (!branchDatabases.find((d) => d.name === database)) throw new Error(`Database not found: ${database}`);
|
|
44
|
+
const { data: { password } } = await props.apiClient.getProjectBranchRolePassword(props.projectId, endpoint.branch_id, role);
|
|
45
|
+
let host = props.pooled ? endpoint.host.replace(endpoint.id, `${endpoint.id}-pooler`) : endpoint.host;
|
|
46
|
+
if (parsedPIT.tag !== "head") host = endpoint.host.replace(endpoint.id, endpoint.branch_id);
|
|
47
|
+
const connectionString = new URL(`postgresql://${host}`);
|
|
48
|
+
connectionString.pathname = database;
|
|
49
|
+
connectionString.username = role;
|
|
50
|
+
connectionString.password = password;
|
|
51
|
+
if (props.prisma) {
|
|
52
|
+
connectionString.searchParams.set("connect_timeout", "30");
|
|
53
|
+
if (props.pooled) {
|
|
54
|
+
connectionString.searchParams.set("pool_timeout", "30");
|
|
55
|
+
connectionString.searchParams.set("pgbouncer", "true");
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
const ssl = props.ssl ?? "require";
|
|
59
|
+
if (ssl !== "omit") {
|
|
60
|
+
connectionString.searchParams.set("sslmode", ssl);
|
|
61
|
+
connectionString.searchParams.set("channel_binding", "require");
|
|
62
|
+
}
|
|
63
|
+
if (parsedPIT.tag === "lsn") connectionString.searchParams.set("options", `neon_lsn:${parsedPIT.lsn}`);
|
|
64
|
+
else if (parsedPIT.tag === "timestamp") connectionString.searchParams.set("options", `neon_timestamp:${parsedPIT.timestamp}`);
|
|
65
|
+
return {
|
|
66
|
+
connectionUri: connectionString.toString(),
|
|
67
|
+
host,
|
|
68
|
+
role,
|
|
69
|
+
password,
|
|
70
|
+
database,
|
|
71
|
+
options: connectionString.searchParams.toString()
|
|
72
|
+
};
|
|
103
73
|
};
|
|
104
74
|
const rowsFrom = (result) => {
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
75
|
+
const names = result.fields.map((f) => f.name);
|
|
76
|
+
return result.rows.map((row) => {
|
|
77
|
+
const obj = {};
|
|
78
|
+
names.forEach((name, i) => {
|
|
79
|
+
obj[name] = row[i];
|
|
80
|
+
});
|
|
81
|
+
return obj;
|
|
82
|
+
});
|
|
113
83
|
};
|
|
114
84
|
/**
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
// `ext` is an internal constant from INSPECT_QUERIES, never user input,
|
|
140
|
-
// so interpolating it into the literal is safe.
|
|
141
|
-
const check = await connection.query(`SELECT 1 FROM pg_extension WHERE extname = '${ext}';`);
|
|
142
|
-
if (check.rows.length === 0) {
|
|
143
|
-
throw new Error(`This query needs the "${ext}" extension, which is not installed. ` +
|
|
144
|
-
`Enable it with: CREATE EXTENSION ${ext};`);
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
const result = await connection.query(sql);
|
|
148
|
-
return rowsFrom(result);
|
|
149
|
-
}
|
|
150
|
-
finally {
|
|
151
|
-
await connection.close();
|
|
152
|
-
}
|
|
85
|
+
* Connect to `connectionUri` with the embedded wire client, run a single
|
|
86
|
+
* read-only `sql`, and return the rows as objects keyed by column name (so the
|
|
87
|
+
* shared `writer` can render them by field). The connection is always closed,
|
|
88
|
+
* even on error.
|
|
89
|
+
*/
|
|
90
|
+
const runInspectQuery = async (connectionUri, sql, options = {}) => {
|
|
91
|
+
const opts = parseConnectionUri(connectionUri);
|
|
92
|
+
let connection;
|
|
93
|
+
try {
|
|
94
|
+
connection = await PgConnection.connect(opts);
|
|
95
|
+
} catch (err) {
|
|
96
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
97
|
+
throw new Error(`Could not connect to Postgres at ${opts.host}:${opts.port}: ${reason}`);
|
|
98
|
+
}
|
|
99
|
+
try {
|
|
100
|
+
const ext = options.requiresExtension;
|
|
101
|
+
if (ext) {
|
|
102
|
+
if ((await connection.query(`SELECT 1 FROM pg_extension WHERE extname = '${ext}';`)).rows.length === 0) throw new Error(`This query needs the "${ext}" extension, which is not installed. Enable it with: CREATE EXTENSION ${ext};`);
|
|
103
|
+
}
|
|
104
|
+
const result = await connection.query(sql);
|
|
105
|
+
return rowsFrom(result);
|
|
106
|
+
} finally {
|
|
107
|
+
await connection.close();
|
|
108
|
+
}
|
|
153
109
|
};
|
|
110
|
+
//#endregion
|
|
111
|
+
export { SSL_MODES, resolveConnectionUri, runInspectQuery };
|