neon 3.0.0 → 3.1.1

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