neonctl 2.37.1 → 2.38.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.
- package/README.md +15 -700
- package/bin/cli.js +3 -0
- package/package.json +8 -75
- package/dist/analytics.js +0 -168
- package/dist/api.js +0 -720
- package/dist/auth.js +0 -125
- package/dist/callback.html +0 -60
- package/dist/cli.js +0 -9
- package/dist/commands/api.js +0 -278
- package/dist/commands/auth.js +0 -214
- package/dist/commands/bootstrap.js +0 -481
- package/dist/commands/branches.js +0 -488
- package/dist/commands/bucket.js +0 -549
- package/dist/commands/checkout.js +0 -321
- package/dist/commands/config.js +0 -626
- package/dist/commands/connection_string.js +0 -172
- package/dist/commands/data_api.js +0 -284
- package/dist/commands/databases.js +0 -82
- package/dist/commands/deploy.js +0 -26
- package/dist/commands/dev.js +0 -698
- package/dist/commands/diff.js +0 -222
- package/dist/commands/env.js +0 -189
- package/dist/commands/functions.js +0 -373
- package/dist/commands/index.js +0 -62
- package/dist/commands/init.js +0 -73
- package/dist/commands/inspect.js +0 -65
- package/dist/commands/ip_allow.js +0 -137
- package/dist/commands/link.js +0 -1121
- package/dist/commands/neon_auth.js +0 -1028
- package/dist/commands/operations.js +0 -28
- package/dist/commands/orgs.js +0 -24
- package/dist/commands/projects.js +0 -413
- package/dist/commands/psql.js +0 -62
- package/dist/commands/roles.js +0 -65
- package/dist/commands/schema_diff.js +0 -151
- package/dist/commands/set_context.js +0 -29
- package/dist/commands/snapshots.js +0 -455
- package/dist/commands/status.js +0 -40
- package/dist/commands/user.js +0 -15
- package/dist/commands/vpc_endpoints.js +0 -134
- package/dist/config.js +0 -11
- package/dist/config_format.js +0 -72
- package/dist/context.js +0 -231
- package/dist/current_branch_fast_path.js +0 -55
- package/dist/dev/env.js +0 -244
- package/dist/dev/functions.js +0 -70
- package/dist/dev/inputs.js +0 -63
- package/dist/dev/runtime.js +0 -146
- package/dist/env.js +0 -36
- package/dist/env_file.js +0 -159
- package/dist/errors.js +0 -80
- package/dist/functions_api.js +0 -48
- package/dist/help.js +0 -146
- package/dist/index.js +0 -240
- package/dist/log.js +0 -18
- package/dist/parameters.gen.js +0 -480
- package/dist/pkg.js +0 -25
- package/dist/psql/cli.js +0 -53
- package/dist/psql/command/cmd_cond.js +0 -437
- package/dist/psql/command/cmd_connect.js +0 -820
- package/dist/psql/command/cmd_copy.js +0 -1035
- package/dist/psql/command/cmd_describe.js +0 -1815
- package/dist/psql/command/cmd_format.js +0 -948
- package/dist/psql/command/cmd_io.js +0 -2193
- package/dist/psql/command/cmd_lo.js +0 -393
- package/dist/psql/command/cmd_meta.js +0 -969
- package/dist/psql/command/cmd_misc.js +0 -187
- package/dist/psql/command/cmd_pipeline.js +0 -1148
- package/dist/psql/command/cmd_restrict.js +0 -171
- package/dist/psql/command/cmd_show.js +0 -766
- package/dist/psql/command/dispatch.js +0 -343
- package/dist/psql/command/inputQueue.js +0 -42
- package/dist/psql/command/shared.js +0 -71
- package/dist/psql/complete/filenames.js +0 -139
- package/dist/psql/complete/index.js +0 -104
- package/dist/psql/complete/matcher.js +0 -315
- package/dist/psql/complete/psqlVars.js +0 -249
- package/dist/psql/complete/queries.js +0 -493
- package/dist/psql/complete/rules.js +0 -2424
- package/dist/psql/core/common.js +0 -1253
- package/dist/psql/core/help.js +0 -576
- package/dist/psql/core/mainloop.js +0 -1360
- package/dist/psql/core/prompt.js +0 -439
- package/dist/psql/core/settings.js +0 -686
- package/dist/psql/core/sqlHelp.js +0 -1066
- package/dist/psql/core/startup.js +0 -846
- package/dist/psql/core/syncVars.js +0 -116
- package/dist/psql/core/variables.js +0 -287
- package/dist/psql/describe/formatters.js +0 -1330
- package/dist/psql/describe/processNamePattern.js +0 -270
- package/dist/psql/describe/queries.js +0 -2452
- package/dist/psql/describe/versionGate.js +0 -44
- package/dist/psql/index.js +0 -2030
- package/dist/psql/io/history.js +0 -299
- package/dist/psql/io/input.js +0 -120
- package/dist/psql/io/lineEditor/buffer.js +0 -325
- package/dist/psql/io/lineEditor/complete.js +0 -227
- package/dist/psql/io/lineEditor/filename.js +0 -159
- package/dist/psql/io/lineEditor/index.js +0 -893
- package/dist/psql/io/lineEditor/keymap.js +0 -745
- package/dist/psql/io/lineEditor/vt100.js +0 -363
- package/dist/psql/io/pgpass.js +0 -202
- package/dist/psql/io/pgservice.js +0 -194
- package/dist/psql/io/psqlrc.js +0 -422
- package/dist/psql/print/aligned.js +0 -1748
- package/dist/psql/print/asciidoc.js +0 -230
- package/dist/psql/print/crosstab.js +0 -463
- package/dist/psql/print/csv.js +0 -76
- package/dist/psql/print/html.js +0 -240
- package/dist/psql/print/json.js +0 -96
- package/dist/psql/print/latex.js +0 -379
- package/dist/psql/print/pager.js +0 -267
- package/dist/psql/print/troff.js +0 -240
- package/dist/psql/print/unaligned.js +0 -99
- package/dist/psql/print/units.js +0 -188
- package/dist/psql/scanner/slash.js +0 -515
- package/dist/psql/scanner/sql.js +0 -914
- package/dist/psql/scanner/stringutils.js +0 -394
- package/dist/psql/types/backslash.js +0 -1
- package/dist/psql/types/connection.js +0 -1
- package/dist/psql/types/index.js +0 -7
- package/dist/psql/types/printer.js +0 -1
- package/dist/psql/types/repl.js +0 -1
- package/dist/psql/types/scanner.js +0 -24
- package/dist/psql/types/settings.js +0 -1
- package/dist/psql/types/variables.js +0 -1
- package/dist/psql/wire/connection.js +0 -2857
- package/dist/psql/wire/copy.js +0 -108
- package/dist/psql/wire/notify.js +0 -59
- package/dist/psql/wire/pipeline.js +0 -521
- package/dist/psql/wire/protocol.js +0 -466
- package/dist/psql/wire/sasl.js +0 -294
- package/dist/psql/wire/tls.js +0 -602
- package/dist/storage_api.js +0 -147
- package/dist/test_utils/fixtures.js +0 -121
- package/dist/test_utils/oauth_server.js +0 -9
- package/dist/types.js +0 -1
- package/dist/utils/ai_gateway_notice.js +0 -180
- package/dist/utils/api_enums.js +0 -33
- package/dist/utils/auth.js +0 -5
- package/dist/utils/branch_notice.js +0 -22
- package/dist/utils/branch_picker.js +0 -103
- package/dist/utils/compute_units.js +0 -28
- package/dist/utils/config_diff.js +0 -185
- package/dist/utils/enrichers.js +0 -161
- package/dist/utils/esbuild.js +0 -158
- package/dist/utils/formats.js +0 -18
- package/dist/utils/git_diff.js +0 -90
- package/dist/utils/inspect_db.js +0 -153
- package/dist/utils/inspect_queries.js +0 -372
- package/dist/utils/middlewares.js +0 -20
- package/dist/utils/openapi.js +0 -114
- package/dist/utils/package_manager.js +0 -68
- package/dist/utils/point_in_time.js +0 -56
- package/dist/utils/psql.js +0 -120
- package/dist/utils/string.js +0 -5
- package/dist/utils/ui.js +0 -59
- package/dist/utils/zip.js +0 -4
- package/dist/writer.js +0 -97
|
@@ -1,373 +0,0 @@
|
|
|
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";
|
|
10
|
-
const FUNCTION_FIELDS = [
|
|
11
|
-
"slug",
|
|
12
|
-
"name",
|
|
13
|
-
"invocation_url",
|
|
14
|
-
"created_at",
|
|
15
|
-
];
|
|
16
|
-
const FUNCTIONS_LIST_LIMIT = 100;
|
|
17
|
-
// Table columns for `functions list`. `status` is a derived field (the
|
|
18
|
-
// table writer reads flat fields only): the current deployment's status.
|
|
19
|
-
const LIST_TABLE_FIELDS = [
|
|
20
|
-
"slug",
|
|
21
|
-
"name",
|
|
22
|
-
"status",
|
|
23
|
-
"invocation_url",
|
|
24
|
-
"created_at",
|
|
25
|
-
];
|
|
26
|
-
const DEPLOYMENT_FIELDS = [
|
|
27
|
-
"id",
|
|
28
|
-
"status",
|
|
29
|
-
"runtime",
|
|
30
|
-
"memory_mib",
|
|
31
|
-
"created_at",
|
|
32
|
-
];
|
|
33
|
-
// Deploy emits the resolved deployment plus the function's invocation_url, so a
|
|
34
|
-
// successful `functions deploy` tells the user exactly where to call the function.
|
|
35
|
-
const DEPLOY_RESULT_FIELDS = [
|
|
36
|
-
"id",
|
|
37
|
-
"status",
|
|
38
|
-
"invocation_url",
|
|
39
|
-
"runtime",
|
|
40
|
-
"memory_mib",
|
|
41
|
-
"created_at",
|
|
42
|
-
];
|
|
43
|
-
// In table mode a failed build's reason gets its own "deployment error"
|
|
44
|
-
// section after the deployment table; json/yaml carry the raw `error` field.
|
|
45
|
-
const writeDeploymentErrorSection = (out, dep) => {
|
|
46
|
-
if (dep.status === "failed" && dep.error) {
|
|
47
|
-
out.write({ reason: dep.error }, { fields: ["reason"], title: "deployment error" });
|
|
48
|
-
}
|
|
49
|
-
};
|
|
50
|
-
const SLUG_PATTERN = /^[a-z0-9]{1,20}$/;
|
|
51
|
-
const SLUG_HELP = "Use 1-20 lowercase letters and digits (no hyphens or other characters).";
|
|
52
|
-
// Entry-point discovery order inside --src.
|
|
53
|
-
const ENTRY_CANDIDATES = ["index.ts", "index.mjs", "index.js"];
|
|
54
|
-
// Overridable so tests can poll fast; defaults to 2s in real use.
|
|
55
|
-
const POLL_INTERVAL_MS = Number(process.env.NEON_FUNCTIONS_POLL_INTERVAL_MS) || 2000;
|
|
56
|
-
// Upper bound on --wait polling so the CLI never hangs (e.g. if our deployment
|
|
57
|
-
// never shows up as current_deployment). Overridable so tests can time out fast;
|
|
58
|
-
// defaults to 10 minutes in real use.
|
|
59
|
-
const POLL_TIMEOUT_MS = Number(process.env.NEON_FUNCTIONS_POLL_TIMEOUT_MS) || 600000;
|
|
60
|
-
export const command = "functions";
|
|
61
|
-
export const describe = "Manage Neon Functions";
|
|
62
|
-
export const aliases = ["function"];
|
|
63
|
-
export const builder = (argv) => argv
|
|
64
|
-
.usage("$0 function <sub-command> [options]")
|
|
65
|
-
.options({
|
|
66
|
-
"project-id": {
|
|
67
|
-
describe: "Project ID",
|
|
68
|
-
type: "string",
|
|
69
|
-
},
|
|
70
|
-
branch: {
|
|
71
|
-
describe: "Branch ID or name",
|
|
72
|
-
type: "string",
|
|
73
|
-
},
|
|
74
|
-
})
|
|
75
|
-
.middleware(fillSingleProject)
|
|
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",
|
|
80
|
-
demandOption: true,
|
|
81
|
-
})
|
|
82
|
-
.options({
|
|
83
|
-
src: {
|
|
84
|
-
describe: "Function source: a directory containing index.ts, index.mjs, or index.js, or a path to the entry file",
|
|
85
|
-
type: "string",
|
|
86
|
-
},
|
|
87
|
-
// Removed flags, kept hidden so old invocations fail loudly instead
|
|
88
|
-
// of being silently ignored (the CLI has no .strictOptions()).
|
|
89
|
-
path: {
|
|
90
|
-
type: "string",
|
|
91
|
-
hidden: true,
|
|
92
|
-
},
|
|
93
|
-
entry: {
|
|
94
|
-
type: "string",
|
|
95
|
-
hidden: true,
|
|
96
|
-
},
|
|
97
|
-
runtime: {
|
|
98
|
-
describe: "Function runtime",
|
|
99
|
-
type: "string",
|
|
100
|
-
choices: ["nodejs24"],
|
|
101
|
-
},
|
|
102
|
-
env: {
|
|
103
|
-
describe: "Environment variable as KEY=VALUE (repeatable)",
|
|
104
|
-
type: "string",
|
|
105
|
-
array: true,
|
|
106
|
-
},
|
|
107
|
-
wait: {
|
|
108
|
-
describe: "Wait for the deployment to finish building",
|
|
109
|
-
type: "boolean",
|
|
110
|
-
default: true,
|
|
111
|
-
},
|
|
112
|
-
}), (args) => deploy(args))
|
|
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",
|
|
118
|
-
demandOption: true,
|
|
119
|
-
})
|
|
120
|
-
.options({
|
|
121
|
-
"list-env-variables": {
|
|
122
|
-
describe: "List the environment variable names of the active deployment",
|
|
123
|
-
type: "boolean",
|
|
124
|
-
alias: "E",
|
|
125
|
-
default: false,
|
|
126
|
-
},
|
|
127
|
-
}), (args) => get(args))
|
|
128
|
-
.command("delete <slug>", "Delete a function on the branch", (yargs) => yargs.positional("slug", {
|
|
129
|
-
describe: "Function slug",
|
|
130
|
-
type: "string",
|
|
131
|
-
demandOption: true,
|
|
132
|
-
}), (args) => deleteFn(args));
|
|
133
|
-
export const handler = (args) => {
|
|
134
|
-
return args;
|
|
135
|
-
};
|
|
136
|
-
const parseEnv = (entries) => {
|
|
137
|
-
if (!entries || entries.length === 0)
|
|
138
|
-
return undefined;
|
|
139
|
-
const map = {};
|
|
140
|
-
for (const entry of entries) {
|
|
141
|
-
const eq = entry.indexOf("=");
|
|
142
|
-
if (eq <= 0) {
|
|
143
|
-
throw new Error(`Invalid --env value "${entry}". Expected KEY=VALUE.`);
|
|
144
|
-
}
|
|
145
|
-
map[entry.slice(0, eq)] = entry.slice(eq + 1);
|
|
146
|
-
}
|
|
147
|
-
return JSON.stringify(map);
|
|
148
|
-
};
|
|
149
|
-
const statusHint = (slug, projectId, branchId) => `Check status with: neonctl function get ${slug} --project-id ${projectId} --branch ${branchId}`;
|
|
150
|
-
// Emit the resolved deployment together with the function's invocation_url, so the
|
|
151
|
-
// deploy output shows where the function is reachable (not just the deployment id).
|
|
152
|
-
const emitDeployResult = (props, deployment, fn) => {
|
|
153
|
-
const out = writer(props).write({ ...deployment, invocation_url: fn?.invocation_url }, { fields: DEPLOY_RESULT_FIELDS });
|
|
154
|
-
if (props.output !== "json" && props.output !== "yaml") {
|
|
155
|
-
writeDeploymentErrorSection(out, deployment);
|
|
156
|
-
}
|
|
157
|
-
out.end();
|
|
158
|
-
};
|
|
159
|
-
// A poll error worth retrying: a network error (no HTTP response), a 5xx, or a
|
|
160
|
-
// 404 from eventual consistency. Anything else (e.g. 401/403) is surfaced.
|
|
161
|
-
const isTransient = (err) => isNeonApiError(err) &&
|
|
162
|
-
(err.status === undefined || err.status === 404 || err.status >= 500);
|
|
163
|
-
const deploy = async (props) => {
|
|
164
|
-
if (props.path !== undefined || props.entry !== undefined) {
|
|
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.");
|
|
167
|
-
}
|
|
168
|
-
// At least one deploy option must be passed (--wait is excluded: it controls
|
|
169
|
-
// output, not what gets deployed).
|
|
170
|
-
const hasOption = props.src !== undefined ||
|
|
171
|
-
props.env !== undefined ||
|
|
172
|
-
props.runtime !== undefined;
|
|
173
|
-
if (!hasOption) {
|
|
174
|
-
throw new Error("Provide at least one option to deploy, e.g. --src or --env. " +
|
|
175
|
-
"See: neonctl function deploy --help.");
|
|
176
|
-
}
|
|
177
|
-
// Cheap, offline validation first - fail before any network round-trip.
|
|
178
|
-
if (!SLUG_PATTERN.test(props.slug)) {
|
|
179
|
-
throw new Error(`Invalid function slug "${props.slug}". ${SLUG_HELP}`);
|
|
180
|
-
}
|
|
181
|
-
const src = props.src ?? ".";
|
|
182
|
-
const runtime = props.runtime ?? "nodejs24";
|
|
183
|
-
const environment = parseEnv(props.env);
|
|
184
|
-
const srcStat = statSync(src, { throwIfNoEntry: false });
|
|
185
|
-
if (srcStat === undefined) {
|
|
186
|
-
throw new Error(`--src path not found: ${src}.`);
|
|
187
|
-
}
|
|
188
|
-
// A file is used as the entry point directly; a directory triggers discovery.
|
|
189
|
-
const source = srcStat.isFile()
|
|
190
|
-
? src
|
|
191
|
-
: ENTRY_CANDIDATES.map((name) => join(src, name)).find((p) => existsSync(p));
|
|
192
|
-
if (source === undefined) {
|
|
193
|
-
throw new Error(`No entry file found in ${src}. Expected one of: ${ENTRY_CANDIDATES.join(", ")}.`);
|
|
194
|
-
}
|
|
195
|
-
// Bundle before any network round-trip so a bundling failure fails fast.
|
|
196
|
-
const zip = zipBundle(await bundleEntry(source));
|
|
197
|
-
const branchId = await branchIdFromProps(props);
|
|
198
|
-
// Snapshot the current version before deploy so we can detect the new one
|
|
199
|
-
// afterward. A missing function (404) or no deployment yet → undefined.
|
|
200
|
-
let before;
|
|
201
|
-
try {
|
|
202
|
-
const fn = await getFunction(props.apiClient, props.projectId, branchId, props.slug);
|
|
203
|
-
before = fn.current_deployment?.id;
|
|
204
|
-
}
|
|
205
|
-
catch (err) {
|
|
206
|
-
if (!(isNeonApiError(err) && err.status === 404))
|
|
207
|
-
throw err;
|
|
208
|
-
}
|
|
209
|
-
await retryOnLock(() => createDeployment(props.apiClient, props.projectId, branchId, props.slug, {
|
|
210
|
-
zip,
|
|
211
|
-
runtime,
|
|
212
|
-
environment,
|
|
213
|
-
}));
|
|
214
|
-
log.info(`Function deployment triggered for function ${props.slug}.`);
|
|
215
|
-
// Best-effort interrupt: a Ctrl-C lands at the next poll boundary. (No
|
|
216
|
-
// automated test; mirrors the resolution branches below, verified manually.)
|
|
217
|
-
let interrupted = false;
|
|
218
|
-
const onSignal = () => {
|
|
219
|
-
interrupted = true;
|
|
220
|
-
};
|
|
221
|
-
process.once("SIGINT", onSignal);
|
|
222
|
-
process.once("SIGTERM", onSignal);
|
|
223
|
-
// Poll until a NEW version appears (id greater than the snapshot, or
|
|
224
|
-
// any version if there was none). --no-wait stops there; --wait stops at a
|
|
225
|
-
// terminal status. Bounded by POLL_TIMEOUT_MS so it never hangs.
|
|
226
|
-
let resolved;
|
|
227
|
-
// The function carries the invocation_url; keep the whole record (not just its
|
|
228
|
-
// current_deployment) so we can surface that URL on success.
|
|
229
|
-
let resolvedFn;
|
|
230
|
-
const deadline = Date.now() + POLL_TIMEOUT_MS;
|
|
231
|
-
try {
|
|
232
|
-
while (!interrupted && Date.now() < deadline) {
|
|
233
|
-
await new Promise((r) => setTimeout(r, POLL_INTERVAL_MS));
|
|
234
|
-
if (interrupted)
|
|
235
|
-
break;
|
|
236
|
-
// The deploy already succeeded server-side; tolerate transient poll
|
|
237
|
-
// failures and retry on the next interval. Surface anything else.
|
|
238
|
-
let fn;
|
|
239
|
-
try {
|
|
240
|
-
fn = await getFunction(props.apiClient, props.projectId, branchId, props.slug);
|
|
241
|
-
}
|
|
242
|
-
catch (err) {
|
|
243
|
-
if (isTransient(err))
|
|
244
|
-
continue;
|
|
245
|
-
throw err;
|
|
246
|
-
}
|
|
247
|
-
const dep = fn.current_deployment;
|
|
248
|
-
const isNew = dep !== undefined && (before === undefined || dep.id > before);
|
|
249
|
-
if (isNew && dep) {
|
|
250
|
-
resolved = dep;
|
|
251
|
-
resolvedFn = fn;
|
|
252
|
-
if (!props.wait)
|
|
253
|
-
break;
|
|
254
|
-
if (dep.status === "completed" || dep.status === "failed")
|
|
255
|
-
break;
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
finally {
|
|
260
|
-
process.removeListener("SIGINT", onSignal);
|
|
261
|
-
process.removeListener("SIGTERM", onSignal);
|
|
262
|
-
}
|
|
263
|
-
if (interrupted) {
|
|
264
|
-
log.info(statusHint(props.slug, props.projectId, branchId));
|
|
265
|
-
if (resolved)
|
|
266
|
-
emitDeployResult(props, resolved, resolvedFn);
|
|
267
|
-
return;
|
|
268
|
-
}
|
|
269
|
-
if (resolved === undefined) {
|
|
270
|
-
log.info(statusHint(props.slug, props.projectId, branchId));
|
|
271
|
-
throw new Error(`Timed out waiting for the deployment of ${props.slug} to start. It may still be in progress.`);
|
|
272
|
-
}
|
|
273
|
-
emitDeployResult(props, resolved, resolvedFn);
|
|
274
|
-
if (!props.wait) {
|
|
275
|
-
log.info(statusHint(props.slug, props.projectId, branchId));
|
|
276
|
-
return;
|
|
277
|
-
}
|
|
278
|
-
if (resolved.status === "completed") {
|
|
279
|
-
log.info(`Function deployment ${props.slug}/${resolved.id} completed.`);
|
|
280
|
-
return;
|
|
281
|
-
}
|
|
282
|
-
if (resolved.status === "failed") {
|
|
283
|
-
throw new Error(`Function deployment ${props.slug}/${resolved.id} failed.`);
|
|
284
|
-
}
|
|
285
|
-
// --wait, new version appeared but the deadline hit before it finished.
|
|
286
|
-
log.info(statusHint(props.slug, props.projectId, branchId));
|
|
287
|
-
throw new Error(`Timed out waiting for function deployment ${props.slug}/${resolved.id} to finish. It may still be building.`);
|
|
288
|
-
};
|
|
289
|
-
const get = async (props) => {
|
|
290
|
-
const branchId = await branchIdFromProps(props);
|
|
291
|
-
const fn = await getFunction(props.apiClient, props.projectId, branchId, props.slug);
|
|
292
|
-
if (props.output === "json" || props.output === "yaml") {
|
|
293
|
-
writer(props).end(fn, { fields: FUNCTION_FIELDS });
|
|
294
|
-
return;
|
|
295
|
-
}
|
|
296
|
-
const out = writer(props).write(fn, {
|
|
297
|
-
fields: FUNCTION_FIELDS,
|
|
298
|
-
title: "function",
|
|
299
|
-
});
|
|
300
|
-
const current = fn.current_deployment;
|
|
301
|
-
const active = fn.active_deployment;
|
|
302
|
-
if (current && active && current.id === active.id) {
|
|
303
|
-
out.write(current, {
|
|
304
|
-
fields: DEPLOYMENT_FIELDS,
|
|
305
|
-
title: "deployment (current, active)",
|
|
306
|
-
});
|
|
307
|
-
writeDeploymentErrorSection(out, current);
|
|
308
|
-
}
|
|
309
|
-
else {
|
|
310
|
-
if (current) {
|
|
311
|
-
out.write(current, {
|
|
312
|
-
fields: DEPLOYMENT_FIELDS,
|
|
313
|
-
title: "current deployment",
|
|
314
|
-
});
|
|
315
|
-
// The failure reason is shown only for the current deployment;
|
|
316
|
-
// the active one completed successfully by definition.
|
|
317
|
-
writeDeploymentErrorSection(out, current);
|
|
318
|
-
}
|
|
319
|
-
if (active) {
|
|
320
|
-
out.write(active, {
|
|
321
|
-
fields: DEPLOYMENT_FIELDS,
|
|
322
|
-
title: "active deployment",
|
|
323
|
-
});
|
|
324
|
-
}
|
|
325
|
-
}
|
|
326
|
-
if (props.listEnvVariables) {
|
|
327
|
-
out.write((fn.active_deployment?.environment ?? []).map((name) => ({ name })), {
|
|
328
|
-
fields: ["name"],
|
|
329
|
-
title: "environment",
|
|
330
|
-
emptyMessage: "No environment variables on the active deployment.",
|
|
331
|
-
});
|
|
332
|
-
}
|
|
333
|
-
out.end();
|
|
334
|
-
};
|
|
335
|
-
const deleteFn = async (props) => {
|
|
336
|
-
const branchId = await branchIdFromProps(props);
|
|
337
|
-
try {
|
|
338
|
-
await retryOnLock(() => deleteFunction(props.apiClient, props.projectId, branchId, props.slug));
|
|
339
|
-
}
|
|
340
|
-
catch (err) {
|
|
341
|
-
if (isNeonApiError(err) && err.status === 404) {
|
|
342
|
-
throw new Error(`Function "${props.slug}" not found on branch ${branchId}.`);
|
|
343
|
-
}
|
|
344
|
-
throw err;
|
|
345
|
-
}
|
|
346
|
-
log.info(`Function ${props.slug} deleted from branch ${branchId}`);
|
|
347
|
-
};
|
|
348
|
-
const list = async (props) => {
|
|
349
|
-
const branchId = await branchIdFromProps(props);
|
|
350
|
-
const functions = [];
|
|
351
|
-
let cursor;
|
|
352
|
-
for (;;) {
|
|
353
|
-
const page = await listFunctions(props.apiClient, props.projectId, branchId, { cursor, limit: FUNCTIONS_LIST_LIMIT });
|
|
354
|
-
functions.push(...page.functions);
|
|
355
|
-
log.debug("Got %d functions, next cursor: %s", page.functions.length, page.next);
|
|
356
|
-
// A server echoing the same cursor would loop forever; treat it as
|
|
357
|
-
// the end of the list.
|
|
358
|
-
if (!page.next || page.next === cursor)
|
|
359
|
-
break;
|
|
360
|
-
cursor = page.next;
|
|
361
|
-
}
|
|
362
|
-
if (props.output === "json" || props.output === "yaml") {
|
|
363
|
-
writer(props).end(functions, { fields: FUNCTION_FIELDS });
|
|
364
|
-
return;
|
|
365
|
-
}
|
|
366
|
-
writer(props).end(functions.map((fn) => ({
|
|
367
|
-
...fn,
|
|
368
|
-
status: fn.current_deployment?.status ?? "",
|
|
369
|
-
})), {
|
|
370
|
-
fields: LIST_TABLE_FIELDS,
|
|
371
|
-
emptyMessage: "No functions found on this branch.",
|
|
372
|
-
});
|
|
373
|
-
};
|
package/dist/commands/index.js
DELETED
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
import * as api from "./api.js";
|
|
2
|
-
import * as auth from "./auth.js";
|
|
3
|
-
import * as bootstrap from "./bootstrap.js";
|
|
4
|
-
import * as branches from "./branches.js";
|
|
5
|
-
import * as bucket from "./bucket.js";
|
|
6
|
-
import * as checkout from "./checkout.js";
|
|
7
|
-
import * as config from "./config.js";
|
|
8
|
-
import * as cs from "./connection_string.js";
|
|
9
|
-
import * as dataApi from "./data_api.js";
|
|
10
|
-
import * as databases from "./databases.js";
|
|
11
|
-
import * as deploy from "./deploy.js";
|
|
12
|
-
import * as dev from "./dev.js";
|
|
13
|
-
import * as diff from "./diff.js";
|
|
14
|
-
import * as env from "./env.js";
|
|
15
|
-
import * as functions from "./functions.js";
|
|
16
|
-
import * as init from "./init.js";
|
|
17
|
-
import * as inspect from "./inspect.js";
|
|
18
|
-
import * as ipAllow from "./ip_allow.js";
|
|
19
|
-
import * as link from "./link.js";
|
|
20
|
-
import * as neonAuth from "./neon_auth.js";
|
|
21
|
-
import * as operations from "./operations.js";
|
|
22
|
-
import * as orgs from "./orgs.js";
|
|
23
|
-
import * as projects from "./projects.js";
|
|
24
|
-
import * as psql from "./psql.js";
|
|
25
|
-
import * as roles from "./roles.js";
|
|
26
|
-
import * as setContext from "./set_context.js";
|
|
27
|
-
import * as snapshots from "./snapshots.js";
|
|
28
|
-
import * as status from "./status.js";
|
|
29
|
-
import * as users from "./user.js";
|
|
30
|
-
import * as vpcEndpoints from "./vpc_endpoints.js";
|
|
31
|
-
export default [
|
|
32
|
-
auth,
|
|
33
|
-
api,
|
|
34
|
-
users,
|
|
35
|
-
orgs,
|
|
36
|
-
projects,
|
|
37
|
-
ipAllow,
|
|
38
|
-
vpcEndpoints,
|
|
39
|
-
neonAuth,
|
|
40
|
-
branches,
|
|
41
|
-
databases,
|
|
42
|
-
roles,
|
|
43
|
-
operations,
|
|
44
|
-
snapshots,
|
|
45
|
-
inspect,
|
|
46
|
-
cs,
|
|
47
|
-
psql,
|
|
48
|
-
setContext,
|
|
49
|
-
checkout,
|
|
50
|
-
link,
|
|
51
|
-
init,
|
|
52
|
-
dataApi,
|
|
53
|
-
functions,
|
|
54
|
-
dev,
|
|
55
|
-
diff,
|
|
56
|
-
config,
|
|
57
|
-
status,
|
|
58
|
-
deploy,
|
|
59
|
-
env,
|
|
60
|
-
bucket,
|
|
61
|
-
bootstrap,
|
|
62
|
-
];
|
package/dist/commands/init.js
DELETED
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
import { detectAgent, enrichResponse, interactiveInit, orchestrate, routeDataStep, } from "neon-init";
|
|
2
|
-
import { sendError } from "../analytics.js";
|
|
3
|
-
import { log } from "../log.js";
|
|
4
|
-
export const command = "init";
|
|
5
|
-
export const describe = "Initialize a project with Neon using your AI coding assistant";
|
|
6
|
-
export const builder = (yargs) => yargs
|
|
7
|
-
.option("context-file", {
|
|
8
|
-
hidden: true,
|
|
9
|
-
})
|
|
10
|
-
.option("agent", {
|
|
11
|
-
alias: "a",
|
|
12
|
-
type: "boolean",
|
|
13
|
-
default: false,
|
|
14
|
-
describe: "Enable agent/JSON mode (agent type is auto-detected).",
|
|
15
|
-
})
|
|
16
|
-
.option("data", {
|
|
17
|
-
type: "string",
|
|
18
|
-
describe: 'JSON object with a "step" field to route to a specific phase and phase-specific options.',
|
|
19
|
-
})
|
|
20
|
-
.option("skip-migrations", {
|
|
21
|
-
type: "boolean",
|
|
22
|
-
default: false,
|
|
23
|
-
describe: "Skip the migrations phase.",
|
|
24
|
-
})
|
|
25
|
-
.option("preview", {
|
|
26
|
-
type: "boolean",
|
|
27
|
-
default: false,
|
|
28
|
-
describe: "Enable preview features (e.g. project bootstrapping from templates).",
|
|
29
|
-
})
|
|
30
|
-
.strict(false);
|
|
31
|
-
export const handler = async (argv) => {
|
|
32
|
-
try {
|
|
33
|
-
// Auto-detect agent from environment. When --agent is explicitly passed,
|
|
34
|
-
// always detect (the user asked for agent mode). Otherwise, require
|
|
35
|
-
// non-TTY stdin to distinguish agent from human in terminal.
|
|
36
|
-
const agent = (argv.agent || !process.stdin.isTTY ? detectAgent() : null) ||
|
|
37
|
-
undefined;
|
|
38
|
-
const isAgentMode = argv.agent || agent !== undefined;
|
|
39
|
-
// --data with a "step" field routes to the appropriate phase
|
|
40
|
-
if (argv.data && isAgentMode) {
|
|
41
|
-
let data;
|
|
42
|
-
try {
|
|
43
|
-
data = JSON.parse(argv.data);
|
|
44
|
-
}
|
|
45
|
-
catch {
|
|
46
|
-
log.error("Invalid JSON in --data flag. Expected a JSON object.");
|
|
47
|
-
process.exit(1);
|
|
48
|
-
return;
|
|
49
|
-
}
|
|
50
|
-
if (typeof data.step === "string") {
|
|
51
|
-
const result = await routeDataStep(data, agent);
|
|
52
|
-
log.info(JSON.stringify(enrichResponse(result), null, 2));
|
|
53
|
-
return;
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
if (isAgentMode) {
|
|
57
|
-
const result = await orchestrate({
|
|
58
|
-
agent,
|
|
59
|
-
skipMigrations: argv.skipMigrations,
|
|
60
|
-
preview: argv.preview,
|
|
61
|
-
});
|
|
62
|
-
log.info(JSON.stringify(enrichResponse(result), null, 2));
|
|
63
|
-
}
|
|
64
|
-
else {
|
|
65
|
-
await interactiveInit({ preview: argv.preview });
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
catch {
|
|
69
|
-
const exitError = new Error(`failed to run neon-init`);
|
|
70
|
-
sendError(exitError, "NEON_INIT_FAILED");
|
|
71
|
-
process.exit(1);
|
|
72
|
-
}
|
|
73
|
-
};
|
package/dist/commands/inspect.js
DELETED
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
import { fillSingleProject } from "../utils/enrichers.js";
|
|
2
|
-
import { resolveConnectionUri, runInspectQuery } from "../utils/inspect_db.js";
|
|
3
|
-
import { INSPECT_QUERIES, } from "../utils/inspect_queries.js";
|
|
4
|
-
import { writer } from "../writer.js";
|
|
5
|
-
export const command = "inspect";
|
|
6
|
-
export const describe = "Inspect a database's health and configuration";
|
|
7
|
-
export const aliases = ["inspection"];
|
|
8
|
-
/**
|
|
9
|
-
* `fillSingleProject` hits the API to auto-resolve a project. When the user
|
|
10
|
-
* passes `--db-url` we bypass the Neon API entirely, so skip it.
|
|
11
|
-
*/
|
|
12
|
-
const fillSingleProjectUnlessDbUrl = async (props) => {
|
|
13
|
-
if (props.dbUrl) {
|
|
14
|
-
return props;
|
|
15
|
-
}
|
|
16
|
-
return fillSingleProject(props);
|
|
17
|
-
};
|
|
18
|
-
const runSubcommand = async (name, props) => {
|
|
19
|
-
const query = INSPECT_QUERIES[name];
|
|
20
|
-
const connectionUri = props.dbUrl ?? (await resolveConnectionUri(props)).connectionUri;
|
|
21
|
-
const rows = await runInspectQuery(connectionUri, query.sql, {
|
|
22
|
-
requiresExtension: query.requiresExtension,
|
|
23
|
-
});
|
|
24
|
-
writer(props).end(rows, {
|
|
25
|
-
fields: query.fields,
|
|
26
|
-
emptyMessage: query.emptyMessage,
|
|
27
|
-
});
|
|
28
|
-
};
|
|
29
|
-
const dbBuilder = (argv) => {
|
|
30
|
-
let builder = argv
|
|
31
|
-
.usage("$0 inspect db <sub-command> [options]")
|
|
32
|
-
.options({
|
|
33
|
-
"project-id": {
|
|
34
|
-
describe: "Project ID",
|
|
35
|
-
type: "string",
|
|
36
|
-
},
|
|
37
|
-
branch: {
|
|
38
|
-
describe: "Branch ID or name",
|
|
39
|
-
type: "string",
|
|
40
|
-
},
|
|
41
|
-
"database-name": {
|
|
42
|
-
describe: "Database name",
|
|
43
|
-
type: "string",
|
|
44
|
-
},
|
|
45
|
-
"role-name": {
|
|
46
|
-
describe: "Role name",
|
|
47
|
-
type: "string",
|
|
48
|
-
},
|
|
49
|
-
"db-url": {
|
|
50
|
-
describe: "Inspect any Postgres via a connection string, bypassing the Neon API (project/branch resolution is skipped)",
|
|
51
|
-
type: "string",
|
|
52
|
-
},
|
|
53
|
-
})
|
|
54
|
-
.middleware(fillSingleProjectUnlessDbUrl);
|
|
55
|
-
for (const name of Object.keys(INSPECT_QUERIES)) {
|
|
56
|
-
builder = builder.command(name, INSPECT_QUERIES[name].describe, (yargs) => yargs, (args) => runSubcommand(name, args));
|
|
57
|
-
}
|
|
58
|
-
return builder;
|
|
59
|
-
};
|
|
60
|
-
export const builder = (argv) => argv
|
|
61
|
-
.usage("$0 inspect <sub-command> [options]")
|
|
62
|
-
.command("db", "Run a diagnostic query against a branch's Postgres", dbBuilder);
|
|
63
|
-
export const handler = (args) => {
|
|
64
|
-
return args;
|
|
65
|
-
};
|