neonctl 2.27.1 → 2.29.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (136) hide show
  1. package/README.md +35 -3
  2. package/dist/analytics.js +52 -34
  3. package/dist/api.js +643 -13
  4. package/dist/auth.js +50 -44
  5. package/dist/cli.js +8 -1
  6. package/dist/commands/auth.js +64 -51
  7. package/dist/commands/bootstrap.js +115 -157
  8. package/dist/commands/branches.js +160 -150
  9. package/dist/commands/bucket.js +183 -146
  10. package/dist/commands/checkout.js +51 -51
  11. package/dist/commands/config.js +228 -82
  12. package/dist/commands/connection_string.js +62 -62
  13. package/dist/commands/data_api.js +100 -101
  14. package/dist/commands/databases.js +29 -26
  15. package/dist/commands/deploy.js +12 -12
  16. package/dist/commands/dev.js +114 -114
  17. package/dist/commands/env.js +43 -43
  18. package/dist/commands/functions.js +101 -104
  19. package/dist/commands/index.js +27 -25
  20. package/dist/commands/init.js +23 -22
  21. package/dist/commands/ip_allow.js +29 -29
  22. package/dist/commands/link.js +232 -182
  23. package/dist/commands/neon_auth.js +385 -370
  24. package/dist/commands/operations.js +11 -11
  25. package/dist/commands/orgs.js +8 -8
  26. package/dist/commands/projects.js +103 -101
  27. package/dist/commands/psql.js +31 -31
  28. package/dist/commands/roles.js +27 -24
  29. package/dist/commands/schema_diff.js +25 -26
  30. package/dist/commands/set_context.js +17 -17
  31. package/dist/commands/status.js +40 -0
  32. package/dist/commands/user.js +5 -5
  33. package/dist/commands/vpc_endpoints.js +50 -50
  34. package/dist/config.js +7 -7
  35. package/dist/config_format.js +5 -5
  36. package/dist/context.js +37 -14
  37. package/dist/current_branch_fast_path.js +55 -0
  38. package/dist/dev/env.js +33 -33
  39. package/dist/dev/functions.js +4 -4
  40. package/dist/dev/inputs.js +6 -6
  41. package/dist/dev/runtime.js +25 -25
  42. package/dist/env.js +14 -14
  43. package/dist/env_file.js +13 -13
  44. package/dist/errors.js +68 -5
  45. package/dist/functions_api.js +10 -10
  46. package/dist/help.js +15 -15
  47. package/dist/index.js +110 -107
  48. package/dist/log.js +2 -2
  49. package/dist/parameters.gen.js +14 -14
  50. package/dist/pkg.js +5 -5
  51. package/dist/psql/cli.js +4 -2
  52. package/dist/psql/command/cmd_cond.js +61 -61
  53. package/dist/psql/command/cmd_connect.js +159 -154
  54. package/dist/psql/command/cmd_copy.js +107 -97
  55. package/dist/psql/command/cmd_describe.js +368 -363
  56. package/dist/psql/command/cmd_format.js +276 -263
  57. package/dist/psql/command/cmd_io.js +269 -263
  58. package/dist/psql/command/cmd_lo.js +74 -66
  59. package/dist/psql/command/cmd_meta.js +148 -148
  60. package/dist/psql/command/cmd_misc.js +17 -17
  61. package/dist/psql/command/cmd_pipeline.js +142 -135
  62. package/dist/psql/command/cmd_restrict.js +25 -25
  63. package/dist/psql/command/cmd_show.js +183 -168
  64. package/dist/psql/command/dispatch.js +26 -26
  65. package/dist/psql/command/shared.js +14 -14
  66. package/dist/psql/complete/filenames.js +16 -16
  67. package/dist/psql/complete/index.js +4 -4
  68. package/dist/psql/complete/matcher.js +33 -32
  69. package/dist/psql/complete/psqlVars.js +173 -173
  70. package/dist/psql/complete/queries.js +5 -3
  71. package/dist/psql/complete/rules.js +900 -863
  72. package/dist/psql/core/common.js +136 -133
  73. package/dist/psql/core/help.js +343 -343
  74. package/dist/psql/core/mainloop.js +160 -153
  75. package/dist/psql/core/prompt.js +126 -123
  76. package/dist/psql/core/settings.js +111 -111
  77. package/dist/psql/core/sqlHelp.js +150 -150
  78. package/dist/psql/core/startup.js +211 -205
  79. package/dist/psql/core/syncVars.js +14 -14
  80. package/dist/psql/core/variables.js +24 -24
  81. package/dist/psql/describe/formatters.js +302 -289
  82. package/dist/psql/describe/processNamePattern.js +28 -28
  83. package/dist/psql/describe/queries.js +656 -651
  84. package/dist/psql/index.js +436 -411
  85. package/dist/psql/io/history.js +36 -36
  86. package/dist/psql/io/input.js +15 -15
  87. package/dist/psql/io/lineEditor/buffer.js +27 -25
  88. package/dist/psql/io/lineEditor/complete.js +15 -15
  89. package/dist/psql/io/lineEditor/filename.js +22 -22
  90. package/dist/psql/io/lineEditor/index.js +65 -62
  91. package/dist/psql/io/lineEditor/keymap.js +325 -318
  92. package/dist/psql/io/lineEditor/vt100.js +60 -60
  93. package/dist/psql/io/pgpass.js +18 -18
  94. package/dist/psql/io/pgservice.js +14 -14
  95. package/dist/psql/io/psqlrc.js +46 -46
  96. package/dist/psql/print/aligned.js +175 -166
  97. package/dist/psql/print/asciidoc.js +51 -51
  98. package/dist/psql/print/crosstab.js +34 -31
  99. package/dist/psql/print/csv.js +25 -22
  100. package/dist/psql/print/html.js +54 -54
  101. package/dist/psql/print/json.js +12 -12
  102. package/dist/psql/print/latex.js +118 -118
  103. package/dist/psql/print/pager.js +28 -26
  104. package/dist/psql/print/troff.js +48 -48
  105. package/dist/psql/print/unaligned.js +15 -14
  106. package/dist/psql/print/units.js +17 -17
  107. package/dist/psql/scanner/slash.js +48 -46
  108. package/dist/psql/scanner/sql.js +88 -84
  109. package/dist/psql/scanner/stringutils.js +21 -17
  110. package/dist/psql/types/index.js +7 -7
  111. package/dist/psql/types/scanner.js +8 -8
  112. package/dist/psql/wire/connection.js +341 -327
  113. package/dist/psql/wire/copy.js +7 -7
  114. package/dist/psql/wire/pipeline.js +26 -24
  115. package/dist/psql/wire/protocol.js +102 -102
  116. package/dist/psql/wire/sasl.js +62 -62
  117. package/dist/psql/wire/tls.js +79 -73
  118. package/dist/storage_api.js +22 -23
  119. package/dist/test_utils/fixtures.js +74 -41
  120. package/dist/test_utils/oauth_server.js +5 -5
  121. package/dist/utils/api_enums.js +33 -0
  122. package/dist/utils/branch_notice.js +5 -5
  123. package/dist/utils/branch_picker.js +26 -26
  124. package/dist/utils/compute_units.js +4 -4
  125. package/dist/utils/enrichers.js +28 -16
  126. package/dist/utils/esbuild.js +28 -28
  127. package/dist/utils/formats.js +1 -1
  128. package/dist/utils/middlewares.js +3 -3
  129. package/dist/utils/package_manager.js +68 -0
  130. package/dist/utils/point_in_time.js +12 -12
  131. package/dist/utils/psql.js +30 -30
  132. package/dist/utils/string.js +2 -2
  133. package/dist/utils/ui.js +9 -9
  134. package/dist/utils/zip.js +1 -1
  135. package/dist/writer.js +17 -17
  136. package/package.json +10 -12
@@ -1,16 +1,20 @@
1
- import chalk from 'chalk';
2
- import { resolveConfig } from '@neondatabase/config';
3
- import { apply, createBranch as createBranchFromPolicy, inspect, loadConfigFromFile, plan, } from '@neondatabase/config-runtime';
4
- import { toNeonConfigView } from '../config_format.js';
5
- import { log } from '../log.js';
6
- import { isCi } from '../env.js';
7
- import { loadEnvFileIntoProcess } from '../env_file.js';
8
- import { fillSingleProject, resolveBranchRef } from '../utils/enrichers.js';
9
- import { announceTargetBranch } from '../utils/branch_notice.js';
10
- import { bundleEntry } from '../utils/esbuild.js';
11
- import { zipBundle } from '../utils/zip.js';
12
- import { writer } from '../writer.js';
13
- import { autoPullEnvAfterPin } from './env.js';
1
+ import { existsSync, readFileSync, writeFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import { resolveConfig } from "@neon/config";
4
+ import { apply, createBranch as createBranchFromPolicy, inspect, loadConfigFromFile, plan, } from "@neon/config-runtime";
5
+ import chalk from "chalk";
6
+ import { toNeonConfigView } from "../config_format.js";
7
+ import { contextBranch, readContextFile } from "../context.js";
8
+ import { isCi } from "../env.js";
9
+ import { loadEnvFileIntoProcess } from "../env_file.js";
10
+ import { log } from "../log.js";
11
+ import { announceTargetBranch } from "../utils/branch_notice.js";
12
+ import { fillSingleProject, resolveBranchRef } from "../utils/enrichers.js";
13
+ import { bundleEntry } from "../utils/esbuild.js";
14
+ import { addDependenciesArgs, resolvePackageManager, runCommand, } from "../utils/package_manager.js";
15
+ import { zipBundle } from "../utils/zip.js";
16
+ import { writer } from "../writer.js";
17
+ import { autoPullEnvAfterPin } from "./env.js";
14
18
  /**
15
19
  * Bundle a function with neonctl's OWN bundler (the shared esbuild helper) so the
16
20
  * config-runtime never has to import esbuild itself. Injecting this keeps esbuild
@@ -18,19 +22,19 @@ import { autoPullEnvAfterPin } from './env.js';
18
22
  * neonctl snapshot, which resolves esbuild dynamically at deploy time.
19
23
  */
20
24
  const neonctlBundler = async (fn) => zipBundle(await bundleEntry(fn.source));
21
- const INSPECT_FIELDS = ['project', 'branch', 'config'];
25
+ const INSPECT_FIELDS = ["project", "branch", "config"];
22
26
  // Deliberately minimal: action/kind/identifier are short and fixed-ish, so the table can
23
27
  // never overflow. Per-change `details` (a function's long invocationUrl in particular) are
24
28
  // intentionally NOT a column — they used to be JSON-stringified into a cell and blew the
25
29
  // table past 190 cols. Function URLs are printed below as a plain list (see reportPushResult),
26
30
  // and the full details are still available via `--output json`.
27
- const APPLIED_FIELDS = ['action', 'kind', 'identifier'];
31
+ const APPLIED_FIELDS = ["action", "kind", "identifier"];
28
32
  const CONFLICT_FIELDS = [
29
- 'identifier',
30
- 'field',
31
- 'current',
32
- 'desired',
33
- 'reason',
33
+ "identifier",
34
+ "field",
35
+ "current",
36
+ "desired",
37
+ "reason",
34
38
  ];
35
39
  /**
36
40
  * Shared `--env` flag for `config plan|apply` and `deploy`. Loads a `.env` into
@@ -38,21 +42,21 @@ const CONFLICT_FIELDS = [
38
42
  */
39
43
  export const envFlag = {
40
44
  env: {
41
- describe: 'Path to a .env file to load into the environment before evaluating neon.ts ' +
42
- '(so function env values resolve from it). Existing env vars are not overridden.',
43
- type: 'string',
45
+ describe: "Path to a .env file to load into the environment before evaluating neon.ts " +
46
+ "(so function env values resolve from it). Existing env vars are not overridden.",
47
+ type: "string",
44
48
  },
45
49
  };
46
50
  /** Apply-only flags, exported so `deploy` can reuse the exact same surface. */
47
51
  export const applyFlags = {
48
- 'update-existing': {
49
- describe: 'Auto-confirm overriding existing remote settings on the branch',
50
- type: 'boolean',
52
+ "update-existing": {
53
+ describe: "Auto-confirm overriding existing remote settings on the branch",
54
+ type: "boolean",
51
55
  default: false,
52
56
  },
53
- 'allow-protected': {
54
- describe: 'Auto-confirm applying to a branch marked protected on Neon',
55
- type: 'boolean',
57
+ "allow-protected": {
58
+ describe: "Auto-confirm applying to a branch marked protected on Neon",
59
+ type: "boolean",
56
60
  default: false,
57
61
  },
58
62
  };
@@ -62,54 +66,181 @@ export const applyFlags = {
62
66
  * same bundled convenience as `link` / `checkout`. On by default; `--no-env-pull` opts out.
63
67
  */
64
68
  export const envPullFlag = {
65
- 'env-pull': {
69
+ "env-pull": {
66
70
  describe: "Pull the branch's Neon env vars (DATABASE_URL, …) into a local .env after a " +
67
- 'successful apply. On by default; use --no-env-pull to skip (e.g. when injecting ' +
68
- 'env at runtime with `neon-env run` / `neon dev`).',
69
- type: 'boolean',
71
+ "successful apply. On by default; use --no-env-pull to skip (e.g. when injecting " +
72
+ "env at runtime with `neon-env run` / `neon dev`).",
73
+ type: "boolean",
70
74
  default: true,
71
75
  },
72
76
  };
73
- export const command = 'config';
74
- export const describe = 'Manage a branch with a neon.ts policy';
77
+ // ── `config init` ─────────────────────────────────────────────────────────────
78
+ /**
79
+ * The published npm packages a `neon.ts` project needs — the `@neon/*` org names.
80
+ *
81
+ * ⚠️ These ship to users the next time `neonctl` is released, so do NOT release
82
+ * neonctl until `@neon/config` and `@neon/env` are published to npm — otherwise
83
+ * `config init` would install packages that don't exist yet. (The libraries are
84
+ * mid-migration from `@neondatabase/*`; track their publish before cutting a CLI
85
+ * release.)
86
+ */
87
+ const CONFIG_PACKAGE = "@neon/config";
88
+ const ENV_PACKAGE = "@neon/env";
89
+ const REQUIRED_PACKAGES = [CONFIG_PACKAGE, ENV_PACKAGE];
90
+ /** package.json fields a dependency can be declared in. */
91
+ const DEPENDENCY_FIELDS = [
92
+ "dependencies",
93
+ "devDependencies",
94
+ "peerDependencies",
95
+ "optionalDependencies",
96
+ ];
97
+ /** Config filenames the runtime loads (mirrors @neon/config's loader). */
98
+ const NEON_CONFIG_FILENAMES = ["neon.ts", "neon.mts", "neon.js", "neon.mjs"];
99
+ /** Whether `dir` already has a Neon config file the runtime would load. */
100
+ export const hasNeonConfigFile = (dir) => NEON_CONFIG_FILENAMES.some((name) => existsSync(join(dir, name)));
101
+ /** Starter `neon.ts` written by `config init` when a project has none. */
102
+ const NEON_CONFIG_TEMPLATE = `import { defineConfig } from "${CONFIG_PACKAGE}/v1";
103
+
104
+ export default defineConfig({
105
+ // Declare your Neon services here
106
+ auth: false,
107
+ // Branch policy: per-branch tuning
108
+ branch: (branch) => {
109
+ if (branch.isDefault) {
110
+ // Default branch: no overrides, uses project defaults
111
+ return {};
112
+ }
113
+ if (!branch.exists) {
114
+ // New non-default branches: auto-expire
115
+ // Run \`neon checkout <name>\` to create a new branch with these settings
116
+ return { ttl: "7d" };
117
+ }
118
+ // Existing branch: no changes
119
+ return {};
120
+ },
121
+ });
122
+ `;
123
+ const isRecord = (value) => typeof value === "object" && value !== null;
124
+ /**
125
+ * The {@link REQUIRED_PACKAGES} not already declared in the project's package.json
126
+ * (any dependency field). A missing or malformed package.json means none are
127
+ * declared, so all are reported missing.
128
+ */
129
+ const missingDependencies = (cwd) => {
130
+ const declared = new Set();
131
+ const pkgPath = join(cwd, "package.json");
132
+ if (existsSync(pkgPath)) {
133
+ let parsed;
134
+ try {
135
+ parsed = JSON.parse(readFileSync(pkgPath, "utf8"));
136
+ }
137
+ catch {
138
+ parsed = undefined;
139
+ }
140
+ if (isRecord(parsed)) {
141
+ for (const field of DEPENDENCY_FIELDS) {
142
+ const deps = parsed[field];
143
+ if (isRecord(deps)) {
144
+ for (const name of Object.keys(deps))
145
+ declared.add(name);
146
+ }
147
+ }
148
+ }
149
+ }
150
+ return REQUIRED_PACKAGES.filter((pkg) => !declared.has(pkg));
151
+ };
152
+ /**
153
+ * Scaffold a `neon.ts` policy and make sure the Neon config packages are
154
+ * installed, so a project can go straight to `neon config plan` / `apply`.
155
+ * Purely local — it never touches the Neon API (see {@link isConfigInit}).
156
+ */
157
+ export const initCmd = async (props) => {
158
+ const cwd = props.cwd ?? process.cwd();
159
+ const run = props.run ?? runCommand;
160
+ // 1. Scaffold neon.ts unless the project already has a Neon config file.
161
+ const existing = NEON_CONFIG_FILENAMES.find((name) => existsSync(join(cwd, name)));
162
+ if (existing) {
163
+ log.info("Found an existing %s — leaving it untouched.", existing);
164
+ }
165
+ else {
166
+ writeFileSync(join(cwd, "neon.ts"), NEON_CONFIG_TEMPLATE);
167
+ log.info("Created neon.ts with a starter policy.");
168
+ }
169
+ // 2. Make sure the config packages are installed.
170
+ const missing = missingDependencies(cwd);
171
+ if (missing.length === 0) {
172
+ log.info("%s are already installed.", REQUIRED_PACKAGES.join(" and "));
173
+ }
174
+ else {
175
+ const pm = resolvePackageManager();
176
+ const args = addDependenciesArgs(pm, missing);
177
+ if (props.install === false) {
178
+ log.info("Install the Neon config packages to use neon.ts: %s %s", pm, args.join(" "));
179
+ }
180
+ else {
181
+ log.info("Installing %s with %s…", missing.join(", "), pm);
182
+ const ok = await run(pm, args, cwd);
183
+ if (!ok) {
184
+ log.warning("Could not install the config packages automatically. Run by hand: %s %s", pm, args.join(" "));
185
+ }
186
+ }
187
+ }
188
+ log.info("Next: edit neon.ts, then run `neon config plan` to preview and `neon config apply`.");
189
+ };
190
+ export const command = "config";
191
+ export const describe = "Manage a branch with a neon.ts policy";
75
192
  export const builder = (argv) => argv
76
- .usage('$0 config <sub-command> [options]')
193
+ .usage("$0 config <sub-command> [options]")
77
194
  .options({
78
- 'project-id': {
79
- describe: 'Project ID',
80
- type: 'string',
195
+ "project-id": {
196
+ describe: "Project ID",
197
+ type: "string",
81
198
  },
82
199
  branch: {
83
- describe: 'Branch ID or name',
84
- type: 'string',
200
+ describe: "Branch ID or name",
201
+ type: "string",
85
202
  },
86
203
  })
87
204
  .middleware(fillSingleProject)
88
- .command('status', "Show the branch's live Neon state", (yargs) => yargs.options({
89
- 'config-json': {
205
+ .command("status", "Show the branch's live Neon state", (yargs) => yargs.options({
206
+ "config-json": {
90
207
  describe: "Print only the branch's live config as neon.ts-shaped JSON " +
91
- '(services + branch tuning + preview), to stdout. Useful for ' +
92
- 'scripting or copying into a neon.ts.',
93
- type: 'boolean',
208
+ "(services + branch tuning + preview), to stdout. Useful for " +
209
+ "scripting or copying into a neon.ts.",
210
+ type: "boolean",
211
+ default: false,
212
+ },
213
+ "current-branch": {
214
+ describe: "Print only the linked branch name from the local .neon file " +
215
+ "(no network). Exits non-zero when no branch is pinned.",
216
+ type: "boolean",
94
217
  default: false,
95
218
  },
96
219
  }), (args) => status(args))
97
- .command('plan', 'Show what `config apply` would change (dry run)', (yargs) => yargs.options({
220
+ .command("plan", "Show what `config apply` would change (dry run)", (yargs) => yargs.options({
98
221
  config: {
99
- describe: 'Path to a neon.ts policy (defaults to walking up from cwd)',
100
- type: 'string',
222
+ describe: "Path to a neon.ts policy (defaults to walking up from cwd)",
223
+ type: "string",
101
224
  },
102
225
  ...envFlag,
103
226
  }), (args) => planCmd(args))
104
- .command('apply', 'Apply a neon.ts policy to the branch', (yargs) => yargs.options({
227
+ .command("apply", "Apply a neon.ts policy to the branch", (yargs) => yargs.options({
105
228
  config: {
106
- describe: 'Path to a neon.ts policy (defaults to walking up from cwd)',
107
- type: 'string',
229
+ describe: "Path to a neon.ts policy (defaults to walking up from cwd)",
230
+ type: "string",
108
231
  },
109
232
  ...envFlag,
110
233
  ...applyFlags,
111
234
  ...envPullFlag,
112
- }), (args) => applyCmd(args));
235
+ }), (args) => applyCmd(args))
236
+ .command("init", "Scaffold a neon.ts policy and install the Neon config packages", (yargs) => yargs.options({
237
+ install: {
238
+ describe: "Install @neon/config and @neon/env if they're missing. " +
239
+ "On by default; use --no-install to just print the command.",
240
+ type: "boolean",
241
+ default: true,
242
+ },
243
+ }), (args) => initCmd(args));
113
244
  export const handler = (args) => {
114
245
  return args;
115
246
  };
@@ -118,7 +249,7 @@ const loadConfig = async (props) => {
118
249
  // `process.env.X` sees them. Must happen before the policy module is imported/evaluated.
119
250
  if (props.env) {
120
251
  const applied = loadEnvFileIntoProcess(props.env);
121
- log.debug('Loaded %d var(s) from %s into the environment: %s', applied.length, props.env, applied.join(', '));
252
+ log.debug("Loaded %d var(s) from %s into the environment: %s", applied.length, props.env, applied.join(", "));
122
253
  }
123
254
  const { config } = await loadConfigFromFile({
124
255
  ...(props.config ? { path: props.config } : {}),
@@ -126,11 +257,26 @@ const loadConfig = async (props) => {
126
257
  return config;
127
258
  };
128
259
  export const status = async (props) => {
260
+ // `--current-branch` short-circuits here (before resolveBranchRef), so it wins
261
+ // over --config-json and ignores --output. See ConfigProps.currentBranch / isCurrentBranchProbe.
262
+ if (props.currentBranch) {
263
+ const branch = contextBranch(readContextFile(props.contextFile));
264
+ if (branch) {
265
+ process.stdout.write(`${branch}\n`);
266
+ }
267
+ else {
268
+ // No branch pinned: hint on stderr and exit non-zero (grep-style) so a prompt's
269
+ // `when` hides the segment cleanly instead of rendering a bare icon.
270
+ log.info("No branch pinned. Run `neonctl checkout <branch>` to pin a branch and pull its env vars.");
271
+ process.exitCode = 1;
272
+ }
273
+ return;
274
+ }
129
275
  const branch = await resolveBranchRef(props);
130
276
  // `--config-json` is a script-friendly mode that emits only JSON to stdout, so keep it
131
277
  // pristine; the regular human view gets the "which branch am I inspecting" guardrail.
132
278
  if (!props.configJson) {
133
- announceTargetBranch(props, branch, 'Inspecting branch');
279
+ announceTargetBranch(props, branch, "Inspecting branch");
134
280
  }
135
281
  const branchId = branch.branchId;
136
282
  const live = await inspect({
@@ -166,7 +312,7 @@ export const status = async (props) => {
166
312
  export const planCmd = async (props) => {
167
313
  const config = await loadConfig(props);
168
314
  const branch = await resolveBranchRef(props);
169
- announceTargetBranch(props, branch, 'Planning against branch');
315
+ announceTargetBranch(props, branch, "Planning against branch");
170
316
  const branchId = branch.branchId;
171
317
  // `plan` is a dry run that never bundles, so its options don't accept (or need)
172
318
  // an injected bundler — only `apply` does (it uses neonctlBundler).
@@ -177,12 +323,12 @@ export const planCmd = async (props) => {
177
323
  ...(props.apiHost ? { apiHost: props.apiHost } : {}),
178
324
  ...(props.runtimeApi ? { api: props.runtimeApi } : {}),
179
325
  });
180
- reportPushResult(props, result, 'plan', utilizedServices(config));
326
+ reportPushResult(props, result, "plan", utilizedServices(config));
181
327
  };
182
328
  export const applyCmd = async (props) => {
183
329
  const config = await loadConfig(props);
184
330
  const branch = await resolveBranchRef(props);
185
- announceTargetBranch(props, branch, 'Applying to branch');
331
+ announceTargetBranch(props, branch, "Applying to branch");
186
332
  const branchId = branch.branchId;
187
333
  const result = await apply(config, {
188
334
  projectId: props.projectId,
@@ -194,7 +340,7 @@ export const applyCmd = async (props) => {
194
340
  ...(props.allowProtected ? { allowProtectedBranch: true } : {}),
195
341
  bundleFunction: neonctlBundler,
196
342
  });
197
- reportPushResult(props, result, 'apply', utilizedServices(config));
343
+ reportPushResult(props, result, "apply", utilizedServices(config));
198
344
  // After a successful apply/deploy, write the branch's Neon env vars to a local .env —
199
345
  // the same bundled convenience as `link` / `checkout`, so the branch is immediately
200
346
  // usable for local dev. `--no-env-pull` opts out; a pull failure degrades to a warning
@@ -210,7 +356,7 @@ export const applyCmd = async (props) => {
210
356
  const isToggleEnabled = (toggle) => {
211
357
  if (toggle === undefined)
212
358
  return false;
213
- if (typeof toggle === 'boolean')
359
+ if (typeof toggle === "boolean")
214
360
  return toggle;
215
361
  return toggle.enabled !== false;
216
362
  };
@@ -224,19 +370,19 @@ const isToggleEnabled = (toggle) => {
224
370
  * lives in the per-branch closure), so reading it straight off `config` is accurate.
225
371
  */
226
372
  const utilizedServices = (config) => {
227
- const services = ['Postgres'];
373
+ const services = ["Postgres"];
228
374
  if (isToggleEnabled(config.auth))
229
- services.push('Neon Auth');
375
+ services.push("Neon Auth");
230
376
  if (isToggleEnabled(config.dataApi))
231
- services.push('Data API');
377
+ services.push("Data API");
232
378
  if (Object.keys(config.preview?.buckets ?? {}).length > 0) {
233
- services.push('Object Storage');
379
+ services.push("Object Storage");
234
380
  }
235
381
  if (Object.keys(config.preview?.functions ?? {}).length > 0) {
236
- services.push('Functions');
382
+ services.push("Functions");
237
383
  }
238
384
  if (isToggleEnabled(config.preview?.aiGateway))
239
- services.push('AI Gateway');
385
+ services.push("AI Gateway");
240
386
  return services;
241
387
  };
242
388
  /**
@@ -248,12 +394,12 @@ const utilizedServices = (config) => {
248
394
  * for being missing from the plan above.
249
395
  */
250
396
  const reportPushResult = (props, result, mode, services) => {
251
- if (props.output === 'json' || props.output === 'yaml') {
397
+ if (props.output === "json" || props.output === "yaml") {
252
398
  writer(props).end({ ...result, services }, { fields: [] });
253
399
  return;
254
400
  }
255
401
  const changes = result.applied
256
- .filter((change) => change.action !== 'noop')
402
+ .filter((change) => change.action !== "noop")
257
403
  .map((change) => ({
258
404
  action: change.action,
259
405
  kind: change.kind,
@@ -271,11 +417,11 @@ const reportPushResult = (props, result, mode, services) => {
271
417
  // Keyed by slug so a function never shows twice.
272
418
  const functionUrlBySlug = new Map();
273
419
  for (const change of result.applied) {
274
- if (change.action === 'noop')
420
+ if (change.action === "noop")
275
421
  continue;
276
422
  const slug = change.details?.slug;
277
423
  const invocationUrl = change.details?.invocationUrl;
278
- if (typeof slug === 'string' && typeof invocationUrl === 'string') {
424
+ if (typeof slug === "string" && typeof invocationUrl === "string") {
279
425
  functionUrlBySlug.set(slug, invocationUrl);
280
426
  }
281
427
  }
@@ -284,11 +430,11 @@ const reportPushResult = (props, result, mode, services) => {
284
430
  if (changes.length > 0) {
285
431
  out.write(changes, {
286
432
  fields: APPLIED_FIELDS,
287
- title: mode === 'plan' ? 'Planned changes' : 'Applied changes',
433
+ title: mode === "plan" ? "Planned changes" : "Applied changes",
288
434
  });
289
435
  }
290
436
  if (conflicts.length > 0) {
291
- out.write(conflicts, { fields: CONFLICT_FIELDS, title: 'Conflicts' });
437
+ out.write(conflicts, { fields: CONFLICT_FIELDS, title: "Conflicts" });
292
438
  }
293
439
  // Flush any tables, then append the lists/summary so they read directly below them.
294
440
  out.end();
@@ -296,7 +442,7 @@ const reportPushResult = (props, result, mode, services) => {
296
442
  // which makes any bordered table overflow and wrap awkwardly in a normal terminal. A list
297
443
  // lets each URL reflow on its own line, and stays copy-pasteable.
298
444
  if (functionUrlBySlug.size > 0) {
299
- const heading = mode === 'plan' ? 'Function URLs (after apply)' : 'Function URLs';
445
+ const heading = mode === "plan" ? "Function URLs (after apply)" : "Function URLs";
300
446
  out.text(`\n${isCi() ? heading : chalk.bold(heading)}\n`);
301
447
  for (const [slug, invocationUrl] of functionUrlBySlug) {
302
448
  out.text(` • ${slug}: ${invocationUrl}\n`);
@@ -305,14 +451,14 @@ const reportPushResult = (props, result, mode, services) => {
305
451
  if (noChanges) {
306
452
  log.info(`No changes — branch ${result.branchName} already matches the policy.`);
307
453
  }
308
- out.text(`\nUtilized services: ${services.join(', ')}\n`);
454
+ out.text(`\nUtilized services: ${services.join(", ")}\n`);
309
455
  if (conflicts.length > 0) {
310
- log.info('Resolve the conflicts above, or re-run with --update-existing to override the current remote settings.');
456
+ log.info("Resolve the conflicts above, or re-run with --update-existing to override the current remote settings.");
311
457
  }
312
458
  };
313
459
  const stringify = (value) => value === undefined
314
- ? ''
315
- : typeof value === 'string'
460
+ ? ""
461
+ : typeof value === "string"
316
462
  ? value
317
463
  : JSON.stringify(value);
318
464
  /**
@@ -337,7 +483,7 @@ export const applyPolicyOnCreate = async (props) => {
337
483
  return;
338
484
  throw err;
339
485
  }
340
- log.info('Applying neon.ts policy to the new branch…');
486
+ log.info("Applying neon.ts policy to the new branch…");
341
487
  const result = await apply(config, {
342
488
  projectId: props.projectId,
343
489
  branchId: props.branchId,
@@ -352,12 +498,12 @@ export const applyPolicyOnCreate = async (props) => {
352
498
  };
353
499
  /** Log a one-line summary of what applying a `neon.ts` policy changed (or that nothing did). */
354
500
  const logPolicyResult = (result) => {
355
- const changes = result.applied.filter((c) => c.action !== 'noop');
501
+ const changes = result.applied.filter((c) => c.action !== "noop");
356
502
  if (changes.length === 0) {
357
- log.info('neon.ts applied — no changes were needed.');
503
+ log.info("neon.ts applied — no changes were needed.");
358
504
  return;
359
505
  }
360
- log.info('neon.ts applied — %d change%s: %s', changes.length, changes.length === 1 ? '' : 's', changes.map((c) => `${c.action} ${c.identifier}`).join(', '));
506
+ log.info("neon.ts applied — %d change%s: %s", changes.length, changes.length === 1 ? "" : "s", changes.map((c) => `${c.action} ${c.identifier}`).join(", "));
361
507
  };
362
508
  /**
363
509
  * Create a branch **from** the local `neon.ts` policy. Returns `null` when there is no
@@ -392,7 +538,7 @@ export const createBranchFromPolicyOnCheckout = async (props) => {
392
538
  ...(props.runtimeApi ? { api: props.runtimeApi } : {}),
393
539
  bundleFunction: neonctlBundler,
394
540
  });
395
- log.info('Created branch %s (%s) from neon.ts policy.', branchName, branchId);
541
+ log.info("Created branch %s (%s) from neon.ts policy.", branchName, branchId);
396
542
  logPolicyResult(result);
397
543
  return { branchId };
398
544
  };