neonctl 2.27.1 → 2.29.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +35 -3
- package/dist/analytics.js +52 -34
- package/dist/api.js +643 -13
- package/dist/auth.js +50 -44
- package/dist/cli.js +8 -1
- package/dist/commands/auth.js +64 -51
- package/dist/commands/bootstrap.js +115 -157
- package/dist/commands/branches.js +160 -150
- package/dist/commands/bucket.js +183 -146
- package/dist/commands/checkout.js +51 -51
- package/dist/commands/config.js +228 -82
- package/dist/commands/connection_string.js +62 -62
- package/dist/commands/data_api.js +100 -101
- package/dist/commands/databases.js +29 -26
- package/dist/commands/deploy.js +12 -12
- package/dist/commands/dev.js +114 -114
- package/dist/commands/env.js +43 -43
- package/dist/commands/functions.js +101 -104
- package/dist/commands/index.js +27 -25
- package/dist/commands/init.js +23 -22
- package/dist/commands/ip_allow.js +29 -29
- package/dist/commands/link.js +232 -182
- package/dist/commands/neon_auth.js +385 -370
- package/dist/commands/operations.js +11 -11
- package/dist/commands/orgs.js +8 -8
- package/dist/commands/projects.js +103 -101
- package/dist/commands/psql.js +31 -31
- package/dist/commands/roles.js +27 -24
- package/dist/commands/schema_diff.js +25 -26
- package/dist/commands/set_context.js +17 -17
- package/dist/commands/status.js +40 -0
- package/dist/commands/user.js +5 -5
- package/dist/commands/vpc_endpoints.js +50 -50
- package/dist/config.js +7 -7
- package/dist/config_format.js +5 -5
- package/dist/context.js +37 -14
- package/dist/current_branch_fast_path.js +55 -0
- package/dist/dev/env.js +33 -33
- package/dist/dev/functions.js +4 -4
- package/dist/dev/inputs.js +6 -6
- package/dist/dev/runtime.js +25 -25
- package/dist/env.js +14 -14
- package/dist/env_file.js +13 -13
- package/dist/errors.js +68 -5
- package/dist/functions_api.js +10 -10
- package/dist/help.js +15 -15
- package/dist/index.js +110 -107
- package/dist/log.js +2 -2
- package/dist/parameters.gen.js +14 -14
- package/dist/pkg.js +5 -5
- package/dist/psql/cli.js +4 -2
- package/dist/psql/command/cmd_cond.js +61 -61
- package/dist/psql/command/cmd_connect.js +159 -154
- package/dist/psql/command/cmd_copy.js +107 -97
- package/dist/psql/command/cmd_describe.js +368 -363
- package/dist/psql/command/cmd_format.js +276 -263
- package/dist/psql/command/cmd_io.js +269 -263
- package/dist/psql/command/cmd_lo.js +74 -66
- package/dist/psql/command/cmd_meta.js +148 -148
- package/dist/psql/command/cmd_misc.js +17 -17
- package/dist/psql/command/cmd_pipeline.js +142 -135
- package/dist/psql/command/cmd_restrict.js +25 -25
- package/dist/psql/command/cmd_show.js +183 -168
- package/dist/psql/command/dispatch.js +26 -26
- package/dist/psql/command/shared.js +14 -14
- package/dist/psql/complete/filenames.js +16 -16
- package/dist/psql/complete/index.js +4 -4
- package/dist/psql/complete/matcher.js +33 -32
- package/dist/psql/complete/psqlVars.js +173 -173
- package/dist/psql/complete/queries.js +5 -3
- package/dist/psql/complete/rules.js +900 -863
- package/dist/psql/core/common.js +136 -133
- package/dist/psql/core/help.js +343 -343
- package/dist/psql/core/mainloop.js +160 -153
- package/dist/psql/core/prompt.js +126 -123
- package/dist/psql/core/settings.js +111 -111
- package/dist/psql/core/sqlHelp.js +150 -150
- package/dist/psql/core/startup.js +211 -205
- package/dist/psql/core/syncVars.js +14 -14
- package/dist/psql/core/variables.js +24 -24
- package/dist/psql/describe/formatters.js +302 -289
- package/dist/psql/describe/processNamePattern.js +28 -28
- package/dist/psql/describe/queries.js +656 -651
- package/dist/psql/index.js +436 -411
- package/dist/psql/io/history.js +36 -36
- package/dist/psql/io/input.js +15 -15
- package/dist/psql/io/lineEditor/buffer.js +27 -25
- package/dist/psql/io/lineEditor/complete.js +15 -15
- package/dist/psql/io/lineEditor/filename.js +22 -22
- package/dist/psql/io/lineEditor/index.js +65 -62
- package/dist/psql/io/lineEditor/keymap.js +325 -318
- package/dist/psql/io/lineEditor/vt100.js +60 -60
- package/dist/psql/io/pgpass.js +18 -18
- package/dist/psql/io/pgservice.js +14 -14
- package/dist/psql/io/psqlrc.js +46 -46
- package/dist/psql/print/aligned.js +175 -166
- package/dist/psql/print/asciidoc.js +51 -51
- package/dist/psql/print/crosstab.js +34 -31
- package/dist/psql/print/csv.js +25 -22
- package/dist/psql/print/html.js +54 -54
- package/dist/psql/print/json.js +12 -12
- package/dist/psql/print/latex.js +118 -118
- package/dist/psql/print/pager.js +28 -26
- package/dist/psql/print/troff.js +48 -48
- package/dist/psql/print/unaligned.js +15 -14
- package/dist/psql/print/units.js +17 -17
- package/dist/psql/scanner/slash.js +48 -46
- package/dist/psql/scanner/sql.js +88 -84
- package/dist/psql/scanner/stringutils.js +21 -17
- package/dist/psql/types/index.js +7 -7
- package/dist/psql/types/scanner.js +8 -8
- package/dist/psql/wire/connection.js +341 -327
- package/dist/psql/wire/copy.js +7 -7
- package/dist/psql/wire/pipeline.js +26 -24
- package/dist/psql/wire/protocol.js +102 -102
- package/dist/psql/wire/sasl.js +62 -62
- package/dist/psql/wire/tls.js +79 -73
- package/dist/storage_api.js +22 -23
- package/dist/test_utils/fixtures.js +74 -41
- package/dist/test_utils/oauth_server.js +5 -5
- package/dist/utils/api_enums.js +33 -0
- package/dist/utils/branch_notice.js +5 -5
- package/dist/utils/branch_picker.js +26 -26
- package/dist/utils/compute_units.js +4 -4
- package/dist/utils/enrichers.js +28 -16
- package/dist/utils/esbuild.js +28 -28
- package/dist/utils/formats.js +1 -1
- package/dist/utils/middlewares.js +3 -3
- package/dist/utils/package_manager.js +68 -0
- package/dist/utils/point_in_time.js +12 -12
- package/dist/utils/psql.js +30 -30
- package/dist/utils/string.js +2 -2
- package/dist/utils/ui.js +9 -9
- package/dist/utils/zip.js +1 -1
- package/dist/writer.js +17 -17
- package/package.json +10 -12
|
@@ -1,75 +1,75 @@
|
|
|
1
|
-
import { EndpointType
|
|
2
|
-
import { branchIdFromProps, fillSingleProject } from
|
|
3
|
-
import {
|
|
4
|
-
import { psql } from
|
|
5
|
-
import {
|
|
1
|
+
import { EndpointType } from "../utils/api_enums.js";
|
|
2
|
+
import { branchIdFromProps, fillSingleProject } from "../utils/enrichers.js";
|
|
3
|
+
import { parsePITBranch } from "../utils/point_in_time.js";
|
|
4
|
+
import { psql } from "../utils/psql.js";
|
|
5
|
+
import { writer } from "../writer.js";
|
|
6
6
|
export const SSL_MODES = [
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
"require",
|
|
8
|
+
"verify-ca",
|
|
9
|
+
"verify-full",
|
|
10
|
+
"omit",
|
|
11
11
|
];
|
|
12
|
-
export const command =
|
|
13
|
-
export const aliases = [
|
|
14
|
-
export const describe =
|
|
12
|
+
export const command = "connection-string [branch]";
|
|
13
|
+
export const aliases = ["cs"];
|
|
14
|
+
export const describe = "Get connection string";
|
|
15
15
|
export const builder = (argv) => {
|
|
16
16
|
return argv
|
|
17
|
-
.usage(
|
|
18
|
-
.example(
|
|
19
|
-
.example(
|
|
20
|
-
.example(
|
|
21
|
-
.positional(
|
|
17
|
+
.usage("$0 connection-string [branch] [options]")
|
|
18
|
+
.example("$0 cs main", "Get connection string for the main branch")
|
|
19
|
+
.example("$0 cs main@2024-01-01T00:00:00Z", "Get connection string for the main branch at a specific point in time")
|
|
20
|
+
.example("$0 cs main@0/234235", "Get connection string for the main branch at a specific LSN")
|
|
21
|
+
.positional("branch", {
|
|
22
22
|
describe: `Branch name or id. Defaults to the default branch if omitted. Can be written in the point-in-time format: "branch@timestamp" or "branch@lsn"`,
|
|
23
|
-
type:
|
|
23
|
+
type: "string",
|
|
24
24
|
})
|
|
25
25
|
.options({
|
|
26
|
-
|
|
27
|
-
type:
|
|
28
|
-
describe:
|
|
26
|
+
"project-id": {
|
|
27
|
+
type: "string",
|
|
28
|
+
describe: "Project ID",
|
|
29
29
|
},
|
|
30
|
-
|
|
31
|
-
type:
|
|
32
|
-
describe:
|
|
30
|
+
"role-name": {
|
|
31
|
+
type: "string",
|
|
32
|
+
describe: "Role name",
|
|
33
33
|
},
|
|
34
|
-
|
|
35
|
-
type:
|
|
36
|
-
describe:
|
|
34
|
+
"database-name": {
|
|
35
|
+
type: "string",
|
|
36
|
+
describe: "Database name",
|
|
37
37
|
},
|
|
38
38
|
pooled: {
|
|
39
|
-
type:
|
|
40
|
-
describe:
|
|
39
|
+
type: "boolean",
|
|
40
|
+
describe: "Use pooled connection",
|
|
41
41
|
default: false,
|
|
42
42
|
},
|
|
43
43
|
prisma: {
|
|
44
|
-
type:
|
|
45
|
-
describe:
|
|
44
|
+
type: "boolean",
|
|
45
|
+
describe: "Use connection string for Prisma setup",
|
|
46
46
|
default: false,
|
|
47
47
|
},
|
|
48
|
-
|
|
49
|
-
type:
|
|
48
|
+
"endpoint-type": {
|
|
49
|
+
type: "string",
|
|
50
50
|
choices: Object.values(EndpointType),
|
|
51
|
-
describe:
|
|
51
|
+
describe: "Endpoint type",
|
|
52
52
|
},
|
|
53
53
|
extended: {
|
|
54
|
-
type:
|
|
55
|
-
describe:
|
|
54
|
+
type: "boolean",
|
|
55
|
+
describe: "Show extended information",
|
|
56
56
|
},
|
|
57
57
|
psql: {
|
|
58
|
-
type:
|
|
59
|
-
describe:
|
|
58
|
+
type: "boolean",
|
|
59
|
+
describe: "Connect to a database via psql using connection string",
|
|
60
60
|
default: false,
|
|
61
61
|
},
|
|
62
62
|
fallback: {
|
|
63
|
-
type:
|
|
64
|
-
describe:
|
|
63
|
+
type: "boolean",
|
|
64
|
+
describe: "Force the embedded TypeScript psql fallback (for testing)",
|
|
65
65
|
default: false,
|
|
66
66
|
hidden: true,
|
|
67
67
|
},
|
|
68
68
|
ssl: {
|
|
69
|
-
type:
|
|
69
|
+
type: "string",
|
|
70
70
|
choices: SSL_MODES,
|
|
71
|
-
default:
|
|
72
|
-
describe:
|
|
71
|
+
default: "require",
|
|
72
|
+
describe: "SSL mode",
|
|
73
73
|
},
|
|
74
74
|
})
|
|
75
75
|
.middleware(fillSingleProject);
|
|
@@ -78,7 +78,7 @@ export const handler = async (props) => {
|
|
|
78
78
|
const projectId = props.projectId;
|
|
79
79
|
const parsedPIT = props.branch
|
|
80
80
|
? parsePITBranch(props.branch)
|
|
81
|
-
: { tag:
|
|
81
|
+
: { tag: "head", branch: "" };
|
|
82
82
|
if (props.branch) {
|
|
83
83
|
props.branch = parsedPIT.branch;
|
|
84
84
|
}
|
|
@@ -90,7 +90,7 @@ export const handler = async (props) => {
|
|
|
90
90
|
endpoint = endpoints[0];
|
|
91
91
|
}
|
|
92
92
|
if (!endpoint) {
|
|
93
|
-
throw new Error(`No ${props.endpointType ??
|
|
93
|
+
throw new Error(`No ${props.endpointType ?? ""} endpoint found for the branch: ${branchId}`);
|
|
94
94
|
}
|
|
95
95
|
const role = props.roleName ||
|
|
96
96
|
(await props.apiClient
|
|
@@ -104,7 +104,7 @@ export const handler = async (props) => {
|
|
|
104
104
|
}
|
|
105
105
|
throw new Error(`Multiple roles found for the branch, please provide one with the --role-name option: ${data.roles
|
|
106
106
|
.map((r) => r.name)
|
|
107
|
-
.join(
|
|
107
|
+
.join(", ")}`);
|
|
108
108
|
}));
|
|
109
109
|
const { data: { databases: branchDatabases }, } = await props.apiClient.listProjectBranchDatabases(projectId, branchId);
|
|
110
110
|
const database = props.databaseName ||
|
|
@@ -117,7 +117,7 @@ export const handler = async (props) => {
|
|
|
117
117
|
}
|
|
118
118
|
throw new Error(`Multiple databases found for the branch, please provide one with the --database-name option: ${branchDatabases
|
|
119
119
|
.map((d) => d.name)
|
|
120
|
-
.join(
|
|
120
|
+
.join(", ")}`);
|
|
121
121
|
})();
|
|
122
122
|
if (!branchDatabases.find((d) => d.name === database)) {
|
|
123
123
|
throw new Error(`Database not found: ${database}`);
|
|
@@ -126,7 +126,7 @@ export const handler = async (props) => {
|
|
|
126
126
|
let host = props.pooled
|
|
127
127
|
? endpoint.host.replace(endpoint.id, `${endpoint.id}-pooler`)
|
|
128
128
|
: endpoint.host;
|
|
129
|
-
if (parsedPIT.tag !==
|
|
129
|
+
if (parsedPIT.tag !== "head") {
|
|
130
130
|
host = endpoint.host.replace(endpoint.id, endpoint.branch_id);
|
|
131
131
|
}
|
|
132
132
|
const connectionString = new URL(`postgresql://${host}`);
|
|
@@ -134,26 +134,26 @@ export const handler = async (props) => {
|
|
|
134
134
|
connectionString.username = role;
|
|
135
135
|
connectionString.password = password;
|
|
136
136
|
if (props.prisma) {
|
|
137
|
-
connectionString.searchParams.set(
|
|
137
|
+
connectionString.searchParams.set("connect_timeout", "30");
|
|
138
138
|
if (props.pooled) {
|
|
139
|
-
connectionString.searchParams.set(
|
|
140
|
-
connectionString.searchParams.set(
|
|
139
|
+
connectionString.searchParams.set("pool_timeout", "30");
|
|
140
|
+
connectionString.searchParams.set("pgbouncer", "true");
|
|
141
141
|
}
|
|
142
142
|
}
|
|
143
|
-
if (props.ssl !==
|
|
144
|
-
connectionString.searchParams.set(
|
|
145
|
-
connectionString.searchParams.set(
|
|
143
|
+
if (props.ssl !== "omit") {
|
|
144
|
+
connectionString.searchParams.set("sslmode", props.ssl);
|
|
145
|
+
connectionString.searchParams.set("channel_binding", "require");
|
|
146
146
|
}
|
|
147
|
-
if (parsedPIT.tag ===
|
|
148
|
-
connectionString.searchParams.set(
|
|
147
|
+
if (parsedPIT.tag === "lsn") {
|
|
148
|
+
connectionString.searchParams.set("options", `neon_lsn:${parsedPIT.lsn}`);
|
|
149
149
|
}
|
|
150
|
-
else if (parsedPIT.tag ===
|
|
151
|
-
connectionString.searchParams.set(
|
|
150
|
+
else if (parsedPIT.tag === "timestamp") {
|
|
151
|
+
connectionString.searchParams.set("options", `neon_timestamp:${parsedPIT.timestamp}`);
|
|
152
152
|
}
|
|
153
153
|
if (props.psql) {
|
|
154
|
-
const psqlArgs = props[
|
|
154
|
+
const psqlArgs = props["--"];
|
|
155
155
|
await psql(connectionString.toString(), psqlArgs, {
|
|
156
|
-
mode: props.fallback ?
|
|
156
|
+
mode: props.fallback ? "ts" : "auto",
|
|
157
157
|
});
|
|
158
158
|
}
|
|
159
159
|
else if (props.extended) {
|
|
@@ -164,9 +164,9 @@ export const handler = async (props) => {
|
|
|
164
164
|
password,
|
|
165
165
|
database,
|
|
166
166
|
options: connectionString.searchParams.toString(),
|
|
167
|
-
}, { fields: [
|
|
167
|
+
}, { fields: ["host", "role", "password", "database"] });
|
|
168
168
|
}
|
|
169
169
|
else {
|
|
170
|
-
process.stdout.write(connectionString.toString() +
|
|
170
|
+
process.stdout.write(connectionString.toString() + "\n");
|
|
171
171
|
}
|
|
172
172
|
};
|
|
@@ -1,142 +1,141 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { branchIdFromProps, fillSingleProject, resolveSingleDatabase, } from
|
|
4
|
-
import {
|
|
5
|
-
import { writer } from '../writer.js';
|
|
1
|
+
import { isNeonApiError, retryOnLock } from "../api.js";
|
|
2
|
+
import { log } from "../log.js";
|
|
3
|
+
import { branchIdFromProps, fillSingleProject, resolveSingleDatabase, } from "../utils/enrichers.js";
|
|
4
|
+
import { writer } from "../writer.js";
|
|
6
5
|
const SETTINGS_FIELDS = [
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
6
|
+
"db_aggregates_enabled",
|
|
7
|
+
"db_anon_role",
|
|
8
|
+
"db_extra_search_path",
|
|
9
|
+
"db_max_rows",
|
|
10
|
+
"db_schemas",
|
|
11
|
+
"jwt_role_claim_key",
|
|
12
|
+
"jwt_cache_max_lifetime",
|
|
13
|
+
"openapi_mode",
|
|
14
|
+
"server_cors_allowed_origins",
|
|
15
|
+
"server_timing_enabled",
|
|
17
16
|
];
|
|
18
17
|
const settingsFlags = {
|
|
19
|
-
|
|
20
|
-
type:
|
|
21
|
-
describe:
|
|
18
|
+
"db-aggregates-enabled": {
|
|
19
|
+
type: "boolean",
|
|
20
|
+
describe: "Enable aggregate functions in queries",
|
|
22
21
|
},
|
|
23
|
-
|
|
24
|
-
type:
|
|
25
|
-
describe:
|
|
22
|
+
"db-anon-role": {
|
|
23
|
+
type: "string",
|
|
24
|
+
describe: "Database role used for anonymous (unauthenticated) requests",
|
|
26
25
|
},
|
|
27
|
-
|
|
28
|
-
type:
|
|
29
|
-
describe:
|
|
26
|
+
"db-extra-search-path": {
|
|
27
|
+
type: "string",
|
|
28
|
+
describe: "Extra schemas appended to the search path",
|
|
30
29
|
},
|
|
31
|
-
|
|
32
|
-
type:
|
|
33
|
-
describe:
|
|
30
|
+
"db-max-rows": {
|
|
31
|
+
type: "number",
|
|
32
|
+
describe: "Maximum number of rows returned by a single request",
|
|
34
33
|
},
|
|
35
|
-
|
|
36
|
-
type:
|
|
37
|
-
describe:
|
|
34
|
+
"db-schemas": {
|
|
35
|
+
type: "string",
|
|
36
|
+
describe: "Comma-separated list of schemas exposed via the Data API",
|
|
38
37
|
},
|
|
39
|
-
|
|
40
|
-
type:
|
|
41
|
-
describe:
|
|
38
|
+
"jwt-role-claim-key": {
|
|
39
|
+
type: "string",
|
|
40
|
+
describe: "JWT claim path used to extract the role",
|
|
42
41
|
},
|
|
43
|
-
|
|
44
|
-
type:
|
|
45
|
-
describe:
|
|
42
|
+
"jwt-cache-max-lifetime": {
|
|
43
|
+
type: "number",
|
|
44
|
+
describe: "Maximum JWT cache lifetime in seconds",
|
|
46
45
|
},
|
|
47
|
-
|
|
48
|
-
type:
|
|
49
|
-
choices: [
|
|
50
|
-
describe:
|
|
46
|
+
"openapi-mode": {
|
|
47
|
+
type: "string",
|
|
48
|
+
choices: ["ignore-privileges", "disabled"],
|
|
49
|
+
describe: "OpenAPI mode",
|
|
51
50
|
},
|
|
52
|
-
|
|
53
|
-
type:
|
|
54
|
-
describe:
|
|
51
|
+
"server-cors-allowed-origins": {
|
|
52
|
+
type: "string",
|
|
53
|
+
describe: "CORS allowed origins",
|
|
55
54
|
},
|
|
56
|
-
|
|
57
|
-
type:
|
|
58
|
-
describe:
|
|
55
|
+
"server-timing-enabled": {
|
|
56
|
+
type: "boolean",
|
|
57
|
+
describe: "Enable Server-Timing response headers",
|
|
59
58
|
},
|
|
60
59
|
};
|
|
61
|
-
export const command =
|
|
62
|
-
export const describe =
|
|
60
|
+
export const command = "data-api";
|
|
61
|
+
export const describe = "Manage the Neon Data API for a database";
|
|
63
62
|
export const builder = (argv) => argv
|
|
64
|
-
.usage(
|
|
63
|
+
.usage("$0 data-api <sub-command> [options]")
|
|
65
64
|
.options({
|
|
66
|
-
|
|
67
|
-
describe:
|
|
68
|
-
type:
|
|
65
|
+
"project-id": {
|
|
66
|
+
describe: "Project ID",
|
|
67
|
+
type: "string",
|
|
69
68
|
},
|
|
70
69
|
branch: {
|
|
71
|
-
describe:
|
|
72
|
-
type:
|
|
70
|
+
describe: "Branch ID or name",
|
|
71
|
+
type: "string",
|
|
73
72
|
},
|
|
74
73
|
database: {
|
|
75
|
-
describe:
|
|
76
|
-
type:
|
|
74
|
+
describe: "Database name",
|
|
75
|
+
type: "string",
|
|
77
76
|
},
|
|
78
77
|
})
|
|
79
78
|
.middleware(fillSingleProject)
|
|
80
|
-
.command(
|
|
81
|
-
|
|
82
|
-
type:
|
|
83
|
-
choices: [
|
|
84
|
-
describe:
|
|
79
|
+
.command("create", "Provision the Neon Data API for a database", (yargs) => yargs.options({
|
|
80
|
+
"auth-provider": {
|
|
81
|
+
type: "string",
|
|
82
|
+
choices: ["neon_auth", "external"],
|
|
83
|
+
describe: "Authentication provider",
|
|
85
84
|
},
|
|
86
|
-
|
|
87
|
-
type:
|
|
88
|
-
describe:
|
|
85
|
+
"jwks-url": {
|
|
86
|
+
type: "string",
|
|
87
|
+
describe: "URL that lists the JWKS (used with external auth)",
|
|
89
88
|
},
|
|
90
|
-
|
|
91
|
-
type:
|
|
92
|
-
describe:
|
|
89
|
+
"provider-name": {
|
|
90
|
+
type: "string",
|
|
91
|
+
describe: "Name of the auth provider (e.g. Clerk, Stytch, Auth0)",
|
|
93
92
|
},
|
|
94
|
-
|
|
95
|
-
type:
|
|
96
|
-
describe:
|
|
93
|
+
"jwt-audience": {
|
|
94
|
+
type: "string",
|
|
95
|
+
describe: "Expected JWT audience claim",
|
|
97
96
|
},
|
|
98
|
-
|
|
99
|
-
type:
|
|
100
|
-
describe:
|
|
97
|
+
"add-default-grants": {
|
|
98
|
+
type: "boolean",
|
|
99
|
+
describe: "Grant all permissions on tables in the public schema to authenticated users",
|
|
101
100
|
},
|
|
102
|
-
|
|
103
|
-
type:
|
|
104
|
-
describe:
|
|
101
|
+
"skip-auth-schema": {
|
|
102
|
+
type: "boolean",
|
|
103
|
+
describe: "Skip creating the auth schema and RLS functions",
|
|
105
104
|
},
|
|
106
105
|
...settingsFlags,
|
|
107
106
|
}), (args) => create(args))
|
|
108
|
-
.command(
|
|
109
|
-
.command(
|
|
107
|
+
.command("get", "Show the Neon Data API status and settings", (yargs) => yargs, (args) => get(args))
|
|
108
|
+
.command("update", "Update Neon Data API settings (merges with current settings by default)", (yargs) => yargs.options({
|
|
110
109
|
replace: {
|
|
111
|
-
type:
|
|
110
|
+
type: "boolean",
|
|
112
111
|
default: false,
|
|
113
|
-
describe:
|
|
112
|
+
describe: "Replace settings with only the flags provided. Omitted settings revert to server defaults.",
|
|
114
113
|
},
|
|
115
114
|
...settingsFlags,
|
|
116
115
|
}), (args) => update(args))
|
|
117
|
-
.command(
|
|
118
|
-
.command(
|
|
116
|
+
.command("refresh-schema", "Refresh the Data API schema cache without changing settings", (yargs) => yargs, (args) => refreshSchema(args))
|
|
117
|
+
.command("delete", "Tear down the Neon Data API for a database", (yargs) => yargs, (args) => deleteDataApi(args));
|
|
119
118
|
export const handler = (args) => {
|
|
120
119
|
return args;
|
|
121
120
|
};
|
|
122
121
|
const TOP_LEVEL_CREATE_FIELDS = [
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
122
|
+
"auth_provider",
|
|
123
|
+
"jwks_url",
|
|
124
|
+
"provider_name",
|
|
125
|
+
"jwt_audience",
|
|
126
|
+
"add_default_grants",
|
|
127
|
+
"skip_auth_schema",
|
|
129
128
|
];
|
|
130
|
-
const argKey = (snake) => snake.replace(/_/g,
|
|
129
|
+
const argKey = (snake) => snake.replace(/_/g, "-");
|
|
131
130
|
const buildSettings = (args) => {
|
|
132
131
|
const settings = {};
|
|
133
132
|
for (const field of SETTINGS_FIELDS) {
|
|
134
133
|
const value = args[argKey(field)];
|
|
135
134
|
if (value === undefined)
|
|
136
135
|
continue;
|
|
137
|
-
if (field ===
|
|
136
|
+
if (field === "db_schemas" && typeof value === "string") {
|
|
138
137
|
settings[field] = value
|
|
139
|
-
.split(
|
|
138
|
+
.split(",")
|
|
140
139
|
.map((s) => s.trim())
|
|
141
140
|
.filter(Boolean);
|
|
142
141
|
}
|
|
@@ -170,9 +169,9 @@ const create = async (props) => {
|
|
|
170
169
|
});
|
|
171
170
|
const body = buildCreateBody(props);
|
|
172
171
|
const { data } = await retryOnLock(() => props.apiClient.createProjectBranchDataApi(props.projectId, branchId, database, body));
|
|
173
|
-
writer(props).end(data, { fields: [
|
|
172
|
+
writer(props).end(data, { fields: ["url"] });
|
|
174
173
|
};
|
|
175
|
-
const GET_FIELDS = [
|
|
174
|
+
const GET_FIELDS = ["url", "status", "db_schemas"];
|
|
176
175
|
const get = async (props) => {
|
|
177
176
|
const branchId = await branchIdFromProps(props);
|
|
178
177
|
const database = await resolveSingleDatabase({
|
|
@@ -189,11 +188,11 @@ const get = async (props) => {
|
|
|
189
188
|
const tableRow = {
|
|
190
189
|
url: publicData.url,
|
|
191
190
|
status: publicData.status,
|
|
192
|
-
db_schemas: (publicData.settings?.db_schemas ?? []).join(
|
|
191
|
+
db_schemas: (publicData.settings?.db_schemas ?? []).join(", "),
|
|
193
192
|
};
|
|
194
|
-
if (props.output ===
|
|
193
|
+
if (props.output === "json" || props.output === "yaml") {
|
|
195
194
|
writer(props).end(publicData, {
|
|
196
|
-
fields: [
|
|
195
|
+
fields: ["url", "status", "settings"],
|
|
197
196
|
});
|
|
198
197
|
return;
|
|
199
198
|
}
|
|
@@ -209,7 +208,7 @@ const update = async (props) => {
|
|
|
209
208
|
});
|
|
210
209
|
const userSettings = buildSettings(props);
|
|
211
210
|
if (!userSettings) {
|
|
212
|
-
throw new Error(
|
|
211
|
+
throw new Error("No settings flags provided. Pass at least one setting flag to update, or use `data-api refresh-schema` to refresh the schema cache without changing settings.");
|
|
213
212
|
}
|
|
214
213
|
let settings;
|
|
215
214
|
if (props.replace) {
|
|
@@ -222,7 +221,7 @@ const update = async (props) => {
|
|
|
222
221
|
current = data.settings ?? undefined;
|
|
223
222
|
}
|
|
224
223
|
catch (err) {
|
|
225
|
-
if (
|
|
224
|
+
if (isNeonApiError(err) && err.status === 404) {
|
|
226
225
|
throw new Error(`Data API is not provisioned for ${database} on branch ${branchId}. Run \`neonctl data-api create\` first.`);
|
|
227
226
|
}
|
|
228
227
|
throw err;
|
|
@@ -239,7 +238,7 @@ const update = async (props) => {
|
|
|
239
238
|
await retryOnLock(() => props.apiClient.updateProjectBranchDataApi(props.projectId, branchId, database, body));
|
|
240
239
|
}
|
|
241
240
|
catch (err) {
|
|
242
|
-
if (
|
|
241
|
+
if (isNeonApiError(err) && err.status === 404) {
|
|
243
242
|
throw new Error(`Data API is not provisioned for ${database} on branch ${branchId}. Run \`neonctl data-api create\` first.`);
|
|
244
243
|
}
|
|
245
244
|
throw err;
|
|
@@ -258,7 +257,7 @@ const refreshSchema = async (props) => {
|
|
|
258
257
|
await retryOnLock(() => props.apiClient.updateProjectBranchDataApi(props.projectId, branchId, database, {}));
|
|
259
258
|
}
|
|
260
259
|
catch (err) {
|
|
261
|
-
if (
|
|
260
|
+
if (isNeonApiError(err) && err.status === 404) {
|
|
262
261
|
throw new Error(`Data API is not provisioned for ${database} on branch ${branchId}. Run \`neonctl data-api create\` first.`);
|
|
263
262
|
}
|
|
264
263
|
throw err;
|
|
@@ -277,7 +276,7 @@ const deleteDataApi = async (props) => {
|
|
|
277
276
|
await retryOnLock(() => props.apiClient.deleteProjectBranchDataApi(props.projectId, branchId, database));
|
|
278
277
|
}
|
|
279
278
|
catch (err) {
|
|
280
|
-
if (
|
|
279
|
+
if (isNeonApiError(err) && err.status === 404) {
|
|
281
280
|
throw new Error(`Data API is not provisioned for ${database} on branch ${branchId}.`);
|
|
282
281
|
}
|
|
283
282
|
throw err;
|
|
@@ -1,36 +1,36 @@
|
|
|
1
|
-
import { retryOnLock } from
|
|
2
|
-
import { branchIdFromProps, fillSingleProject } from
|
|
3
|
-
import { writer } from
|
|
4
|
-
export const DATABASE_FIELDS = [
|
|
5
|
-
export const command =
|
|
6
|
-
export const describe =
|
|
7
|
-
export const aliases = [
|
|
1
|
+
import { retryOnLock } from "../api.js";
|
|
2
|
+
import { branchIdFromProps, fillSingleProject } from "../utils/enrichers.js";
|
|
3
|
+
import { writer } from "../writer.js";
|
|
4
|
+
export const DATABASE_FIELDS = ["name", "owner_name", "created_at"];
|
|
5
|
+
export const command = "databases";
|
|
6
|
+
export const describe = "Manage databases";
|
|
7
|
+
export const aliases = ["database", "db"];
|
|
8
8
|
export const builder = (argv) => argv
|
|
9
|
-
.usage(
|
|
9
|
+
.usage("$0 databases <sub-command> [options]")
|
|
10
10
|
.options({
|
|
11
|
-
|
|
12
|
-
describe:
|
|
13
|
-
type:
|
|
11
|
+
"project-id": {
|
|
12
|
+
describe: "Project ID",
|
|
13
|
+
type: "string",
|
|
14
14
|
},
|
|
15
15
|
branch: {
|
|
16
|
-
describe:
|
|
17
|
-
type:
|
|
16
|
+
describe: "Branch ID or name",
|
|
17
|
+
type: "string",
|
|
18
18
|
},
|
|
19
19
|
})
|
|
20
20
|
.middleware(fillSingleProject)
|
|
21
|
-
.command(
|
|
22
|
-
.command(
|
|
21
|
+
.command("list", "List databases", (yargs) => yargs, (args) => list(args))
|
|
22
|
+
.command("create", "Create a database", (yargs) => yargs.options({
|
|
23
23
|
name: {
|
|
24
|
-
describe:
|
|
25
|
-
type:
|
|
24
|
+
describe: "Database name",
|
|
25
|
+
type: "string",
|
|
26
26
|
demandOption: true,
|
|
27
27
|
},
|
|
28
|
-
|
|
29
|
-
describe:
|
|
30
|
-
type:
|
|
28
|
+
"owner-name": {
|
|
29
|
+
describe: "Owner name",
|
|
30
|
+
type: "string",
|
|
31
31
|
},
|
|
32
32
|
}), (args) => create(args))
|
|
33
|
-
.command(
|
|
33
|
+
.command("delete <database>", "Delete a database", (yargs) => yargs, (args) => deleteDb(args));
|
|
34
34
|
export const handler = (args) => {
|
|
35
35
|
return args;
|
|
36
36
|
};
|
|
@@ -53,12 +53,12 @@ export const create = async (props) => {
|
|
|
53
53
|
if (data.roles.length > 1) {
|
|
54
54
|
throw new Error(`More than one role found in branch ${branchId}. Please specify the owner name. Roles: ${data.roles
|
|
55
55
|
.map((r) => r.name)
|
|
56
|
-
.join(
|
|
56
|
+
.join(", ")}`);
|
|
57
57
|
}
|
|
58
58
|
return data.roles[0].name;
|
|
59
59
|
}));
|
|
60
60
|
if (!owner) {
|
|
61
|
-
throw new Error(
|
|
61
|
+
throw new Error("No owner found");
|
|
62
62
|
}
|
|
63
63
|
const { data } = await retryOnLock(() => props.apiClient.createProjectBranchDatabase(props.projectId, branchId, {
|
|
64
64
|
database: {
|
|
@@ -73,7 +73,10 @@ export const create = async (props) => {
|
|
|
73
73
|
export const deleteDb = async (props) => {
|
|
74
74
|
const branchId = await branchIdFromProps(props);
|
|
75
75
|
const { data } = await retryOnLock(() => props.apiClient.deleteProjectBranchDatabase(props.projectId, branchId, props.database));
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
76
|
+
// A 204 (database already gone) carries no body; only a 200 returns it.
|
|
77
|
+
if (data) {
|
|
78
|
+
writer(props).end(data.database, {
|
|
79
|
+
fields: DATABASE_FIELDS,
|
|
80
|
+
});
|
|
81
|
+
}
|
|
79
82
|
};
|
package/dist/commands/deploy.js
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
import { fillSingleProject } from
|
|
2
|
-
import { applyCmd, applyFlags, envFlag, envPullFlag, } from
|
|
3
|
-
export const command =
|
|
4
|
-
export const describe =
|
|
1
|
+
import { fillSingleProject } from "../utils/enrichers.js";
|
|
2
|
+
import { applyCmd, applyFlags, envFlag, envPullFlag, } from "./config.js";
|
|
3
|
+
export const command = "deploy";
|
|
4
|
+
export const describe = "Apply a neon.ts policy to a branch (alias for `config apply`)";
|
|
5
5
|
export const builder = (argv) => argv
|
|
6
|
-
.usage(
|
|
6
|
+
.usage("$0 deploy [options]")
|
|
7
7
|
.options({
|
|
8
|
-
|
|
9
|
-
describe:
|
|
10
|
-
type:
|
|
8
|
+
"project-id": {
|
|
9
|
+
describe: "Project ID",
|
|
10
|
+
type: "string",
|
|
11
11
|
},
|
|
12
12
|
branch: {
|
|
13
|
-
describe:
|
|
14
|
-
type:
|
|
13
|
+
describe: "Branch ID or name",
|
|
14
|
+
type: "string",
|
|
15
15
|
},
|
|
16
16
|
config: {
|
|
17
|
-
describe:
|
|
18
|
-
type:
|
|
17
|
+
describe: "Path to a neon.ts policy (defaults to walking up from cwd)",
|
|
18
|
+
type: "string",
|
|
19
19
|
},
|
|
20
20
|
...envFlag,
|
|
21
21
|
...applyFlags,
|