neonctl 2.28.0 → 2.29.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 (135) hide show
  1. package/README.md +71 -71
  2. package/dist/analytics.js +35 -33
  3. package/dist/api.js +34 -34
  4. package/dist/auth.js +50 -44
  5. package/dist/cli.js +2 -2
  6. package/dist/commands/auth.js +58 -52
  7. package/dist/commands/bootstrap.js +115 -157
  8. package/dist/commands/branches.js +154 -147
  9. package/dist/commands/bucket.js +124 -118
  10. package/dist/commands/checkout.js +49 -49
  11. package/dist/commands/config.js +212 -88
  12. package/dist/commands/connection_string.js +62 -62
  13. package/dist/commands/data_api.js +96 -96
  14. package/dist/commands/databases.js +23 -23
  15. package/dist/commands/deploy.js +12 -12
  16. package/dist/commands/dev.js +114 -114
  17. package/dist/commands/env.js +43 -43
  18. package/dist/commands/functions.js +97 -98
  19. package/dist/commands/index.js +26 -26
  20. package/dist/commands/init.js +23 -22
  21. package/dist/commands/ip_allow.js +29 -29
  22. package/dist/commands/link.js +223 -166
  23. package/dist/commands/neon_auth.js +381 -363
  24. package/dist/commands/operations.js +11 -11
  25. package/dist/commands/orgs.js +8 -8
  26. package/dist/commands/projects.js +101 -99
  27. package/dist/commands/psql.js +31 -31
  28. package/dist/commands/roles.js +21 -21
  29. package/dist/commands/schema_diff.js +23 -23
  30. package/dist/commands/set_context.js +17 -17
  31. package/dist/commands/status.js +17 -17
  32. package/dist/commands/user.js +5 -5
  33. package/dist/commands/vpc_endpoints.js +50 -50
  34. package/dist/config.js +7 -7
  35. package/dist/config_format.js +5 -5
  36. package/dist/context.js +23 -16
  37. package/dist/current_branch_fast_path.js +6 -6
  38. package/dist/dev/env.js +34 -34
  39. package/dist/dev/functions.js +4 -4
  40. package/dist/dev/inputs.js +6 -6
  41. package/dist/dev/runtime.js +25 -25
  42. package/dist/env.js +14 -14
  43. package/dist/env_file.js +13 -13
  44. package/dist/errors.js +19 -19
  45. package/dist/functions_api.js +10 -10
  46. package/dist/help.js +15 -15
  47. package/dist/index.js +94 -92
  48. package/dist/log.js +2 -2
  49. package/dist/pkg.js +5 -5
  50. package/dist/psql/cli.js +4 -2
  51. package/dist/psql/command/cmd_cond.js +61 -61
  52. package/dist/psql/command/cmd_connect.js +159 -154
  53. package/dist/psql/command/cmd_copy.js +107 -97
  54. package/dist/psql/command/cmd_describe.js +368 -363
  55. package/dist/psql/command/cmd_format.js +276 -263
  56. package/dist/psql/command/cmd_io.js +269 -263
  57. package/dist/psql/command/cmd_lo.js +74 -66
  58. package/dist/psql/command/cmd_meta.js +148 -148
  59. package/dist/psql/command/cmd_misc.js +17 -17
  60. package/dist/psql/command/cmd_pipeline.js +142 -135
  61. package/dist/psql/command/cmd_restrict.js +25 -25
  62. package/dist/psql/command/cmd_show.js +183 -168
  63. package/dist/psql/command/dispatch.js +26 -26
  64. package/dist/psql/command/shared.js +14 -14
  65. package/dist/psql/complete/filenames.js +16 -16
  66. package/dist/psql/complete/index.js +4 -4
  67. package/dist/psql/complete/matcher.js +33 -32
  68. package/dist/psql/complete/psqlVars.js +173 -173
  69. package/dist/psql/complete/queries.js +5 -3
  70. package/dist/psql/complete/rules.js +900 -863
  71. package/dist/psql/core/common.js +136 -133
  72. package/dist/psql/core/help.js +343 -343
  73. package/dist/psql/core/mainloop.js +160 -153
  74. package/dist/psql/core/prompt.js +126 -123
  75. package/dist/psql/core/settings.js +111 -111
  76. package/dist/psql/core/sqlHelp.js +150 -150
  77. package/dist/psql/core/startup.js +211 -205
  78. package/dist/psql/core/syncVars.js +14 -14
  79. package/dist/psql/core/variables.js +24 -24
  80. package/dist/psql/describe/formatters.js +302 -289
  81. package/dist/psql/describe/processNamePattern.js +28 -28
  82. package/dist/psql/describe/queries.js +656 -651
  83. package/dist/psql/index.js +436 -411
  84. package/dist/psql/io/history.js +36 -36
  85. package/dist/psql/io/input.js +15 -15
  86. package/dist/psql/io/lineEditor/buffer.js +27 -25
  87. package/dist/psql/io/lineEditor/complete.js +15 -15
  88. package/dist/psql/io/lineEditor/filename.js +22 -22
  89. package/dist/psql/io/lineEditor/index.js +65 -62
  90. package/dist/psql/io/lineEditor/keymap.js +325 -318
  91. package/dist/psql/io/lineEditor/vt100.js +60 -60
  92. package/dist/psql/io/pgpass.js +18 -18
  93. package/dist/psql/io/pgservice.js +14 -14
  94. package/dist/psql/io/psqlrc.js +46 -46
  95. package/dist/psql/print/aligned.js +175 -166
  96. package/dist/psql/print/asciidoc.js +51 -51
  97. package/dist/psql/print/crosstab.js +34 -31
  98. package/dist/psql/print/csv.js +25 -22
  99. package/dist/psql/print/html.js +54 -54
  100. package/dist/psql/print/json.js +12 -12
  101. package/dist/psql/print/latex.js +118 -118
  102. package/dist/psql/print/pager.js +28 -26
  103. package/dist/psql/print/troff.js +48 -48
  104. package/dist/psql/print/unaligned.js +15 -14
  105. package/dist/psql/print/units.js +17 -17
  106. package/dist/psql/scanner/slash.js +48 -46
  107. package/dist/psql/scanner/sql.js +88 -84
  108. package/dist/psql/scanner/stringutils.js +21 -17
  109. package/dist/psql/types/index.js +7 -7
  110. package/dist/psql/types/scanner.js +8 -8
  111. package/dist/psql/wire/connection.js +341 -327
  112. package/dist/psql/wire/copy.js +7 -7
  113. package/dist/psql/wire/pipeline.js +26 -24
  114. package/dist/psql/wire/protocol.js +102 -102
  115. package/dist/psql/wire/sasl.js +62 -62
  116. package/dist/psql/wire/tls.js +79 -73
  117. package/dist/storage_api.js +15 -15
  118. package/dist/test_utils/fixtures.js +34 -31
  119. package/dist/test_utils/oauth_server.js +5 -5
  120. package/dist/utils/api_enums.js +13 -13
  121. package/dist/utils/branch_notice.js +5 -5
  122. package/dist/utils/branch_picker.js +26 -26
  123. package/dist/utils/compute_units.js +4 -4
  124. package/dist/utils/enrichers.js +20 -15
  125. package/dist/utils/esbuild.js +28 -28
  126. package/dist/utils/formats.js +1 -1
  127. package/dist/utils/middlewares.js +3 -3
  128. package/dist/utils/package_manager.js +68 -0
  129. package/dist/utils/point_in_time.js +12 -12
  130. package/dist/utils/psql.js +30 -30
  131. package/dist/utils/string.js +2 -2
  132. package/dist/utils/ui.js +9 -9
  133. package/dist/utils/zip.js +1 -1
  134. package/dist/writer.js +17 -17
  135. package/package.json +6 -7
@@ -1,39 +1,39 @@
1
- import chalk from 'chalk';
2
- import { NEON_ENV_VAR_KEYS } from '@neondatabase/env';
3
- import { existsSync } from 'node:fs';
4
- import { log } from '../log.js';
5
- import { resolveNeonEnvVars } from '../dev/env.js';
6
- import { mergeEnvFile, readEnvFile, resolveEnvFilePath } from '../env_file.js';
7
- import { fillSingleProject, resolveBranchRef } from '../utils/enrichers.js';
8
- import { announceTargetBranch } from '../utils/branch_notice.js';
9
- export const command = 'env';
1
+ import { existsSync } from "node:fs";
2
+ import { NEON_ENV_VAR_KEYS } from "@neon/env";
3
+ import chalk from "chalk";
4
+ import { resolveNeonEnvVars } from "../dev/env.js";
5
+ import { mergeEnvFile, readEnvFile, resolveEnvFilePath } from "../env_file.js";
6
+ import { log } from "../log.js";
7
+ import { announceTargetBranch } from "../utils/branch_notice.js";
8
+ import { fillSingleProject, resolveBranchRef } from "../utils/enrichers.js";
9
+ export const command = "env";
10
10
  export const describe = "Manage a branch's Neon env variables locally";
11
11
  /**
12
12
  * Shown (to stderr) when `link` / `checkout` skip the bundled env pull because the user passed
13
13
  * `--no-env-pull`. Names the two ways to get the branch's vars without an on-disk file written
14
14
  * eagerly: an explicit `neonctl env pull`, or runtime injection via `neon-env run`.
15
15
  */
16
- export const ENV_PULL_SKIPPED_HINT = 'Skipped env pull (--no-env-pull). Run `neonctl env pull` to write this branch’s env vars ' +
17
- '(DATABASE_URL, …) into a local .env, or inject them at runtime with `neon-env run -- <your dev command>`.';
16
+ export const ENV_PULL_SKIPPED_HINT = "Skipped env pull (--no-env-pull). Run `neonctl env pull` to write this branch’s env vars " +
17
+ "(DATABASE_URL, …) into a local .env, or inject them at runtime with `neon-env run -- <your dev command>`.";
18
18
  export const builder = (argv) => argv
19
- .usage('$0 env <sub-command> [options]')
19
+ .usage("$0 env <sub-command> [options]")
20
20
  .options({
21
- 'project-id': { describe: 'Project ID', type: 'string' },
22
- branch: { describe: 'Branch ID or name', type: 'string' },
21
+ "project-id": { describe: "Project ID", type: "string" },
22
+ branch: { describe: "Branch ID or name", type: "string" },
23
23
  })
24
24
  .middleware(fillSingleProject)
25
- .command('pull', "Write the branch's Neon env variables to a local .env file", (yargs) => yargs
26
- .usage('$0 env pull [options]')
25
+ .command("pull", "Write the branch's Neon env variables to a local .env file", (yargs) => yargs
26
+ .usage("$0 env pull [options]")
27
27
  .options({
28
28
  file: {
29
- describe: 'Target .env file to write. Defaults to an existing .env, ' +
30
- 'otherwise .env.local. Only Neon variables are updated; other ' +
31
- 'lines are preserved.',
32
- type: 'string',
29
+ describe: "Target .env file to write. Defaults to an existing .env, " +
30
+ "otherwise .env.local. Only Neon variables are updated; other " +
31
+ "lines are preserved.",
32
+ type: "string",
33
33
  },
34
34
  })
35
- .example('$0 env pull', "Write the linked branch's Neon vars into .env.local (or .env if present)")
36
- .example('$0 env pull --branch preview --file .env.preview', 'Pull a specific branch into a specific file'), async (args) => {
35
+ .example("$0 env pull", "Write the linked branch's Neon vars into .env.local (or .env if present)")
36
+ .example("$0 env pull --branch preview --file .env.preview", "Pull a specific branch into a specific file"), async (args) => {
37
37
  // Explicit `env pull` announces the branch it's reading from up front so the user
38
38
  // can catch "pulled env from the wrong branch" before it overwrites their .env. The
39
39
  // bundled auto-pull (link / checkout / apply) stays quiet — those already report the
@@ -42,7 +42,7 @@ export const builder = (argv) => argv
42
42
  })
43
43
  .demandCommand(1);
44
44
  export const handler = (args) => args;
45
- /** Every OS-level env var name `@neondatabase/env` can emit, used only for reporting. */
45
+ /** Every OS-level env var name `@neon/env` can emit, used only for reporting. */
46
46
  const NEON_VAR_NAMES = Object.values(NEON_ENV_VAR_KEYS).flatMap((group) => Object.values(group));
47
47
  /**
48
48
  * The Neon env vars `env pull` *owns*, so it removes any that the branch no longer has when
@@ -68,7 +68,7 @@ export const pull = async (props, opts = {}) => {
68
68
  const cwd = props.cwd ?? process.cwd();
69
69
  const branch = await resolveBranchRef(props);
70
70
  if (opts.announce) {
71
- announceTargetBranch(props, branch, 'Pulling env from branch');
71
+ announceTargetBranch(props, branch, "Pulling env from branch");
72
72
  }
73
73
  const branchId = branch.branchId;
74
74
  // Resolve the target file first and layer its current contents under the resolver's env
@@ -91,9 +91,9 @@ export const pull = async (props, opts = {}) => {
91
91
  });
92
92
  const neonVars = pickNeonVars(vars);
93
93
  if (Object.keys(neonVars).length === 0) {
94
- log.info('No Neon env variables to pull for this branch (no DATABASE_URL or ' +
95
- 'enabled Auth / Data API).');
96
- return { status: 'empty' };
94
+ log.info("No Neon env variables to pull for this branch (no DATABASE_URL or " +
95
+ "enabled Auth / Data API).");
96
+ return { status: "empty" };
97
97
  }
98
98
  // Reconcile rather than blindly merge: write the branch's current Neon vars and prune any
99
99
  // Neon-owned vars the branch no longer has (e.g. NEON_AUTH_* / NEON_DATA_API_* carried over
@@ -101,11 +101,11 @@ export const pull = async (props, opts = {}) => {
101
101
  const { written, removed } = mergeEnvFile(targetPath, neonVars, {
102
102
  managedKeys: NEON_OWNED_ENV_KEYS,
103
103
  });
104
- log.info('Pulled %d Neon variable%s into %s: %s', written.length, written.length === 1 ? '' : 's', targetPath, written.join(', '));
104
+ log.info("Pulled %d Neon variable%s into %s: %s", written.length, written.length === 1 ? "" : "s", targetPath, written.join(", "));
105
105
  if (removed.length > 0) {
106
- log.info('Removed %d stale Neon variable%s not enabled on this branch: %s', removed.length, removed.length === 1 ? '' : 's', removed.join(', '));
106
+ log.info("Removed %d stale Neon variable%s not enabled on this branch: %s", removed.length, removed.length === 1 ? "" : "s", removed.join(", "));
107
107
  }
108
- return { status: 'written', written, file: targetPath };
108
+ return { status: "written", written, file: targetPath };
109
109
  };
110
110
  /**
111
111
  * Pull a freshly-pinned branch's Neon env vars into a local `.env`, bundled into `link` and
@@ -120,17 +120,17 @@ export const pull = async (props, opts = {}) => {
120
120
  export const autoPullEnvAfterPin = async (props) => {
121
121
  if (!props.envPull) {
122
122
  log.info(chalk.dim(ENV_PULL_SKIPPED_HINT));
123
- return { status: 'skipped' };
123
+ return { status: "skipped" };
124
124
  }
125
125
  try {
126
126
  return await pull(props);
127
127
  }
128
128
  catch (err) {
129
129
  const message = err instanceof Error ? err.message : String(err);
130
- log.warning('Branch pinned, but pulling its Neon env vars failed: %s\n' +
131
- 'Run `neonctl env pull` once resolved (e.g. `neonctl deploy` if a declared service ' +
132
- 'is missing), or inject them at runtime with `neon-env run -- <your dev command>`.', message);
133
- return { status: 'failed', message };
130
+ log.warning("Branch pinned, but pulling its Neon env vars failed: %s\n" +
131
+ "Run `neonctl env pull` once resolved (e.g. `neonctl deploy` if a declared service " +
132
+ "is missing), or inject them at runtime with `neon-env run -- <your dev command>`.", message);
133
+ return { status: "failed", message };
134
134
  }
135
135
  };
136
136
  /**
@@ -139,14 +139,14 @@ export const autoPullEnvAfterPin = async (props) => {
139
139
  */
140
140
  export const renderAgentPullNote = (result) => {
141
141
  switch (result.status) {
142
- case 'written':
143
- return ` Pulled ${result.written.length} Neon env var${result.written.length === 1 ? '' : 's'} into ${result.file}.`;
144
- case 'empty':
145
- return ' No Neon env vars to pull for this branch yet.';
146
- case 'skipped':
147
- return (' Skipped env pull (--no-env-pull); run `neonctl env pull` later, ' +
148
- 'or inject env at runtime with `neon-env run -- <your dev command>`.');
149
- case 'failed':
142
+ case "written":
143
+ return ` Pulled ${result.written.length} Neon env var${result.written.length === 1 ? "" : "s"} into ${result.file}.`;
144
+ case "empty":
145
+ return " No Neon env vars to pull for this branch yet.";
146
+ case "skipped":
147
+ return (" Skipped env pull (--no-env-pull); run `neonctl env pull` later, " +
148
+ "or inject env at runtime with `neon-env run -- <your dev command>`.");
149
+ case "failed":
150
150
  return ` Could not pull env vars (${result.message}); run \`neonctl env pull\` once resolved.`;
151
151
  }
152
152
  };
@@ -1,134 +1,133 @@
1
- import { existsSync, statSync } from 'node:fs';
2
- import { join } from 'node:path';
3
- import { isNeonApiError } from '../api.js';
4
- import { retryOnLock } from '../api.js';
5
- import { log } from '../log.js';
6
- import { branchIdFromProps, fillSingleProject } from '../utils/enrichers.js';
7
- import { zipBundle } from '../utils/zip.js';
8
- import { bundleEntry } from '../utils/esbuild.js';
9
- import { writer } from '../writer.js';
10
- import { createDeployment, deleteFunction, getFunction, listFunctions, } from '../functions_api.js';
1
+ import { existsSync, statSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import { isNeonApiError, retryOnLock } from "../api.js";
4
+ import { createDeployment, deleteFunction, getFunction, listFunctions, } from "../functions_api.js";
5
+ import { log } from "../log.js";
6
+ import { branchIdFromProps, fillSingleProject } from "../utils/enrichers.js";
7
+ import { bundleEntry } from "../utils/esbuild.js";
8
+ import { zipBundle } from "../utils/zip.js";
9
+ import { writer } from "../writer.js";
11
10
  const FUNCTION_FIELDS = [
12
- 'slug',
13
- 'name',
14
- 'invocation_url',
15
- 'created_at',
11
+ "slug",
12
+ "name",
13
+ "invocation_url",
14
+ "created_at",
16
15
  ];
17
16
  const FUNCTIONS_LIST_LIMIT = 100;
18
17
  // Table columns for `functions list`. `status` is a derived field (the
19
18
  // table writer reads flat fields only): the current deployment's status.
20
19
  const LIST_TABLE_FIELDS = [
21
- 'slug',
22
- 'name',
23
- 'status',
24
- 'invocation_url',
25
- 'created_at',
20
+ "slug",
21
+ "name",
22
+ "status",
23
+ "invocation_url",
24
+ "created_at",
26
25
  ];
27
26
  const DEPLOYMENT_FIELDS = [
28
- 'id',
29
- 'status',
30
- 'runtime',
31
- 'memory_mib',
32
- 'created_at',
27
+ "id",
28
+ "status",
29
+ "runtime",
30
+ "memory_mib",
31
+ "created_at",
33
32
  ];
34
33
  // Deploy emits the resolved deployment plus the function's invocation_url, so a
35
34
  // successful `functions deploy` tells the user exactly where to call the function.
36
35
  const DEPLOY_RESULT_FIELDS = [
37
- 'id',
38
- 'status',
39
- 'invocation_url',
40
- 'runtime',
41
- 'memory_mib',
42
- 'created_at',
36
+ "id",
37
+ "status",
38
+ "invocation_url",
39
+ "runtime",
40
+ "memory_mib",
41
+ "created_at",
43
42
  ];
44
43
  // In table mode a failed build's reason gets its own "deployment error"
45
44
  // section after the deployment table; json/yaml carry the raw `error` field.
46
45
  const writeDeploymentErrorSection = (out, dep) => {
47
- if (dep.status === 'failed' && dep.error) {
48
- out.write({ reason: dep.error }, { fields: ['reason'], title: 'deployment error' });
46
+ if (dep.status === "failed" && dep.error) {
47
+ out.write({ reason: dep.error }, { fields: ["reason"], title: "deployment error" });
49
48
  }
50
49
  };
51
50
  const SLUG_PATTERN = /^[a-z0-9]{1,20}$/;
52
- const SLUG_HELP = 'Use 1-20 lowercase letters and digits (no hyphens or other characters).';
51
+ const SLUG_HELP = "Use 1-20 lowercase letters and digits (no hyphens or other characters).";
53
52
  // Entry-point discovery order inside --src.
54
- const ENTRY_CANDIDATES = ['index.ts', 'index.mjs', 'index.js'];
53
+ const ENTRY_CANDIDATES = ["index.ts", "index.mjs", "index.js"];
55
54
  // Overridable so tests can poll fast; defaults to 2s in real use.
56
55
  const POLL_INTERVAL_MS = Number(process.env.NEON_FUNCTIONS_POLL_INTERVAL_MS) || 2000;
57
56
  // Upper bound on --wait polling so the CLI never hangs (e.g. if our deployment
58
57
  // never shows up as current_deployment). Overridable so tests can time out fast;
59
58
  // defaults to 10 minutes in real use.
60
59
  const POLL_TIMEOUT_MS = Number(process.env.NEON_FUNCTIONS_POLL_TIMEOUT_MS) || 600000;
61
- export const command = 'functions';
62
- export const describe = 'Manage Neon Functions';
63
- export const aliases = ['function'];
60
+ export const command = "functions";
61
+ export const describe = "Manage Neon Functions";
62
+ export const aliases = ["function"];
64
63
  export const builder = (argv) => argv
65
- .usage('$0 function <sub-command> [options]')
64
+ .usage("$0 function <sub-command> [options]")
66
65
  .options({
67
- 'project-id': {
68
- describe: 'Project ID',
69
- type: 'string',
66
+ "project-id": {
67
+ describe: "Project ID",
68
+ type: "string",
70
69
  },
71
70
  branch: {
72
- describe: 'Branch ID or name',
73
- type: 'string',
71
+ describe: "Branch ID or name",
72
+ type: "string",
74
73
  },
75
74
  })
76
75
  .middleware(fillSingleProject)
77
- .command('deploy <slug>', 'Deploy a function from a local directory', (yargs) => yargs
78
- .positional('slug', {
79
- describe: 'Function slug (1-20 lowercase letters and digits)',
80
- type: 'string',
76
+ .command("deploy <slug>", "Deploy a function from a local directory", (yargs) => yargs
77
+ .positional("slug", {
78
+ describe: "Function slug (1-20 lowercase letters and digits)",
79
+ type: "string",
81
80
  demandOption: true,
82
81
  })
83
82
  .options({
84
83
  src: {
85
- describe: 'Function source: a directory containing index.ts, index.mjs, or index.js, or a path to the entry file',
86
- type: 'string',
84
+ describe: "Function source: a directory containing index.ts, index.mjs, or index.js, or a path to the entry file",
85
+ type: "string",
87
86
  },
88
87
  // Removed flags, kept hidden so old invocations fail loudly instead
89
88
  // of being silently ignored (the CLI has no .strictOptions()).
90
89
  path: {
91
- type: 'string',
90
+ type: "string",
92
91
  hidden: true,
93
92
  },
94
93
  entry: {
95
- type: 'string',
94
+ type: "string",
96
95
  hidden: true,
97
96
  },
98
97
  runtime: {
99
- describe: 'Function runtime',
100
- type: 'string',
101
- choices: ['nodejs24'],
98
+ describe: "Function runtime",
99
+ type: "string",
100
+ choices: ["nodejs24"],
102
101
  },
103
102
  env: {
104
- describe: 'Environment variable as KEY=VALUE (repeatable)',
105
- type: 'string',
103
+ describe: "Environment variable as KEY=VALUE (repeatable)",
104
+ type: "string",
106
105
  array: true,
107
106
  },
108
107
  wait: {
109
- describe: 'Wait for the deployment to finish building',
110
- type: 'boolean',
108
+ describe: "Wait for the deployment to finish building",
109
+ type: "boolean",
111
110
  default: true,
112
111
  },
113
112
  }), (args) => deploy(args))
114
- .command('list', 'List functions on the branch', (yargs) => yargs, (args) => list(args))
115
- .command('get <slug>', "Show a function's details", (yargs) => yargs
116
- .positional('slug', {
117
- describe: 'Function slug',
118
- type: 'string',
113
+ .command("list", "List functions on the branch", (yargs) => yargs, (args) => list(args))
114
+ .command("get <slug>", "Show a function's details", (yargs) => yargs
115
+ .positional("slug", {
116
+ describe: "Function slug",
117
+ type: "string",
119
118
  demandOption: true,
120
119
  })
121
120
  .options({
122
- 'list-env-variables': {
123
- describe: 'List the environment variable names of the active deployment',
124
- type: 'boolean',
125
- alias: 'E',
121
+ "list-env-variables": {
122
+ describe: "List the environment variable names of the active deployment",
123
+ type: "boolean",
124
+ alias: "E",
126
125
  default: false,
127
126
  },
128
127
  }), (args) => get(args))
129
- .command('delete <slug>', 'Delete a function on the branch', (yargs) => yargs.positional('slug', {
130
- describe: 'Function slug',
131
- type: 'string',
128
+ .command("delete <slug>", "Delete a function on the branch", (yargs) => yargs.positional("slug", {
129
+ describe: "Function slug",
130
+ type: "string",
132
131
  demandOption: true,
133
132
  }), (args) => deleteFn(args));
134
133
  export const handler = (args) => {
@@ -139,7 +138,7 @@ const parseEnv = (entries) => {
139
138
  return undefined;
140
139
  const map = {};
141
140
  for (const entry of entries) {
142
- const eq = entry.indexOf('=');
141
+ const eq = entry.indexOf("=");
143
142
  if (eq <= 0) {
144
143
  throw new Error(`Invalid --env value "${entry}". Expected KEY=VALUE.`);
145
144
  }
@@ -152,7 +151,7 @@ const statusHint = (slug, projectId, branchId) => `Check status with: neonctl fu
152
151
  // deploy output shows where the function is reachable (not just the deployment id).
153
152
  const emitDeployResult = (props, deployment, fn) => {
154
153
  const out = writer(props).write({ ...deployment, invocation_url: fn?.invocation_url }, { fields: DEPLOY_RESULT_FIELDS });
155
- if (props.output !== 'json' && props.output !== 'yaml') {
154
+ if (props.output !== "json" && props.output !== "yaml") {
156
155
  writeDeploymentErrorSection(out, deployment);
157
156
  }
158
157
  out.end();
@@ -163,8 +162,8 @@ const isTransient = (err) => isNeonApiError(err) &&
163
162
  (err.status === undefined || err.status === 404 || err.status >= 500);
164
163
  const deploy = async (props) => {
165
164
  if (props.path !== undefined || props.entry !== undefined) {
166
- throw new Error('--path and --entry were removed. Use --src <dir>; the entry point ' +
167
- 'is discovered as index.ts, index.mjs, or index.js in that directory.');
165
+ throw new Error("--path and --entry were removed. Use --src <dir>; the entry point " +
166
+ "is discovered as index.ts, index.mjs, or index.js in that directory.");
168
167
  }
169
168
  // At least one deploy option must be passed (--wait is excluded: it controls
170
169
  // output, not what gets deployed).
@@ -172,15 +171,15 @@ const deploy = async (props) => {
172
171
  props.env !== undefined ||
173
172
  props.runtime !== undefined;
174
173
  if (!hasOption) {
175
- throw new Error('Provide at least one option to deploy, e.g. --src or --env. ' +
176
- 'See: neonctl function deploy --help.');
174
+ throw new Error("Provide at least one option to deploy, e.g. --src or --env. " +
175
+ "See: neonctl function deploy --help.");
177
176
  }
178
177
  // Cheap, offline validation first - fail before any network round-trip.
179
178
  if (!SLUG_PATTERN.test(props.slug)) {
180
179
  throw new Error(`Invalid function slug "${props.slug}". ${SLUG_HELP}`);
181
180
  }
182
- const src = props.src ?? '.';
183
- const runtime = props.runtime ?? 'nodejs24';
181
+ const src = props.src ?? ".";
182
+ const runtime = props.runtime ?? "nodejs24";
184
183
  const environment = parseEnv(props.env);
185
184
  const srcStat = statSync(src, { throwIfNoEntry: false });
186
185
  if (srcStat === undefined) {
@@ -191,7 +190,7 @@ const deploy = async (props) => {
191
190
  ? src
192
191
  : ENTRY_CANDIDATES.map((name) => join(src, name)).find((p) => existsSync(p));
193
192
  if (source === undefined) {
194
- throw new Error(`No entry file found in ${src}. Expected one of: ${ENTRY_CANDIDATES.join(', ')}.`);
193
+ throw new Error(`No entry file found in ${src}. Expected one of: ${ENTRY_CANDIDATES.join(", ")}.`);
195
194
  }
196
195
  // Bundle before any network round-trip so a bundling failure fails fast.
197
196
  const zip = zipBundle(await bundleEntry(source));
@@ -219,8 +218,8 @@ const deploy = async (props) => {
219
218
  const onSignal = () => {
220
219
  interrupted = true;
221
220
  };
222
- process.once('SIGINT', onSignal);
223
- process.once('SIGTERM', onSignal);
221
+ process.once("SIGINT", onSignal);
222
+ process.once("SIGTERM", onSignal);
224
223
  // Poll until a NEW version appears (id greater than the snapshot, or
225
224
  // any version if there was none). --no-wait stops there; --wait stops at a
226
225
  // terminal status. Bounded by POLL_TIMEOUT_MS so it never hangs.
@@ -252,14 +251,14 @@ const deploy = async (props) => {
252
251
  resolvedFn = fn;
253
252
  if (!props.wait)
254
253
  break;
255
- if (dep.status === 'completed' || dep.status === 'failed')
254
+ if (dep.status === "completed" || dep.status === "failed")
256
255
  break;
257
256
  }
258
257
  }
259
258
  }
260
259
  finally {
261
- process.removeListener('SIGINT', onSignal);
262
- process.removeListener('SIGTERM', onSignal);
260
+ process.removeListener("SIGINT", onSignal);
261
+ process.removeListener("SIGTERM", onSignal);
263
262
  }
264
263
  if (interrupted) {
265
264
  log.info(statusHint(props.slug, props.projectId, branchId));
@@ -276,11 +275,11 @@ const deploy = async (props) => {
276
275
  log.info(statusHint(props.slug, props.projectId, branchId));
277
276
  return;
278
277
  }
279
- if (resolved.status === 'completed') {
278
+ if (resolved.status === "completed") {
280
279
  log.info(`Function deployment ${props.slug}/${resolved.id} completed.`);
281
280
  return;
282
281
  }
283
- if (resolved.status === 'failed') {
282
+ if (resolved.status === "failed") {
284
283
  throw new Error(`Function deployment ${props.slug}/${resolved.id} failed.`);
285
284
  }
286
285
  // --wait, new version appeared but the deadline hit before it finished.
@@ -290,20 +289,20 @@ const deploy = async (props) => {
290
289
  const get = async (props) => {
291
290
  const branchId = await branchIdFromProps(props);
292
291
  const fn = await getFunction(props.apiClient, props.projectId, branchId, props.slug);
293
- if (props.output === 'json' || props.output === 'yaml') {
292
+ if (props.output === "json" || props.output === "yaml") {
294
293
  writer(props).end(fn, { fields: FUNCTION_FIELDS });
295
294
  return;
296
295
  }
297
296
  const out = writer(props).write(fn, {
298
297
  fields: FUNCTION_FIELDS,
299
- title: 'function',
298
+ title: "function",
300
299
  });
301
300
  const current = fn.current_deployment;
302
301
  const active = fn.active_deployment;
303
302
  if (current && active && current.id === active.id) {
304
303
  out.write(current, {
305
304
  fields: DEPLOYMENT_FIELDS,
306
- title: 'deployment (current, active)',
305
+ title: "deployment (current, active)",
307
306
  });
308
307
  writeDeploymentErrorSection(out, current);
309
308
  }
@@ -311,7 +310,7 @@ const get = async (props) => {
311
310
  if (current) {
312
311
  out.write(current, {
313
312
  fields: DEPLOYMENT_FIELDS,
314
- title: 'current deployment',
313
+ title: "current deployment",
315
314
  });
316
315
  // The failure reason is shown only for the current deployment;
317
316
  // the active one completed successfully by definition.
@@ -320,15 +319,15 @@ const get = async (props) => {
320
319
  if (active) {
321
320
  out.write(active, {
322
321
  fields: DEPLOYMENT_FIELDS,
323
- title: 'active deployment',
322
+ title: "active deployment",
324
323
  });
325
324
  }
326
325
  }
327
326
  if (props.listEnvVariables) {
328
327
  out.write((fn.active_deployment?.environment ?? []).map((name) => ({ name })), {
329
- fields: ['name'],
330
- title: 'environment',
331
- emptyMessage: 'No environment variables on the active deployment.',
328
+ fields: ["name"],
329
+ title: "environment",
330
+ emptyMessage: "No environment variables on the active deployment.",
332
331
  });
333
332
  }
334
333
  out.end();
@@ -353,22 +352,22 @@ const list = async (props) => {
353
352
  for (;;) {
354
353
  const page = await listFunctions(props.apiClient, props.projectId, branchId, { cursor, limit: FUNCTIONS_LIST_LIMIT });
355
354
  functions.push(...page.functions);
356
- log.debug('Got %d functions, next cursor: %s', page.functions.length, page.next);
355
+ log.debug("Got %d functions, next cursor: %s", page.functions.length, page.next);
357
356
  // A server echoing the same cursor would loop forever; treat it as
358
357
  // the end of the list.
359
358
  if (!page.next || page.next === cursor)
360
359
  break;
361
360
  cursor = page.next;
362
361
  }
363
- if (props.output === 'json' || props.output === 'yaml') {
362
+ if (props.output === "json" || props.output === "yaml") {
364
363
  writer(props).end(functions, { fields: FUNCTION_FIELDS });
365
364
  return;
366
365
  }
367
366
  writer(props).end(functions.map((fn) => ({
368
367
  ...fn,
369
- status: fn.current_deployment?.status ?? '',
368
+ status: fn.current_deployment?.status ?? "",
370
369
  })), {
371
370
  fields: LIST_TABLE_FIELDS,
372
- emptyMessage: 'No functions found on this branch.',
371
+ emptyMessage: "No functions found on this branch.",
373
372
  });
374
373
  };
@@ -1,29 +1,29 @@
1
- import * as auth from './auth.js';
2
- import * as projects from './projects.js';
3
- import * as ipAllow from './ip_allow.js';
4
- import * as vpcEndpoints from './vpc_endpoints.js';
5
- import * as users from './user.js';
6
- import * as orgs from './orgs.js';
7
- import * as branches from './branches.js';
8
- import * as databases from './databases.js';
9
- import * as roles from './roles.js';
10
- import * as operations from './operations.js';
11
- import * as cs from './connection_string.js';
12
- import * as psql from './psql.js';
13
- import * as setContext from './set_context.js';
14
- import * as checkout from './checkout.js';
15
- import * as link from './link.js';
16
- import * as init from './init.js';
17
- import * as dataApi from './data_api.js';
18
- import * as neonAuth from './neon_auth.js';
19
- import * as functions from './functions.js';
20
- import * as dev from './dev.js';
21
- import * as config from './config.js';
22
- import * as status from './status.js';
23
- import * as deploy from './deploy.js';
24
- import * as env from './env.js';
25
- import * as bucket from './bucket.js';
26
- import * as bootstrap from './bootstrap.js';
1
+ import * as auth from "./auth.js";
2
+ import * as bootstrap from "./bootstrap.js";
3
+ import * as branches from "./branches.js";
4
+ import * as bucket from "./bucket.js";
5
+ import * as checkout from "./checkout.js";
6
+ import * as config from "./config.js";
7
+ import * as cs from "./connection_string.js";
8
+ import * as dataApi from "./data_api.js";
9
+ import * as databases from "./databases.js";
10
+ import * as deploy from "./deploy.js";
11
+ import * as dev from "./dev.js";
12
+ import * as env from "./env.js";
13
+ import * as functions from "./functions.js";
14
+ import * as init from "./init.js";
15
+ import * as ipAllow from "./ip_allow.js";
16
+ import * as link from "./link.js";
17
+ import * as neonAuth from "./neon_auth.js";
18
+ import * as operations from "./operations.js";
19
+ import * as orgs from "./orgs.js";
20
+ import * as projects from "./projects.js";
21
+ import * as psql from "./psql.js";
22
+ import * as roles from "./roles.js";
23
+ import * as setContext from "./set_context.js";
24
+ import * as status from "./status.js";
25
+ import * as users from "./user.js";
26
+ import * as vpcEndpoints from "./vpc_endpoints.js";
27
27
  export default [
28
28
  auth,
29
29
  users,