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.
Files changed (136) hide show
  1. package/README.md +35 -3
  2. package/dist/analytics.js +52 -34
  3. package/dist/api.js +643 -13
  4. package/dist/auth.js +50 -44
  5. package/dist/cli.js +8 -1
  6. package/dist/commands/auth.js +64 -51
  7. package/dist/commands/bootstrap.js +115 -157
  8. package/dist/commands/branches.js +160 -150
  9. package/dist/commands/bucket.js +183 -146
  10. package/dist/commands/checkout.js +51 -51
  11. package/dist/commands/config.js +228 -82
  12. package/dist/commands/connection_string.js +62 -62
  13. package/dist/commands/data_api.js +100 -101
  14. package/dist/commands/databases.js +29 -26
  15. package/dist/commands/deploy.js +12 -12
  16. package/dist/commands/dev.js +114 -114
  17. package/dist/commands/env.js +43 -43
  18. package/dist/commands/functions.js +101 -104
  19. package/dist/commands/index.js +27 -25
  20. package/dist/commands/init.js +23 -22
  21. package/dist/commands/ip_allow.js +29 -29
  22. package/dist/commands/link.js +232 -182
  23. package/dist/commands/neon_auth.js +385 -370
  24. package/dist/commands/operations.js +11 -11
  25. package/dist/commands/orgs.js +8 -8
  26. package/dist/commands/projects.js +103 -101
  27. package/dist/commands/psql.js +31 -31
  28. package/dist/commands/roles.js +27 -24
  29. package/dist/commands/schema_diff.js +25 -26
  30. package/dist/commands/set_context.js +17 -17
  31. package/dist/commands/status.js +40 -0
  32. package/dist/commands/user.js +5 -5
  33. package/dist/commands/vpc_endpoints.js +50 -50
  34. package/dist/config.js +7 -7
  35. package/dist/config_format.js +5 -5
  36. package/dist/context.js +37 -14
  37. package/dist/current_branch_fast_path.js +55 -0
  38. package/dist/dev/env.js +33 -33
  39. package/dist/dev/functions.js +4 -4
  40. package/dist/dev/inputs.js +6 -6
  41. package/dist/dev/runtime.js +25 -25
  42. package/dist/env.js +14 -14
  43. package/dist/env_file.js +13 -13
  44. package/dist/errors.js +68 -5
  45. package/dist/functions_api.js +10 -10
  46. package/dist/help.js +15 -15
  47. package/dist/index.js +110 -107
  48. package/dist/log.js +2 -2
  49. package/dist/parameters.gen.js +14 -14
  50. package/dist/pkg.js +5 -5
  51. package/dist/psql/cli.js +4 -2
  52. package/dist/psql/command/cmd_cond.js +61 -61
  53. package/dist/psql/command/cmd_connect.js +159 -154
  54. package/dist/psql/command/cmd_copy.js +107 -97
  55. package/dist/psql/command/cmd_describe.js +368 -363
  56. package/dist/psql/command/cmd_format.js +276 -263
  57. package/dist/psql/command/cmd_io.js +269 -263
  58. package/dist/psql/command/cmd_lo.js +74 -66
  59. package/dist/psql/command/cmd_meta.js +148 -148
  60. package/dist/psql/command/cmd_misc.js +17 -17
  61. package/dist/psql/command/cmd_pipeline.js +142 -135
  62. package/dist/psql/command/cmd_restrict.js +25 -25
  63. package/dist/psql/command/cmd_show.js +183 -168
  64. package/dist/psql/command/dispatch.js +26 -26
  65. package/dist/psql/command/shared.js +14 -14
  66. package/dist/psql/complete/filenames.js +16 -16
  67. package/dist/psql/complete/index.js +4 -4
  68. package/dist/psql/complete/matcher.js +33 -32
  69. package/dist/psql/complete/psqlVars.js +173 -173
  70. package/dist/psql/complete/queries.js +5 -3
  71. package/dist/psql/complete/rules.js +900 -863
  72. package/dist/psql/core/common.js +136 -133
  73. package/dist/psql/core/help.js +343 -343
  74. package/dist/psql/core/mainloop.js +160 -153
  75. package/dist/psql/core/prompt.js +126 -123
  76. package/dist/psql/core/settings.js +111 -111
  77. package/dist/psql/core/sqlHelp.js +150 -150
  78. package/dist/psql/core/startup.js +211 -205
  79. package/dist/psql/core/syncVars.js +14 -14
  80. package/dist/psql/core/variables.js +24 -24
  81. package/dist/psql/describe/formatters.js +302 -289
  82. package/dist/psql/describe/processNamePattern.js +28 -28
  83. package/dist/psql/describe/queries.js +656 -651
  84. package/dist/psql/index.js +436 -411
  85. package/dist/psql/io/history.js +36 -36
  86. package/dist/psql/io/input.js +15 -15
  87. package/dist/psql/io/lineEditor/buffer.js +27 -25
  88. package/dist/psql/io/lineEditor/complete.js +15 -15
  89. package/dist/psql/io/lineEditor/filename.js +22 -22
  90. package/dist/psql/io/lineEditor/index.js +65 -62
  91. package/dist/psql/io/lineEditor/keymap.js +325 -318
  92. package/dist/psql/io/lineEditor/vt100.js +60 -60
  93. package/dist/psql/io/pgpass.js +18 -18
  94. package/dist/psql/io/pgservice.js +14 -14
  95. package/dist/psql/io/psqlrc.js +46 -46
  96. package/dist/psql/print/aligned.js +175 -166
  97. package/dist/psql/print/asciidoc.js +51 -51
  98. package/dist/psql/print/crosstab.js +34 -31
  99. package/dist/psql/print/csv.js +25 -22
  100. package/dist/psql/print/html.js +54 -54
  101. package/dist/psql/print/json.js +12 -12
  102. package/dist/psql/print/latex.js +118 -118
  103. package/dist/psql/print/pager.js +28 -26
  104. package/dist/psql/print/troff.js +48 -48
  105. package/dist/psql/print/unaligned.js +15 -14
  106. package/dist/psql/print/units.js +17 -17
  107. package/dist/psql/scanner/slash.js +48 -46
  108. package/dist/psql/scanner/sql.js +88 -84
  109. package/dist/psql/scanner/stringutils.js +21 -17
  110. package/dist/psql/types/index.js +7 -7
  111. package/dist/psql/types/scanner.js +8 -8
  112. package/dist/psql/wire/connection.js +341 -327
  113. package/dist/psql/wire/copy.js +7 -7
  114. package/dist/psql/wire/pipeline.js +26 -24
  115. package/dist/psql/wire/protocol.js +102 -102
  116. package/dist/psql/wire/sasl.js +62 -62
  117. package/dist/psql/wire/tls.js +79 -73
  118. package/dist/storage_api.js +22 -23
  119. package/dist/test_utils/fixtures.js +74 -41
  120. package/dist/test_utils/oauth_server.js +5 -5
  121. package/dist/utils/api_enums.js +33 -0
  122. package/dist/utils/branch_notice.js +5 -5
  123. package/dist/utils/branch_picker.js +26 -26
  124. package/dist/utils/compute_units.js +4 -4
  125. package/dist/utils/enrichers.js +28 -16
  126. package/dist/utils/esbuild.js +28 -28
  127. package/dist/utils/formats.js +1 -1
  128. package/dist/utils/middlewares.js +3 -3
  129. package/dist/utils/package_manager.js +68 -0
  130. package/dist/utils/point_in_time.js +12 -12
  131. package/dist/utils/psql.js +30 -30
  132. package/dist/utils/string.js +2 -2
  133. package/dist/utils/ui.js +9 -9
  134. package/dist/utils/zip.js +1 -1
  135. package/dist/writer.js +17 -17
  136. package/package.json +10 -12
@@ -1,75 +1,75 @@
1
- import { EndpointType, } from '@neondatabase/api-client';
2
- import { branchIdFromProps, fillSingleProject } from '../utils/enrichers.js';
3
- import { writer } from '../writer.js';
4
- import { psql } from '../utils/psql.js';
5
- import { parsePITBranch } from '../utils/point_in_time.js';
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
- 'require',
8
- 'verify-ca',
9
- 'verify-full',
10
- 'omit',
7
+ "require",
8
+ "verify-ca",
9
+ "verify-full",
10
+ "omit",
11
11
  ];
12
- export const command = 'connection-string [branch]';
13
- export const aliases = ['cs'];
14
- export const describe = 'Get connection string';
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('$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', {
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: 'string',
23
+ type: "string",
24
24
  })
25
25
  .options({
26
- 'project-id': {
27
- type: 'string',
28
- describe: 'Project ID',
26
+ "project-id": {
27
+ type: "string",
28
+ describe: "Project ID",
29
29
  },
30
- 'role-name': {
31
- type: 'string',
32
- describe: 'Role name',
30
+ "role-name": {
31
+ type: "string",
32
+ describe: "Role name",
33
33
  },
34
- 'database-name': {
35
- type: 'string',
36
- describe: 'Database name',
34
+ "database-name": {
35
+ type: "string",
36
+ describe: "Database name",
37
37
  },
38
38
  pooled: {
39
- type: 'boolean',
40
- describe: 'Use pooled connection',
39
+ type: "boolean",
40
+ describe: "Use pooled connection",
41
41
  default: false,
42
42
  },
43
43
  prisma: {
44
- type: 'boolean',
45
- describe: 'Use connection string for Prisma setup',
44
+ type: "boolean",
45
+ describe: "Use connection string for Prisma setup",
46
46
  default: false,
47
47
  },
48
- 'endpoint-type': {
49
- type: 'string',
48
+ "endpoint-type": {
49
+ type: "string",
50
50
  choices: Object.values(EndpointType),
51
- describe: 'Endpoint type',
51
+ describe: "Endpoint type",
52
52
  },
53
53
  extended: {
54
- type: 'boolean',
55
- describe: 'Show extended information',
54
+ type: "boolean",
55
+ describe: "Show extended information",
56
56
  },
57
57
  psql: {
58
- type: 'boolean',
59
- describe: 'Connect to a database via psql using connection string',
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: 'boolean',
64
- describe: 'Force the embedded TypeScript psql fallback (for testing)',
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: 'string',
69
+ type: "string",
70
70
  choices: SSL_MODES,
71
- default: 'require',
72
- describe: 'SSL mode',
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: 'head', branch: '' };
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 ?? ''} endpoint found for the branch: ${branchId}`);
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 !== 'head') {
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('connect_timeout', '30');
137
+ connectionString.searchParams.set("connect_timeout", "30");
138
138
  if (props.pooled) {
139
- connectionString.searchParams.set('pool_timeout', '30');
140
- connectionString.searchParams.set('pgbouncer', 'true');
139
+ connectionString.searchParams.set("pool_timeout", "30");
140
+ connectionString.searchParams.set("pgbouncer", "true");
141
141
  }
142
142
  }
143
- if (props.ssl !== 'omit') {
144
- connectionString.searchParams.set('sslmode', props.ssl);
145
- connectionString.searchParams.set('channel_binding', 'require');
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 === 'lsn') {
148
- connectionString.searchParams.set('options', `neon_lsn:${parsedPIT.lsn}`);
147
+ if (parsedPIT.tag === "lsn") {
148
+ connectionString.searchParams.set("options", `neon_lsn:${parsedPIT.lsn}`);
149
149
  }
150
- else if (parsedPIT.tag === 'timestamp') {
151
- connectionString.searchParams.set('options', `neon_timestamp:${parsedPIT.timestamp}`);
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 ? 'ts' : 'auto',
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: ['host', 'role', 'password', 'database'] });
167
+ }, { fields: ["host", "role", "password", "database"] });
168
168
  }
169
169
  else {
170
- process.stdout.write(connectionString.toString() + '\n');
170
+ process.stdout.write(connectionString.toString() + "\n");
171
171
  }
172
172
  };
@@ -1,142 +1,141 @@
1
- import { isAxiosError } from 'axios';
2
- import { retryOnLock } from '../api.js';
3
- import { branchIdFromProps, fillSingleProject, resolveSingleDatabase, } from '../utils/enrichers.js';
4
- import { log } from '../log.js';
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
- 'db_aggregates_enabled',
8
- 'db_anon_role',
9
- 'db_extra_search_path',
10
- 'db_max_rows',
11
- 'db_schemas',
12
- 'jwt_role_claim_key',
13
- 'jwt_cache_max_lifetime',
14
- 'openapi_mode',
15
- 'server_cors_allowed_origins',
16
- 'server_timing_enabled',
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
- 'db-aggregates-enabled': {
20
- type: 'boolean',
21
- describe: 'Enable aggregate functions in queries',
18
+ "db-aggregates-enabled": {
19
+ type: "boolean",
20
+ describe: "Enable aggregate functions in queries",
22
21
  },
23
- 'db-anon-role': {
24
- type: 'string',
25
- describe: 'Database role used for anonymous (unauthenticated) requests',
22
+ "db-anon-role": {
23
+ type: "string",
24
+ describe: "Database role used for anonymous (unauthenticated) requests",
26
25
  },
27
- 'db-extra-search-path': {
28
- type: 'string',
29
- describe: 'Extra schemas appended to the search path',
26
+ "db-extra-search-path": {
27
+ type: "string",
28
+ describe: "Extra schemas appended to the search path",
30
29
  },
31
- 'db-max-rows': {
32
- type: 'number',
33
- describe: 'Maximum number of rows returned by a single request',
30
+ "db-max-rows": {
31
+ type: "number",
32
+ describe: "Maximum number of rows returned by a single request",
34
33
  },
35
- 'db-schemas': {
36
- type: 'string',
37
- describe: 'Comma-separated list of schemas exposed via the Data API',
34
+ "db-schemas": {
35
+ type: "string",
36
+ describe: "Comma-separated list of schemas exposed via the Data API",
38
37
  },
39
- 'jwt-role-claim-key': {
40
- type: 'string',
41
- describe: 'JWT claim path used to extract the role',
38
+ "jwt-role-claim-key": {
39
+ type: "string",
40
+ describe: "JWT claim path used to extract the role",
42
41
  },
43
- 'jwt-cache-max-lifetime': {
44
- type: 'number',
45
- describe: 'Maximum JWT cache lifetime in seconds',
42
+ "jwt-cache-max-lifetime": {
43
+ type: "number",
44
+ describe: "Maximum JWT cache lifetime in seconds",
46
45
  },
47
- 'openapi-mode': {
48
- type: 'string',
49
- choices: ['ignore-privileges', 'disabled'],
50
- describe: 'OpenAPI mode',
46
+ "openapi-mode": {
47
+ type: "string",
48
+ choices: ["ignore-privileges", "disabled"],
49
+ describe: "OpenAPI mode",
51
50
  },
52
- 'server-cors-allowed-origins': {
53
- type: 'string',
54
- describe: 'CORS allowed origins',
51
+ "server-cors-allowed-origins": {
52
+ type: "string",
53
+ describe: "CORS allowed origins",
55
54
  },
56
- 'server-timing-enabled': {
57
- type: 'boolean',
58
- describe: 'Enable Server-Timing response headers',
55
+ "server-timing-enabled": {
56
+ type: "boolean",
57
+ describe: "Enable Server-Timing response headers",
59
58
  },
60
59
  };
61
- export const command = 'data-api';
62
- export const describe = 'Manage the Neon Data API for a database';
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('$0 data-api <sub-command> [options]')
63
+ .usage("$0 data-api <sub-command> [options]")
65
64
  .options({
66
- 'project-id': {
67
- describe: 'Project ID',
68
- type: 'string',
65
+ "project-id": {
66
+ describe: "Project ID",
67
+ type: "string",
69
68
  },
70
69
  branch: {
71
- describe: 'Branch ID or name',
72
- type: 'string',
70
+ describe: "Branch ID or name",
71
+ type: "string",
73
72
  },
74
73
  database: {
75
- describe: 'Database name',
76
- type: 'string',
74
+ describe: "Database name",
75
+ type: "string",
77
76
  },
78
77
  })
79
78
  .middleware(fillSingleProject)
80
- .command('create', 'Provision the Neon Data API for a database', (yargs) => yargs.options({
81
- 'auth-provider': {
82
- type: 'string',
83
- choices: ['neon_auth', 'external'],
84
- describe: 'Authentication provider',
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
- 'jwks-url': {
87
- type: 'string',
88
- describe: 'URL that lists the JWKS (used with external auth)',
85
+ "jwks-url": {
86
+ type: "string",
87
+ describe: "URL that lists the JWKS (used with external auth)",
89
88
  },
90
- 'provider-name': {
91
- type: 'string',
92
- describe: 'Name of the auth provider (e.g. Clerk, Stytch, Auth0)',
89
+ "provider-name": {
90
+ type: "string",
91
+ describe: "Name of the auth provider (e.g. Clerk, Stytch, Auth0)",
93
92
  },
94
- 'jwt-audience': {
95
- type: 'string',
96
- describe: 'Expected JWT audience claim',
93
+ "jwt-audience": {
94
+ type: "string",
95
+ describe: "Expected JWT audience claim",
97
96
  },
98
- 'add-default-grants': {
99
- type: 'boolean',
100
- describe: 'Grant all permissions on tables in the public schema to authenticated users',
97
+ "add-default-grants": {
98
+ type: "boolean",
99
+ describe: "Grant all permissions on tables in the public schema to authenticated users",
101
100
  },
102
- 'skip-auth-schema': {
103
- type: 'boolean',
104
- describe: 'Skip creating the auth schema and RLS functions',
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('get', 'Show the Neon Data API status and settings', (yargs) => yargs, (args) => get(args))
109
- .command('update', 'Update Neon Data API settings (merges with current settings by default)', (yargs) => yargs.options({
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: 'boolean',
110
+ type: "boolean",
112
111
  default: false,
113
- describe: 'Replace settings with only the flags provided. Omitted settings revert to server defaults.',
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('refresh-schema', 'Refresh the Data API schema cache without changing settings', (yargs) => yargs, (args) => refreshSchema(args))
118
- .command('delete', 'Tear down the Neon Data API for a database', (yargs) => yargs, (args) => deleteDataApi(args));
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
- 'auth_provider',
124
- 'jwks_url',
125
- 'provider_name',
126
- 'jwt_audience',
127
- 'add_default_grants',
128
- 'skip_auth_schema',
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 === 'db_schemas' && typeof value === 'string') {
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: ['url'] });
172
+ writer(props).end(data, { fields: ["url"] });
174
173
  };
175
- const GET_FIELDS = ['url', 'status', 'db_schemas'];
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 === 'json' || props.output === 'yaml') {
193
+ if (props.output === "json" || props.output === "yaml") {
195
194
  writer(props).end(publicData, {
196
- fields: ['url', 'status', 'settings'],
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('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.');
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 (isAxiosError(err) && err.response?.status === 404) {
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 (isAxiosError(err) && err.response?.status === 404) {
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 (isAxiosError(err) && err.response?.status === 404) {
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 (isAxiosError(err) && err.response?.status === 404) {
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 '../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'];
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('$0 databases <sub-command> [options]')
9
+ .usage("$0 databases <sub-command> [options]")
10
10
  .options({
11
- 'project-id': {
12
- describe: 'Project ID',
13
- type: 'string',
11
+ "project-id": {
12
+ describe: "Project ID",
13
+ type: "string",
14
14
  },
15
15
  branch: {
16
- describe: 'Branch ID or name',
17
- type: 'string',
16
+ describe: "Branch ID or name",
17
+ type: "string",
18
18
  },
19
19
  })
20
20
  .middleware(fillSingleProject)
21
- .command('list', 'List databases', (yargs) => yargs, (args) => list(args))
22
- .command('create', 'Create a database', (yargs) => yargs.options({
21
+ .command("list", "List databases", (yargs) => yargs, (args) => list(args))
22
+ .command("create", "Create a database", (yargs) => yargs.options({
23
23
  name: {
24
- describe: 'Database name',
25
- type: 'string',
24
+ describe: "Database name",
25
+ type: "string",
26
26
  demandOption: true,
27
27
  },
28
- 'owner-name': {
29
- describe: 'Owner name',
30
- type: 'string',
28
+ "owner-name": {
29
+ describe: "Owner name",
30
+ type: "string",
31
31
  },
32
32
  }), (args) => create(args))
33
- .command('delete <database>', 'Delete a database', (yargs) => yargs, (args) => deleteDb(args));
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('No owner found');
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
- writer(props).end(data.database, {
77
- fields: DATABASE_FIELDS,
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
  };
@@ -1,21 +1,21 @@
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`)';
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('$0 deploy [options]')
6
+ .usage("$0 deploy [options]")
7
7
  .options({
8
- 'project-id': {
9
- describe: 'Project ID',
10
- type: 'string',
8
+ "project-id": {
9
+ describe: "Project ID",
10
+ type: "string",
11
11
  },
12
12
  branch: {
13
- describe: 'Branch ID or name',
14
- type: 'string',
13
+ describe: "Branch ID or name",
14
+ type: "string",
15
15
  },
16
16
  config: {
17
- describe: 'Path to a neon.ts policy (defaults to walking up from cwd)',
18
- type: 'string',
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,