neon 2.43.0 → 2.45.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.
package/dist/config.js CHANGED
@@ -1,27 +1,6 @@
1
1
  import { existsSync, mkdirSync } from "node:fs";
2
- import { configDir, resolveConfigFile } from "@neon/config/paths";
3
2
  import { isCi } from "./env.js";
4
- export const CREDENTIALS_FILE = "credentials.json";
5
- /**
6
- * Default for `--config-dir`: `$XDG_CONFIG_HOME/neon`, else `~/.config/neon`.
7
- *
8
- * The directory was called `neonctl` until the CLI was renamed. An existing one is still
9
- * read — see {@link credentialsPath} — but it is never written to, moved, or deleted.
10
- */
11
- export const defaultDir = configDir();
12
- /**
13
- * Where this invocation's `credentials.json` lives.
14
- *
15
- * When `--config-dir` was left at its default, an existing file in the legacy `neonctl`
16
- * directory is used **in place**: an install that predates the rename keeps working, and
17
- * its credentials are never duplicated into a second location where one copy could go
18
- * stale while another tool still reads it.
19
- *
20
- * A `--config-dir` the user actually passed is used exactly as given. Falling back out of
21
- * an explicitly chosen directory would defeat the reason for choosing it — a CI run
22
- * pointed at a scratch directory must never pick up a developer's real credentials.
23
- */
24
- export const credentialsPath = (dir) => resolveConfigFile(CREDENTIALS_FILE, dir === defaultDir ? {} : { dir }).path;
3
+ export { CREDENTIALS_FILE, credentialsPath, defaultDir, isInsideConfigDir, isOwnedCredentialPath, } from "./_shared/paths.js";
25
4
  export const ensureConfigDir = ({ "config-dir": configDirArg, "force-auth": forceAuth, }) => {
26
5
  if (!existsSync(configDirArg) && (!isCi() || forceAuth)) {
27
6
  mkdirSync(configDirArg, { recursive: true });
package/dist/context.js CHANGED
@@ -128,6 +128,13 @@ export const enrichFromContext = (args) => {
128
128
  if (isApiKeysCommand(args)) {
129
129
  return;
130
130
  }
131
+ // `profile create --mint` mints one too, and for the same reason must take its scope only
132
+ // from what was typed: enriched here, running it inside a linked directory would quietly
133
+ // produce a key scoped to that project rather than the account or organization asked for.
134
+ // No `profile` subcommand has any use for a project or branch.
135
+ if (isProfileCommand(args)) {
136
+ return;
137
+ }
131
138
  const context = readContextFile(args.contextFile);
132
139
  if (!args.orgId) {
133
140
  args.orgId = context.orgId;
@@ -2,6 +2,7 @@ import { createServer } from "node:http";
2
2
  import { resolve } from "node:path";
3
3
  import { pathToFileURL } from "node:url";
4
4
  import { getRequestListener } from "@hono/node-server";
5
+ import { createUpgradeListener, installWebSocketBridge, } from "./websocket.js";
5
6
  const isFunction = (value) => typeof value === "function";
6
7
  const hasFetchMethod = (value) => typeof value === "object" &&
7
8
  value !== null &&
@@ -27,6 +28,30 @@ export const resolveFetchHandler = (mod) => {
27
28
  " export default { fetch(req) { /* ... */ } }\n" +
28
29
  " export default function (req) { /* ... */ }");
29
30
  };
31
+ const hasUpgradeMethod = (value) => typeof value === "object" &&
32
+ value !== null &&
33
+ "upgrade" in value &&
34
+ typeof value.upgrade === "function";
35
+ /**
36
+ * Resolve the user's optional WebSocket entrypoint: a named `export function upgrade`,
37
+ * or an `upgrade` method on the default export. `undefined` when the module has
38
+ * neither, which is the common case — a function without one either uses
39
+ * `upgradeWebSocket()` inside `fetch` or serves no WebSockets at all.
40
+ *
41
+ * Resolution order matches the deployed runtime exactly (named export first, then the
42
+ * default-export method), so a module that resolves one way locally cannot resolve the
43
+ * other way once deployed.
44
+ */
45
+ export const resolveUpgradeHandler = (mod) => {
46
+ if (typeof mod.upgrade === "function")
47
+ return mod.upgrade;
48
+ const defaultExport = mod.default;
49
+ if (hasUpgradeMethod(defaultExport)) {
50
+ const target = defaultExport;
51
+ return (req, socket, head) => target.upgrade(req, socket, head);
52
+ }
53
+ return undefined;
54
+ };
30
55
  /**
31
56
  * Wrap a fetch handler so user errors become a 500 response (with the message
32
57
  * in the body during dev) instead of crashing the child process.
@@ -87,11 +112,30 @@ const listen = (server, port, hostname) => new Promise((resolveListen, rejectLis
87
112
  export const startRuntime = async ({ source, port, hostname, }) => {
88
113
  const absoluteSource = resolve(process.cwd(), source);
89
114
  const mod = (await import(pathToFileURL(absoluteSource).href));
90
- const handler = withErrorBoundary(resolveFetchHandler(mod));
115
+ const fetchHandler = resolveFetchHandler(mod);
116
+ const handler = withErrorBoundary(fetchHandler);
117
+ // Publish the bridge `upgradeWebSocket()` reads before the user module can serve a
118
+ // request, so the helper resolves locally exactly as it does when deployed.
119
+ installWebSocketBridge();
91
120
  const listener = getRequestListener(handler, { hostname });
92
121
  const server = createServer((incoming, outgoing) => {
93
122
  void listener(incoming, outgoing);
94
123
  });
124
+ // Node emits 'upgrade' rather than 'request' for a WebSocket handshake. Without
125
+ // this listener Node hands the handshake to the ordinary request handler, which
126
+ // answers 200 on a connection the client expects to be a 101 — so a function's
127
+ // WebSocket code silently never ran under `neon dev`.
128
+ //
129
+ // The upgrade path takes the RAW handler, not the error-boundary-wrapped one. The
130
+ // boundary turns a throw into a 500 Response, which on this path is
131
+ // indistinguishable from a handler that deliberately declined the upgrade — so a
132
+ // crashing handler would answer 501 ("no WebSocket support") instead of surfacing
133
+ // the error. The upgrade listener has its own equivalent boundary, and reports a
134
+ // throw as the 502 the deployed runtime returns.
135
+ server.on("upgrade", createUpgradeListener({
136
+ fetch: fetchHandler,
137
+ upgrade: resolveUpgradeHandler(mod),
138
+ }));
95
139
  const boundPort = await bindPort(server, port, hostname);
96
140
  process.stdout.write(`neon-dev:ready ${boundPort}\n`);
97
141
  return boundPort;