neonctl 2.28.0 → 2.29.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +71 -71
- package/dist/analytics.js +35 -33
- package/dist/api.js +34 -34
- package/dist/auth.js +50 -44
- package/dist/cli.js +2 -2
- package/dist/commands/auth.js +58 -52
- package/dist/commands/bootstrap.js +115 -157
- package/dist/commands/branches.js +154 -147
- package/dist/commands/bucket.js +124 -118
- package/dist/commands/checkout.js +49 -49
- package/dist/commands/config.js +212 -88
- package/dist/commands/connection_string.js +62 -62
- package/dist/commands/data_api.js +96 -96
- package/dist/commands/databases.js +23 -23
- package/dist/commands/deploy.js +12 -12
- package/dist/commands/dev.js +114 -114
- package/dist/commands/env.js +43 -43
- package/dist/commands/functions.js +97 -98
- package/dist/commands/index.js +26 -26
- package/dist/commands/init.js +23 -22
- package/dist/commands/ip_allow.js +29 -29
- package/dist/commands/link.js +223 -166
- package/dist/commands/neon_auth.js +381 -363
- package/dist/commands/operations.js +11 -11
- package/dist/commands/orgs.js +8 -8
- package/dist/commands/projects.js +101 -99
- package/dist/commands/psql.js +31 -31
- package/dist/commands/roles.js +21 -21
- package/dist/commands/schema_diff.js +23 -23
- package/dist/commands/set_context.js +17 -17
- package/dist/commands/status.js +17 -17
- package/dist/commands/user.js +5 -5
- package/dist/commands/vpc_endpoints.js +50 -50
- package/dist/config.js +7 -7
- package/dist/config_format.js +5 -5
- package/dist/context.js +23 -16
- package/dist/current_branch_fast_path.js +6 -6
- package/dist/dev/env.js +34 -34
- package/dist/dev/functions.js +4 -4
- package/dist/dev/inputs.js +6 -6
- package/dist/dev/runtime.js +25 -25
- package/dist/env.js +14 -14
- package/dist/env_file.js +13 -13
- package/dist/errors.js +19 -19
- package/dist/functions_api.js +10 -10
- package/dist/help.js +15 -15
- package/dist/index.js +94 -92
- package/dist/log.js +2 -2
- package/dist/pkg.js +5 -5
- package/dist/psql/cli.js +4 -2
- package/dist/psql/command/cmd_cond.js +61 -61
- package/dist/psql/command/cmd_connect.js +159 -154
- package/dist/psql/command/cmd_copy.js +107 -97
- package/dist/psql/command/cmd_describe.js +368 -363
- package/dist/psql/command/cmd_format.js +276 -263
- package/dist/psql/command/cmd_io.js +269 -263
- package/dist/psql/command/cmd_lo.js +74 -66
- package/dist/psql/command/cmd_meta.js +148 -148
- package/dist/psql/command/cmd_misc.js +17 -17
- package/dist/psql/command/cmd_pipeline.js +142 -135
- package/dist/psql/command/cmd_restrict.js +25 -25
- package/dist/psql/command/cmd_show.js +183 -168
- package/dist/psql/command/dispatch.js +26 -26
- package/dist/psql/command/shared.js +14 -14
- package/dist/psql/complete/filenames.js +16 -16
- package/dist/psql/complete/index.js +4 -4
- package/dist/psql/complete/matcher.js +33 -32
- package/dist/psql/complete/psqlVars.js +173 -173
- package/dist/psql/complete/queries.js +5 -3
- package/dist/psql/complete/rules.js +900 -863
- package/dist/psql/core/common.js +136 -133
- package/dist/psql/core/help.js +343 -343
- package/dist/psql/core/mainloop.js +160 -153
- package/dist/psql/core/prompt.js +126 -123
- package/dist/psql/core/settings.js +111 -111
- package/dist/psql/core/sqlHelp.js +150 -150
- package/dist/psql/core/startup.js +211 -205
- package/dist/psql/core/syncVars.js +14 -14
- package/dist/psql/core/variables.js +24 -24
- package/dist/psql/describe/formatters.js +302 -289
- package/dist/psql/describe/processNamePattern.js +28 -28
- package/dist/psql/describe/queries.js +656 -651
- package/dist/psql/index.js +436 -411
- package/dist/psql/io/history.js +36 -36
- package/dist/psql/io/input.js +15 -15
- package/dist/psql/io/lineEditor/buffer.js +27 -25
- package/dist/psql/io/lineEditor/complete.js +15 -15
- package/dist/psql/io/lineEditor/filename.js +22 -22
- package/dist/psql/io/lineEditor/index.js +65 -62
- package/dist/psql/io/lineEditor/keymap.js +325 -318
- package/dist/psql/io/lineEditor/vt100.js +60 -60
- package/dist/psql/io/pgpass.js +18 -18
- package/dist/psql/io/pgservice.js +14 -14
- package/dist/psql/io/psqlrc.js +46 -46
- package/dist/psql/print/aligned.js +175 -166
- package/dist/psql/print/asciidoc.js +51 -51
- package/dist/psql/print/crosstab.js +34 -31
- package/dist/psql/print/csv.js +25 -22
- package/dist/psql/print/html.js +54 -54
- package/dist/psql/print/json.js +12 -12
- package/dist/psql/print/latex.js +118 -118
- package/dist/psql/print/pager.js +28 -26
- package/dist/psql/print/troff.js +48 -48
- package/dist/psql/print/unaligned.js +15 -14
- package/dist/psql/print/units.js +17 -17
- package/dist/psql/scanner/slash.js +48 -46
- package/dist/psql/scanner/sql.js +88 -84
- package/dist/psql/scanner/stringutils.js +21 -17
- package/dist/psql/types/index.js +7 -7
- package/dist/psql/types/scanner.js +8 -8
- package/dist/psql/wire/connection.js +341 -327
- package/dist/psql/wire/copy.js +7 -7
- package/dist/psql/wire/pipeline.js +26 -24
- package/dist/psql/wire/protocol.js +102 -102
- package/dist/psql/wire/sasl.js +62 -62
- package/dist/psql/wire/tls.js +79 -73
- package/dist/storage_api.js +15 -15
- package/dist/test_utils/fixtures.js +34 -31
- package/dist/test_utils/oauth_server.js +5 -5
- package/dist/utils/api_enums.js +13 -13
- package/dist/utils/branch_notice.js +5 -5
- package/dist/utils/branch_picker.js +26 -26
- package/dist/utils/compute_units.js +4 -4
- package/dist/utils/enrichers.js +20 -15
- package/dist/utils/esbuild.js +28 -28
- package/dist/utils/formats.js +1 -1
- package/dist/utils/middlewares.js +3 -3
- package/dist/utils/package_manager.js +68 -0
- package/dist/utils/point_in_time.js +12 -12
- package/dist/utils/psql.js +30 -30
- package/dist/utils/string.js +2 -2
- package/dist/utils/ui.js +9 -9
- package/dist/utils/zip.js +1 -1
- package/dist/writer.js +17 -17
- package/package.json +6 -7
package/dist/commands/dev.js
CHANGED
|
@@ -1,33 +1,33 @@
|
|
|
1
|
-
import { spawn, spawnSync } from
|
|
2
|
-
import { once } from
|
|
3
|
-
import { existsSync, mkdirSync, rmSync, writeFileSync } from
|
|
4
|
-
import { dirname, join, resolve } from
|
|
5
|
-
import { fileURLToPath } from
|
|
6
|
-
import chalk from
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
export const command =
|
|
14
|
-
export const describe =
|
|
1
|
+
import { spawn, spawnSync } from "node:child_process";
|
|
2
|
+
import { once } from "node:events";
|
|
3
|
+
import { existsSync, mkdirSync, rmSync, writeFileSync } from "node:fs";
|
|
4
|
+
import { dirname, join, resolve } from "node:path";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
import chalk from "chalk";
|
|
7
|
+
import { resolveDevEnv } from "../dev/env.js";
|
|
8
|
+
import { resolveFunctionsFromConfig, } from "../dev/functions.js";
|
|
9
|
+
import { resolveWatchInputs } from "../dev/inputs.js";
|
|
10
|
+
import { log } from "../log.js";
|
|
11
|
+
import { branchIdResolve } from "../utils/enrichers.js";
|
|
12
|
+
import { bundleEntry } from "../utils/esbuild.js";
|
|
13
|
+
export const command = "dev";
|
|
14
|
+
export const describe = "Run Neon Functions locally with a dev server";
|
|
15
15
|
export const builder = (argv) => argv
|
|
16
|
-
.usage(
|
|
17
|
-
.example(
|
|
18
|
-
.example(
|
|
19
|
-
.example(
|
|
16
|
+
.usage("$0 dev [--source <path>] [options]")
|
|
17
|
+
.example("$0 dev --source ./functions/hello.ts", "Serve one function on a free port with hot reload")
|
|
18
|
+
.example("$0 dev", "Serve every function declared in neon.ts (one dev server each)")
|
|
19
|
+
.example("$0 dev --source ./functions/hello.ts --port 3000", "Serve one function on an explicit port (fails if the port is taken)")
|
|
20
20
|
.options({
|
|
21
21
|
source: {
|
|
22
|
-
describe:
|
|
23
|
-
|
|
24
|
-
type:
|
|
22
|
+
describe: "Path to a single function entry module. Omit to serve every " +
|
|
23
|
+
"function declared in neon.ts.",
|
|
24
|
+
type: "string",
|
|
25
25
|
},
|
|
26
26
|
port: {
|
|
27
|
-
describe:
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
type:
|
|
27
|
+
describe: "Port to listen on (single-function mode only, with --source). " +
|
|
28
|
+
"Fails if taken. Without it (and without a PORT env var) a free " +
|
|
29
|
+
"port is chosen automatically.",
|
|
30
|
+
type: "number",
|
|
31
31
|
},
|
|
32
32
|
})
|
|
33
33
|
.strict();
|
|
@@ -39,8 +39,8 @@ export const handler = async (props) => {
|
|
|
39
39
|
// No --source: --port has no single target to bind, so reject it explicitly
|
|
40
40
|
// rather than silently ignoring it.
|
|
41
41
|
if (props.port !== undefined) {
|
|
42
|
-
throw new Error(
|
|
43
|
-
|
|
42
|
+
throw new Error("--port can only be used with --source. To set ports for the functions " +
|
|
43
|
+
"in neon.ts, give each one a `dev.port` in its config.");
|
|
44
44
|
}
|
|
45
45
|
await runFromConfig(props);
|
|
46
46
|
};
|
|
@@ -61,7 +61,7 @@ const runSingleSource = async (props) => {
|
|
|
61
61
|
const unit = {
|
|
62
62
|
slug: null,
|
|
63
63
|
source,
|
|
64
|
-
bundleDir: join(process.cwd(),
|
|
64
|
+
bundleDir: join(process.cwd(), "node_modules", ".neon-dev"),
|
|
65
65
|
childEnv: buildChildEnv(neonEnv, portFromProps(props.port)),
|
|
66
66
|
label: null,
|
|
67
67
|
envSummary: { neon: Object.keys(neonEnv), fn: [] },
|
|
@@ -80,14 +80,14 @@ const runFromConfig = async (props) => {
|
|
|
80
80
|
const branchId = await resolveBranchId(props);
|
|
81
81
|
const resolved = await resolveFunctionsFromConfig(process.cwd());
|
|
82
82
|
if (resolved === null) {
|
|
83
|
-
throw new Error(
|
|
84
|
-
|
|
85
|
-
|
|
83
|
+
throw new Error("No --source given and no neon.ts found. Pass --source <path> to run a " +
|
|
84
|
+
"single function, or add a neon.ts that declares functions under " +
|
|
85
|
+
"`preview.functions`.");
|
|
86
86
|
}
|
|
87
87
|
const { configPath, functions } = resolved;
|
|
88
88
|
if (functions.length === 0) {
|
|
89
|
-
throw new Error(
|
|
90
|
-
|
|
89
|
+
throw new Error("neon.ts has no functions to serve. Add at least one under " +
|
|
90
|
+
"`preview.functions`, or pass --source <path>.");
|
|
91
91
|
}
|
|
92
92
|
const { vars: neonEnv, skipped } = await resolveDevEnv({
|
|
93
93
|
cwd: process.cwd(),
|
|
@@ -147,18 +147,18 @@ const resolveBranchId = async (props) => {
|
|
|
147
147
|
});
|
|
148
148
|
}
|
|
149
149
|
catch (err) {
|
|
150
|
-
log.debug(
|
|
150
|
+
log.debug("dev: could not resolve branch id: %s", err instanceof Error ? err.message : String(err));
|
|
151
151
|
return undefined;
|
|
152
152
|
}
|
|
153
153
|
};
|
|
154
154
|
const DEFAULT_PORT_BASE = 8787;
|
|
155
155
|
const portFromProps = (port) => {
|
|
156
156
|
if (port !== undefined)
|
|
157
|
-
return { mode:
|
|
158
|
-
if (process.env.PORT !== undefined && process.env.PORT !==
|
|
159
|
-
return { mode:
|
|
157
|
+
return { mode: "explicit", port };
|
|
158
|
+
if (process.env.PORT !== undefined && process.env.PORT !== "") {
|
|
159
|
+
return { mode: "explicit", port: Number(process.env.PORT) };
|
|
160
160
|
}
|
|
161
|
-
return { mode:
|
|
161
|
+
return { mode: "search", from: DEFAULT_PORT_BASE };
|
|
162
162
|
};
|
|
163
163
|
/**
|
|
164
164
|
* Translate a {@link PlannedFunction} into a {@link ServedUnit}. Port rules:
|
|
@@ -168,13 +168,13 @@ const portFromProps = (port) => {
|
|
|
168
168
|
*/
|
|
169
169
|
const plannedToUnit = (fn, branchEnv, searchBase) => {
|
|
170
170
|
const port = fn.port !== undefined
|
|
171
|
-
? { mode:
|
|
172
|
-
: { mode:
|
|
171
|
+
? { mode: "explicit", port: fn.port }
|
|
172
|
+
: { mode: "search", from: searchBase };
|
|
173
173
|
const childEnv = buildChildEnv({ ...branchEnv, ...fn.env }, port);
|
|
174
174
|
return {
|
|
175
175
|
slug: fn.slug,
|
|
176
176
|
source: fn.source,
|
|
177
|
-
bundleDir: join(process.cwd(),
|
|
177
|
+
bundleDir: join(process.cwd(), "node_modules", ".neon-dev", fn.slug),
|
|
178
178
|
childEnv,
|
|
179
179
|
label: fn.slug,
|
|
180
180
|
envSummary: { neon: Object.keys(branchEnv), fn: Object.keys(fn.env) },
|
|
@@ -199,10 +199,10 @@ const buildChildEnv = (neonEnv, port) => {
|
|
|
199
199
|
const env = { ...process.env, ...neonEnv };
|
|
200
200
|
delete env.NEON_DEV_PORT;
|
|
201
201
|
delete env.NEON_DEV_PORT_BASE;
|
|
202
|
-
if (port.mode ===
|
|
202
|
+
if (port.mode === "explicit") {
|
|
203
203
|
env.NEON_DEV_PORT = String(port.port);
|
|
204
204
|
}
|
|
205
|
-
else if (port.mode ===
|
|
205
|
+
else if (port.mode === "search") {
|
|
206
206
|
env.NEON_DEV_PORT_BASE = String(port.from);
|
|
207
207
|
}
|
|
208
208
|
return env;
|
|
@@ -232,8 +232,8 @@ const runSupervisor = async (units, options = {}) => {
|
|
|
232
232
|
bundlePath = await writeBundle(r.unit.source, r.unit.bundleDir);
|
|
233
233
|
}
|
|
234
234
|
catch (err) {
|
|
235
|
-
r.status =
|
|
236
|
-
logUnit(r.unit, chalk.red(
|
|
235
|
+
r.status = "error";
|
|
236
|
+
logUnit(r.unit, chalk.red("bundle failed: ") +
|
|
237
237
|
(err instanceof Error ? err.message : String(err)));
|
|
238
238
|
return;
|
|
239
239
|
}
|
|
@@ -243,15 +243,15 @@ const runSupervisor = async (units, options = {}) => {
|
|
|
243
243
|
r.child = next;
|
|
244
244
|
const ready = waitForReady(next);
|
|
245
245
|
pipeChildOutput(next, r.unit.label);
|
|
246
|
-
next.on(
|
|
246
|
+
next.on("exit", (code, signal) => {
|
|
247
247
|
if (shuttingDown || r.child !== next)
|
|
248
248
|
return;
|
|
249
249
|
if (signal) {
|
|
250
|
-
log.debug(
|
|
250
|
+
log.debug("runtime for %s exited via %s", r.unit.slug ?? "(source)", signal);
|
|
251
251
|
return;
|
|
252
252
|
}
|
|
253
253
|
if (code && code !== 0 && r.everReady) {
|
|
254
|
-
r.status =
|
|
254
|
+
r.status = "error";
|
|
255
255
|
logUnit(r.unit, chalk.red(`exited with code ${code} (waiting for a change)`));
|
|
256
256
|
}
|
|
257
257
|
});
|
|
@@ -259,10 +259,10 @@ const runSupervisor = async (units, options = {}) => {
|
|
|
259
259
|
if (port !== null) {
|
|
260
260
|
r.boundPort = port;
|
|
261
261
|
r.everReady = true;
|
|
262
|
-
r.status =
|
|
262
|
+
r.status = "ready";
|
|
263
263
|
}
|
|
264
264
|
else {
|
|
265
|
-
r.status =
|
|
265
|
+
r.status = "error";
|
|
266
266
|
}
|
|
267
267
|
};
|
|
268
268
|
const restart = (r) => {
|
|
@@ -272,14 +272,14 @@ const runSupervisor = async (units, options = {}) => {
|
|
|
272
272
|
clearTimeout(r.restartTimer);
|
|
273
273
|
r.restartTimer = setTimeout(() => {
|
|
274
274
|
void (async () => {
|
|
275
|
-
logUnit(r.unit, chalk.dim(
|
|
275
|
+
logUnit(r.unit, chalk.dim("change detected, restarting…"));
|
|
276
276
|
if (r.child)
|
|
277
277
|
await killTree(r.child);
|
|
278
278
|
if (shuttingDown)
|
|
279
279
|
return;
|
|
280
280
|
await bundleAndStart(r);
|
|
281
|
-
if (r.status ===
|
|
282
|
-
logUnit(r.unit, chalk.green(
|
|
281
|
+
if (r.status === "ready") {
|
|
282
|
+
logUnit(r.unit, chalk.green("ready") + ` ${urlFor(r.boundPort)}`);
|
|
283
283
|
}
|
|
284
284
|
})();
|
|
285
285
|
}, 150);
|
|
@@ -304,9 +304,9 @@ const runSupervisor = async (units, options = {}) => {
|
|
|
304
304
|
};
|
|
305
305
|
// Start every unit. They are independent: keep going if one fails.
|
|
306
306
|
await Promise.all(running.map((r) => startUnit(r)));
|
|
307
|
-
if (running.every((r) => r.status ===
|
|
307
|
+
if (running.every((r) => r.status === "error")) {
|
|
308
308
|
await Promise.all(running.map((r) => stopUnit(r)));
|
|
309
|
-
throw new Error(
|
|
309
|
+
throw new Error("No function started. See the output above for details.");
|
|
310
310
|
}
|
|
311
311
|
printBanner(running, envNote);
|
|
312
312
|
// Config mode only: watch neon.ts and reconcile the live unit set when it changes.
|
|
@@ -347,12 +347,12 @@ const runSupervisor = async (units, options = {}) => {
|
|
|
347
347
|
void (async () => {
|
|
348
348
|
await configWatcher?.close();
|
|
349
349
|
await Promise.all(running.map((r) => stopUnit(r)));
|
|
350
|
-
log.info(chalk.dim(
|
|
350
|
+
log.info(chalk.dim("Stopped the dev server."));
|
|
351
351
|
resolveRun();
|
|
352
352
|
})();
|
|
353
353
|
};
|
|
354
|
-
process.on(
|
|
355
|
-
process.on(
|
|
354
|
+
process.on("SIGINT", shutdown);
|
|
355
|
+
process.on("SIGTERM", shutdown);
|
|
356
356
|
});
|
|
357
357
|
};
|
|
358
358
|
const makeRunningUnit = (unit) => ({
|
|
@@ -362,7 +362,7 @@ const makeRunningUnit = (unit) => ({
|
|
|
362
362
|
everReady: false,
|
|
363
363
|
restartTimer: null,
|
|
364
364
|
watcher: null,
|
|
365
|
-
status:
|
|
365
|
+
status: "starting",
|
|
366
366
|
});
|
|
367
367
|
/**
|
|
368
368
|
* Pure slug-keyed diff of the live units against the freshly-resolved desired set:
|
|
@@ -417,16 +417,16 @@ const reconcileOnce = async (running, replan, ops) => {
|
|
|
417
417
|
desired = await replan(nextSearchBase(running));
|
|
418
418
|
}
|
|
419
419
|
catch (err) {
|
|
420
|
-
log.info(chalk.red(
|
|
420
|
+
log.info(chalk.red("neon.ts change ignored: ") +
|
|
421
421
|
(err instanceof Error ? err.message : String(err)) +
|
|
422
|
-
chalk.dim(
|
|
422
|
+
chalk.dim(" (fix it and save again)"));
|
|
423
423
|
return;
|
|
424
424
|
}
|
|
425
425
|
if (ops.isShuttingDown())
|
|
426
426
|
return;
|
|
427
427
|
const plan = diffUnits(running, desired);
|
|
428
428
|
for (const r of plan.remove) {
|
|
429
|
-
logUnit(r.unit, chalk.dim(
|
|
429
|
+
logUnit(r.unit, chalk.dim("removed from neon.ts, stopping…"));
|
|
430
430
|
await ops.stopUnit(r);
|
|
431
431
|
const idx = running.indexOf(r);
|
|
432
432
|
if (idx !== -1)
|
|
@@ -438,16 +438,16 @@ const reconcileOnce = async (running, replan, ops) => {
|
|
|
438
438
|
const added = plan.add.map((unit) => {
|
|
439
439
|
const r = makeRunningUnit(unit);
|
|
440
440
|
running.push(r);
|
|
441
|
-
logUnit(unit, chalk.dim(
|
|
441
|
+
logUnit(unit, chalk.dim("added in neon.ts, starting…"));
|
|
442
442
|
return r;
|
|
443
443
|
});
|
|
444
444
|
await Promise.all(added.map((r) => ops.startUnit(r)));
|
|
445
445
|
for (const r of added) {
|
|
446
|
-
if (r.status ===
|
|
446
|
+
if (r.status === "ready") {
|
|
447
447
|
const env = formatEnvSummary(r.unit.envSummary);
|
|
448
|
-
logUnit(r.unit, chalk.green(
|
|
448
|
+
logUnit(r.unit, chalk.green("ready") +
|
|
449
449
|
` ${urlFor(r.boundPort)}` +
|
|
450
|
-
(env ? chalk.dim(` ${env}`) :
|
|
450
|
+
(env ? chalk.dim(` ${env}`) : ""));
|
|
451
451
|
}
|
|
452
452
|
}
|
|
453
453
|
}
|
|
@@ -472,7 +472,7 @@ const nextSearchBase = (running) => {
|
|
|
472
472
|
*/
|
|
473
473
|
const spawnChild = (unit, runtimePath, bundlePath) => {
|
|
474
474
|
return spawn(process.execPath, [runtimePath, bundlePath], {
|
|
475
|
-
stdio: [
|
|
475
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
476
476
|
env: unit.childEnv,
|
|
477
477
|
detached: true,
|
|
478
478
|
});
|
|
@@ -485,23 +485,23 @@ const writeBundle = async (source, bundleDir) => {
|
|
|
485
485
|
for (const [name, contents] of Object.entries(files)) {
|
|
486
486
|
writeFileSync(join(bundleDir, name), contents);
|
|
487
487
|
}
|
|
488
|
-
return join(bundleDir,
|
|
488
|
+
return join(bundleDir, "index.mjs");
|
|
489
489
|
};
|
|
490
|
-
const urlFor = (port) => port === null ? chalk.red(
|
|
490
|
+
const urlFor = (port) => port === null ? chalk.red("not running") : `http://localhost:${port}`;
|
|
491
491
|
const waitForReady = (child) => new Promise((resolveReady) => {
|
|
492
492
|
let settled = false;
|
|
493
|
-
let buffer =
|
|
493
|
+
let buffer = "";
|
|
494
494
|
const onData = (chunk) => {
|
|
495
495
|
buffer += chunk.toString();
|
|
496
496
|
const match = READY_PATTERN.exec(buffer);
|
|
497
497
|
if (match && !settled) {
|
|
498
498
|
settled = true;
|
|
499
|
-
child.stdout?.off(
|
|
499
|
+
child.stdout?.off("data", onData);
|
|
500
500
|
resolveReady(Number(match[1]));
|
|
501
501
|
}
|
|
502
502
|
};
|
|
503
|
-
child.stdout?.on(
|
|
504
|
-
child.once(
|
|
503
|
+
child.stdout?.on("data", onData);
|
|
504
|
+
child.once("exit", () => {
|
|
505
505
|
if (!settled) {
|
|
506
506
|
settled = true;
|
|
507
507
|
resolveReady(null);
|
|
@@ -514,13 +514,13 @@ const waitForReady = (child) => new Promise((resolveReady) => {
|
|
|
514
514
|
* prefixed with `[slug]` so concurrent servers' output stays readable.
|
|
515
515
|
*/
|
|
516
516
|
const pipeChildOutput = (child, label) => {
|
|
517
|
-
const prefix = label ? chalk.dim(`[${label}] `) :
|
|
517
|
+
const prefix = label ? chalk.dim(`[${label}] `) : "";
|
|
518
518
|
const forward = (stream) => {
|
|
519
|
-
let buffer =
|
|
520
|
-
child[stream]?.on(
|
|
519
|
+
let buffer = "";
|
|
520
|
+
child[stream]?.on("data", (chunk) => {
|
|
521
521
|
buffer += chunk.toString();
|
|
522
|
-
const lines = buffer.split(
|
|
523
|
-
buffer = lines.pop() ??
|
|
522
|
+
const lines = buffer.split("\n");
|
|
523
|
+
buffer = lines.pop() ?? "";
|
|
524
524
|
for (const line of lines) {
|
|
525
525
|
if (READY_PATTERN.test(line))
|
|
526
526
|
continue;
|
|
@@ -528,26 +528,26 @@ const pipeChildOutput = (child, label) => {
|
|
|
528
528
|
}
|
|
529
529
|
});
|
|
530
530
|
};
|
|
531
|
-
forward(
|
|
532
|
-
forward(
|
|
531
|
+
forward("stdout");
|
|
532
|
+
forward("stderr");
|
|
533
533
|
};
|
|
534
534
|
const printBanner = (running, envNote) => {
|
|
535
|
-
log.info(
|
|
536
|
-
log.info(chalk.green.bold(
|
|
537
|
-
log.info(
|
|
535
|
+
log.info("");
|
|
536
|
+
log.info(chalk.green.bold(" Neon Functions dev server"));
|
|
537
|
+
log.info("");
|
|
538
538
|
for (const r of running) {
|
|
539
|
-
const name = r.unit.label ??
|
|
539
|
+
const name = r.unit.label ?? "function";
|
|
540
540
|
const url = urlFor(r.boundPort);
|
|
541
541
|
log.info(` ${chalk.dim(name.padEnd(20))} ${url}`);
|
|
542
542
|
const env = formatEnvSummary(r.unit.envSummary);
|
|
543
543
|
if (env)
|
|
544
|
-
log.info(` ${
|
|
544
|
+
log.info(` ${" ".repeat(20)} ${chalk.dim(env)}`);
|
|
545
545
|
}
|
|
546
546
|
if (envNote) {
|
|
547
|
-
log.info(
|
|
548
|
-
log.info(` ${chalk.yellow(
|
|
547
|
+
log.info("");
|
|
548
|
+
log.info(` ${chalk.yellow("!")} ${chalk.dim(`Neon env: ${envNote}`)}`);
|
|
549
549
|
}
|
|
550
|
-
log.info(
|
|
550
|
+
log.info("");
|
|
551
551
|
};
|
|
552
552
|
/**
|
|
553
553
|
* Render a unit's injected env into one transparent line for the banner, e.g.
|
|
@@ -557,22 +557,22 @@ const printBanner = (running, envNote) => {
|
|
|
557
557
|
*/
|
|
558
558
|
export const formatEnvSummary = (summary) => {
|
|
559
559
|
if (!summary)
|
|
560
|
-
return
|
|
560
|
+
return "";
|
|
561
561
|
const parts = [];
|
|
562
562
|
if (summary.neon.length > 0) {
|
|
563
|
-
parts.push(`env: ${[...summary.neon].sort().join(
|
|
563
|
+
parts.push(`env: ${[...summary.neon].sort().join(", ")}`);
|
|
564
564
|
}
|
|
565
565
|
if (summary.fn.length > 0) {
|
|
566
|
-
parts.push(`neon.ts: ${[...summary.fn].sort().join(
|
|
566
|
+
parts.push(`neon.ts: ${[...summary.fn].sort().join(", ")}`);
|
|
567
567
|
}
|
|
568
|
-
return parts.join(
|
|
568
|
+
return parts.join(" · ");
|
|
569
569
|
};
|
|
570
570
|
const logUnit = (unit, message) => {
|
|
571
|
-
const prefix = unit.label ? chalk.dim(`[${unit.label}] `) :
|
|
571
|
+
const prefix = unit.label ? chalk.dim(`[${unit.label}] `) : "";
|
|
572
572
|
log.info(`${prefix}${message}`);
|
|
573
573
|
};
|
|
574
574
|
const startWatcher = async (source, restart) => {
|
|
575
|
-
const { default: chokidar } = await import(
|
|
575
|
+
const { default: chokidar } = await import("chokidar");
|
|
576
576
|
const initialInputs = await resolveWatchInputs(source);
|
|
577
577
|
if (initialInputs === null) {
|
|
578
578
|
return startDirectoryWatcher(chokidar, source, restart);
|
|
@@ -588,10 +588,10 @@ const startWatcher = async (source, restart) => {
|
|
|
588
588
|
* jiti-loaded config.
|
|
589
589
|
*/
|
|
590
590
|
const startConfigWatcher = async (configPath, onChange) => {
|
|
591
|
-
const { default: chokidar } = await import(
|
|
591
|
+
const { default: chokidar } = await import("chokidar");
|
|
592
592
|
const watcher = chokidar.watch(configPath, { ignoreInitial: true });
|
|
593
|
-
await once(watcher,
|
|
594
|
-
watcher.on(
|
|
593
|
+
await once(watcher, "ready");
|
|
594
|
+
watcher.on("all", () => {
|
|
595
595
|
onChange();
|
|
596
596
|
});
|
|
597
597
|
return { sync: () => Promise.resolve(), close: () => watcher.close() };
|
|
@@ -599,8 +599,8 @@ const startConfigWatcher = async (configPath, onChange) => {
|
|
|
599
599
|
const startInputWatcher = async (chokidar, source, initialInputs, restart) => {
|
|
600
600
|
const watched = new Set([source, ...initialInputs]);
|
|
601
601
|
const watcher = chokidar.watch([...watched], { ignoreInitial: true });
|
|
602
|
-
await once(watcher,
|
|
603
|
-
watcher.on(
|
|
602
|
+
await once(watcher, "ready");
|
|
603
|
+
watcher.on("all", () => {
|
|
604
604
|
restart();
|
|
605
605
|
});
|
|
606
606
|
const sync = async () => {
|
|
@@ -627,16 +627,16 @@ const startDirectoryWatcher = async (chokidar, source, restart) => {
|
|
|
627
627
|
const watchedDir = dirname(source);
|
|
628
628
|
const isIgnored = (p) => {
|
|
629
629
|
const segments = p.split(/[/\\]/);
|
|
630
|
-
return (segments.includes(
|
|
631
|
-
segments.includes(
|
|
632
|
-
segments.includes(
|
|
630
|
+
return (segments.includes("node_modules") ||
|
|
631
|
+
segments.includes(".git") ||
|
|
632
|
+
segments.includes("dist"));
|
|
633
633
|
};
|
|
634
634
|
const watcher = chokidar.watch(watchedDir, {
|
|
635
635
|
ignoreInitial: true,
|
|
636
636
|
ignored: (path) => isIgnored(path),
|
|
637
637
|
});
|
|
638
|
-
await once(watcher,
|
|
639
|
-
watcher.on(
|
|
638
|
+
await once(watcher, "ready");
|
|
639
|
+
watcher.on("all", () => {
|
|
640
640
|
restart();
|
|
641
641
|
});
|
|
642
642
|
return { sync: () => Promise.resolve(), close: () => watcher.close() };
|
|
@@ -656,41 +656,41 @@ const killTree = (child) => {
|
|
|
656
656
|
const timeout = setTimeout(() => {
|
|
657
657
|
forceKill(child, pid);
|
|
658
658
|
}, 2000);
|
|
659
|
-
child.once(
|
|
659
|
+
child.once("exit", () => {
|
|
660
660
|
clearTimeout(timeout);
|
|
661
661
|
resolveKill();
|
|
662
662
|
});
|
|
663
|
-
if (pid !== undefined && process.platform !==
|
|
663
|
+
if (pid !== undefined && process.platform !== "win32") {
|
|
664
664
|
try {
|
|
665
|
-
process.kill(-pid,
|
|
665
|
+
process.kill(-pid, "SIGTERM");
|
|
666
666
|
return;
|
|
667
667
|
}
|
|
668
668
|
catch {
|
|
669
669
|
// Fall through to a direct kill if the group is already gone.
|
|
670
670
|
}
|
|
671
671
|
}
|
|
672
|
-
child.kill(
|
|
672
|
+
child.kill("SIGTERM");
|
|
673
673
|
});
|
|
674
674
|
};
|
|
675
675
|
const forceKill = (child, pid) => {
|
|
676
676
|
if (pid === undefined) {
|
|
677
|
-
child.kill(
|
|
677
|
+
child.kill("SIGKILL");
|
|
678
678
|
return;
|
|
679
679
|
}
|
|
680
|
-
if (process.platform ===
|
|
681
|
-
spawnSync(
|
|
680
|
+
if (process.platform === "win32") {
|
|
681
|
+
spawnSync("taskkill", ["/pid", String(pid), "/T", "/F"]);
|
|
682
682
|
return;
|
|
683
683
|
}
|
|
684
684
|
try {
|
|
685
|
-
process.kill(-pid,
|
|
685
|
+
process.kill(-pid, "SIGKILL");
|
|
686
686
|
}
|
|
687
687
|
catch {
|
|
688
|
-
child.kill(
|
|
688
|
+
child.kill("SIGKILL");
|
|
689
689
|
}
|
|
690
690
|
};
|
|
691
691
|
const resolveRuntimePath = () => {
|
|
692
692
|
const here = dirname(fileURLToPath(import.meta.url));
|
|
693
|
-
const candidate = join(here,
|
|
693
|
+
const candidate = join(here, "..", "dev", "runtime.js");
|
|
694
694
|
if (!existsSync(candidate)) {
|
|
695
695
|
throw new Error(`Could not locate the dev runtime at ${candidate}`);
|
|
696
696
|
}
|