neon 2.47.0 → 3.1.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 (198) hide show
  1. package/README.md +54 -0
  2. package/dist/_shared/auth_selection.js +76 -79
  3. package/dist/_shared/credentials.js +166 -187
  4. package/dist/_shared/env-core/env.js +395 -0
  5. package/dist/_shared/env-core/reuse-secrets.js +179 -0
  6. package/dist/_shared/paths.js +129 -126
  7. package/dist/_shared/profiles.js +192 -242
  8. package/dist/_shared/secure_file.js +36 -38
  9. package/dist/_virtual/_rolldown/runtime.js +13 -0
  10. package/dist/analytics.js +163 -207
  11. package/dist/api.js +815 -758
  12. package/dist/auth.js +121 -141
  13. package/dist/auth_context.js +39 -53
  14. package/dist/cli.js +4 -7
  15. package/dist/commands/api.js +220 -250
  16. package/dist/commands/api_keys.js +251 -314
  17. package/dist/commands/auth.js +283 -328
  18. package/dist/commands/bootstrap.js +372 -437
  19. package/dist/commands/branches.js +304 -455
  20. package/dist/commands/bucket.js +374 -514
  21. package/dist/commands/checkout.js +213 -298
  22. package/dist/commands/config.js +575 -658
  23. package/dist/commands/connection_string.js +137 -165
  24. package/dist/commands/data_api.js +238 -260
  25. package/dist/commands/databases.js +67 -76
  26. package/dist/commands/deploy.js +31 -25
  27. package/dist/commands/dev.js +642 -681
  28. package/dist/commands/diff.js +156 -200
  29. package/dist/commands/env.js +243 -303
  30. package/dist/commands/functions.js +275 -341
  31. package/dist/commands/index.js +70 -65
  32. package/dist/commands/init.js +84 -119
  33. package/dist/commands/inspect.js +55 -55
  34. package/dist/commands/ip_allow.js +88 -120
  35. package/dist/commands/link.js +874 -1019
  36. package/dist/commands/logs.js +291 -0
  37. package/dist/commands/neon_auth.js +725 -933
  38. package/dist/commands/operations.js +34 -25
  39. package/dist/commands/orgs.js +28 -18
  40. package/dist/commands/profile.js +614 -845
  41. package/dist/commands/projects.js +313 -373
  42. package/dist/commands/psql.js +60 -58
  43. package/dist/commands/roles.js +55 -58
  44. package/dist/commands/schema_diff.js +87 -131
  45. package/dist/commands/set_context.js +34 -26
  46. package/dist/commands/snapshots.js +288 -413
  47. package/dist/commands/status.js +41 -37
  48. package/dist/commands/user.js +21 -10
  49. package/dist/commands/vpc_endpoints.js +85 -113
  50. package/dist/config.js +7 -6
  51. package/dist/config_format.js +50 -66
  52. package/dist/config_template.js +128 -157
  53. package/dist/context.js +183 -235
  54. package/dist/current_branch_fast_path.js +40 -49
  55. package/dist/dev/env.js +313 -394
  56. package/dist/dev/functions.js +54 -64
  57. package/dist/dev/inputs.js +46 -58
  58. package/dist/dev/runtime.js +135 -164
  59. package/dist/dev/websocket.js +766 -959
  60. package/dist/env.js +27 -33
  61. package/dist/env_file.js +118 -132
  62. package/dist/env_services.js +36 -38
  63. package/dist/errors.js +57 -68
  64. package/dist/functions_api.js +45 -43
  65. package/dist/help.js +189 -140
  66. package/dist/index.js +182 -257
  67. package/dist/init/agents.js +137 -118
  68. package/dist/init/auth.js +58 -68
  69. package/dist/init/bootstrap.js +325 -396
  70. package/dist/init/build_config.js +4 -2
  71. package/dist/init/detect_agent.js +56 -101
  72. package/dist/init/editors.js +35 -52
  73. package/dist/init/enrich_output.js +51 -66
  74. package/dist/init/extension.js +134 -171
  75. package/dist/init/inspect.js +179 -266
  76. package/dist/init/interactive.js +510 -622
  77. package/dist/init/neonctl.js +117 -168
  78. package/dist/init/orchestrate.js +157 -173
  79. package/dist/init/phases/auth.js +188 -202
  80. package/dist/init/phases/cleanup.js +23 -23
  81. package/dist/init/phases/db.js +251 -277
  82. package/dist/init/phases/getting_started.js +213 -223
  83. package/dist/init/phases/mcp.js +174 -224
  84. package/dist/init/phases/migrations.js +247 -248
  85. package/dist/init/phases/neon_auth.js +114 -133
  86. package/dist/init/phases/setup.js +546 -703
  87. package/dist/init/phases/skills.js +75 -86
  88. package/dist/init/phases/status.js +72 -67
  89. package/dist/init/resolve_context.js +102 -99
  90. package/dist/init/route_command.js +91 -98
  91. package/dist/init/skills.js +174 -218
  92. package/dist/init/vsix.js +77 -99
  93. package/dist/log.js +17 -16
  94. package/dist/neon_services.js +104 -129
  95. package/dist/parameters.gen.js +481 -471
  96. package/dist/pkg.js +17 -19
  97. package/dist/profile_keys.js +44 -47
  98. package/dist/psql/cli.js +44 -47
  99. package/dist/psql/command/cmd_cond.js +231 -406
  100. package/dist/psql/command/cmd_connect.js +557 -764
  101. package/dist/psql/command/cmd_copy.js +727 -983
  102. package/dist/psql/command/cmd_describe.js +1499 -1688
  103. package/dist/psql/command/cmd_format.js +733 -905
  104. package/dist/psql/command/cmd_io.js +1293 -2082
  105. package/dist/psql/command/cmd_lo.js +297 -359
  106. package/dist/psql/command/cmd_meta.js +727 -878
  107. package/dist/psql/command/cmd_misc.js +138 -172
  108. package/dist/psql/command/cmd_pipeline.js +547 -1099
  109. package/dist/psql/command/cmd_restrict.js +119 -155
  110. package/dist/psql/command/cmd_show.js +529 -688
  111. package/dist/psql/command/dispatch.js +261 -325
  112. package/dist/psql/command/inputQueue.js +35 -33
  113. package/dist/psql/command/shared.js +49 -63
  114. package/dist/psql/complete/filenames.js +90 -133
  115. package/dist/psql/complete/index.js +59 -97
  116. package/dist/psql/complete/matcher.js +236 -300
  117. package/dist/psql/complete/psqlVars.js +218 -223
  118. package/dist/psql/complete/queries.js +159 -177
  119. package/dist/psql/complete/rules.js +1493 -2299
  120. package/dist/psql/core/common.js +762 -1180
  121. package/dist/psql/core/help.js +456 -546
  122. package/dist/psql/core/mainloop.js +692 -1302
  123. package/dist/psql/core/prompt.js +391 -408
  124. package/dist/psql/core/settings.js +429 -644
  125. package/dist/psql/core/sqlHelp.js +480 -554
  126. package/dist/psql/core/startup.js +626 -815
  127. package/dist/psql/core/syncVars.js +67 -110
  128. package/dist/psql/core/variables.js +156 -278
  129. package/dist/psql/describe/formatters.js +884 -1285
  130. package/dist/psql/describe/processNamePattern.js +173 -260
  131. package/dist/psql/describe/queries.js +1368 -2403
  132. package/dist/psql/describe/versionGate.js +32 -41
  133. package/dist/psql/index.js +1414 -1927
  134. package/dist/psql/io/history.js +232 -271
  135. package/dist/psql/io/input.js +103 -108
  136. package/dist/psql/io/lineEditor/buffer.js +238 -319
  137. package/dist/psql/io/lineEditor/complete.js +135 -213
  138. package/dist/psql/io/lineEditor/filename.js +139 -148
  139. package/dist/psql/io/lineEditor/index.js +653 -870
  140. package/dist/psql/io/lineEditor/keymap.js +544 -702
  141. package/dist/psql/io/lineEditor/vt100.js +294 -341
  142. package/dist/psql/io/pgpass.js +158 -187
  143. package/dist/psql/io/pgservice.js +146 -183
  144. package/dist/psql/io/psqlrc.js +328 -403
  145. package/dist/psql/print/aligned.js +1020 -1683
  146. package/dist/psql/print/asciidoc.js +180 -214
  147. package/dist/psql/print/crosstab.js +281 -442
  148. package/dist/psql/print/csv.js +48 -70
  149. package/dist/psql/print/html.js +195 -226
  150. package/dist/psql/print/json.js +75 -88
  151. package/dist/psql/print/latex.js +291 -364
  152. package/dist/psql/print/pager.js +171 -242
  153. package/dist/psql/print/troff.js +194 -226
  154. package/dist/psql/print/unaligned.js +69 -95
  155. package/dist/psql/print/units.js +167 -169
  156. package/dist/psql/scanner/slash.js +428 -483
  157. package/dist/psql/scanner/sql.js +445 -889
  158. package/dist/psql/scanner/stringutils.js +309 -379
  159. package/dist/psql/types/index.js +2 -7
  160. package/dist/psql/types/scanner.js +25 -22
  161. package/dist/psql/wire/connection.js +2042 -2803
  162. package/dist/psql/wire/copy.js +84 -100
  163. package/dist/psql/wire/notify.js +39 -59
  164. package/dist/psql/wire/pipeline.js +305 -518
  165. package/dist/psql/wire/protocol.js +349 -417
  166. package/dist/psql/wire/sasl.js +180 -265
  167. package/dist/psql/wire/tls.js +400 -561
  168. package/dist/storage_api.js +115 -129
  169. package/dist/test_utils/fixtures.js +94 -113
  170. package/dist/test_utils/oauth_server.js +10 -7
  171. package/dist/test_utils/project_dir.js +33 -0
  172. package/dist/utils/ai_gateway_notice.js +131 -162
  173. package/dist/utils/api_enums.js +21 -28
  174. package/dist/utils/auth.js +10 -4
  175. package/dist/utils/branch_notice.js +20 -19
  176. package/dist/utils/branch_picker.js +83 -89
  177. package/dist/utils/cli_name.js +15 -12
  178. package/dist/utils/compute_units.js +20 -27
  179. package/dist/utils/config_diff.js +127 -158
  180. package/dist/utils/enrichers.js +95 -148
  181. package/dist/utils/esbuild.js +133 -147
  182. package/dist/utils/flags.js +35 -47
  183. package/dist/utils/formats.js +8 -15
  184. package/dist/utils/git_diff.js +69 -80
  185. package/dist/utils/inspect_db.js +101 -143
  186. package/dist/utils/inspect_queries.js +179 -142
  187. package/dist/utils/middlewares.js +37 -44
  188. package/dist/utils/openapi.js +87 -99
  189. package/dist/utils/package_manager.js +312 -110
  190. package/dist/utils/point_in_time.js +49 -53
  191. package/dist/utils/psql.js +89 -106
  192. package/dist/utils/service_picker.js +55 -58
  193. package/dist/utils/string.js +5 -5
  194. package/dist/utils/ui.js +38 -55
  195. package/dist/utils/write_sync.js +26 -35
  196. package/dist/utils/zip.js +4 -3
  197. package/dist/writer.js +67 -87
  198. package/package.json +9 -7
@@ -1,374 +1,308 @@
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";
1
+ import { __exportAll } from "../_virtual/_rolldown/runtime.js";
5
2
  import { log } from "../log.js";
3
+ import { isNeonApiError, retryOnLock } from "../api.js";
6
4
  import { getCliName } from "../utils/cli_name.js";
5
+ import { createDeployment, deleteFunction, getFunction, listFunctions } from "../functions_api.js";
6
+ import { writer } from "../writer.js";
7
7
  import { branchIdFromProps, fillSingleProject } from "../utils/enrichers.js";
8
8
  import { bundleEntry } from "../utils/esbuild.js";
9
9
  import { zipBundle } from "../utils/zip.js";
10
- import { writer } from "../writer.js";
10
+ import { existsSync, statSync } from "node:fs";
11
+ import { dirname, join } from "node:path";
12
+ import { describeNativeFinding, findUndeclaredNativePackages } from "@neon/config-runtime";
13
+ //#region src/commands/functions.ts
14
+ var functions_exports = /* @__PURE__ */ __exportAll({
15
+ aliases: () => aliases,
16
+ builder: () => builder,
17
+ command: () => command,
18
+ describe: () => describe,
19
+ handler: () => handler
20
+ });
11
21
  const FUNCTION_FIELDS = [
12
- "slug",
13
- "name",
14
- "invocation_url",
15
- "created_at",
22
+ "slug",
23
+ "name",
24
+ "invocation_url",
25
+ "created_at"
16
26
  ];
17
27
  const FUNCTIONS_LIST_LIMIT = 100;
18
- // Table columns for `functions list`. `status` is a derived field (the
19
- // table writer reads flat fields only): the current deployment's status.
20
28
  const LIST_TABLE_FIELDS = [
21
- "slug",
22
- "name",
23
- "status",
24
- "invocation_url",
25
- "created_at",
29
+ "slug",
30
+ "name",
31
+ "status",
32
+ "invocation_url",
33
+ "created_at"
26
34
  ];
27
35
  const DEPLOYMENT_FIELDS = [
28
- "id",
29
- "status",
30
- "runtime",
31
- "memory_mib",
32
- "created_at",
36
+ "id",
37
+ "status",
38
+ "runtime",
39
+ "memory_mib",
40
+ "created_at"
33
41
  ];
34
- // Deploy emits the resolved deployment plus the function's invocation_url, so a
35
- // successful `functions deploy` tells the user exactly where to call the function.
36
42
  const DEPLOY_RESULT_FIELDS = [
37
- "id",
38
- "status",
39
- "invocation_url",
40
- "runtime",
41
- "memory_mib",
42
- "created_at",
43
+ "id",
44
+ "status",
45
+ "invocation_url",
46
+ "runtime",
47
+ "memory_mib",
48
+ "created_at"
43
49
  ];
44
- // In table mode a failed build's reason gets its own "deployment error"
45
- // section after the deployment table; json/yaml carry the raw `error` field.
46
50
  const writeDeploymentErrorSection = (out, dep) => {
47
- if (dep.status === "failed" && dep.error) {
48
- out.write({ reason: dep.error }, { fields: ["reason"], title: "deployment error" });
49
- }
51
+ if (dep.status === "failed" && dep.error) out.write({ reason: dep.error }, {
52
+ fields: ["reason"],
53
+ title: "deployment error"
54
+ });
50
55
  };
51
56
  const SLUG_PATTERN = /^[a-z0-9]{1,20}$/;
52
57
  const SLUG_HELP = "Use 1-20 lowercase letters and digits (no hyphens or other characters).";
53
- // Entry-point discovery order inside --src.
54
- const ENTRY_CANDIDATES = ["index.ts", "index.mjs", "index.js"];
55
- // Overridable so tests can poll fast; defaults to 2s in real use.
56
- const POLL_INTERVAL_MS = Number(process.env.NEON_FUNCTIONS_POLL_INTERVAL_MS) || 2000;
57
- // Upper bound on --wait polling so the CLI never hangs (e.g. if our deployment
58
- // never shows up as current_deployment). Overridable so tests can time out fast;
59
- // defaults to 10 minutes in real use.
60
- 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"];
64
- export const builder = (argv) => argv
65
- .usage("$0 function <sub-command> [options]")
66
- .options({
67
- "project-id": {
68
- describe: "Project ID",
69
- type: "string",
70
- },
71
- branch: {
72
- describe: "Branch ID or name",
73
- type: "string",
74
- },
75
- })
76
- .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",
81
- demandOption: true,
82
- })
83
- .options({
84
- 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",
87
- },
88
- // Removed flags, kept hidden so old invocations fail loudly instead
89
- // of being silently ignored (the CLI has no .strictOptions()).
90
- path: {
91
- type: "string",
92
- hidden: true,
93
- },
94
- entry: {
95
- type: "string",
96
- hidden: true,
97
- },
98
- runtime: {
99
- describe: "Function runtime",
100
- type: "string",
101
- choices: ["nodejs24"],
102
- },
103
- env: {
104
- describe: "Environment variable as KEY=VALUE (repeatable)",
105
- type: "string",
106
- array: true,
107
- },
108
- wait: {
109
- describe: "Wait for the deployment to finish building",
110
- type: "boolean",
111
- default: true,
112
- },
113
- }), (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",
119
- demandOption: true,
120
- })
121
- .options({
122
- "list-env-variables": {
123
- describe: "List the environment variable names of the active deployment",
124
- type: "boolean",
125
- alias: "E",
126
- default: false,
127
- },
128
- }), (args) => get(args))
129
- .command("delete <slug>", "Delete a function on the branch", (yargs) => yargs.positional("slug", {
130
- describe: "Function slug",
131
- type: "string",
132
- demandOption: true,
58
+ const ENTRY_CANDIDATES = [
59
+ "index.ts",
60
+ "index.mjs",
61
+ "index.js"
62
+ ];
63
+ const POLL_INTERVAL_MS = Number(process.env.NEON_FUNCTIONS_POLL_INTERVAL_MS) || 2e3;
64
+ const POLL_TIMEOUT_MS = Number(process.env.NEON_FUNCTIONS_POLL_TIMEOUT_MS) || 6e5;
65
+ const command = "functions";
66
+ const describe = "Manage Neon Functions";
67
+ const aliases = ["function"];
68
+ const builder = (argv) => argv.usage("$0 function <sub-command> [options]").options({
69
+ "project-id": {
70
+ describe: "Project ID",
71
+ type: "string"
72
+ },
73
+ branch: {
74
+ describe: "Branch ID or name",
75
+ type: "string"
76
+ }
77
+ }).middleware(fillSingleProject).command("deploy <slug>", "Deploy a function from a local directory", (yargs) => yargs.positional("slug", {
78
+ describe: "Function slug (1-20 lowercase letters and digits)",
79
+ type: "string",
80
+ demandOption: true
81
+ }).options({
82
+ src: {
83
+ describe: "Function source: a directory containing index.ts, index.mjs, or index.js, or a path to the entry file",
84
+ type: "string"
85
+ },
86
+ path: {
87
+ type: "string",
88
+ hidden: true
89
+ },
90
+ entry: {
91
+ type: "string",
92
+ hidden: true
93
+ },
94
+ runtime: {
95
+ describe: "Function runtime",
96
+ type: "string",
97
+ choices: ["nodejs24"]
98
+ },
99
+ env: {
100
+ describe: "Environment variable as KEY=VALUE (repeatable)",
101
+ type: "string",
102
+ array: true
103
+ },
104
+ wait: {
105
+ describe: "Wait for the deployment to finish building",
106
+ type: "boolean",
107
+ default: true
108
+ }
109
+ }), (args) => deploy(args)).command("list", "List functions on the branch", (yargs) => yargs, (args) => list(args)).command("get <slug>", "Show a function's details", (yargs) => yargs.positional("slug", {
110
+ describe: "Function slug",
111
+ type: "string",
112
+ demandOption: true
113
+ }).options({ "list-env-variables": {
114
+ describe: "List the environment variable names of the active deployment",
115
+ type: "boolean",
116
+ alias: "E",
117
+ default: false
118
+ } }), (args) => get(args)).command("delete <slug>", "Delete a function on the branch", (yargs) => yargs.positional("slug", {
119
+ describe: "Function slug",
120
+ type: "string",
121
+ demandOption: true
133
122
  }), (args) => deleteFn(args));
134
- export const handler = (args) => {
135
- return args;
123
+ const handler = (args) => {
124
+ return args;
136
125
  };
137
126
  const parseEnv = (entries) => {
138
- if (!entries || entries.length === 0)
139
- return undefined;
140
- const map = {};
141
- for (const entry of entries) {
142
- const eq = entry.indexOf("=");
143
- if (eq <= 0) {
144
- throw new Error(`Invalid --env value "${entry}". Expected KEY=VALUE.`);
145
- }
146
- map[entry.slice(0, eq)] = entry.slice(eq + 1);
147
- }
148
- return JSON.stringify(map);
127
+ if (!entries || entries.length === 0) return void 0;
128
+ const map = {};
129
+ for (const entry of entries) {
130
+ const eq = entry.indexOf("=");
131
+ if (eq <= 0) throw new Error(`Invalid --env value "${entry}". Expected KEY=VALUE.`);
132
+ map[entry.slice(0, eq)] = entry.slice(eq + 1);
133
+ }
134
+ return JSON.stringify(map);
149
135
  };
150
136
  const statusHint = (slug, projectId, branchId) => `Check status with: ${getCliName()} function get ${slug} --project-id ${projectId} --branch ${branchId}`;
151
- // Emit the resolved deployment together with the function's invocation_url, so the
152
- // deploy output shows where the function is reachable (not just the deployment id).
153
137
  const emitDeployResult = (props, deployment, fn) => {
154
- const out = writer(props).write({ ...deployment, invocation_url: fn?.invocation_url }, { fields: DEPLOY_RESULT_FIELDS });
155
- if (props.output !== "json" && props.output !== "yaml") {
156
- writeDeploymentErrorSection(out, deployment);
157
- }
158
- out.end();
138
+ const out = writer(props).write({
139
+ ...deployment,
140
+ invocation_url: fn?.invocation_url
141
+ }, { fields: DEPLOY_RESULT_FIELDS });
142
+ if (props.output !== "json" && props.output !== "yaml") writeDeploymentErrorSection(out, deployment);
143
+ out.end();
159
144
  };
160
- // A poll error worth retrying: a network error (no HTTP response), a 5xx, or a
161
- // 404 from eventual consistency. Anything else (e.g. 401/403) is surfaced.
162
- const isTransient = (err) => isNeonApiError(err) &&
163
- (err.status === undefined || err.status === 404 || err.status >= 500);
145
+ const isTransient = (err) => isNeonApiError(err) && (err.status === void 0 || err.status === 404 || err.status >= 500);
164
146
  const deploy = async (props) => {
165
- 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.");
168
- }
169
- // At least one deploy option must be passed (--wait is excluded: it controls
170
- // output, not what gets deployed).
171
- const hasOption = props.src !== undefined ||
172
- props.env !== undefined ||
173
- props.runtime !== undefined;
174
- if (!hasOption) {
175
- throw new Error("Provide at least one option to deploy, e.g. --src or --env. " +
176
- `See: ${getCliName()} function deploy --help.`);
177
- }
178
- // Cheap, offline validation first - fail before any network round-trip.
179
- if (!SLUG_PATTERN.test(props.slug)) {
180
- throw new Error(`Invalid function slug "${props.slug}". ${SLUG_HELP}`);
181
- }
182
- const src = props.src ?? ".";
183
- const runtime = props.runtime ?? "nodejs24";
184
- const environment = parseEnv(props.env);
185
- const srcStat = statSync(src, { throwIfNoEntry: false });
186
- if (srcStat === undefined) {
187
- throw new Error(`--src path not found: ${src}.`);
188
- }
189
- // A file is used as the entry point directly; a directory triggers discovery.
190
- const source = srcStat.isFile()
191
- ? src
192
- : ENTRY_CANDIDATES.map((name) => join(src, name)).find((p) => existsSync(p));
193
- if (source === undefined) {
194
- throw new Error(`No entry file found in ${src}. Expected one of: ${ENTRY_CANDIDATES.join(", ")}.`);
195
- }
196
- // Bundle before any network round-trip so a bundling failure fails fast.
197
- const zip = zipBundle(await bundleEntry(source));
198
- const branchId = await branchIdFromProps(props);
199
- // Snapshot the current version before deploy so we can detect the new one
200
- // afterward. A missing function (404) or no deployment yet → undefined.
201
- let before;
202
- try {
203
- const fn = await getFunction(props.apiClient, props.projectId, branchId, props.slug);
204
- before = fn.current_deployment?.id;
205
- }
206
- catch (err) {
207
- if (!(isNeonApiError(err) && err.status === 404))
208
- throw err;
209
- }
210
- await retryOnLock(() => createDeployment(props.apiClient, props.projectId, branchId, props.slug, {
211
- zip,
212
- runtime,
213
- environment,
214
- }));
215
- log.info(`Function deployment triggered for function ${props.slug}.`);
216
- // Best-effort interrupt: a Ctrl-C lands at the next poll boundary. (No
217
- // automated test; mirrors the resolution branches below, verified manually.)
218
- let interrupted = false;
219
- const onSignal = () => {
220
- interrupted = true;
221
- };
222
- process.once("SIGINT", onSignal);
223
- process.once("SIGTERM", onSignal);
224
- // Poll until a NEW version appears (id greater than the snapshot, or
225
- // any version if there was none). --no-wait stops there; --wait stops at a
226
- // terminal status. Bounded by POLL_TIMEOUT_MS so it never hangs.
227
- let resolved;
228
- // The function carries the invocation_url; keep the whole record (not just its
229
- // current_deployment) so we can surface that URL on success.
230
- let resolvedFn;
231
- const deadline = Date.now() + POLL_TIMEOUT_MS;
232
- try {
233
- while (!interrupted && Date.now() < deadline) {
234
- await new Promise((r) => setTimeout(r, POLL_INTERVAL_MS));
235
- if (interrupted)
236
- break;
237
- // The deploy already succeeded server-side; tolerate transient poll
238
- // failures and retry on the next interval. Surface anything else.
239
- let fn;
240
- try {
241
- fn = await getFunction(props.apiClient, props.projectId, branchId, props.slug);
242
- }
243
- catch (err) {
244
- if (isTransient(err))
245
- continue;
246
- throw err;
247
- }
248
- const dep = fn.current_deployment;
249
- const isNew = dep !== undefined && (before === undefined || dep.id > before);
250
- if (isNew && dep) {
251
- resolved = dep;
252
- resolvedFn = fn;
253
- if (!props.wait)
254
- break;
255
- if (dep.status === "completed" || dep.status === "failed")
256
- break;
257
- }
258
- }
259
- }
260
- finally {
261
- process.removeListener("SIGINT", onSignal);
262
- process.removeListener("SIGTERM", onSignal);
263
- }
264
- if (interrupted) {
265
- log.info(statusHint(props.slug, props.projectId, branchId));
266
- if (resolved)
267
- emitDeployResult(props, resolved, resolvedFn);
268
- return;
269
- }
270
- if (resolved === undefined) {
271
- log.info(statusHint(props.slug, props.projectId, branchId));
272
- throw new Error(`Timed out waiting for the deployment of ${props.slug} to start. It may still be in progress.`);
273
- }
274
- emitDeployResult(props, resolved, resolvedFn);
275
- if (!props.wait) {
276
- log.info(statusHint(props.slug, props.projectId, branchId));
277
- return;
278
- }
279
- if (resolved.status === "completed") {
280
- log.info(`Function deployment ${props.slug}/${resolved.id} completed.`);
281
- return;
282
- }
283
- if (resolved.status === "failed") {
284
- throw new Error(`Function deployment ${props.slug}/${resolved.id} failed.`);
285
- }
286
- // --wait, new version appeared but the deadline hit before it finished.
287
- log.info(statusHint(props.slug, props.projectId, branchId));
288
- throw new Error(`Timed out waiting for function deployment ${props.slug}/${resolved.id} to finish. It may still be building.`);
147
+ if (props.path !== void 0 || props.entry !== void 0) throw new Error("--path and --entry were removed. Use --src <dir>; the entry point is discovered as index.ts, index.mjs, or index.js in that directory.");
148
+ if (!(props.src !== void 0 || props.env !== void 0 || props.runtime !== void 0)) throw new Error(`Provide at least one option to deploy, e.g. --src or --env. See: ${getCliName()} function deploy --help.`);
149
+ if (!SLUG_PATTERN.test(props.slug)) throw new Error(`Invalid function slug "${props.slug}". ${SLUG_HELP}`);
150
+ const src = props.src ?? ".";
151
+ const runtime = props.runtime ?? "nodejs24";
152
+ const environment = parseEnv(props.env);
153
+ const srcStat = statSync(src, { throwIfNoEntry: false });
154
+ if (srcStat === void 0) throw new Error(`--src path not found: ${src}.`);
155
+ const source = srcStat.isFile() ? src : ENTRY_CANDIDATES.map((name) => join(src, name)).find((p) => existsSync(p));
156
+ if (source === void 0) throw new Error(`No entry file found in ${src}. Expected one of: ${ENTRY_CANDIDATES.join(", ")}.`);
157
+ const bundled = await bundleEntry(source);
158
+ for (const warning of bundled.warnings) log.warning(warning);
159
+ for (const finding of findUndeclaredNativePackages({
160
+ metafile: bundled.metafile,
161
+ declared: [],
162
+ projectDir: dirname(source)
163
+ })) log.warning(describeNativeFinding(props.slug, finding));
164
+ const zip = zipBundle(bundled.files);
165
+ const branchId = await branchIdFromProps(props);
166
+ let before;
167
+ try {
168
+ before = (await getFunction(props.apiClient, props.projectId, branchId, props.slug)).current_deployment?.id;
169
+ } catch (err) {
170
+ if (!(isNeonApiError(err) && err.status === 404)) throw err;
171
+ }
172
+ await retryOnLock(() => createDeployment(props.apiClient, props.projectId, branchId, props.slug, {
173
+ zip,
174
+ runtime,
175
+ environment
176
+ }));
177
+ log.info(`Function deployment triggered for function ${props.slug}.`);
178
+ let interrupted = false;
179
+ const onSignal = () => {
180
+ interrupted = true;
181
+ };
182
+ process.once("SIGINT", onSignal);
183
+ process.once("SIGTERM", onSignal);
184
+ let resolved;
185
+ let resolvedFn;
186
+ const deadline = Date.now() + POLL_TIMEOUT_MS;
187
+ try {
188
+ while (!interrupted && Date.now() < deadline) {
189
+ await new Promise((r) => setTimeout(r, POLL_INTERVAL_MS));
190
+ if (interrupted) break;
191
+ let fn;
192
+ try {
193
+ fn = await getFunction(props.apiClient, props.projectId, branchId, props.slug);
194
+ } catch (err) {
195
+ if (isTransient(err)) continue;
196
+ throw err;
197
+ }
198
+ const dep = fn.current_deployment;
199
+ if (dep !== void 0 && (before === void 0 || dep.id > before) && dep) {
200
+ resolved = dep;
201
+ resolvedFn = fn;
202
+ if (!props.wait) break;
203
+ if (dep.status === "completed" || dep.status === "failed") break;
204
+ }
205
+ }
206
+ } finally {
207
+ process.removeListener("SIGINT", onSignal);
208
+ process.removeListener("SIGTERM", onSignal);
209
+ }
210
+ if (interrupted) {
211
+ log.info(statusHint(props.slug, props.projectId, branchId));
212
+ if (resolved) emitDeployResult(props, resolved, resolvedFn);
213
+ return;
214
+ }
215
+ if (resolved === void 0) {
216
+ log.info(statusHint(props.slug, props.projectId, branchId));
217
+ throw new Error(`Timed out waiting for the deployment of ${props.slug} to start. It may still be in progress.`);
218
+ }
219
+ emitDeployResult(props, resolved, resolvedFn);
220
+ if (!props.wait) {
221
+ log.info(statusHint(props.slug, props.projectId, branchId));
222
+ return;
223
+ }
224
+ if (resolved.status === "completed") {
225
+ log.info(`Function deployment ${props.slug}/${resolved.id} completed.`);
226
+ return;
227
+ }
228
+ if (resolved.status === "failed") throw new Error(`Function deployment ${props.slug}/${resolved.id} failed.`);
229
+ log.info(statusHint(props.slug, props.projectId, branchId));
230
+ throw new Error(`Timed out waiting for function deployment ${props.slug}/${resolved.id} to finish. It may still be building.`);
289
231
  };
290
232
  const get = async (props) => {
291
- const branchId = await branchIdFromProps(props);
292
- const fn = await getFunction(props.apiClient, props.projectId, branchId, props.slug);
293
- if (props.output === "json" || props.output === "yaml") {
294
- writer(props).end(fn, { fields: FUNCTION_FIELDS });
295
- return;
296
- }
297
- const out = writer(props).write(fn, {
298
- fields: FUNCTION_FIELDS,
299
- title: "function",
300
- });
301
- const current = fn.current_deployment;
302
- const active = fn.active_deployment;
303
- if (current && active && current.id === active.id) {
304
- out.write(current, {
305
- fields: DEPLOYMENT_FIELDS,
306
- title: "deployment (current, active)",
307
- });
308
- writeDeploymentErrorSection(out, current);
309
- }
310
- else {
311
- if (current) {
312
- out.write(current, {
313
- fields: DEPLOYMENT_FIELDS,
314
- title: "current deployment",
315
- });
316
- // The failure reason is shown only for the current deployment;
317
- // the active one completed successfully by definition.
318
- writeDeploymentErrorSection(out, current);
319
- }
320
- if (active) {
321
- out.write(active, {
322
- fields: DEPLOYMENT_FIELDS,
323
- title: "active deployment",
324
- });
325
- }
326
- }
327
- if (props.listEnvVariables) {
328
- out.write((fn.active_deployment?.environment ?? []).map((name) => ({ name })), {
329
- fields: ["name"],
330
- title: "environment",
331
- emptyMessage: "No environment variables on the active deployment.",
332
- });
333
- }
334
- out.end();
233
+ const branchId = await branchIdFromProps(props);
234
+ const fn = await getFunction(props.apiClient, props.projectId, branchId, props.slug);
235
+ if (props.output === "json" || props.output === "yaml") {
236
+ writer(props).end(fn, { fields: FUNCTION_FIELDS });
237
+ return;
238
+ }
239
+ const out = writer(props).write(fn, {
240
+ fields: FUNCTION_FIELDS,
241
+ title: "function"
242
+ });
243
+ const current = fn.current_deployment;
244
+ const active = fn.active_deployment;
245
+ if (current && active && current.id === active.id) {
246
+ out.write(current, {
247
+ fields: DEPLOYMENT_FIELDS,
248
+ title: "deployment (current, active)"
249
+ });
250
+ writeDeploymentErrorSection(out, current);
251
+ } else {
252
+ if (current) {
253
+ out.write(current, {
254
+ fields: DEPLOYMENT_FIELDS,
255
+ title: "current deployment"
256
+ });
257
+ writeDeploymentErrorSection(out, current);
258
+ }
259
+ if (active) out.write(active, {
260
+ fields: DEPLOYMENT_FIELDS,
261
+ title: "active deployment"
262
+ });
263
+ }
264
+ if (props.listEnvVariables) out.write((fn.active_deployment?.environment ?? []).map((name) => ({ name })), {
265
+ fields: ["name"],
266
+ title: "environment",
267
+ emptyMessage: "No environment variables on the active deployment."
268
+ });
269
+ out.end();
335
270
  };
336
271
  const deleteFn = async (props) => {
337
- const branchId = await branchIdFromProps(props);
338
- try {
339
- await retryOnLock(() => deleteFunction(props.apiClient, props.projectId, branchId, props.slug));
340
- }
341
- catch (err) {
342
- if (isNeonApiError(err) && err.status === 404) {
343
- throw new Error(`Function "${props.slug}" not found on branch ${branchId}.`);
344
- }
345
- throw err;
346
- }
347
- log.info(`Function ${props.slug} deleted from branch ${branchId}`);
272
+ const branchId = await branchIdFromProps(props);
273
+ try {
274
+ await retryOnLock(() => deleteFunction(props.apiClient, props.projectId, branchId, props.slug));
275
+ } catch (err) {
276
+ if (isNeonApiError(err) && err.status === 404) throw new Error(`Function "${props.slug}" not found on branch ${branchId}.`);
277
+ throw err;
278
+ }
279
+ log.info(`Function ${props.slug} deleted from branch ${branchId}`);
348
280
  };
349
281
  const list = async (props) => {
350
- const branchId = await branchIdFromProps(props);
351
- const functions = [];
352
- let cursor;
353
- for (;;) {
354
- const page = await listFunctions(props.apiClient, props.projectId, branchId, { cursor, limit: FUNCTIONS_LIST_LIMIT });
355
- functions.push(...page.functions);
356
- log.debug("Got %d functions, next cursor: %s", page.functions.length, page.next);
357
- // A server echoing the same cursor would loop forever; treat it as
358
- // the end of the list.
359
- if (!page.next || page.next === cursor)
360
- break;
361
- cursor = page.next;
362
- }
363
- if (props.output === "json" || props.output === "yaml") {
364
- writer(props).end(functions, { fields: FUNCTION_FIELDS });
365
- return;
366
- }
367
- writer(props).end(functions.map((fn) => ({
368
- ...fn,
369
- status: fn.current_deployment?.status ?? "",
370
- })), {
371
- fields: LIST_TABLE_FIELDS,
372
- emptyMessage: "No functions found on this branch.",
373
- });
282
+ const branchId = await branchIdFromProps(props);
283
+ const functions = [];
284
+ let cursor;
285
+ for (;;) {
286
+ const page = await listFunctions(props.apiClient, props.projectId, branchId, {
287
+ cursor,
288
+ limit: FUNCTIONS_LIST_LIMIT
289
+ });
290
+ functions.push(...page.functions);
291
+ log.debug("Got %d functions, next cursor: %s", page.functions.length, page.next);
292
+ if (!page.next || page.next === cursor) break;
293
+ cursor = page.next;
294
+ }
295
+ if (props.output === "json" || props.output === "yaml") {
296
+ writer(props).end(functions, { fields: FUNCTION_FIELDS });
297
+ return;
298
+ }
299
+ writer(props).end(functions.map((fn) => ({
300
+ ...fn,
301
+ status: fn.current_deployment?.status ?? ""
302
+ })), {
303
+ fields: LIST_TABLE_FIELDS,
304
+ emptyMessage: "No functions found on this branch."
305
+ });
374
306
  };
307
+ //#endregion
308
+ export { aliases, builder, command, describe, functions_exports, handler };