neon 7.0.0 → 7.0.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.
@@ -82,7 +82,7 @@ const builder = (argv) => argv.usage("$0 bucket <sub-command> [options]").option
82
82
  demandOption: true
83
83
  }).options(scopeOptions),
84
84
  handler: (args) => deleteBucket(args)
85
- }).command("object <sub-command>", "List, download, upload or delete objects in a bucket", (yargs) => yargs.usage("$0 bucket object <sub-command> [options]").command({
85
+ }).command("object", "List, download, upload or delete objects in a bucket", (yargs) => yargs.usage("$0 bucket object <sub-command> [options]").command({
86
86
  command: "list <target>",
87
87
  aliases: ["ls"],
88
88
  describe: "List objects in a bucket. By default folders are collapsed (like \"aws s3 ls\"); pass --recursive for a flat listing of every key",
@@ -153,7 +153,7 @@ const builder = (argv) => argv.usage("$0 bucket <sub-command> [options]").option
153
153
  }
154
154
  }),
155
155
  handler: (args) => deleteObject(args)
156
- }).demandCommand(1, "").strictCommands());
156
+ }).strictCommands());
157
157
  const handler = (args) => {
158
158
  return args;
159
159
  };
@@ -476,7 +476,7 @@ const builder = (argv) => argv.usage("$0 config <sub-command> [options]").option
476
476
  kind: "bucket",
477
477
  name: args.name,
478
478
  access: args.access
479
- }))).demandCommand(1, "Specify what to add: auth, data-api, ai-gateway, function, or bucket.")).command("init", "Scaffold a neon.ts policy and install the Neon config packages", (yargs) => yargs.options({
479
+ })))).command("init", "Scaffold a neon.ts policy and install the Neon config packages", (yargs) => yargs.options({
480
480
  install: {
481
481
  describe: "Install @neon/config and @neon/env if they're missing. On by default; use --no-install to just print the command.",
482
482
  type: "boolean",
@@ -129,7 +129,7 @@ const builder = (argv) => argv.usage("$0 function <sub-command> [options]").opti
129
129
  describe: "Function slug",
130
130
  type: "string",
131
131
  demandOption: true
132
- }), (args) => deleteFn(args)).command(["domains", "domain"], "Manage custom domains on the branch (beta)", (yargs) => yargs.demandCommand(1).command("list", "List custom domains on the branch", (yargs) => yargs, (args) => listDomains(args)).command("register <domain>", "Point a domain you already own at a function on the branch", (yargs) => yargs.positional("domain", {
132
+ }), (args) => deleteFn(args)).command(["domains", "domain"], "Manage custom domains on the branch (beta)", (yargs) => yargs.command("list", "List custom domains on the branch", (yargs) => yargs, (args) => listDomains(args)).command("register <domain>", "Point a domain you already own at a function on the branch", (yargs) => yargs.positional("domain", {
133
133
  describe: "Domain you already own (for example docs.example.com)",
134
134
  type: "string",
135
135
  demandOption: true
@@ -146,7 +146,7 @@ const builder = (argv) => argv.usage("$0 snapshots <sub-command> [options]").opt
146
146
  describe: "Full schedule as JSON, for multi-entry schedules, e.g. '[{\"frequency\":\"daily\",\"hour\":3,\"retention_seconds\":604800}]'. Overrides the single-entry flags.",
147
147
  type: "string"
148
148
  }
149
- }).example([["$0 snapshots schedule set --branch main --frequency daily --hour 3 --retention 604800", "A daily 03:00 snapshot kept for 7 days"], ["$0 snapshots schedule set --branch main --schedule '[{\"frequency\":\"weekly\",\"day\":1,\"hour\":2},{\"frequency\":\"daily\",\"hour\":3}]'", "A multi-entry schedule via JSON"]]), (args) => scheduleSet(args)).demandCommand(1, "Specify `get` or `set`."), () => {});
149
+ }).example([["$0 snapshots schedule set --branch main --frequency daily --hour 3 --retention 604800", "A daily 03:00 snapshot kept for 7 days"], ["$0 snapshots schedule set --branch main --schedule '[{\"frequency\":\"weekly\",\"day\":1,\"hour\":2},{\"frequency\":\"daily\",\"hour\":3}]'", "A multi-entry schedule via JSON"]]), (args) => scheduleSet(args)), () => {});
150
150
  const handler = (args) => {
151
151
  return args;
152
152
  };
@@ -28,10 +28,9 @@ const builder = (argv) => {
28
28
  },
29
29
  "region-id": {
30
30
  describe: `The region ID. Possible values: ${REGIONS.join(", ")}`,
31
- type: "string",
32
- demandOption: true
31
+ type: "string"
33
32
  }
34
- }).middleware(fillSingleOrg).command("list", "List configured VPC endpoints for this organization.", (yargs) => yargs, async (args) => {
33
+ }).middleware(fillSingleOrg).command("list", "List configured VPC endpoints for this organization.", (yargs) => yargs.demandOption("region-id"), async (args) => {
35
34
  await listOrg(args);
36
35
  }).command({
37
36
  command: "assign <id>",
@@ -40,13 +39,13 @@ const builder = (argv) => {
40
39
  builder: (yargs) => yargs.options({ label: {
41
40
  describe: "An optional descriptive label for the VPC endpoint",
42
41
  type: "string"
43
- } }),
42
+ } }).demandOption("region-id"),
44
43
  handler: async (args) => {
45
44
  await assignOrg(args);
46
45
  }
47
- }).command("remove <id>", "Remove a VPC endpoint from this organization.", (yargs) => yargs, async (args) => {
46
+ }).command("remove <id>", "Remove a VPC endpoint from this organization.", (yargs) => yargs.demandOption("region-id"), async (args) => {
48
47
  await removeOrg(args);
49
- }).command("status <id>", "Get the status of a VPC endpoint for this organization.", (yargs) => yargs, async (args) => {
48
+ }).command("status <id>", "Get the status of a VPC endpoint for this organization.", (yargs) => yargs.demandOption("region-id"), async (args) => {
50
49
  await statusOrg(args);
51
50
  });
52
51
  }).command("project", "Manage project-level VPC endpoint restrictions.\nBy default, connections are accepted from any VPC configured at the organization level.\nA project-level VPC endpoint restriction can be used to restrict connections to a specific VPC.", (yargs) => {
package/dist/help.js CHANGED
@@ -232,19 +232,58 @@ const formatHelp = (help) => {
232
232
  }
233
233
  return [...result, ...lines];
234
234
  };
235
- const showHelp = async (argv) => {
236
- const help = await argv.getHelp();
237
- const text = `${formatHelp(help).join("\n")}\n`;
238
- await new Promise((resolve, reject) => {
239
- process.stdout.write(text, (err) => {
240
- if (err) {
241
- reject(err);
242
- return;
243
- }
244
- resolve();
245
- });
235
+ const writeHelp = (help, stream) => new Promise((resolve, reject) => {
236
+ stream.write(`${formatHelp(help).join("\n")}\n`, (err) => {
237
+ if (err) {
238
+ reject(err);
239
+ return;
240
+ }
241
+ resolve();
246
242
  });
243
+ });
244
+ const showHelp = async (argv) => {
245
+ await writeHelp(await argv.getHelp(), process.stdout);
247
246
  process.exit(0);
248
247
  };
248
+ /**
249
+ * A yargs validation failure: a missing required option or positional, an unknown
250
+ * subcommand, a value outside `choices`. Carries the failing command's help, captured
251
+ * in `.fail` because yargs has restored the parent's context by the time the error
252
+ * reaches the caller.
253
+ */
254
+ var UsageError = class extends Error {
255
+ constructor(message, help) {
256
+ super(message);
257
+ this.help = help;
258
+ }
259
+ };
260
+ /** On stderr, so `-o json` stdout stays empty. */
261
+ const showUsageErrorHelp = (err) => writeHelp(err.help, process.stderr);
262
+ /**
263
+ * The yargs `.fail` handler. yargs passes its usage instance as the third argument
264
+ * (`@types/yargs` declares it as `Argv`). Errors thrown by a handler, a `coerce`, or the
265
+ * parser arrive with their own `err` and are rethrown unchanged.
266
+ */
267
+ const failOnUsageError = (msg, err, usage) => {
268
+ if (err) throw err;
269
+ if (typeof usage !== "object" || usage === null || !("help" in usage) || typeof usage.help !== "function") throw new Error("yargs no longer passes its usage instance to .fail(); update failOnUsageError for the installed yargs");
270
+ const help = usage.help();
271
+ if (typeof help !== "string") throw new Error("yargs usage.help() did not return a string");
272
+ throw new UsageError(msg ?? "Invalid usage", help);
273
+ };
274
+ function assertYargsInternals(argv) {
275
+ if (!("getInternalMethods" in argv) || typeof argv.getInternalMethods !== "function") throw new Error("yargs no longer exposes getInternalMethods(); update isBareParentCommand for the installed yargs");
276
+ }
277
+ /**
278
+ * True when the command yargs is running groups subcommands and none was given, at any
279
+ * depth: `neon config add`, `neon snapshots schedule`. `@types/yargs` doesn't declare the
280
+ * running command's context, so this reads yargs 17's internals.
281
+ */
282
+ const isBareParentCommand = (argv, positionals) => {
283
+ assertYargsInternals(argv);
284
+ const internals = argv.getInternalMethods();
285
+ const path = internals.getContext().commands;
286
+ return path.length > 0 && positionals.length === path.length && internals.getCommandInstance().getCommands().length > 0;
287
+ };
249
288
  //#endregion
250
- export { showHelp };
289
+ export { UsageError, failOnUsageError, isBareParentCommand, showHelp, showUsageErrorHelp };
package/dist/index.js CHANGED
@@ -11,7 +11,7 @@ import { AuthRefreshError, defaultClientID } from "./auth.js";
11
11
  import { getCliName } from "./utils/cli_name.js";
12
12
  import { ensureAuth } from "./commands/auth.js";
13
13
  import { recoverFrom401 } from "./auth_recovery.js";
14
- import { showHelp } from "./help.js";
14
+ import { UsageError, failOnUsageError, isBareParentCommand, showHelp, showUsageErrorHelp } from "./help.js";
15
15
  import { rewriteUnknownAgentArg } from "./init/plan.js";
16
16
  import commands_default from "./commands/index.js";
17
17
  import { recoverFromSSO } from "./sso_recovery.js";
@@ -125,13 +125,16 @@ builder = builder.scriptName(pkg_default.name).locale("en").usage("$0 <command>
125
125
  type: "boolean",
126
126
  default: false
127
127
  }).alias("help", "h").middleware(async (args) => {
128
- if (args.help || args._.length === 1 && !NO_SUBCOMMANDS_VERBS.includes(args._[0])) await showHelp(builder);
129
- }).middleware(ensureAuth).middleware(enrichFromContext).middleware(analyticsMiddleware).command(commands_default).strictCommands().version(pkg_default.version).group("version", "Global options:").alias("version", "v").completion().scriptName(getCliName()).epilog("For more information, visit https://neon.com/docs/reference/neon-cli").wrap(null).fail(false);
128
+ if (args.help) await showHelp(builder);
129
+ }, true).middleware(async (args) => {
130
+ if (args._.length === 1 && !NO_SUBCOMMANDS_VERBS.includes(args._[0]) || args._.length > 1 && isBareParentCommand(builder, args._)) await showHelp(builder);
131
+ }).middleware(ensureAuth).middleware(enrichFromContext).middleware(analyticsMiddleware).command(commands_default).strictCommands().version(pkg_default.version).group("version", "Global options:").alias("version", "v").completion().scriptName(getCliName()).epilog("For more information, visit https://neon.com/docs/reference/neon-cli").wrap(null).fail(failOnUsageError);
130
132
  async function handleError(msg, err, canRetry) {
131
133
  if (process.argv.some((arg) => arg === "--help" || arg === "-h")) {
132
134
  await showHelp(builder);
133
135
  process.exit(0);
134
136
  }
137
+ if (err instanceof UsageError) await showUsageErrorHelp(err);
135
138
  if (err instanceof Error && err.stack) log.debug("Stack: %s", err.stack);
136
139
  if (err instanceof Error) {
137
140
  const rewritten = rewriteUnknownAgentArg({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "neon",
3
- "version": "7.0.0",
3
+ "version": "7.0.1",
4
4
  "description": "CLI tool for Neon, the cloud backend primitives built around Lakebase Postgres",
5
5
  "keywords": [
6
6
  "neon",
@@ -99,9 +99,9 @@
99
99
  "tsx": "4.22.3",
100
100
  "typescript": "^5.9.0",
101
101
  "vitest": "^3.0.9",
102
- "@neon-internals/cli-core": "0.0.0",
103
102
  "@neon-internals/env-core": "0.0.26",
104
103
  "@neon/e2e-harness": "0.0.0",
104
+ "@neon-internals/cli-core": "0.0.0",
105
105
  "@neon/functions": "0.11.0"
106
106
  },
107
107
  "publishConfig": {