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,155 +1,154 @@
1
- import { NeonAuthSupportedAuthProvider, NeonAuthOauthProviderId, NeonAuthOauthProviderType, NeonAuthEmailVerificationMethod, } from '@neondatabase/api-client';
2
- import { isAxiosError } from 'axios';
3
- import chalk from 'chalk';
4
- import { retryOnLock } from '../api.js';
5
- import { branchIdFromProps, fillSingleProject } from '../utils/enrichers.js';
6
- import { writer } from '../writer.js';
1
+ import chalk from "chalk";
2
+ import { codeFromBody, isNeonApiError, retryOnLock } from "../api.js";
3
+ import { NeonAuthEmailVerificationMethod, NeonAuthOauthProviderId, NeonAuthOauthProviderType, NeonAuthSupportedAuthProvider, } from "../utils/api_enums.js";
4
+ import { branchIdFromProps, fillSingleProject } from "../utils/enrichers.js";
5
+ import { writer } from "../writer.js";
7
6
  // Shared styled output helpers
8
7
  const printKvBlock = (title, entries) => {
9
8
  process.stdout.write(`\n${chalk.green(title)}\n`);
10
9
  for (const [key, value] of entries) {
11
- process.stdout.write(` ${chalk.green(key)} ${value ?? ''}\n`);
10
+ process.stdout.write(` ${chalk.green(key)} ${value ?? ""}\n`);
12
11
  }
13
- process.stdout.write('\n');
12
+ process.stdout.write("\n");
14
13
  };
15
14
  const printMessage = (message) => {
16
15
  process.stdout.write(`\n${chalk.green(message)}\n\n`);
17
16
  };
18
17
  const INTEGRATION_RESPONSE_FIELDS = [
19
- 'auth_provider',
20
- 'db_name',
21
- 'base_url',
22
- 'schema_name',
23
- 'table_name',
24
- 'jwks_url',
18
+ "auth_provider",
19
+ "db_name",
20
+ "base_url",
21
+ "schema_name",
22
+ "table_name",
23
+ "jwks_url",
25
24
  ];
26
25
  const INTEGRATION_STATUS_FIELDS = [
27
- 'auth_provider',
28
- 'branch_id',
29
- 'db_name',
30
- 'base_url',
31
- 'created_at',
32
- 'jwks_url',
26
+ "auth_provider",
27
+ "branch_id",
28
+ "db_name",
29
+ "base_url",
30
+ "created_at",
31
+ "jwks_url",
33
32
  ];
34
- const OAUTH_PROVIDER_FIELDS = ['id', 'type', 'client_id'];
35
- const ALLOW_LOCALHOST_FIELDS = ['allow_localhost'];
33
+ const OAUTH_PROVIDER_FIELDS = ["id", "type", "client_id"];
34
+ const ALLOW_LOCALHOST_FIELDS = ["allow_localhost"];
36
35
  const SUPPORTED_OAUTH_PROVIDERS = [
37
36
  NeonAuthOauthProviderId.Google,
38
37
  NeonAuthOauthProviderId.Github,
39
38
  NeonAuthOauthProviderId.Vercel,
40
39
  ];
41
- const DOMAIN_FIELDS = ['domain'];
40
+ const DOMAIN_FIELDS = ["domain"];
42
41
  const EMAIL_PASSWORD_FIELDS = [
43
- 'enabled',
44
- 'email_verification_method',
45
- 'require_email_verification',
46
- 'auto_sign_in_after_verification',
47
- 'send_verification_email_on_sign_up',
48
- 'send_verification_email_on_sign_in',
49
- 'disable_sign_up',
42
+ "enabled",
43
+ "email_verification_method",
44
+ "require_email_verification",
45
+ "auto_sign_in_after_verification",
46
+ "send_verification_email_on_sign_up",
47
+ "send_verification_email_on_sign_in",
48
+ "disable_sign_up",
50
49
  ];
51
50
  const EMAIL_PROVIDER_FIELDS = [
52
- 'type',
53
- 'host',
54
- 'port',
55
- 'username',
56
- 'sender_email',
57
- 'sender_name',
51
+ "type",
52
+ "host",
53
+ "port",
54
+ "username",
55
+ "sender_email",
56
+ "sender_name",
58
57
  ];
59
58
  const ORGANIZATION_FIELDS = [
60
- 'enabled',
61
- 'organization_limit',
62
- 'creator_role',
59
+ "enabled",
60
+ "organization_limit",
61
+ "creator_role",
63
62
  ];
64
63
  const WEBHOOK_FIELDS = [
65
- 'enabled',
66
- 'webhook_url',
67
- 'enabled_events',
68
- 'timeout_seconds',
64
+ "enabled",
65
+ "webhook_url",
66
+ "enabled_events",
67
+ "timeout_seconds",
69
68
  ];
70
- const TEST_EMAIL_FIELDS = ['success', 'error_message'];
71
- export const command = 'neon-auth';
72
- export const describe = 'Manage Neon Auth';
69
+ const TEST_EMAIL_FIELDS = ["success", "error_message"];
70
+ export const command = "neon-auth";
71
+ export const describe = "Manage Neon Auth";
73
72
  export const builder = (argv) => {
74
73
  return argv
75
- .usage('$0 neon-auth <sub-command> [options]')
74
+ .usage("$0 neon-auth <sub-command> [options]")
76
75
  .options({
77
- 'project-id': {
78
- describe: 'Project ID',
79
- type: 'string',
76
+ "project-id": {
77
+ describe: "Project ID",
78
+ type: "string",
80
79
  },
81
80
  branch: {
82
- describe: 'Branch ID or name',
83
- type: 'string',
81
+ describe: "Branch ID or name",
82
+ type: "string",
84
83
  },
85
84
  })
86
85
  .middleware(fillSingleProject)
87
- .command('enable', 'Enable Neon Auth on a branch', (yargs) => yargs.options({
88
- 'database-name': {
89
- describe: 'Database name to use for auth data',
90
- type: 'string',
86
+ .command("enable", "Enable Neon Auth on a branch", (yargs) => yargs.options({
87
+ "database-name": {
88
+ describe: "Database name to use for auth data",
89
+ type: "string",
91
90
  },
92
91
  }), async (args) => {
93
92
  await enable(args);
94
93
  })
95
- .command('status', 'Get Neon Auth status for a branch', (yargs) => yargs, async (args) => {
94
+ .command("status", "Get Neon Auth status for a branch", (yargs) => yargs, async (args) => {
96
95
  await status(args);
97
96
  })
98
- .command('disable', 'Disable Neon Auth on a branch', (yargs) => yargs.options({
99
- 'delete-data': {
100
- describe: 'Permanently delete all Neon Auth data and schema from the database',
101
- type: 'boolean',
97
+ .command("disable", "Disable Neon Auth on a branch", (yargs) => yargs.options({
98
+ "delete-data": {
99
+ describe: "Permanently delete all Neon Auth data and schema from the database",
100
+ type: "boolean",
102
101
  default: false,
103
102
  },
104
103
  }), async (args) => {
105
104
  await disable(args);
106
105
  })
107
- .command('oauth-provider', 'Manage OAuth providers', (yargs) => {
106
+ .command("oauth-provider", "Manage OAuth providers", (yargs) => {
108
107
  return yargs
109
- .usage('$0 neon-auth oauth-provider <sub-command> [options]')
110
- .command('list', 'List OAuth providers', (yargs) => yargs, async (args) => {
108
+ .usage("$0 neon-auth oauth-provider <sub-command> [options]")
109
+ .command("list", "List OAuth providers", (yargs) => yargs, async (args) => {
111
110
  await oauthProviderList(args);
112
111
  })
113
- .command('add', 'Add an OAuth provider', (yargs) => yargs.options({
114
- 'provider-id': {
115
- describe: `OAuth provider ID. Supported values: ${SUPPORTED_OAUTH_PROVIDERS.join(', ')}`,
116
- type: 'string',
112
+ .command("add", "Add an OAuth provider", (yargs) => yargs.options({
113
+ "provider-id": {
114
+ describe: `OAuth provider ID. Supported values: ${SUPPORTED_OAUTH_PROVIDERS.join(", ")}`,
115
+ type: "string",
117
116
  choices: SUPPORTED_OAUTH_PROVIDERS,
118
117
  demandOption: true,
119
118
  },
120
- 'oauth-client-id': {
119
+ "oauth-client-id": {
121
120
  describe: "OAuth client ID from your provider app. Omit to use Neon's shared OAuth app.",
122
- type: 'string',
121
+ type: "string",
123
122
  },
124
- 'oauth-client-secret': {
123
+ "oauth-client-secret": {
125
124
  describe: "OAuth client secret from your provider app. Omit to use Neon's shared OAuth app.",
126
- type: 'string',
125
+ type: "string",
127
126
  },
128
127
  }), async (args) => {
129
128
  await oauthProviderAdd(args);
130
129
  })
131
- .command('update', 'Update an OAuth provider', (yargs) => yargs.options({
132
- 'provider-id': {
133
- describe: `OAuth provider ID. Supported values: ${SUPPORTED_OAUTH_PROVIDERS.join(', ')}`,
134
- type: 'string',
130
+ .command("update", "Update an OAuth provider", (yargs) => yargs.options({
131
+ "provider-id": {
132
+ describe: `OAuth provider ID. Supported values: ${SUPPORTED_OAUTH_PROVIDERS.join(", ")}`,
133
+ type: "string",
135
134
  choices: SUPPORTED_OAUTH_PROVIDERS,
136
135
  demandOption: true,
137
136
  },
138
- 'oauth-client-id': {
137
+ "oauth-client-id": {
139
138
  describe: "OAuth client ID from your provider app. Omit to use Neon's shared OAuth app.",
140
- type: 'string',
139
+ type: "string",
141
140
  },
142
- 'oauth-client-secret': {
141
+ "oauth-client-secret": {
143
142
  describe: "OAuth client secret from your provider app. Omit to use Neon's shared OAuth app.",
144
- type: 'string',
143
+ type: "string",
145
144
  },
146
145
  }), async (args) => {
147
146
  await oauthProviderUpdate(args);
148
147
  })
149
- .command('delete', 'Delete an OAuth provider', (yargs) => yargs.options({
150
- 'provider-id': {
151
- describe: `OAuth provider ID. Supported values: ${SUPPORTED_OAUTH_PROVIDERS.join(', ')}`,
152
- type: 'string',
148
+ .command("delete", "Delete an OAuth provider", (yargs) => yargs.options({
149
+ "provider-id": {
150
+ describe: `OAuth provider ID. Supported values: ${SUPPORTED_OAUTH_PROVIDERS.join(", ")}`,
151
+ type: "string",
153
152
  choices: SUPPORTED_OAUTH_PROVIDERS,
154
153
  demandOption: true,
155
154
  },
@@ -157,277 +156,283 @@ export const builder = (argv) => {
157
156
  await oauthProviderDelete(args);
158
157
  });
159
158
  })
160
- .command('domain', 'Manage redirect URI trusted domains', (yargs) => {
159
+ .command("domain", "Manage redirect URI trusted domains", (yargs) => {
161
160
  return yargs
162
- .usage('$0 neon-auth domain <sub-command> [options]')
163
- .command('list', 'List trusted domains', (yargs) => yargs, async (args) => {
161
+ .usage("$0 neon-auth domain <sub-command> [options]")
162
+ .command("list", "List trusted domains", (yargs) => yargs, async (args) => {
164
163
  await domainList(args);
165
164
  })
166
- .command('add <domain>', 'Add a trusted domain', (yargs) => yargs
167
- .usage('$0 neon-auth domain add <domain> [options]')
168
- .positional('domain', {
169
- describe: 'Domain to add',
170
- type: 'string',
165
+ .command("add <domain>", "Add a trusted domain", (yargs) => yargs
166
+ .usage("$0 neon-auth domain add <domain> [options]")
167
+ .positional("domain", {
168
+ describe: "Domain to add",
169
+ type: "string",
171
170
  demandOption: true,
172
171
  }), async (args) => {
173
172
  await domainAdd(args);
174
173
  })
175
- .command('delete <domain>', 'Delete a trusted domain', (yargs) => yargs
176
- .usage('$0 neon-auth domain delete <domain> [options]')
177
- .positional('domain', {
178
- describe: 'Domain to delete',
179
- type: 'string',
174
+ .command("delete <domain>", "Delete a trusted domain", (yargs) => yargs
175
+ .usage("$0 neon-auth domain delete <domain> [options]")
176
+ .positional("domain", {
177
+ describe: "Domain to delete",
178
+ type: "string",
180
179
  demandOption: true,
181
180
  }), async (args) => {
182
181
  await domainDelete(args);
183
182
  })
184
- .command('allow-localhost', 'Manage localhost connection settings', (yargs) => yargs
185
- .usage('$0 neon-auth domain allow-localhost <sub-command> [options]')
186
- .command('get', 'Get localhost connection setting', (yargs) => yargs, async (args) => {
183
+ .command("allow-localhost", "Manage localhost connection settings", (yargs) => yargs
184
+ .usage("$0 neon-auth domain allow-localhost <sub-command> [options]")
185
+ .command("get", "Get localhost connection setting", (yargs) => yargs, async (args) => {
187
186
  await allowLocalhostGet(args);
188
187
  })
189
- .command('enable', 'Allow localhost connections', (yargs) => yargs, async (args) => {
188
+ .command("enable", "Allow localhost connections", (yargs) => yargs, async (args) => {
190
189
  await allowLocalhostEnable(args);
191
190
  })
192
- .command('disable', 'Restrict localhost connections', (yargs) => yargs, async (args) => {
191
+ .command("disable", "Restrict localhost connections", (yargs) => yargs, async (args) => {
193
192
  await allowLocalhostDisable(args);
194
193
  }));
195
194
  })
196
- .command('config', 'Manage Neon Auth configuration', (yargs) => {
195
+ .command("config", "Manage Neon Auth configuration", (yargs) => {
197
196
  return yargs
198
- .usage('$0 neon-auth config <sub-command> [options]')
199
- .command('email-password', 'Manage email and password authentication settings', (yargs) => {
197
+ .usage("$0 neon-auth config <sub-command> [options]")
198
+ .command("email-password", "Manage email and password authentication settings", (yargs) => {
200
199
  return yargs
201
- .usage('$0 neon-auth config email-password <sub-command> [options]')
202
- .command('get', 'Get email and password config', (yargs) => yargs, async (args) => {
200
+ .usage("$0 neon-auth config email-password <sub-command> [options]")
201
+ .command("get", "Get email and password config", (yargs) => yargs, async (args) => {
203
202
  await emailPasswordGet(args);
204
203
  })
205
- .command('update', 'Update email and password config', (yargs) => yargs.options({
204
+ .command("update", "Update email and password config", (yargs) => yargs.options({
206
205
  enabled: {
207
- describe: 'Enable email and password authentication',
208
- type: 'boolean',
206
+ describe: "Enable email and password authentication",
207
+ type: "boolean",
209
208
  },
210
- 'email-verification-method': {
211
- describe: 'Email verification method',
212
- type: 'string',
209
+ "email-verification-method": {
210
+ describe: "Email verification method",
211
+ type: "string",
213
212
  choices: Object.values(NeonAuthEmailVerificationMethod),
214
213
  },
215
- 'require-email-verification': {
216
- describe: 'Require email verification before users can sign in',
217
- type: 'boolean',
214
+ "require-email-verification": {
215
+ describe: "Require email verification before users can sign in",
216
+ type: "boolean",
218
217
  },
219
- 'auto-sign-in-after-verification': {
220
- describe: 'Auto sign in users after verifying their email',
221
- type: 'boolean',
218
+ "auto-sign-in-after-verification": {
219
+ describe: "Auto sign in users after verifying their email",
220
+ type: "boolean",
222
221
  },
223
- 'send-verification-email-on-sign-up': {
224
- describe: 'Send verification email on sign up',
225
- type: 'boolean',
222
+ "send-verification-email-on-sign-up": {
223
+ describe: "Send verification email on sign up",
224
+ type: "boolean",
226
225
  },
227
- 'send-verification-email-on-sign-in': {
228
- describe: 'Send verification email on sign in',
229
- type: 'boolean',
226
+ "send-verification-email-on-sign-in": {
227
+ describe: "Send verification email on sign in",
228
+ type: "boolean",
230
229
  },
231
- 'disable-sign-up': {
232
- describe: 'Disable new user sign ups',
233
- type: 'boolean',
230
+ "disable-sign-up": {
231
+ describe: "Disable new user sign ups",
232
+ type: "boolean",
234
233
  },
235
234
  }), async (args) => {
236
235
  await emailPasswordUpdate(args);
237
236
  });
238
237
  })
239
- .command('email-provider', 'Manage email provider configuration', (yargs) => {
238
+ .command("email-provider", "Manage email provider configuration", (yargs) => {
240
239
  return yargs
241
- .usage('$0 neon-auth config email-provider <sub-command> [options]')
242
- .command('get', 'Get email provider config', (yargs) => yargs, async (args) => {
240
+ .usage("$0 neon-auth config email-provider <sub-command> [options]")
241
+ .command("get", "Get email provider config", (yargs) => yargs, async (args) => {
243
242
  await emailProviderGet(args);
244
243
  })
245
- .command('update', 'Update email provider config', (yargs) => yargs.options({
244
+ .command("update", "Update email provider config", (yargs) => yargs.options({
246
245
  type: {
247
- describe: 'Email provider type',
248
- type: 'string',
249
- choices: ['standard', 'shared'],
246
+ describe: "Email provider type",
247
+ type: "string",
248
+ choices: [
249
+ "standard",
250
+ "shared",
251
+ ],
250
252
  demandOption: true,
251
253
  },
252
254
  host: {
253
- describe: 'SMTP host (required for standard)',
254
- type: 'string',
255
+ describe: "SMTP host (required for standard)",
256
+ type: "string",
255
257
  },
256
258
  port: {
257
- describe: 'SMTP port (required for standard)',
258
- type: 'number',
259
+ describe: "SMTP port (required for standard)",
260
+ type: "number",
259
261
  },
260
262
  username: {
261
- describe: 'SMTP username (required for standard)',
262
- type: 'string',
263
+ describe: "SMTP username (required for standard)",
264
+ type: "string",
263
265
  },
264
266
  password: {
265
- describe: 'SMTP password (required for standard)',
266
- type: 'string',
267
+ describe: "SMTP password (required for standard)",
268
+ type: "string",
267
269
  },
268
- 'sender-email': {
269
- describe: 'Sender email address',
270
- type: 'string',
270
+ "sender-email": {
271
+ describe: "Sender email address",
272
+ type: "string",
271
273
  },
272
- 'sender-name': {
273
- describe: 'Sender display name',
274
- type: 'string',
274
+ "sender-name": {
275
+ describe: "Sender display name",
276
+ type: "string",
275
277
  },
276
278
  }), async (args) => {
277
279
  await emailProviderUpdate(args);
278
280
  })
279
- .command('test', 'Send a test email', (yargs) => yargs.options({
280
- 'recipient-email': {
281
- describe: 'Email address to send test email to',
282
- type: 'string',
281
+ .command("test", "Send a test email", (yargs) => yargs.options({
282
+ "recipient-email": {
283
+ describe: "Email address to send test email to",
284
+ type: "string",
283
285
  demandOption: true,
284
286
  },
285
287
  host: {
286
- describe: 'SMTP host',
287
- type: 'string',
288
+ describe: "SMTP host",
289
+ type: "string",
288
290
  demandOption: true,
289
291
  },
290
292
  port: {
291
- describe: 'SMTP port',
292
- type: 'number',
293
+ describe: "SMTP port",
294
+ type: "number",
293
295
  demandOption: true,
294
296
  },
295
297
  username: {
296
- describe: 'SMTP username',
297
- type: 'string',
298
+ describe: "SMTP username",
299
+ type: "string",
298
300
  demandOption: true,
299
301
  },
300
302
  password: {
301
- describe: 'SMTP password',
302
- type: 'string',
303
+ describe: "SMTP password",
304
+ type: "string",
303
305
  demandOption: true,
304
306
  },
305
- 'sender-email': {
306
- describe: 'Sender email address',
307
- type: 'string',
307
+ "sender-email": {
308
+ describe: "Sender email address",
309
+ type: "string",
308
310
  demandOption: true,
309
311
  },
310
- 'sender-name': {
311
- describe: 'Sender display name',
312
- type: 'string',
312
+ "sender-name": {
313
+ describe: "Sender display name",
314
+ type: "string",
313
315
  demandOption: true,
314
316
  },
315
317
  }), async (args) => {
316
318
  await emailProviderTest(args);
317
319
  });
318
320
  })
319
- .command('organization', 'Manage organization plugin settings', (yargs) => {
321
+ .command("organization", "Manage organization plugin settings", (yargs) => {
320
322
  return yargs
321
- .usage('$0 neon-auth config organization <sub-command> [options]')
322
- .command('get', 'Get organization plugin config', (yargs) => yargs, async (args) => {
323
+ .usage("$0 neon-auth config organization <sub-command> [options]")
324
+ .command("get", "Get organization plugin config", (yargs) => yargs, async (args) => {
323
325
  await organizationGet(args);
324
326
  })
325
- .command('update', 'Update organization plugin config', (yargs) => yargs.options({
327
+ .command("update", "Update organization plugin config", (yargs) => yargs.options({
326
328
  enabled: {
327
- describe: 'Enable the organization plugin',
328
- type: 'boolean',
329
+ describe: "Enable the organization plugin",
330
+ type: "boolean",
329
331
  },
330
332
  limit: {
331
- describe: 'Maximum number of organizations a user can create',
332
- type: 'number',
333
+ describe: "Maximum number of organizations a user can create",
334
+ type: "number",
333
335
  },
334
- 'creator-role': {
335
- describe: 'Role assigned to organization creator',
336
- type: 'string',
337
- choices: ['admin', 'owner'],
336
+ "creator-role": {
337
+ describe: "Role assigned to organization creator",
338
+ type: "string",
339
+ choices: [
340
+ "admin",
341
+ "owner",
342
+ ],
338
343
  },
339
344
  }), async (args) => {
340
345
  await organizationUpdate(args);
341
346
  });
342
347
  })
343
- .command('webhook', 'Manage webhook configuration', (yargs) => {
348
+ .command("webhook", "Manage webhook configuration", (yargs) => {
344
349
  return yargs
345
- .usage('$0 neon-auth config webhook <sub-command> [options]')
346
- .command('get', 'Get webhook config', (yargs) => yargs, async (args) => {
350
+ .usage("$0 neon-auth config webhook <sub-command> [options]")
351
+ .command("get", "Get webhook config", (yargs) => yargs, async (args) => {
347
352
  await webhookGet(args);
348
353
  })
349
- .command('update', 'Update webhook config', (yargs) => yargs.options({
354
+ .command("update", "Update webhook config", (yargs) => yargs.options({
350
355
  enabled: {
351
- describe: 'Enable webhooks',
352
- type: 'boolean',
356
+ describe: "Enable webhooks",
357
+ type: "boolean",
353
358
  demandOption: true,
354
359
  },
355
360
  url: {
356
- describe: 'Webhook endpoint URL',
357
- type: 'string',
361
+ describe: "Webhook endpoint URL",
362
+ type: "string",
358
363
  },
359
- 'enabled-events': {
360
- describe: 'Events to enable',
361
- type: 'string',
364
+ "enabled-events": {
365
+ describe: "Events to enable",
366
+ type: "string",
362
367
  choices: [
363
- 'user.before_create',
364
- 'user.created',
365
- 'send.otp',
366
- 'send.magic_link',
368
+ "user.before_create",
369
+ "user.created",
370
+ "send.otp",
371
+ "send.magic_link",
367
372
  ],
368
373
  array: true,
369
374
  },
370
375
  timeout: {
371
- describe: 'Webhook timeout in seconds (1-10)',
372
- type: 'number',
376
+ describe: "Webhook timeout in seconds (1-10)",
377
+ type: "number",
373
378
  },
374
379
  }), async (args) => {
375
380
  await webhookUpdate(args);
376
381
  });
377
382
  });
378
383
  })
379
- .command('plugins', 'View Neon Auth plugin configurations', (yargs) => {
384
+ .command("plugins", "View Neon Auth plugin configurations", (yargs) => {
380
385
  return yargs
381
- .usage('$0 neon-auth plugins <sub-command> [options]')
382
- .command('list', 'List all plugin configurations', (yargs) => yargs, async (args) => {
386
+ .usage("$0 neon-auth plugins <sub-command> [options]")
387
+ .command("list", "List all plugin configurations", (yargs) => yargs, async (args) => {
383
388
  await pluginsList(args);
384
389
  })
385
- .command('get <plugin-name>', 'Get a specific plugin configuration', (yargs) => yargs
386
- .usage('$0 neon-auth plugins get <plugin-name> [options]')
387
- .positional('plugin-name', {
388
- describe: 'Plugin name (e.g. organization, email_provider, email_and_password, oauth_providers, allow_localhost)',
389
- type: 'string',
390
+ .command("get <plugin-name>", "Get a specific plugin configuration", (yargs) => yargs
391
+ .usage("$0 neon-auth plugins get <plugin-name> [options]")
392
+ .positional("plugin-name", {
393
+ describe: "Plugin name (e.g. organization, email_provider, email_and_password, oauth_providers, allow_localhost)",
394
+ type: "string",
390
395
  demandOption: true,
391
396
  }), async (args) => {
392
397
  await pluginsGet(args);
393
398
  });
394
399
  })
395
- .command('user', 'Manage Neon Auth users', (yargs) => {
400
+ .command("user", "Manage Neon Auth users", (yargs) => {
396
401
  return yargs
397
- .usage('$0 neon-auth user <sub-command> [options]')
398
- .command('create', 'Create an auth user', (yargs) => yargs.options({
402
+ .usage("$0 neon-auth user <sub-command> [options]")
403
+ .command("create", "Create an auth user", (yargs) => yargs.options({
399
404
  email: {
400
- describe: 'User email address',
401
- type: 'string',
405
+ describe: "User email address",
406
+ type: "string",
402
407
  demandOption: true,
403
408
  },
404
409
  name: {
405
- describe: 'User display name (defaults to email if not provided)',
406
- type: 'string',
410
+ describe: "User display name (defaults to email if not provided)",
411
+ type: "string",
407
412
  },
408
413
  }), async (args) => {
409
414
  await userCreate(args);
410
415
  })
411
- .command('delete <user-id>', 'Delete an auth user', (yargs) => yargs
412
- .usage('$0 neon-auth user delete <user-id> [options]')
413
- .positional('user-id', {
414
- describe: 'ID of the user to delete',
415
- type: 'string',
416
+ .command("delete <user-id>", "Delete an auth user", (yargs) => yargs
417
+ .usage("$0 neon-auth user delete <user-id> [options]")
418
+ .positional("user-id", {
419
+ describe: "ID of the user to delete",
420
+ type: "string",
416
421
  demandOption: true,
417
422
  }), async (args) => {
418
423
  await userDelete(args);
419
424
  })
420
- .command('set-role <user-id>', 'Set roles for an auth user', (yargs) => yargs
421
- .usage('$0 neon-auth user set-role <user-id> [options]')
422
- .positional('user-id', {
423
- describe: 'ID of the user to update',
424
- type: 'string',
425
+ .command("set-role <user-id>", "Set roles for an auth user", (yargs) => yargs
426
+ .usage("$0 neon-auth user set-role <user-id> [options]")
427
+ .positional("user-id", {
428
+ describe: "ID of the user to update",
429
+ type: "string",
425
430
  demandOption: true,
426
431
  })
427
432
  .options({
428
433
  roles: {
429
- describe: 'Roles to assign',
430
- type: 'string',
434
+ describe: "Roles to assign",
435
+ type: "string",
431
436
  array: true,
432
437
  demandOption: true,
433
438
  },
@@ -453,7 +458,7 @@ const enable = async (props) => {
453
458
  })));
454
459
  }
455
460
  catch (err) {
456
- if (isAxiosError(err) && err.response?.status === 409) {
461
+ if (isNeonApiError(err) && err.status === 409) {
457
462
  alreadyEnabled = true;
458
463
  ({ data } = await props.apiClient.getNeonAuth(props.projectId, branchId));
459
464
  }
@@ -461,27 +466,29 @@ const enable = async (props) => {
461
466
  throw err;
462
467
  }
463
468
  }
464
- if (props.output === 'json' || props.output === 'yaml') {
469
+ if (props.output === "json" || props.output === "yaml") {
465
470
  writer(props).end(data, { fields: INTEGRATION_RESPONSE_FIELDS });
466
471
  return;
467
472
  }
468
473
  // Access type-specific fields loosely — CREATE response has schema/table,
469
474
  // GET response (already-enabled path) has db_name instead.
470
475
  const d = data;
471
- printKvBlock(alreadyEnabled ? 'Neon Auth is already enabled' : 'Neon Auth enabled', [
472
- ['Auth Provider:', data.auth_provider],
473
- ...(d.db_name ? [['Database: ', d.db_name]] : []),
474
- ['Base URL: ', data.base_url],
476
+ printKvBlock(alreadyEnabled ? "Neon Auth is already enabled" : "Neon Auth enabled", [
477
+ ["Auth Provider:", data.auth_provider],
478
+ ...(d.db_name
479
+ ? [["Database: ", d.db_name]]
480
+ : []),
481
+ ["Base URL: ", data.base_url],
475
482
  ...(d.schema_name
476
- ? [['Schema Name: ', d.schema_name]]
483
+ ? [["Schema Name: ", d.schema_name]]
477
484
  : []),
478
485
  ...(d.table_name
479
- ? [['Table Name: ', d.table_name]]
486
+ ? [["Table Name: ", d.table_name]]
480
487
  : []),
481
- ['JWKS URL: ', data.jwks_url],
488
+ ["JWKS URL: ", data.jwks_url],
482
489
  ]);
483
490
  if (data.base_url) {
484
- process.stdout.write(` ${chalk.green('Set this environment variable in your application:')}\n`);
491
+ process.stdout.write(` ${chalk.green("Set this environment variable in your application:")}\n`);
485
492
  process.stdout.write(` NEON_AUTH_BASE_URL=${data.base_url}\n\n`);
486
493
  }
487
494
  };
@@ -492,23 +499,23 @@ const status = async (props) => {
492
499
  ({ data } = await props.apiClient.getNeonAuth(props.projectId, branchId));
493
500
  }
494
501
  catch (err) {
495
- if (isAxiosError(err) && err.response?.status === 404) {
496
- printMessage('Neon Auth is not configured for this branch');
502
+ if (isNeonApiError(err) && err.status === 404) {
503
+ printMessage("Neon Auth is not configured for this branch");
497
504
  return;
498
505
  }
499
506
  throw err;
500
507
  }
501
- if (props.output === 'json' || props.output === 'yaml') {
508
+ if (props.output === "json" || props.output === "yaml") {
502
509
  writer(props).end(data, { fields: INTEGRATION_STATUS_FIELDS });
503
510
  return;
504
511
  }
505
- printKvBlock('Neon Auth status', [
506
- ['Auth Provider:', data.auth_provider],
507
- ['Branch ID: ', data.branch_id],
508
- ['Database: ', data.db_name],
509
- ['Base URL: ', data.base_url],
510
- ['Created At: ', data.created_at],
511
- ['JWKS URL: ', data.jwks_url],
512
+ printKvBlock("Neon Auth status", [
513
+ ["Auth Provider:", data.auth_provider],
514
+ ["Branch ID: ", data.branch_id],
515
+ ["Database: ", data.db_name],
516
+ ["Base URL: ", data.base_url],
517
+ ["Created At: ", data.created_at],
518
+ ["JWKS URL: ", data.jwks_url],
512
519
  ]);
513
520
  };
514
521
  const disable = async (props) => {
@@ -516,23 +523,23 @@ const disable = async (props) => {
516
523
  await retryOnLock(() => props.apiClient.disableNeonAuth(props.projectId, branchId, {
517
524
  delete_data: props.deleteData,
518
525
  }));
519
- printMessage('Neon Auth has been disabled');
526
+ printMessage("Neon Auth has been disabled");
520
527
  };
521
528
  // --- OAuth provider ---
522
- const SHARED_PROVIDER_DISCLAIMER = 'Shared keys are created by the Neon team for development only ' +
523
- 'and should not be used for production apps. It helps you get started, ' +
524
- 'but will show Neon branding (logo and name) on the OAuth consent screen.';
529
+ const SHARED_PROVIDER_DISCLAIMER = "Shared keys are created by the Neon team for development only " +
530
+ "and should not be used for production apps. It helps you get started, " +
531
+ "but will show Neon branding (logo and name) on the OAuth consent screen.";
525
532
  const oauthProviderList = async (props) => {
526
533
  const branchId = await resolveBranch(props);
527
534
  const { data } = await props.apiClient.listBranchNeonAuthOauthProviders(props.projectId, branchId);
528
- if (data.providers.length === 0 && props.output === 'table') {
529
- printMessage('No OAuth providers are configured for this branch.');
535
+ if (data.providers.length === 0 && props.output === "table") {
536
+ printMessage("No OAuth providers are configured for this branch.");
530
537
  return;
531
538
  }
532
539
  writer(props).end(data.providers, { fields: OAUTH_PROVIDER_FIELDS });
533
540
  const hasShared = data.providers.some((p) => p.type === NeonAuthOauthProviderType.Shared);
534
- if (hasShared && props.output === 'table') {
535
- process.stdout.write(`\n${chalk.yellow('Caution:')} ${SHARED_PROVIDER_DISCLAIMER}\n\n`);
541
+ if (hasShared && props.output === "table") {
542
+ process.stdout.write(`\n${chalk.yellow("Caution:")} ${SHARED_PROVIDER_DISCLAIMER}\n\n`);
536
543
  }
537
544
  };
538
545
  const oauthProviderAdd = async (props) => {
@@ -546,24 +553,23 @@ const oauthProviderAdd = async (props) => {
546
553
  }));
547
554
  }
548
555
  catch (err) {
549
- if (isAxiosError(err) &&
550
- err.response?.data?.code ===
551
- 'INVALID_SHARED_OAUTH_PROVIDER') {
556
+ if (isNeonApiError(err) &&
557
+ codeFromBody(err.data) === "INVALID_SHARED_OAUTH_PROVIDER") {
552
558
  throw new Error(`The "${props.providerId}" provider requires your own OAuth app credentials.\n` +
553
559
  `Re-run with --oauth-client-id and --oauth-client-secret to provide them.\n` +
554
560
  `Create an OAuth app at your provider and use those credentials.`);
555
561
  }
556
562
  throw err;
557
563
  }
558
- if (props.output === 'json' || props.output === 'yaml') {
564
+ if (props.output === "json" || props.output === "yaml") {
559
565
  writer(props).end(data, { fields: OAUTH_PROVIDER_FIELDS });
560
566
  }
561
567
  else {
562
- printKvBlock('OAuth provider added', [
563
- ['ID: ', data.id],
564
- ['Type: ', data.type],
568
+ printKvBlock("OAuth provider added", [
569
+ ["ID: ", data.id],
570
+ ["Type: ", data.type],
565
571
  ...(data.client_id
566
- ? [['Client ID: ', data.client_id]]
572
+ ? [["Client ID: ", data.client_id]]
567
573
  : []),
568
574
  ]);
569
575
  }
@@ -579,24 +585,23 @@ const oauthProviderUpdate = async (props) => {
579
585
  }));
580
586
  }
581
587
  catch (err) {
582
- if (isAxiosError(err) &&
583
- err.response?.data?.code ===
584
- 'INVALID_SHARED_OAUTH_PROVIDER') {
588
+ if (isNeonApiError(err) &&
589
+ codeFromBody(err.data) === "INVALID_SHARED_OAUTH_PROVIDER") {
585
590
  throw new Error(`The "${props.providerId}" provider requires your own OAuth app credentials.\n` +
586
591
  `Re-run with --oauth-client-id and --oauth-client-secret to provide them.\n` +
587
592
  `Create an OAuth app at your provider and use those credentials.`);
588
593
  }
589
594
  throw err;
590
595
  }
591
- if (props.output === 'json' || props.output === 'yaml') {
596
+ if (props.output === "json" || props.output === "yaml") {
592
597
  writer(props).end(data, { fields: OAUTH_PROVIDER_FIELDS });
593
598
  }
594
599
  else {
595
- printKvBlock('OAuth provider updated', [
596
- ['ID: ', data.id],
597
- ['Type: ', data.type],
600
+ printKvBlock("OAuth provider updated", [
601
+ ["ID: ", data.id],
602
+ ["Type: ", data.type],
598
603
  ...(data.client_id
599
- ? [['Client ID: ', data.client_id]]
604
+ ? [["Client ID: ", data.client_id]]
600
605
  : []),
601
606
  ]);
602
607
  }
@@ -604,23 +609,23 @@ const oauthProviderUpdate = async (props) => {
604
609
  };
605
610
  const CALLBACK_INSTRUCTIONS = {
606
611
  github: {
607
- lead: 'Create an OAuth app in the GitHub Developer Portal and add the following authorization callback URL:',
608
- urlLabel: 'callback/github',
612
+ lead: "Create an OAuth app in the GitHub Developer Portal and add the following authorization callback URL:",
613
+ urlLabel: "callback/github",
609
614
  },
610
615
  vercel: {
611
- lead: 'Create a Vercel App in your Vercel Dashboard and add the following authorization callback URL:',
612
- urlLabel: 'callback/vercel',
616
+ lead: "Create a Vercel App in your Vercel Dashboard and add the following authorization callback URL:",
617
+ urlLabel: "callback/vercel",
613
618
  },
614
619
  google: {
615
- lead: 'Get Google credentials by creating an OAuth client in Google Cloud Console > Credentials, and add the following authorized redirect URL:',
616
- urlLabel: 'callback/google',
620
+ lead: "Get Google credentials by creating an OAuth client in Google Cloud Console > Credentials, and add the following authorized redirect URL:",
621
+ urlLabel: "callback/google",
617
622
  },
618
623
  };
619
624
  const printCallbackInstructions = async (props, branchId, providerId) => {
620
625
  const instructions = CALLBACK_INSTRUCTIONS[providerId];
621
626
  if (!instructions)
622
627
  return;
623
- if (props.output === 'json' || props.output === 'yaml')
628
+ if (props.output === "json" || props.output === "yaml")
624
629
  return;
625
630
  let baseUrl;
626
631
  try {
@@ -632,8 +637,8 @@ const printCallbackInstructions = async (props, branchId, providerId) => {
632
637
  }
633
638
  if (!baseUrl)
634
639
  return;
635
- const callbackUrl = `${baseUrl.replace(/\/$/, '')}/${instructions.urlLabel}`;
636
- printKvBlock(instructions.lead, [['URL: ', callbackUrl]]);
640
+ const callbackUrl = `${baseUrl.replace(/\/$/, "")}/${instructions.urlLabel}`;
641
+ printKvBlock(instructions.lead, [["URL: ", callbackUrl]]);
637
642
  };
638
643
  const oauthProviderDelete = async (props) => {
639
644
  const branchId = await resolveBranch(props);
@@ -644,8 +649,8 @@ const oauthProviderDelete = async (props) => {
644
649
  const domainList = async (props) => {
645
650
  const branchId = await resolveBranch(props);
646
651
  const { data } = await props.apiClient.listBranchNeonAuthTrustedDomains(props.projectId, branchId);
647
- if (data.domains.length === 0 && props.output === 'table') {
648
- printMessage('No trusted domains are configured for this branch.');
652
+ if (data.domains.length === 0 && props.output === "table") {
653
+ printMessage("No trusted domains are configured for this branch.");
649
654
  return;
650
655
  }
651
656
  writer(props).end(data.domains, { fields: DOMAIN_FIELDS });
@@ -658,7 +663,7 @@ const validateDomainUri = (domain) => {
658
663
  catch {
659
664
  throw new Error(`Invalid domain URI "${domain}". Must be a full URI including scheme, e.g. https://${domain}`);
660
665
  }
661
- if (!['http:', 'https:'].includes(url.protocol)) {
666
+ if (!["http:", "https:"].includes(url.protocol)) {
662
667
  throw new Error(`Invalid domain URI "${domain}". Must use http or https scheme, e.g. https://${url.host}`);
663
668
  }
664
669
  };
@@ -688,12 +693,12 @@ const domainDelete = async (props) => {
688
693
  const allowLocalhostGet = async (props) => {
689
694
  const branchId = await resolveBranch(props);
690
695
  const { data } = await props.apiClient.getNeonAuthAllowLocalhost(props.projectId, branchId);
691
- if (props.output === 'json' || props.output === 'yaml') {
696
+ if (props.output === "json" || props.output === "yaml") {
692
697
  writer(props).end(data, { fields: ALLOW_LOCALHOST_FIELDS });
693
698
  return;
694
699
  }
695
- printKvBlock('Localhost connection settings', [
696
- ['Allow localhost:', String(data.allow_localhost)],
700
+ printKvBlock("Localhost connection settings", [
701
+ ["Allow localhost:", String(data.allow_localhost)],
697
702
  ]);
698
703
  };
699
704
  const allowLocalhostEnable = async (props) => {
@@ -701,42 +706,42 @@ const allowLocalhostEnable = async (props) => {
701
706
  await props.apiClient.updateNeonAuthAllowLocalhost(props.projectId, branchId, {
702
707
  allow_localhost: true,
703
708
  });
704
- printMessage('Localhost connections allowed');
709
+ printMessage("Localhost connections allowed");
705
710
  };
706
711
  const allowLocalhostDisable = async (props) => {
707
712
  const branchId = await resolveBranch(props);
708
713
  await props.apiClient.updateNeonAuthAllowLocalhost(props.projectId, branchId, {
709
714
  allow_localhost: false,
710
715
  });
711
- printMessage('Localhost connections restricted');
716
+ printMessage("Localhost connections restricted");
712
717
  };
713
718
  // --- Email and password ---
714
719
  const printEmailPasswordEntries = (data) => [
715
- ['Enabled: ', String(data.enabled)],
716
- ['Verification Method: ', data.email_verification_method],
717
- ['Require Verification: ', String(data.require_email_verification)],
720
+ ["Enabled: ", String(data.enabled)],
721
+ ["Verification Method: ", data.email_verification_method],
722
+ ["Require Verification: ", String(data.require_email_verification)],
718
723
  [
719
- 'Auto Sign In After Verify: ',
724
+ "Auto Sign In After Verify: ",
720
725
  String(data.auto_sign_in_after_verification),
721
726
  ],
722
727
  [
723
- 'Send Email On Sign Up: ',
728
+ "Send Email On Sign Up: ",
724
729
  String(data.send_verification_email_on_sign_up),
725
730
  ],
726
731
  [
727
- 'Send Email On Sign In: ',
732
+ "Send Email On Sign In: ",
728
733
  String(data.send_verification_email_on_sign_in),
729
734
  ],
730
- ['Disable Sign Up: ', String(data.disable_sign_up)],
735
+ ["Disable Sign Up: ", String(data.disable_sign_up)],
731
736
  ];
732
737
  const emailPasswordGet = async (props) => {
733
738
  const branchId = await resolveBranch(props);
734
739
  const { data } = await props.apiClient.getNeonAuthEmailAndPasswordConfig(props.projectId, branchId);
735
- if (props.output === 'json' || props.output === 'yaml') {
740
+ if (props.output === "json" || props.output === "yaml") {
736
741
  writer(props).end(data, { fields: EMAIL_PASSWORD_FIELDS });
737
742
  return;
738
743
  }
739
- printKvBlock('Email & password auth configuration', printEmailPasswordEntries(data));
744
+ printKvBlock("Email & password auth configuration", printEmailPasswordEntries(data));
740
745
  };
741
746
  const emailPasswordUpdate = async (props) => {
742
747
  const branchId = await resolveBranch(props);
@@ -749,45 +754,50 @@ const emailPasswordUpdate = async (props) => {
749
754
  send_verification_email_on_sign_in: props.sendVerificationEmailOnSignIn,
750
755
  disable_sign_up: props.disableSignUp,
751
756
  });
752
- if (props.output === 'json' || props.output === 'yaml') {
757
+ if (props.output === "json" || props.output === "yaml") {
753
758
  writer(props).end(data, { fields: EMAIL_PASSWORD_FIELDS });
754
759
  return;
755
760
  }
756
- printKvBlock('Email & password auth configuration updated', printEmailPasswordEntries(data));
761
+ printKvBlock("Email & password auth configuration updated", printEmailPasswordEntries(data));
757
762
  };
758
763
  // --- Email provider ---
759
764
  const printEmailProviderEntries = (data) => [
760
- ['Type: ', data.type],
761
- ...(data.type === 'standard'
765
+ ["Type: ", data.type],
766
+ ...(data.type === "standard"
762
767
  ? [
763
- ['Host: ', data.host],
764
- ['Port: ', data.port != null ? String(data.port) : undefined],
765
- ['Username: ', data.username],
768
+ ["Host: ", data.host],
769
+ [
770
+ "Port: ",
771
+ data.port != null ? String(data.port) : undefined,
772
+ ],
773
+ ["Username: ", data.username],
766
774
  ]
767
775
  : []),
768
- ['Sender Email: ', data.sender_email],
769
- ['Sender Name: ', data.sender_name],
776
+ ["Sender Email: ", data.sender_email],
777
+ ["Sender Name: ", data.sender_name],
770
778
  ];
771
779
  const emailProviderGet = async (props) => {
772
780
  const branchId = await resolveBranch(props);
773
781
  const { data } = await props.apiClient.getNeonAuthEmailProvider(props.projectId, branchId);
774
- if (props.output === 'json' || props.output === 'yaml') {
775
- writer(props).end(data, { fields: EMAIL_PROVIDER_FIELDS });
782
+ if (props.output === "json" || props.output === "yaml") {
783
+ writer(props).end(data, {
784
+ fields: EMAIL_PROVIDER_FIELDS,
785
+ });
776
786
  return;
777
787
  }
778
- printKvBlock('Email provider configuration', printEmailProviderEntries(data));
788
+ printKvBlock("Email provider configuration", printEmailProviderEntries(data));
779
789
  };
780
790
  const emailProviderUpdate = async (props) => {
781
- if (props.type === 'standard' &&
791
+ if (props.type === "standard" &&
782
792
  (!props.host || !props.port || !props.username || !props.password)) {
783
- throw new Error('--host, --port, --username, and --password are required for standard email provider');
793
+ throw new Error("--host, --port, --username, and --password are required for standard email provider");
784
794
  }
785
- const warnSharedSender = props.type === 'shared' && (props.senderEmail || props.senderName);
795
+ const warnSharedSender = props.type === "shared" && (props.senderEmail || props.senderName);
786
796
  const branchId = await resolveBranch(props);
787
797
  let config;
788
- if (props.type === 'standard') {
798
+ if (props.type === "standard") {
789
799
  config = {
790
- type: 'standard',
800
+ type: "standard",
791
801
  host: props.host,
792
802
  port: props.port,
793
803
  username: props.username,
@@ -798,18 +808,20 @@ const emailProviderUpdate = async (props) => {
798
808
  }
799
809
  else {
800
810
  config = {
801
- type: 'shared',
811
+ type: "shared",
802
812
  };
803
813
  }
804
814
  const { data } = await props.apiClient.updateNeonAuthEmailProvider(props.projectId, branchId, config);
805
- if (props.output === 'json' || props.output === 'yaml') {
806
- writer(props).end(data, { fields: EMAIL_PROVIDER_FIELDS });
815
+ if (props.output === "json" || props.output === "yaml") {
816
+ writer(props).end(data, {
817
+ fields: EMAIL_PROVIDER_FIELDS,
818
+ });
807
819
  }
808
820
  else {
809
- printKvBlock('Email provider configuration updated', printEmailProviderEntries(data));
821
+ printKvBlock("Email provider configuration updated", printEmailProviderEntries(data));
810
822
  }
811
823
  if (warnSharedSender) {
812
- process.stderr.write(`${chalk.yellow('Warning:')} --sender-email and --sender-name are ignored for the shared email provider. ` +
824
+ process.stderr.write(`${chalk.yellow("Warning:")} --sender-email and --sender-name are ignored for the shared email provider. ` +
813
825
  `These values only take effect with --type standard.\n\n`);
814
826
  }
815
827
  };
@@ -824,39 +836,41 @@ const emailProviderTest = async (props) => {
824
836
  sender_email: props.senderEmail,
825
837
  sender_name: props.senderName,
826
838
  });
827
- if (props.output === 'json' || props.output === 'yaml') {
839
+ if (props.output === "json" || props.output === "yaml") {
828
840
  writer(props).end(data, { fields: TEST_EMAIL_FIELDS });
829
841
  }
830
842
  else if (data.success) {
831
- printMessage('Test email sent successfully');
843
+ printMessage("Test email sent successfully");
832
844
  }
833
845
  else {
834
- process.stdout.write(`\n${chalk.red('Test email failed')}\n ${data.error_message ?? 'Unknown error'}\n\n`);
846
+ process.stdout.write(`\n${chalk.red("Test email failed")}\n ${data.error_message ?? "Unknown error"}\n\n`);
835
847
  }
836
848
  };
837
849
  // --- Organization plugin ---
838
850
  const printOrganizationEntries = (data) => [
839
- ['Enabled: ', String(data.enabled)],
840
- ['Org Limit: ', String(data.organization_limit)],
841
- ['Creator Role: ', data.creator_role],
851
+ ["Enabled: ", String(data.enabled)],
852
+ ["Org Limit: ", String(data.organization_limit)],
853
+ ["Creator Role: ", data.creator_role],
842
854
  ];
843
855
  const organizationGet = async (props) => {
844
856
  const branchId = await resolveBranch(props);
845
857
  const { data } = await props.apiClient.getNeonAuthPluginConfigs(props.projectId, branchId);
846
858
  const org = data.organization;
847
859
  if (!org) {
848
- if (props.output === 'json' || props.output === 'yaml') {
849
- writer(props).end({}, { fields: ORGANIZATION_FIELDS });
860
+ if (props.output === "json" || props.output === "yaml") {
861
+ writer(props).end({}, {
862
+ fields: ORGANIZATION_FIELDS,
863
+ });
850
864
  return;
851
865
  }
852
- printMessage('No organization plugin config found.');
866
+ printMessage("No organization plugin config found.");
853
867
  return;
854
868
  }
855
- if (props.output === 'json' || props.output === 'yaml') {
869
+ if (props.output === "json" || props.output === "yaml") {
856
870
  writer(props).end(org, { fields: ORGANIZATION_FIELDS });
857
871
  return;
858
872
  }
859
- printKvBlock('Organization configuration', printOrganizationEntries(org));
873
+ printKvBlock("Organization configuration", printOrganizationEntries(org));
860
874
  };
861
875
  const organizationUpdate = async (props) => {
862
876
  const branchId = await resolveBranch(props);
@@ -865,30 +879,30 @@ const organizationUpdate = async (props) => {
865
879
  organization_limit: props.limit,
866
880
  creator_role: props.creatorRole,
867
881
  });
868
- if (props.output === 'json' || props.output === 'yaml') {
882
+ if (props.output === "json" || props.output === "yaml") {
869
883
  writer(props).end(data, { fields: ORGANIZATION_FIELDS });
870
884
  return;
871
885
  }
872
- printKvBlock('Organization configuration updated', printOrganizationEntries(data));
886
+ printKvBlock("Organization configuration updated", printOrganizationEntries(data));
873
887
  };
874
888
  // --- Webhook ---
875
889
  const printWebhookEntries = (data) => [
876
- ['Enabled: ', String(data.enabled)],
877
- ['URL: ', data.webhook_url ?? ''],
878
- ['Events: ', (data.enabled_events ?? []).join(', ')],
890
+ ["Enabled: ", String(data.enabled)],
891
+ ["URL: ", data.webhook_url ?? ""],
892
+ ["Events: ", (data.enabled_events ?? []).join(", ")],
879
893
  [
880
- 'Timeout (sec): ',
881
- data.timeout_seconds != null ? String(data.timeout_seconds) : '',
894
+ "Timeout (sec): ",
895
+ data.timeout_seconds != null ? String(data.timeout_seconds) : "",
882
896
  ],
883
897
  ];
884
898
  const webhookGet = async (props) => {
885
899
  const branchId = await resolveBranch(props);
886
900
  const { data } = await props.apiClient.getNeonAuthWebhookConfig(props.projectId, branchId);
887
- if (props.output === 'json' || props.output === 'yaml') {
901
+ if (props.output === "json" || props.output === "yaml") {
888
902
  writer(props).end(data, { fields: WEBHOOK_FIELDS });
889
903
  return;
890
904
  }
891
- printKvBlock('Webhook configuration', printWebhookEntries(data));
905
+ printKvBlock("Webhook configuration", printWebhookEntries(data));
892
906
  };
893
907
  const webhookUpdate = async (props) => {
894
908
  const branchId = await resolveBranch(props);
@@ -898,28 +912,28 @@ const webhookUpdate = async (props) => {
898
912
  enabled_events: props.enabledEvents,
899
913
  timeout_seconds: props.timeout,
900
914
  });
901
- if (props.output === 'json' || props.output === 'yaml') {
915
+ if (props.output === "json" || props.output === "yaml") {
902
916
  writer(props).end(data, { fields: WEBHOOK_FIELDS });
903
917
  return;
904
918
  }
905
- printKvBlock('Webhook configuration updated', printWebhookEntries(data));
919
+ printKvBlock("Webhook configuration updated", printWebhookEntries(data));
906
920
  };
907
921
  // --- Plugins ---
908
- const pluginTitle = (name) => name.replace(/_/g, ' ').replace(/^\w/, (c) => c.toUpperCase()) +
909
- ' configuration';
922
+ const pluginTitle = (name) => name.replace(/_/g, " ").replace(/^\w/, (c) => c.toUpperCase()) +
923
+ " configuration";
910
924
  const formatValue = (v) => {
911
925
  if (v == null)
912
- return '';
913
- if (typeof v === 'string')
926
+ return "";
927
+ if (typeof v === "string")
914
928
  return v;
915
- if (typeof v === 'number' || typeof v === 'boolean')
929
+ if (typeof v === "number" || typeof v === "boolean")
916
930
  return String(v);
917
931
  return JSON.stringify(v);
918
932
  };
919
933
  const pluginsList = async (props) => {
920
934
  const branchId = await resolveBranch(props);
921
935
  const { data } = await props.apiClient.getNeonAuthPluginConfigs(props.projectId, branchId);
922
- if (props.output === 'json' || props.output === 'yaml') {
936
+ if (props.output === "json" || props.output === "yaml") {
923
937
  writer(props).end(data, {
924
938
  fields: Object.keys(data),
925
939
  });
@@ -927,59 +941,60 @@ const pluginsList = async (props) => {
927
941
  }
928
942
  const summarize = (value) => {
929
943
  if (value == null)
930
- return 'not configured';
931
- if (typeof value === 'boolean')
944
+ return "not configured";
945
+ if (typeof value === "boolean")
932
946
  return String(value);
933
- if (typeof value === 'string')
947
+ if (typeof value === "string")
934
948
  return value;
935
- if (typeof value === 'number')
949
+ if (typeof value === "number")
936
950
  return String(value);
937
951
  if (Array.isArray(value))
938
- return value.length === 1 ? '1 item' : `${String(value.length)} items`;
939
- if (typeof value === 'object') {
952
+ return value.length === 1
953
+ ? "1 item"
954
+ : `${String(value.length)} items`;
955
+ if (typeof value === "object") {
940
956
  const obj = value;
941
- if ('enabled' in obj)
942
- return obj.enabled ? 'enabled' : 'disabled';
943
- if ('type' in obj)
957
+ if ("enabled" in obj)
958
+ return obj.enabled ? "enabled" : "disabled";
959
+ if ("type" in obj)
944
960
  return formatValue(obj.type);
945
961
  }
946
962
  return JSON.stringify(value);
947
963
  };
948
964
  const entries = Object.entries(data).map(([key, value]) => [key.padEnd(24), summarize(value)]);
949
- printKvBlock('Neon Auth plugins', entries);
965
+ printKvBlock("Neon Auth plugins", entries);
950
966
  };
951
967
  const pluginsGet = async (props) => {
952
968
  const branchId = await resolveBranch(props);
953
969
  const { data } = await props.apiClient.getNeonAuthPluginConfigs(props.projectId, branchId);
954
970
  const plugin = data[props.pluginName];
955
971
  if (plugin === undefined) {
956
- const available = Object.keys(data).join(', ');
972
+ const available = Object.keys(data).join(", ");
957
973
  throw new Error(`Unknown plugin "${props.pluginName}". Available plugins: ${available}`);
958
974
  }
959
- if (props.output === 'json' || props.output === 'yaml') {
960
- const fields = typeof plugin === 'object' && !Array.isArray(plugin)
975
+ if (props.output === "json" || props.output === "yaml") {
976
+ const fields = typeof plugin === "object" && !Array.isArray(plugin)
961
977
  ? Object.keys(plugin)
962
978
  : [];
963
979
  writer(props).end(plugin, { fields: fields });
964
980
  return;
965
981
  }
966
- if (typeof plugin === 'object' && !Array.isArray(plugin) && plugin != null) {
967
- const entries = Object.entries(plugin).map(([k, v]) => [
968
- `${k}:`.padEnd(18),
969
- formatValue(v),
970
- ]);
982
+ if (typeof plugin === "object" &&
983
+ !Array.isArray(plugin) &&
984
+ plugin != null) {
985
+ const entries = Object.entries(plugin).map(([k, v]) => [`${k}:`.padEnd(18), formatValue(v)]);
971
986
  printKvBlock(pluginTitle(props.pluginName), entries);
972
987
  }
973
988
  else if (Array.isArray(plugin)) {
974
989
  const entries = plugin.map((item, i) => [
975
990
  `[${i}]:`.padEnd(18),
976
- typeof item === 'object' ? JSON.stringify(item) : String(item),
991
+ typeof item === "object" ? JSON.stringify(item) : String(item),
977
992
  ]);
978
993
  printKvBlock(pluginTitle(props.pluginName), entries);
979
994
  }
980
995
  else {
981
996
  printKvBlock(pluginTitle(props.pluginName), [
982
- ['Value:'.padEnd(18), String(plugin)],
997
+ ["Value:".padEnd(18), String(plugin)],
983
998
  ]);
984
999
  }
985
1000
  };
@@ -992,10 +1007,10 @@ const userCreate = async (props) => {
992
1007
  };
993
1008
  const { data } = await props.apiClient.createBranchNeonAuthNewUser(props.projectId, branchId, requestBody);
994
1009
  const displayName = requestBody.name !== props.email ? requestBody.name : undefined;
995
- printKvBlock('User created', [
996
- ['ID: ', data.id],
997
- ['Email: ', requestBody.email],
998
- ...(displayName ? [['Name: ', displayName]] : []),
1010
+ printKvBlock("User created", [
1011
+ ["ID: ", data.id],
1012
+ ["Email: ", requestBody.email],
1013
+ ...(displayName ? [["Name: ", displayName]] : []),
999
1014
  ]);
1000
1015
  };
1001
1016
  const userDelete = async (props) => {
@@ -1006,8 +1021,8 @@ const userDelete = async (props) => {
1006
1021
  const userSetRole = async (props) => {
1007
1022
  const branchId = await resolveBranch(props);
1008
1023
  const { data } = await props.apiClient.updateNeonAuthUserRole(props.projectId, branchId, props.userId, { roles: props.roles });
1009
- printKvBlock('Roles updated', [
1010
- ['User ID: ', data.id],
1011
- ['Roles: ', props.roles.join(', ')],
1024
+ printKvBlock("Roles updated", [
1025
+ ["User ID: ", data.id],
1026
+ ["Roles: ", props.roles.join(", ")],
1012
1027
  ]);
1013
1028
  };