@neon/env 0.13.3 → 0.14.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/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.js","names":[],"sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport { readFileSync } from \"node:fs\";\nimport { fileURLToPath } from \"node:url\";\nimport yargs from \"yargs\";\nimport { hideBin } from \"yargs/helpers\";\nimport {\n\ttype CommandResult,\n\trunEnvExport,\n\trunEnvRun,\n} from \"./lib/cli/commands.js\";\n\nconst pkgVersion = readPackageVersion();\n\nconst argv = yargs(hideBin(process.argv))\n\t.scriptName(\"neon-env\")\n\t.usage(\"$0 <command> [options]\")\n\t.parserConfiguration({ \"populate--\": true })\n\t.option(\"debug\", {\n\t\ttype: \"boolean\",\n\t\tdefault: false,\n\t\tdescribe:\n\t\t\t\"Print stack traces and structured error details when something fails\",\n\t})\n\t.command(\n\t\t\"run\",\n\t\t\"Run a command with Neon env vars (from your neon.ts policy) injected into its environment. Use `--` to separate the command: `neon-env run -- npm run dev`.\",\n\t\t(y) =>\n\t\t\ty\n\t\t\t\t.option(\"config\", {\n\t\t\t\t\ttype: \"string\",\n\t\t\t\t\tdescribe:\n\t\t\t\t\t\t\"Path to neon.ts (defaults to walking up from cwd)\",\n\t\t\t\t})\n\t\t\t\t.option(\"project-id\", {\n\t\t\t\t\ttype: \"string\",\n\t\t\t\t\tdescribe: \"Override the .neon/project.json projectId\",\n\t\t\t\t})\n\t\t\t\t.option(\"branch\", {\n\t\t\t\t\ttype: \"string\",\n\t\t\t\t\tdescribe:\n\t\t\t\t\t\t\"Branch name or id to target (overrides .neon / NEON_BRANCH / NEON_BRANCH_ID)\",\n\t\t\t\t})\n\t\t\t\t.option(\"api-key\", {\n\t\t\t\t\ttype: \"string\",\n\t\t\t\t\tdescribe: \"Neon API key (defaults to NEON_API_KEY)\",\n\t\t\t\t}),\n\t)\n\t.command(\n\t\t\"export\",\n\t\t\"Print the branch's Neon env vars (from your neon.ts policy) to stdout, as dotenv lines or JSON. Useful for piping into other env tools, e.g. `neon-env export --format json`.\",\n\t\t(y) =>\n\t\t\ty\n\t\t\t\t.option(\"format\", {\n\t\t\t\t\tchoices: [\"dotenv\", \"json\"] as const,\n\t\t\t\t\tdefault: \"dotenv\",\n\t\t\t\t\tdescribe: \"Output format: dotenv (KEY=value lines) or json\",\n\t\t\t\t})\n\t\t\t\t.option(\"config\", {\n\t\t\t\t\ttype: \"string\",\n\t\t\t\t\tdescribe:\n\t\t\t\t\t\t\"Path to neon.ts (defaults to walking up from cwd)\",\n\t\t\t\t})\n\t\t\t\t.option(\"project-id\", {\n\t\t\t\t\ttype: \"string\",\n\t\t\t\t\tdescribe: \"Override the .neon/project.json projectId\",\n\t\t\t\t})\n\t\t\t\t.option(\"branch\", {\n\t\t\t\t\ttype: \"string\",\n\t\t\t\t\tdescribe:\n\t\t\t\t\t\t\"Branch name or id to target (overrides .neon / NEON_BRANCH / NEON_BRANCH_ID)\",\n\t\t\t\t})\n\t\t\t\t.option(\"api-key\", {\n\t\t\t\t\ttype: \"string\",\n\t\t\t\t\tdescribe: \"Neon API key (defaults to NEON_API_KEY)\",\n\t\t\t\t}),\n\t)\n\t.demandCommand(1, \"Run `neon-env --help` to see the available commands.\")\n\t.strict()\n\t.help()\n\t.version(pkgVersion)\n\t.parseSync();\n\nconst command = String(argv._[0]);\nconst cwd = process.cwd();\n\nlet result: CommandResult;\nswitch (command) {\n\tcase \"run\": {\n\t\tconst passthrough = Array.isArray(argv[\"--\"])\n\t\t\t? argv[\"--\"].map(String)\n\t\t\t: [];\n\t\tresult = await runEnvRun(\n\t\t\t{\n\t\t\t\tcommand: passthrough,\n\t\t\t\t...(typeof argv.config === \"string\"\n\t\t\t\t\t? { configPath: argv.config }\n\t\t\t\t\t: {}),\n\t\t\t\t...(typeof argv[\"project-id\"] === \"string\"\n\t\t\t\t\t? { projectId: argv[\"project-id\"] }\n\t\t\t\t\t: {}),\n\t\t\t\t...(typeof argv.branch === \"string\"\n\t\t\t\t\t? { branch: argv.branch }\n\t\t\t\t\t: {}),\n\t\t\t\t...(typeof argv[\"api-key\"] === \"string\"\n\t\t\t\t\t? { apiKey: argv[\"api-key\"] }\n\t\t\t\t\t: {}),\n\t\t\t},\n\t\t\t{ cwd },\n\t\t);\n\t\tbreak;\n\t}\n\tcase \"export\": {\n\t\tresult = await runEnvExport(\n\t\t\t{\n\t\t\t\tformat: argv.format === \"json\" ? \"json\" : \"dotenv\",\n\t\t\t\t...(typeof argv.config === \"string\"\n\t\t\t\t\t? { configPath: argv.config }\n\t\t\t\t\t: {}),\n\t\t\t\t...(typeof argv[\"project-id\"] === \"string\"\n\t\t\t\t\t? { projectId: argv[\"project-id\"] }\n\t\t\t\t\t: {}),\n\t\t\t\t...(typeof argv.branch === \"string\"\n\t\t\t\t\t? { branch: argv.branch }\n\t\t\t\t\t: {}),\n\t\t\t\t...(typeof argv[\"api-key\"] === \"string\"\n\t\t\t\t\t? { apiKey: argv[\"api-key\"] }\n\t\t\t\t\t: {}),\n\t\t\t},\n\t\t\t{ cwd },\n\t\t);\n\t\tbreak;\n\t}\n\tdefault:\n\t\tresult = {\n\t\t\texitCode: 1,\n\t\t\tstdout: \"\",\n\t\t\tstderr: `Unknown command: ${command}\\n`,\n\t\t};\n}\n\nif (result.stdout) process.stdout.write(result.stdout);\nif (result.stderr) process.stderr.write(result.stderr);\nif (argv.debug && result.exitCode !== 0 && result.debugInfo) {\n\tprocess.stderr.write(`\\n--- debug ---\\n${result.debugInfo}\\n`);\n}\nprocess.exit(result.exitCode);\n\nfunction readPackageVersion(): string {\n\t// The built CLI lives at `dist/cli.js`, so `package.json` is one directory up. When\n\t// running from source (tsx, vitest), the file lives at `src/cli.ts` and `package.json`\n\t// is again one directory up. Single resolution covers both layouts.\n\ttry {\n\t\tconst pkgUrl = new URL(\"../package.json\", import.meta.url);\n\t\tconst raw = readFileSync(fileURLToPath(pkgUrl), \"utf-8\");\n\t\tconst parsed = JSON.parse(raw) as { version?: unknown };\n\t\treturn typeof parsed.version === \"string\" ? parsed.version : \"0.0.0\";\n\t} catch {\n\t\treturn \"0.0.0\";\n\t}\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"cli.js","names":[],"sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport { readFileSync } from \"node:fs\";\nimport { fileURLToPath } from \"node:url\";\nimport yargs from \"yargs\";\nimport { hideBin } from \"yargs/helpers\";\nimport {\n\ttype CommandResult,\n\trunEnvExport,\n\trunEnvRun,\n} from \"./lib/cli/commands.js\";\n\nconst pkgVersion = readPackageVersion();\n\nconst argv = yargs(hideBin(process.argv))\n\t.scriptName(\"neon-env\")\n\t.usage(\"$0 <command> [options]\")\n\t.parserConfiguration({ \"populate--\": true })\n\t.option(\"debug\", {\n\t\ttype: \"boolean\",\n\t\tdefault: false,\n\t\tdescribe:\n\t\t\t\"Print stack traces and structured error details when something fails\",\n\t})\n\t.command(\n\t\t\"run\",\n\t\t\"Run a command with Neon env vars (from your neon.ts policy) injected into its environment. Use `--` to separate the command: `neon-env run -- npm run dev`.\",\n\t\t(y) =>\n\t\t\ty\n\t\t\t\t.option(\"config\", {\n\t\t\t\t\ttype: \"string\",\n\t\t\t\t\tdescribe:\n\t\t\t\t\t\t\"Path to neon.ts (defaults to walking up from cwd)\",\n\t\t\t\t})\n\t\t\t\t.option(\"project-id\", {\n\t\t\t\t\ttype: \"string\",\n\t\t\t\t\tdescribe: \"Override the .neon/project.json projectId\",\n\t\t\t\t})\n\t\t\t\t.option(\"branch\", {\n\t\t\t\t\ttype: \"string\",\n\t\t\t\t\tdescribe:\n\t\t\t\t\t\t\"Branch name or id to target (overrides .neon / NEON_BRANCH / NEON_BRANCH_ID)\",\n\t\t\t\t})\n\t\t\t\t.option(\"api-key\", {\n\t\t\t\t\ttype: \"string\",\n\t\t\t\t\tdescribe: \"Neon API key (defaults to NEON_API_KEY)\",\n\t\t\t\t})\n\t\t\t\t.option(\"profile\", {\n\t\t\t\t\ttype: \"string\",\n\t\t\t\t\tdescribe:\n\t\t\t\t\t\t\"Neon CLI profile whose stored credential to use (defaults to NEON_PROFILE, else DEFAULT)\",\n\t\t\t\t}),\n\t)\n\t.command(\n\t\t\"export\",\n\t\t\"Print the branch's Neon env vars (from your neon.ts policy) to stdout, as dotenv lines or JSON. Useful for piping into other env tools, e.g. `neon-env export --format json`.\",\n\t\t(y) =>\n\t\t\ty\n\t\t\t\t.option(\"format\", {\n\t\t\t\t\tchoices: [\"dotenv\", \"json\"] as const,\n\t\t\t\t\tdefault: \"dotenv\",\n\t\t\t\t\tdescribe: \"Output format: dotenv (KEY=value lines) or json\",\n\t\t\t\t})\n\t\t\t\t.option(\"config\", {\n\t\t\t\t\ttype: \"string\",\n\t\t\t\t\tdescribe:\n\t\t\t\t\t\t\"Path to neon.ts (defaults to walking up from cwd)\",\n\t\t\t\t})\n\t\t\t\t.option(\"project-id\", {\n\t\t\t\t\ttype: \"string\",\n\t\t\t\t\tdescribe: \"Override the .neon/project.json projectId\",\n\t\t\t\t})\n\t\t\t\t.option(\"branch\", {\n\t\t\t\t\ttype: \"string\",\n\t\t\t\t\tdescribe:\n\t\t\t\t\t\t\"Branch name or id to target (overrides .neon / NEON_BRANCH / NEON_BRANCH_ID)\",\n\t\t\t\t})\n\t\t\t\t.option(\"api-key\", {\n\t\t\t\t\ttype: \"string\",\n\t\t\t\t\tdescribe: \"Neon API key (defaults to NEON_API_KEY)\",\n\t\t\t\t})\n\t\t\t\t.option(\"profile\", {\n\t\t\t\t\ttype: \"string\",\n\t\t\t\t\tdescribe:\n\t\t\t\t\t\t\"Neon CLI profile whose stored credential to use (defaults to NEON_PROFILE, else DEFAULT)\",\n\t\t\t\t}),\n\t)\n\t.demandCommand(1, \"Run `neon-env --help` to see the available commands.\")\n\t.strict()\n\t.help()\n\t.version(pkgVersion)\n\t.parseSync();\n\nconst command = String(argv._[0]);\nconst cwd = process.cwd();\n\nlet result: CommandResult;\nswitch (command) {\n\tcase \"run\": {\n\t\tconst passthrough = Array.isArray(argv[\"--\"])\n\t\t\t? argv[\"--\"].map(String)\n\t\t\t: [];\n\t\tresult = await runEnvRun(\n\t\t\t{\n\t\t\t\tcommand: passthrough,\n\t\t\t\t...(typeof argv.config === \"string\"\n\t\t\t\t\t? { configPath: argv.config }\n\t\t\t\t\t: {}),\n\t\t\t\t...(typeof argv[\"project-id\"] === \"string\"\n\t\t\t\t\t? { projectId: argv[\"project-id\"] }\n\t\t\t\t\t: {}),\n\t\t\t\t...(typeof argv.branch === \"string\"\n\t\t\t\t\t? { branch: argv.branch }\n\t\t\t\t\t: {}),\n\t\t\t\t...(typeof argv[\"api-key\"] === \"string\"\n\t\t\t\t\t? { apiKey: argv[\"api-key\"] }\n\t\t\t\t\t: {}),\n\t\t\t\t...(typeof argv.profile === \"string\"\n\t\t\t\t\t? { profile: argv.profile }\n\t\t\t\t\t: {}),\n\t\t\t},\n\t\t\t{ cwd },\n\t\t);\n\t\tbreak;\n\t}\n\tcase \"export\": {\n\t\tresult = await runEnvExport(\n\t\t\t{\n\t\t\t\tformat: argv.format === \"json\" ? \"json\" : \"dotenv\",\n\t\t\t\t...(typeof argv.config === \"string\"\n\t\t\t\t\t? { configPath: argv.config }\n\t\t\t\t\t: {}),\n\t\t\t\t...(typeof argv[\"project-id\"] === \"string\"\n\t\t\t\t\t? { projectId: argv[\"project-id\"] }\n\t\t\t\t\t: {}),\n\t\t\t\t...(typeof argv.branch === \"string\"\n\t\t\t\t\t? { branch: argv.branch }\n\t\t\t\t\t: {}),\n\t\t\t\t...(typeof argv[\"api-key\"] === \"string\"\n\t\t\t\t\t? { apiKey: argv[\"api-key\"] }\n\t\t\t\t\t: {}),\n\t\t\t\t...(typeof argv.profile === \"string\"\n\t\t\t\t\t? { profile: argv.profile }\n\t\t\t\t\t: {}),\n\t\t\t},\n\t\t\t{ cwd },\n\t\t);\n\t\tbreak;\n\t}\n\tdefault:\n\t\tresult = {\n\t\t\texitCode: 1,\n\t\t\tstdout: \"\",\n\t\t\tstderr: `Unknown command: ${command}\\n`,\n\t\t};\n}\n\nif (result.stdout) process.stdout.write(result.stdout);\nif (result.stderr) process.stderr.write(result.stderr);\nif (argv.debug && result.exitCode !== 0 && result.debugInfo) {\n\tprocess.stderr.write(`\\n--- debug ---\\n${result.debugInfo}\\n`);\n}\nprocess.exit(result.exitCode);\n\nfunction readPackageVersion(): string {\n\t// The built CLI lives at `dist/cli.js`, so `package.json` is one directory up. When\n\t// running from source (tsx, vitest), the file lives at `src/cli.ts` and `package.json`\n\t// is again one directory up. Single resolution covers both layouts.\n\ttry {\n\t\tconst pkgUrl = new URL(\"../package.json\", import.meta.url);\n\t\tconst raw = readFileSync(fileURLToPath(pkgUrl), \"utf-8\");\n\t\tconst parsed = JSON.parse(raw) as { version?: unknown };\n\t\treturn typeof parsed.version === \"string\" ? parsed.version : \"0.0.0\";\n\t} catch {\n\t\treturn \"0.0.0\";\n\t}\n}\n"],"mappings":"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"}
@@ -38,6 +38,8 @@ interface EnvResolveOptions {
38
38
  projectId?: string;
39
39
  branch?: string;
40
40
  apiKey?: string;
41
+ /** Neon CLI profile whose stored credential to use. `--profile`, else `NEON_PROFILE`. */
42
+ profile?: string;
41
43
  }
42
44
  interface EnvRunCommandOptions extends EnvResolveOptions {
43
45
  /** The user command to spawn (after `--`). The first element is the executable. */
@@ -1 +1 @@
1
- {"version":3,"file":"commands.d.ts","names":[],"sources":["../../../src/lib/cli/commands.ts"],"mappings":";;;;;;;;AAuBA;AAUA;AAiBiB,UA3BA,UAAA,CA2BiB;EAOjB,GAAA,EAAA,MAAA;EAWK;AAAS;AACrB;AACJ;AACK;EAAR,GAAA,CAAA,EAzCI,OAyCJ;AAAO;AA2CO,UAjFA,aAAA,CAiFwB;EAWnB;EAAY,QAAA,EAAA,MAAA;EACxB;EACJ,MAAA,EAAA,MAAA;EACK;EAAR,MAAA,EAAA,MAAA;EAAO;;;;;;;;;UA9EO,iBAAA;;;;;;UAOA,oBAAA,SAA6B;;;;;;;;;;iBAWxB,SAAA,UACZ,2BACJ,aACH,QAAQ;UA2CM,uBAAA,SAAgC;;;;;;;;;;iBAW3B,YAAA,UACZ,8BACJ,aACH,QAAQ"}
1
+ {"version":3,"file":"commands.d.ts","names":[],"sources":["../../../src/lib/cli/commands.ts"],"mappings":";;;;;;;;AAuBA;AAUA;AAiBiB,UA3BA,UAAA,CA2BiB;EASjB,GAAA,EAAA,MAAA;EAWK;AAAS;AACrB;AACJ;AACK;EAAR,GAAA,CAAA,EA3CI,OA2CJ;AAAO;AA2CO,UAnFA,aAAA,CAmFwB;EAWnB;EAAY,QAAA,EAAA,MAAA;EACxB;EACJ,MAAA,EAAA,MAAA;EACK;EAAR,MAAA,EAAA,MAAA;EAAO;;;;;;;;;UAhFO,iBAAA;;;;;;;;UASA,oBAAA,SAA6B;;;;;;;;;;iBAWxB,SAAA,UACZ,2BACJ,aACH,QAAQ;UA2CM,uBAAA,SAAgC;;;;;;;;;;iBAW3B,YAAA,UACZ,8BACJ,aACH,QAAQ"}
@@ -98,7 +98,10 @@ async function loadConfigAndFetchEnv(options, ctx, resolved) {
98
98
  });
99
99
  const envFileSource = join(dirname(resolvedPath), DEFAULT_ENV_FILE);
100
100
  const fileEnv = existsSync(envFileSource) ? parseEnvFile(readFileSync(envFileSource, "utf-8")) : {};
101
- const apiKey = resolveApiKey({ ...options.apiKey ? { apiKey: options.apiKey } : {} });
101
+ const apiKey = resolveApiKey({
102
+ ...options.apiKey ? { apiKey: options.apiKey } : {},
103
+ ...options.profile ? { profile: options.profile } : {}
104
+ });
102
105
  const { vars } = await fetchEnvReusingSecrets(config, {
103
106
  projectId: resolved.projectId,
104
107
  branch: resolved.branch,
@@ -1 +1 @@
1
- {"version":3,"file":"commands.js","names":[],"sources":["../../../src/lib/cli/commands.ts"],"sourcesContent":["import { spawn } from \"node:child_process\";\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport {\n\tConfigLoadError,\n\tErrorCode,\n\tloadConfigFromFile,\n\tMissingContextError,\n\ttype NeonApi,\n\tPlatformError,\n} from \"@neon/config/v1\";\nimport { fetchEnvReusingSecrets } from \"../reuse-secrets.js\";\nimport { resolveApiKey } from \"./resolve-api-key.js\";\nimport { resolveContext } from \"./resolve-context.js\";\n\n/** File `env run` reads to layer one-time auth keys. Matches the Vercel/Next.js convention. */\nconst DEFAULT_ENV_FILE = \".env.local\";\n\n/**\n * Cross-cutting environment a CLI command is allowed to touch. Injected so tests can drive\n * the handler with a custom NeonApi and a controlled `cwd` without spawning child\n * processes.\n */\nexport interface CommandEnv {\n\tcwd: string;\n\t/**\n\t * When set, used directly as the NeonApi. When omitted, the real adapter is built from\n\t * the key {@link resolveApiKey} resolves (`--api-key` → `NEON_API_KEY` → the Neon CLI's\n\t * stored credentials).\n\t */\n\tapi?: NeonApi;\n}\n\nexport interface CommandResult {\n\t/** Process exit code. `0` for success, non-zero for failure. */\n\texitCode: number;\n\t/** Text intended for stdout. */\n\tstdout: string;\n\t/** Text intended for stderr (human-readable status / error messages). */\n\tstderr: string;\n\t/** Optional structured debug payload — printed only when `--debug` is passed. */\n\tdebugInfo?: string;\n}\n\n/**\n * Inputs needed to resolve a branch and fetch its env, shared by `run` and `export`: an\n * optional explicit `neon.ts` path, project/branch overrides, and an API key. Everything\n * ambient — `.neon`, `NEON_*` env, the Neon CLI's stored credentials — is resolved by the\n * CLI (see `resolveContext` and `resolveApiKey`), never by the library.\n */\nexport interface EnvResolveOptions {\n\tconfigPath?: string;\n\tprojectId?: string;\n\tbranch?: string;\n\tapiKey?: string;\n}\n\nexport interface EnvRunCommandOptions extends EnvResolveOptions {\n\t/** The user command to spawn (after `--`). The first element is the executable. */\n\tcommand: string[];\n}\n\n/**\n * Implementation of `neon-env run -- <cmd...>`. Loads `neon.ts`, fetches the env from\n * Neon, then spawns the user-supplied command with the env vars injected on top of the\n * inherited `process.env`. Stdio is inherited so interactive dev servers keep working.\n * The parent process exits with the child's exit code.\n */\nexport async function runEnvRun(\n\toptions: EnvRunCommandOptions,\n\tctx: CommandEnv,\n): Promise<CommandResult> {\n\tif (options.command.length === 0) {\n\t\treturn failure(\n\t\t\t[\n\t\t\t\t\"`env run` requires a command to spawn.\",\n\t\t\t\t\"Usage: neon-env run -- <command> [args...]\",\n\t\t\t\t\"Example: neon-env run -- npm run dev\",\n\t\t\t].join(\"\\n\"),\n\t\t);\n\t}\n\n\t// The CLI owns project/branch resolution (flags → NEON_* env → .neon file) so the\n\t// library functions stay filesystem/env-agnostic.\n\tconst resolved = resolveContext({\n\t\tcwd: ctx.cwd,\n\t\t...(options.projectId ? { projectId: options.projectId } : {}),\n\t\t...(options.branch ? { branch: options.branch } : {}),\n\t});\n\tif (!resolved.ok) {\n\t\treturn failure(\n\t\t\t[\n\t\t\t\t\"`env run` could not resolve the Neon project and branch:\",\n\t\t\t\t...resolved.missing.map((m) => ` - ${m}`),\n\t\t\t].join(\"\\n\"),\n\t\t\t3,\n\t\t);\n\t}\n\n\tlet injected: Record<string, string>;\n\ttry {\n\t\tinjected = await loadConfigAndFetchEnv(options, ctx, resolved.context);\n\t} catch (err) {\n\t\treturn handleError(err);\n\t}\n\n\tconst [executable, ...args] = options.command;\n\tconst exitCode = await spawnAndWait(executable, args, {\n\t\tcwd: ctx.cwd,\n\t\tenv: { ...process.env, ...injected },\n\t});\n\treturn { exitCode, stdout: \"\", stderr: \"\" };\n}\n\nexport interface EnvExportCommandOptions extends EnvResolveOptions {\n\t/** Output format. `dotenv` (KEY=value lines) by default; `json` for tooling / bulk loaders. */\n\tformat?: \"dotenv\" | \"json\";\n}\n\n/**\n * Implementation of `neon-env export`. Resolves the branch's Neon env the same way `run`\n * does (neon.ts policy + linked branch), then writes it to stdout — as dotenv lines or JSON —\n * instead of spawning a process, so other env tools can consume it. For example, varlock can\n * bulk-load it with `@setValuesBulk(exec(\"neon-env export --format json\"), format=json)`.\n */\nexport async function runEnvExport(\n\toptions: EnvExportCommandOptions,\n\tctx: CommandEnv,\n): Promise<CommandResult> {\n\tconst resolved = resolveContext({\n\t\tcwd: ctx.cwd,\n\t\t...(options.projectId ? { projectId: options.projectId } : {}),\n\t\t...(options.branch ? { branch: options.branch } : {}),\n\t});\n\tif (!resolved.ok) {\n\t\treturn failure(\n\t\t\t[\n\t\t\t\t\"`env export` could not resolve the Neon project and branch:\",\n\t\t\t\t...resolved.missing.map((m) => ` - ${m}`),\n\t\t\t].join(\"\\n\"),\n\t\t\t3,\n\t\t);\n\t}\n\n\tlet entries: Record<string, string>;\n\ttry {\n\t\tentries = await loadConfigAndFetchEnv(options, ctx, resolved.context);\n\t} catch (err) {\n\t\treturn handleError(err);\n\t}\n\n\tconst stdout =\n\t\toptions.format === \"json\"\n\t\t\t? `${JSON.stringify(entries, null, 2)}\\n`\n\t\t\t: toDotenv(entries);\n\treturn { exitCode: 0, stdout, stderr: \"\" };\n}\n\n/** Render an env map as dotenv `KEY=value` lines, quoting values that need it. */\nfunction toDotenv(entries: Record<string, string>): string {\n\tconst lines = Object.entries(entries).map(([key, value]) =>\n\t\tformatDotenvLine(key, value),\n\t);\n\treturn lines.length > 0 ? `${lines.join(\"\\n\")}\\n` : \"\";\n}\n\n/**\n * Render a single `KEY=value` dotenv line, double-quoting (and escaping) values that contain\n * whitespace, `#`, quotes, or `=` so connection strings round-trip through dotenv parsers.\n */\nfunction formatDotenvLine(key: string, value: string): string {\n\tif (!/[\\s#\"'=]/.test(value)) return `${key}=${value}`;\n\tconst escaped = value.replace(/\\\\/g, \"\\\\\\\\\").replace(/\"/g, '\\\\\"');\n\treturn `${key}=\"${escaped}\"`;\n}\n\n/**\n * Load `neon.ts`, then resolve the branch env for the explicitly-resolved project + branch.\n * Layers `.env.local` (next to the config file) into the env source so re-runs keep the\n * one-time secrets the Neon API only returns once — the branch credential's, and any Auth\n * values a pre-`base_url` integration can no longer report. Uses\n * {@link fetchEnvReusingSecrets} rather than a bare `fetchEnv` so a run that already has a\n * working credential verifies and keeps it instead of minting another one per invocation.\n */\nasync function loadConfigAndFetchEnv(\n\toptions: EnvResolveOptions,\n\tctx: CommandEnv,\n\tresolved: { projectId: string; branch: string },\n): Promise<Record<string, string>> {\n\tconst { config, resolvedPath } = await loadConfigFromFile({\n\t\t...(options.configPath ? { path: options.configPath } : {}),\n\t\tcwd: ctx.cwd,\n\t});\n\tconst envFileSource = join(dirname(resolvedPath), DEFAULT_ENV_FILE);\n\tconst fileEnv = existsSync(envFileSource)\n\t\t? parseEnvFile(readFileSync(envFileSource, \"utf-8\"))\n\t\t: {};\n\tconst apiKey = resolveApiKey({\n\t\t...(options.apiKey ? { apiKey: options.apiKey } : {}),\n\t});\n\tconst { vars } = await fetchEnvReusingSecrets(config, {\n\t\tprojectId: resolved.projectId,\n\t\tbranch: resolved.branch,\n\t\tenv: { ...process.env, ...fileEnv },\n\t\t...(ctx.api ? { api: ctx.api } : {}),\n\t\t...(apiKey ? { apiKey } : {}),\n\t});\n\treturn vars;\n}\n\n/**\n * Spawn a child process with stdio inherited so dev servers stay interactive. Resolves\n * with the child's exit code (treating signal terminations as code 1 so the CLI surfaces\n * a non-zero exit consistently).\n */\nfunction spawnAndWait(\n\tcommand: string,\n\targs: string[],\n\toptions: { cwd: string; env: Record<string, string | undefined> },\n): Promise<number> {\n\treturn new Promise((resolve) => {\n\t\tconst child = spawn(command, args, {\n\t\t\tcwd: options.cwd,\n\t\t\tenv: options.env,\n\t\t\tstdio: \"inherit\",\n\t\t});\n\t\tchild.on(\"error\", (err) => {\n\t\t\tprocess.stderr.write(\n\t\t\t\t`neon-env run: failed to spawn '${command}': ${err.message}\\n`,\n\t\t\t);\n\t\t\tresolve(1);\n\t\t});\n\t\tchild.on(\"exit\", (code, signal) => {\n\t\t\tif (typeof code === \"number\") {\n\t\t\t\tresolve(code);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (signal) {\n\t\t\t\tprocess.stderr.write(\n\t\t\t\t\t`neon-env run: child terminated by signal ${signal}\\n`,\n\t\t\t\t);\n\t\t\t\tresolve(1);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tresolve(1);\n\t\t});\n\t});\n}\n\nfunction parseEnvFile(body: string): NodeJS.ProcessEnv {\n\tconst out: NodeJS.ProcessEnv = {};\n\tfor (const line of body.split(\"\\n\")) {\n\t\tconst parsed = parseEnvLine(line);\n\t\tif (parsed) out[parsed.key] = parsed.value;\n\t}\n\treturn out;\n}\n\nfunction parseEnvLine(line: string): { key: string; value: string } | null {\n\tconst match = line.match(\n\t\t/^\\s*(?:export\\s+)?([A-Za-z_][A-Za-z0-9_]*)\\s*=\\s*(.*)$/,\n\t);\n\tconst key = match?.[1];\n\tconst rawValue = match?.[2];\n\tif (key === undefined || rawValue === undefined) return null;\n\treturn { key, value: unescapeEnvValue(rawValue.trim()) };\n}\n\nfunction unescapeEnvValue(value: string): string {\n\tif (value.length >= 2 && value.startsWith('\"') && value.endsWith('\"')) {\n\t\treturn value.slice(1, -1).replace(/\\\\\"/g, '\"').replace(/\\\\\\\\/g, \"\\\\\");\n\t}\n\tif (value.length >= 2 && value.startsWith(\"'\") && value.endsWith(\"'\")) {\n\t\treturn value.slice(1, -1);\n\t}\n\treturn value;\n}\n\n/**\n * Stable exit code per `PlatformError` code. Mirrors the table in the config package so\n * shell pipelines can branch on the specific failure mode without parsing free text.\n */\nconst EXIT_CODE_BY_PLATFORM_ERROR_CODE: Readonly<Record<string, number>> = {\n\t[ErrorCode.MissingApiKey]: 1,\n\t[ErrorCode.Unauthorized]: 6,\n\t[ErrorCode.Forbidden]: 7,\n\t[ErrorCode.NotFound]: 8,\n\t[ErrorCode.RateLimited]: 9,\n\t[ErrorCode.NetworkError]: 10,\n\t[ErrorCode.ServerError]: 11,\n\t[ErrorCode.Locked]: 11,\n\t[ErrorCode.InternalError]: 99,\n};\n\nfunction handleError(err: unknown): CommandResult {\n\tif (err instanceof MissingContextError)\n\t\treturn errorResult(err, `Missing context: ${err.message}`, 3);\n\tif (err instanceof ConfigLoadError)\n\t\treturn errorResult(err, `Failed to load config: ${err.message}`, 4);\n\t// The library's own wording is right for a library (\"this package never reads\n\t// NEON_API_KEY on your behalf\") and wrong here: `neon-env` does read it. Render the\n\t// chain this CLI actually implements, the same way an unresolved context is rendered.\n\tif (err instanceof PlatformError && err.code === ErrorCode.MissingApiKey) {\n\t\treturn errorResult(\n\t\t\terr,\n\t\t\t[\n\t\t\t\t\"No Neon API key. `neon-env` looks for one in this order:\",\n\t\t\t\t\" - the `--api-key` flag\",\n\t\t\t\t\" - the `NEON_API_KEY` environment variable\",\n\t\t\t\t\" - `credentials.json` in `NEONCTL_CONFIG_DIR` (else `~/.config/neonctl`) — run `neon auth` to create it\",\n\t\t\t].join(\"\\n\"),\n\t\t\tEXIT_CODE_BY_PLATFORM_ERROR_CODE[ErrorCode.MissingApiKey] ?? 1,\n\t\t);\n\t}\n\tif (err instanceof PlatformError) {\n\t\tconst exitCode = EXIT_CODE_BY_PLATFORM_ERROR_CODE[err.code];\n\t\tif (exitCode !== undefined)\n\t\t\treturn errorResult(err, err.message, exitCode);\n\t\treturn errorResult(err, `[${err.code}] ${err.message}`, 5);\n\t}\n\tif (err instanceof Error) return errorResult(err, err.message, 1);\n\treturn failure(String(err), 1);\n}\n\nfunction errorResult(\n\terr: unknown,\n\tmessage: string,\n\texitCode: number,\n): CommandResult {\n\tconst result: CommandResult = {\n\t\texitCode,\n\t\tstdout: \"\",\n\t\tstderr: `${message}\\n`,\n\t};\n\tconst debug = buildDebugInfo(err);\n\tif (debug) result.debugInfo = debug;\n\treturn result;\n}\n\nfunction buildDebugInfo(err: unknown): string | undefined {\n\tif (!(err instanceof Error)) return undefined;\n\tconst lines: string[] = [];\n\tif (err instanceof PlatformError) {\n\t\tlines.push(`code : ${err.code}`);\n\t\tif (Object.keys(err.details).length > 0) {\n\t\t\tlines.push(`details : ${JSON.stringify(err.details, null, 2)}`);\n\t\t}\n\t}\n\tif (err.cause instanceof Error) {\n\t\tlines.push(`cause : ${err.cause.name}: ${err.cause.message}`);\n\t}\n\tif (err.stack) {\n\t\tlines.push(err.stack);\n\t}\n\treturn lines.length > 0 ? lines.join(\"\\n\") : undefined;\n}\n\nfunction failure(message: string, exitCode = 1): CommandResult {\n\treturn { exitCode, stdout: \"\", stderr: `${message}\\n` };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"commands.js","names":[],"sources":["../../../src/lib/cli/commands.ts"],"sourcesContent":["import { spawn } from \"node:child_process\";\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport {\n\tConfigLoadError,\n\tErrorCode,\n\tloadConfigFromFile,\n\tMissingContextError,\n\ttype NeonApi,\n\tPlatformError,\n} from \"@neon/config/v1\";\nimport { fetchEnvReusingSecrets } from \"../reuse-secrets.js\";\nimport { resolveApiKey } from \"./resolve-api-key.js\";\nimport { resolveContext } from \"./resolve-context.js\";\n\n/** File `env run` reads to layer one-time auth keys. Matches the Vercel/Next.js convention. */\nconst DEFAULT_ENV_FILE = \".env.local\";\n\n/**\n * Cross-cutting environment a CLI command is allowed to touch. Injected so tests can drive\n * the handler with a custom NeonApi and a controlled `cwd` without spawning child\n * processes.\n */\nexport interface CommandEnv {\n\tcwd: string;\n\t/**\n\t * When set, used directly as the NeonApi. When omitted, the real adapter is built from\n\t * the key {@link resolveApiKey} resolves (`--api-key` → `NEON_API_KEY` → the Neon CLI's\n\t * stored credentials).\n\t */\n\tapi?: NeonApi;\n}\n\nexport interface CommandResult {\n\t/** Process exit code. `0` for success, non-zero for failure. */\n\texitCode: number;\n\t/** Text intended for stdout. */\n\tstdout: string;\n\t/** Text intended for stderr (human-readable status / error messages). */\n\tstderr: string;\n\t/** Optional structured debug payload — printed only when `--debug` is passed. */\n\tdebugInfo?: string;\n}\n\n/**\n * Inputs needed to resolve a branch and fetch its env, shared by `run` and `export`: an\n * optional explicit `neon.ts` path, project/branch overrides, and an API key. Everything\n * ambient — `.neon`, `NEON_*` env, the Neon CLI's stored credentials — is resolved by the\n * CLI (see `resolveContext` and `resolveApiKey`), never by the library.\n */\nexport interface EnvResolveOptions {\n\tconfigPath?: string;\n\tprojectId?: string;\n\tbranch?: string;\n\tapiKey?: string;\n\t/** Neon CLI profile whose stored credential to use. `--profile`, else `NEON_PROFILE`. */\n\tprofile?: string;\n}\n\nexport interface EnvRunCommandOptions extends EnvResolveOptions {\n\t/** The user command to spawn (after `--`). The first element is the executable. */\n\tcommand: string[];\n}\n\n/**\n * Implementation of `neon-env run -- <cmd...>`. Loads `neon.ts`, fetches the env from\n * Neon, then spawns the user-supplied command with the env vars injected on top of the\n * inherited `process.env`. Stdio is inherited so interactive dev servers keep working.\n * The parent process exits with the child's exit code.\n */\nexport async function runEnvRun(\n\toptions: EnvRunCommandOptions,\n\tctx: CommandEnv,\n): Promise<CommandResult> {\n\tif (options.command.length === 0) {\n\t\treturn failure(\n\t\t\t[\n\t\t\t\t\"`env run` requires a command to spawn.\",\n\t\t\t\t\"Usage: neon-env run -- <command> [args...]\",\n\t\t\t\t\"Example: neon-env run -- npm run dev\",\n\t\t\t].join(\"\\n\"),\n\t\t);\n\t}\n\n\t// The CLI owns project/branch resolution (flags → NEON_* env → .neon file) so the\n\t// library functions stay filesystem/env-agnostic.\n\tconst resolved = resolveContext({\n\t\tcwd: ctx.cwd,\n\t\t...(options.projectId ? { projectId: options.projectId } : {}),\n\t\t...(options.branch ? { branch: options.branch } : {}),\n\t});\n\tif (!resolved.ok) {\n\t\treturn failure(\n\t\t\t[\n\t\t\t\t\"`env run` could not resolve the Neon project and branch:\",\n\t\t\t\t...resolved.missing.map((m) => ` - ${m}`),\n\t\t\t].join(\"\\n\"),\n\t\t\t3,\n\t\t);\n\t}\n\n\tlet injected: Record<string, string>;\n\ttry {\n\t\tinjected = await loadConfigAndFetchEnv(options, ctx, resolved.context);\n\t} catch (err) {\n\t\treturn handleError(err);\n\t}\n\n\tconst [executable, ...args] = options.command;\n\tconst exitCode = await spawnAndWait(executable, args, {\n\t\tcwd: ctx.cwd,\n\t\tenv: { ...process.env, ...injected },\n\t});\n\treturn { exitCode, stdout: \"\", stderr: \"\" };\n}\n\nexport interface EnvExportCommandOptions extends EnvResolveOptions {\n\t/** Output format. `dotenv` (KEY=value lines) by default; `json` for tooling / bulk loaders. */\n\tformat?: \"dotenv\" | \"json\";\n}\n\n/**\n * Implementation of `neon-env export`. Resolves the branch's Neon env the same way `run`\n * does (neon.ts policy + linked branch), then writes it to stdout — as dotenv lines or JSON —\n * instead of spawning a process, so other env tools can consume it. For example, varlock can\n * bulk-load it with `@setValuesBulk(exec(\"neon-env export --format json\"), format=json)`.\n */\nexport async function runEnvExport(\n\toptions: EnvExportCommandOptions,\n\tctx: CommandEnv,\n): Promise<CommandResult> {\n\tconst resolved = resolveContext({\n\t\tcwd: ctx.cwd,\n\t\t...(options.projectId ? { projectId: options.projectId } : {}),\n\t\t...(options.branch ? { branch: options.branch } : {}),\n\t});\n\tif (!resolved.ok) {\n\t\treturn failure(\n\t\t\t[\n\t\t\t\t\"`env export` could not resolve the Neon project and branch:\",\n\t\t\t\t...resolved.missing.map((m) => ` - ${m}`),\n\t\t\t].join(\"\\n\"),\n\t\t\t3,\n\t\t);\n\t}\n\n\tlet entries: Record<string, string>;\n\ttry {\n\t\tentries = await loadConfigAndFetchEnv(options, ctx, resolved.context);\n\t} catch (err) {\n\t\treturn handleError(err);\n\t}\n\n\tconst stdout =\n\t\toptions.format === \"json\"\n\t\t\t? `${JSON.stringify(entries, null, 2)}\\n`\n\t\t\t: toDotenv(entries);\n\treturn { exitCode: 0, stdout, stderr: \"\" };\n}\n\n/** Render an env map as dotenv `KEY=value` lines, quoting values that need it. */\nfunction toDotenv(entries: Record<string, string>): string {\n\tconst lines = Object.entries(entries).map(([key, value]) =>\n\t\tformatDotenvLine(key, value),\n\t);\n\treturn lines.length > 0 ? `${lines.join(\"\\n\")}\\n` : \"\";\n}\n\n/**\n * Render a single `KEY=value` dotenv line, double-quoting (and escaping) values that contain\n * whitespace, `#`, quotes, or `=` so connection strings round-trip through dotenv parsers.\n */\nfunction formatDotenvLine(key: string, value: string): string {\n\tif (!/[\\s#\"'=]/.test(value)) return `${key}=${value}`;\n\tconst escaped = value.replace(/\\\\/g, \"\\\\\\\\\").replace(/\"/g, '\\\\\"');\n\treturn `${key}=\"${escaped}\"`;\n}\n\n/**\n * Load `neon.ts`, then resolve the branch env for the explicitly-resolved project + branch.\n * Layers `.env.local` (next to the config file) into the env source so re-runs keep the\n * one-time secrets the Neon API only returns once — the branch credential's, and any Auth\n * values a pre-`base_url` integration can no longer report. Uses\n * {@link fetchEnvReusingSecrets} rather than a bare `fetchEnv` so a run that already has a\n * working credential verifies and keeps it instead of minting another one per invocation.\n */\nasync function loadConfigAndFetchEnv(\n\toptions: EnvResolveOptions,\n\tctx: CommandEnv,\n\tresolved: { projectId: string; branch: string },\n): Promise<Record<string, string>> {\n\tconst { config, resolvedPath } = await loadConfigFromFile({\n\t\t...(options.configPath ? { path: options.configPath } : {}),\n\t\tcwd: ctx.cwd,\n\t});\n\tconst envFileSource = join(dirname(resolvedPath), DEFAULT_ENV_FILE);\n\tconst fileEnv = existsSync(envFileSource)\n\t\t? parseEnvFile(readFileSync(envFileSource, \"utf-8\"))\n\t\t: {};\n\tconst apiKey = resolveApiKey({\n\t\t...(options.apiKey ? { apiKey: options.apiKey } : {}),\n\t\t...(options.profile ? { profile: options.profile } : {}),\n\t});\n\tconst { vars } = await fetchEnvReusingSecrets(config, {\n\t\tprojectId: resolved.projectId,\n\t\tbranch: resolved.branch,\n\t\tenv: { ...process.env, ...fileEnv },\n\t\t...(ctx.api ? { api: ctx.api } : {}),\n\t\t...(apiKey ? { apiKey } : {}),\n\t});\n\treturn vars;\n}\n\n/**\n * Spawn a child process with stdio inherited so dev servers stay interactive. Resolves\n * with the child's exit code (treating signal terminations as code 1 so the CLI surfaces\n * a non-zero exit consistently).\n */\nfunction spawnAndWait(\n\tcommand: string,\n\targs: string[],\n\toptions: { cwd: string; env: Record<string, string | undefined> },\n): Promise<number> {\n\treturn new Promise((resolve) => {\n\t\tconst child = spawn(command, args, {\n\t\t\tcwd: options.cwd,\n\t\t\tenv: options.env,\n\t\t\tstdio: \"inherit\",\n\t\t});\n\t\tchild.on(\"error\", (err) => {\n\t\t\tprocess.stderr.write(\n\t\t\t\t`neon-env run: failed to spawn '${command}': ${err.message}\\n`,\n\t\t\t);\n\t\t\tresolve(1);\n\t\t});\n\t\tchild.on(\"exit\", (code, signal) => {\n\t\t\tif (typeof code === \"number\") {\n\t\t\t\tresolve(code);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (signal) {\n\t\t\t\tprocess.stderr.write(\n\t\t\t\t\t`neon-env run: child terminated by signal ${signal}\\n`,\n\t\t\t\t);\n\t\t\t\tresolve(1);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tresolve(1);\n\t\t});\n\t});\n}\n\nfunction parseEnvFile(body: string): NodeJS.ProcessEnv {\n\tconst out: NodeJS.ProcessEnv = {};\n\tfor (const line of body.split(\"\\n\")) {\n\t\tconst parsed = parseEnvLine(line);\n\t\tif (parsed) out[parsed.key] = parsed.value;\n\t}\n\treturn out;\n}\n\nfunction parseEnvLine(line: string): { key: string; value: string } | null {\n\tconst match = line.match(\n\t\t/^\\s*(?:export\\s+)?([A-Za-z_][A-Za-z0-9_]*)\\s*=\\s*(.*)$/,\n\t);\n\tconst key = match?.[1];\n\tconst rawValue = match?.[2];\n\tif (key === undefined || rawValue === undefined) return null;\n\treturn { key, value: unescapeEnvValue(rawValue.trim()) };\n}\n\nfunction unescapeEnvValue(value: string): string {\n\tif (value.length >= 2 && value.startsWith('\"') && value.endsWith('\"')) {\n\t\treturn value.slice(1, -1).replace(/\\\\\"/g, '\"').replace(/\\\\\\\\/g, \"\\\\\");\n\t}\n\tif (value.length >= 2 && value.startsWith(\"'\") && value.endsWith(\"'\")) {\n\t\treturn value.slice(1, -1);\n\t}\n\treturn value;\n}\n\n/**\n * Stable exit code per `PlatformError` code. Mirrors the table in the config package so\n * shell pipelines can branch on the specific failure mode without parsing free text.\n */\nconst EXIT_CODE_BY_PLATFORM_ERROR_CODE: Readonly<Record<string, number>> = {\n\t[ErrorCode.MissingApiKey]: 1,\n\t[ErrorCode.Unauthorized]: 6,\n\t[ErrorCode.Forbidden]: 7,\n\t[ErrorCode.NotFound]: 8,\n\t[ErrorCode.RateLimited]: 9,\n\t[ErrorCode.NetworkError]: 10,\n\t[ErrorCode.ServerError]: 11,\n\t[ErrorCode.Locked]: 11,\n\t[ErrorCode.InternalError]: 99,\n};\n\nfunction handleError(err: unknown): CommandResult {\n\tif (err instanceof MissingContextError)\n\t\treturn errorResult(err, `Missing context: ${err.message}`, 3);\n\tif (err instanceof ConfigLoadError)\n\t\treturn errorResult(err, `Failed to load config: ${err.message}`, 4);\n\t// The library's own wording is right for a library (\"this package never reads\n\t// NEON_API_KEY on your behalf\") and wrong here: `neon-env` does read it. Render the\n\t// chain this CLI actually implements, the same way an unresolved context is rendered.\n\tif (err instanceof PlatformError && err.code === ErrorCode.MissingApiKey) {\n\t\treturn errorResult(\n\t\t\terr,\n\t\t\t[\n\t\t\t\t\"No Neon API key. `neon-env` looks for one in this order:\",\n\t\t\t\t\" - the `--api-key` flag\",\n\t\t\t\t\" - the `NEON_API_KEY` environment variable\",\n\t\t\t\t\" - `credentials.json` in `NEONCTL_CONFIG_DIR` (else `~/.config/neonctl`) — run `neon auth` to create it\",\n\t\t\t].join(\"\\n\"),\n\t\t\tEXIT_CODE_BY_PLATFORM_ERROR_CODE[ErrorCode.MissingApiKey] ?? 1,\n\t\t);\n\t}\n\tif (err instanceof PlatformError) {\n\t\tconst exitCode = EXIT_CODE_BY_PLATFORM_ERROR_CODE[err.code];\n\t\tif (exitCode !== undefined)\n\t\t\treturn errorResult(err, err.message, exitCode);\n\t\treturn errorResult(err, `[${err.code}] ${err.message}`, 5);\n\t}\n\tif (err instanceof Error) return errorResult(err, err.message, 1);\n\treturn failure(String(err), 1);\n}\n\nfunction errorResult(\n\terr: unknown,\n\tmessage: string,\n\texitCode: number,\n): CommandResult {\n\tconst result: CommandResult = {\n\t\texitCode,\n\t\tstdout: \"\",\n\t\tstderr: `${message}\\n`,\n\t};\n\tconst debug = buildDebugInfo(err);\n\tif (debug) result.debugInfo = debug;\n\treturn result;\n}\n\nfunction buildDebugInfo(err: unknown): string | undefined {\n\tif (!(err instanceof Error)) return undefined;\n\tconst lines: string[] = [];\n\tif (err instanceof PlatformError) {\n\t\tlines.push(`code : ${err.code}`);\n\t\tif (Object.keys(err.details).length > 0) {\n\t\t\tlines.push(`details : ${JSON.stringify(err.details, null, 2)}`);\n\t\t}\n\t}\n\tif (err.cause instanceof Error) {\n\t\tlines.push(`cause : ${err.cause.name}: ${err.cause.message}`);\n\t}\n\tif (err.stack) {\n\t\tlines.push(err.stack);\n\t}\n\treturn lines.length > 0 ? lines.join(\"\\n\") : undefined;\n}\n\nfunction failure(message: string, exitCode = 1): CommandResult {\n\treturn { exitCode, stdout: \"\", stderr: `${message}\\n` };\n}\n"],"mappings":"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"}
@@ -1,21 +1,29 @@
1
+ import { DEFAULT_PROFILE } from "../../_shared/profiles.js";
2
+
1
3
  //#region src/lib/cli/resolve-api-key.d.ts
4
+
2
5
  /**
3
- * Resolve the Neon API key for a `neon-env` CLI invocation. Precedence (each wins over the
4
- * next): `--api-key` flag → `NEON_API_KEY` → `access_token` from the Neon CLI's
5
- * `credentials.json`, located by `@neon/config/paths`.
6
+ * Resolve the Neon API key for a `neon-env` CLI invocation.
6
7
  *
7
- * The CLI owns this resolution — `@neon/config` and `@neon/env` are deliberately
8
- * environment- and filesystem-agnostic and only ever accept an explicit `apiKey`, so the
9
- * ambient sources a *user* expects have to be read here. This mirrors `resolveContext`,
10
- * which does the same for project and branch.
8
+ * Precedence is the `neon` CLI's, from the same module: **an explicit flag beats an ambient
9
+ * environment variable.** `--api-key` and `--profile` together is an error; `--profile` beats
10
+ * `NEON_API_KEY`; `--api-key` beats `NEON_PROFILE`; two ambient sources resolve to the key.
11
11
  *
12
- * Returns `undefined` rather than throwing when nothing provides a key: the caller passes
13
- * it straight through, and the library raises the uniform `PLATFORM_MISSING_API_KEY` error.
12
+ * Sharing that decision rather than restating it is the point. An earlier version of this file
13
+ * checked `NEON_API_KEY` before the selected profile, so `NEON_API_KEY=… neon-env run --profile
14
+ * work` silently used the wrong account — the very bug this feature fixes in `neon`.
15
+ *
16
+ * The CLI owns the resolution because `@neon/config` and `@neon/env`'s root export are
17
+ * deliberately environment- and filesystem-agnostic: they accept an explicit `apiKey` and
18
+ * nothing else, so the ambient sources a *user* expects have to be read out here.
14
19
  */
15
20
  declare function resolveApiKey(options: {
16
21
  apiKey?: string;
22
+ profile?: string;
17
23
  env?: NodeJS.ProcessEnv;
24
+ /** Where to say that an exported key displaced an exported profile. Defaults to stderr. */
25
+ warn?: (message: string) => void;
18
26
  }): string | undefined;
19
27
  //#endregion
20
- export { resolveApiKey };
28
+ export { DEFAULT_PROFILE, resolveApiKey };
21
29
  //# sourceMappingURL=resolve-api-key.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"resolve-api-key.d.ts","names":[],"sources":["../../../src/lib/cli/resolve-api-key.ts"],"mappings":";;AAgBA;;;;;;;;;;;;iBAAgB,aAAA;;QAET,MAAA,CAAO"}
1
+ {"version":3,"file":"resolve-api-key.d.ts","names":[],"sources":["../../../src/lib/cli/resolve-api-key.ts"],"mappings":";;;;;;AA0BA;;;;;;;;;;;;;iBAAgB,aAAA;;;QAGT,MAAA,CAAO"}
@@ -1,57 +1,74 @@
1
- import { existsSync, readFileSync } from "node:fs";
2
- import { resolveConfigFile } from "@neon/config/paths";
1
+ import { configDir, resolveConfigFile } from "../../_shared/paths.js";
2
+ import { DEFAULT_PROFILE, resolveProfile } from "../../_shared/profiles.js";
3
+ import { displacedProfileWarning, selectCredential } from "../../_shared/auth_selection.js";
4
+ import { inspectCredentials, interpretCredentials } from "../../_shared/credentials.js";
3
5
  //#region src/lib/cli/resolve-api-key.ts
4
6
  /**
5
- * Resolve the Neon API key for a `neon-env` CLI invocation. Precedence (each wins over the
6
- * next): `--api-key` flag → `NEON_API_KEY` → `access_token` from the Neon CLI's
7
- * `credentials.json`, located by `@neon/config/paths`.
7
+ * Resolve the Neon API key for a `neon-env` CLI invocation.
8
8
  *
9
- * The CLI owns this resolution — `@neon/config` and `@neon/env` are deliberately
10
- * environment- and filesystem-agnostic and only ever accept an explicit `apiKey`, so the
11
- * ambient sources a *user* expects have to be read here. This mirrors `resolveContext`,
12
- * which does the same for project and branch.
9
+ * Precedence is the `neon` CLI's, from the same module: **an explicit flag beats an ambient
10
+ * environment variable.** `--api-key` and `--profile` together is an error; `--profile` beats
11
+ * `NEON_API_KEY`; `--api-key` beats `NEON_PROFILE`; two ambient sources resolve to the key.
13
12
  *
14
- * Returns `undefined` rather than throwing when nothing provides a key: the caller passes
15
- * it straight through, and the library raises the uniform `PLATFORM_MISSING_API_KEY` error.
13
+ * Sharing that decision rather than restating it is the point. An earlier version of this file
14
+ * checked `NEON_API_KEY` before the selected profile, so `NEON_API_KEY=… neon-env run --profile
15
+ * work` silently used the wrong account — the very bug this feature fixes in `neon`.
16
+ *
17
+ * The CLI owns the resolution because `@neon/config` and `@neon/env`'s root export are
18
+ * deliberately environment- and filesystem-agnostic: they accept an explicit `apiKey` and
19
+ * nothing else, so the ambient sources a *user* expects have to be read out here.
16
20
  */
17
21
  function resolveApiKey(options) {
18
22
  const env = options.env ?? process.env;
19
- return nonEmpty(options.apiKey) ?? nonEmpty(env.NEON_API_KEY) ?? readStoredAccessToken(env);
23
+ const selection = selectCredential({
24
+ ...options.apiKey !== void 0 ? { apiKeyFlag: options.apiKey } : {},
25
+ ...options.profile !== void 0 ? { profileFlag: options.profile } : {},
26
+ ...env.NEON_API_KEY !== void 0 ? { apiKeyEnv: env.NEON_API_KEY } : {},
27
+ ...env.NEON_PROFILE !== void 0 ? { profileEnv: env.NEON_PROFILE } : {}
28
+ });
29
+ const displaced = displacedProfileWarning(selection);
30
+ if (displaced !== null) (options.warn ?? ((message) => process.stderr.write(`${message}\n`)))(displaced);
31
+ if (selection.source !== "profile") return selection.apiKey;
32
+ return readStoredCredential(selection, env);
20
33
  }
21
34
  /**
22
- * Read `access_token` from the Neon CLI's credentials file.
23
- *
24
- * Location resolution is delegated to `@neon/config/paths` so this agrees with the `neon`
25
- * CLI itself — `NEON_CONFIG_DIR` / `NEONCTL_CONFIG_DIR`, else `$XDG_CONFIG_HOME/neon`, else
26
- * `~/.config/neon`, with an existing legacy `neonctl` directory still read. Rolling that
27
- * lookup by hand here is how the two drifted in the first place: this file honoured the env
28
- * var but not XDG, while the CLI honoured XDG but not the env var, so with
29
- * `XDG_CONFIG_HOME` set they disagreed about where credentials lived.
35
+ * The credential stored for the selected profile.
30
36
  *
31
- * Reads only `DEFAULT` — a profile is a CLI-invocation concept, and `neon-env` has no
32
- * `--profile` of its own to read one from.
33
- *
34
- * Never throws: a missing, unreadable, malformed, or token-less file is simply "no key",
35
- * so this can sit in a resolution chain without try/catch noise.
37
+ * Two different situations, deliberately not merged. A **missing** credential under `DEFAULT` is
38
+ * the ordinary not-signed-in state and resolves to no key; under a profile the user named it is
39
+ * an error, because reporting a missing credential would hide that the real problem is the name
40
+ * they typed. A **damaged** credential is always an error: the file is there, it is not an
41
+ * absence, and no amount of signing in elsewhere explains it.
36
42
  */
37
- function readStoredAccessToken(env) {
38
- const { path: credentialsPath } = resolveConfigFile("credentials.json", { env });
39
- if (!existsSync(credentialsPath)) return void 0;
40
- let parsed;
43
+ function readStoredCredential(selection, env) {
44
+ const { profile, explicit } = selection;
45
+ /**
46
+ * An *absence* is only an error when the user named the profile. Not being signed in under
47
+ * `DEFAULT` is the ordinary state, and the library's `PLATFORM_MISSING_API_KEY` says it
48
+ * better than a stack trace.
49
+ */
50
+ const absent = (reason) => {
51
+ if (explicit) throw new Error(reason);
52
+ };
53
+ let path;
41
54
  try {
42
- parsed = JSON.parse(readFileSync(credentialsPath, "utf-8"));
43
- } catch {
44
- return;
55
+ path = profile === "DEFAULT" ? resolveConfigFile("credentials.json", { env }).path : resolveProfile(configDir({ env }), profile).credentialsPath;
56
+ } catch (err) {
57
+ throw err instanceof Error ? err : new Error(String(err));
45
58
  }
46
- if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) return void 0;
47
- return nonEmpty(parsed.access_token);
48
- }
49
- function nonEmpty(value) {
50
- if (typeof value !== "string") return void 0;
51
- const trimmed = value.trim();
52
- return trimmed === "" ? void 0 : trimmed;
59
+ const read = inspectCredentials(path);
60
+ if (read.kind === "absent") return absent(`Profile "${profile}" has no stored credential at ${path}. Sign in with \`neon profile create ${profile}\`.`);
61
+ if (read.kind === "unusable") throw new Error(`${read.reason}. Replace it deliberately with \`neon profile create ${profile} --force\`, or delete the file.`);
62
+ const credential = interpretCredentials(read.credentials, {
63
+ path,
64
+ profile
65
+ });
66
+ if (credential.kind === "api_key") return credential.apiKey;
67
+ const token = read.credentials.access_token;
68
+ if (typeof token === "string" && token.trim() !== "") return token.trim();
69
+ throw new Error(`Profile "${profile}" holds a browser sign-in with no usable token at ${path}. Sign in again with \`neon auth --profile ${profile}\`.`);
53
70
  }
54
71
  //#endregion
55
- export { resolveApiKey };
72
+ export { DEFAULT_PROFILE, resolveApiKey };
56
73
 
57
74
  //# sourceMappingURL=resolve-api-key.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"resolve-api-key.js","names":[],"sources":["../../../src/lib/cli/resolve-api-key.ts"],"sourcesContent":["import { existsSync, readFileSync } from \"node:fs\";\nimport { resolveConfigFile } from \"@neon/config/paths\";\n\n/**\n * Resolve the Neon API key for a `neon-env` CLI invocation. Precedence (each wins over the\n * next): `--api-key` flag → `NEON_API_KEY` → `access_token` from the Neon CLI's\n * `credentials.json`, located by `@neon/config/paths`.\n *\n * The CLI owns this resolution — `@neon/config` and `@neon/env` are deliberately\n * environment- and filesystem-agnostic and only ever accept an explicit `apiKey`, so the\n * ambient sources a *user* expects have to be read here. This mirrors `resolveContext`,\n * which does the same for project and branch.\n *\n * Returns `undefined` rather than throwing when nothing provides a key: the caller passes\n * it straight through, and the library raises the uniform `PLATFORM_MISSING_API_KEY` error.\n */\nexport function resolveApiKey(options: {\n\tapiKey?: string;\n\tenv?: NodeJS.ProcessEnv;\n}): string | undefined {\n\tconst env = options.env ?? process.env;\n\treturn (\n\t\tnonEmpty(options.apiKey) ??\n\t\tnonEmpty(env.NEON_API_KEY) ??\n\t\treadStoredAccessToken(env)\n\t);\n}\n\n/**\n * Read `access_token` from the Neon CLI's credentials file.\n *\n * Location resolution is delegated to `@neon/config/paths` so this agrees with the `neon`\n * CLI itself — `NEON_CONFIG_DIR` / `NEONCTL_CONFIG_DIR`, else `$XDG_CONFIG_HOME/neon`, else\n * `~/.config/neon`, with an existing legacy `neonctl` directory still read. Rolling that\n * lookup by hand here is how the two drifted in the first place: this file honoured the env\n * var but not XDG, while the CLI honoured XDG but not the env var, so with\n * `XDG_CONFIG_HOME` set they disagreed about where credentials lived.\n *\n * Reads only `DEFAULT` — a profile is a CLI-invocation concept, and `neon-env` has no\n * `--profile` of its own to read one from.\n *\n * Never throws: a missing, unreadable, malformed, or token-less file is simply \"no key\",\n * so this can sit in a resolution chain without try/catch noise.\n */\nfunction readStoredAccessToken(env: NodeJS.ProcessEnv): string | undefined {\n\tconst { path: credentialsPath } = resolveConfigFile(\"credentials.json\", {\n\t\tenv,\n\t});\n\tif (!existsSync(credentialsPath)) return undefined;\n\n\tlet parsed: unknown;\n\ttry {\n\t\tparsed = JSON.parse(readFileSync(credentialsPath, \"utf-8\"));\n\t} catch {\n\t\treturn undefined;\n\t}\n\n\tif (parsed === null || typeof parsed !== \"object\" || Array.isArray(parsed))\n\t\treturn undefined;\n\treturn nonEmpty((parsed as Record<string, unknown>).access_token as string);\n}\n\nfunction nonEmpty(value: string | undefined): string | undefined {\n\tif (typeof value !== \"string\") return undefined;\n\tconst trimmed = value.trim();\n\treturn trimmed === \"\" ? undefined : trimmed;\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAgBA,SAAgB,cAAc,SAGP;CACtB,MAAM,MAAM,QAAQ,OAAO,QAAQ;CACnC,OACC,SAAS,QAAQ,MAAM,KACvB,SAAS,IAAI,YAAY,KACzB,sBAAsB,GAAG;AAE3B;;;;;;;;;;;;;;;;;AAkBA,SAAS,sBAAsB,KAA4C;CAC1E,MAAM,EAAE,MAAM,oBAAoB,kBAAkB,oBAAoB,EACvE,IACD,CAAC;CACD,IAAI,CAAC,WAAW,eAAe,GAAG,OAAO,KAAA;CAEzC,IAAI;CACJ,IAAI;EACH,SAAS,KAAK,MAAM,aAAa,iBAAiB,OAAO,CAAC;CAC3D,QAAQ;EACP;CACD;CAEA,IAAI,WAAW,QAAQ,OAAO,WAAW,YAAY,MAAM,QAAQ,MAAM,GACxE,OAAO,KAAA;CACR,OAAO,SAAU,OAAmC,YAAsB;AAC3E;AAEA,SAAS,SAAS,OAA+C;CAChE,IAAI,OAAO,UAAU,UAAU,OAAO,KAAA;CACtC,MAAM,UAAU,MAAM,KAAK;CAC3B,OAAO,YAAY,KAAK,KAAA,IAAY;AACrC"}
1
+ {"version":3,"file":"resolve-api-key.js","names":[],"sources":["../../../src/lib/cli/resolve-api-key.ts"],"sourcesContent":["import {\n\tdisplacedProfileWarning,\n\tselectCredential,\n} from \"../../_shared/auth_selection.js\";\nimport {\n\tinspectCredentials,\n\tinterpretCredentials,\n} from \"../../_shared/credentials.js\";\nimport { configDir, resolveConfigFile } from \"../../_shared/paths.js\";\nimport { DEFAULT_PROFILE, resolveProfile } from \"../../_shared/profiles.js\";\n\n/**\n * Resolve the Neon API key for a `neon-env` CLI invocation.\n *\n * Precedence is the `neon` CLI's, from the same module: **an explicit flag beats an ambient\n * environment variable.** `--api-key` and `--profile` together is an error; `--profile` beats\n * `NEON_API_KEY`; `--api-key` beats `NEON_PROFILE`; two ambient sources resolve to the key.\n *\n * Sharing that decision rather than restating it is the point. An earlier version of this file\n * checked `NEON_API_KEY` before the selected profile, so `NEON_API_KEY=… neon-env run --profile\n * work` silently used the wrong account — the very bug this feature fixes in `neon`.\n *\n * The CLI owns the resolution because `@neon/config` and `@neon/env`'s root export are\n * deliberately environment- and filesystem-agnostic: they accept an explicit `apiKey` and\n * nothing else, so the ambient sources a *user* expects have to be read out here.\n */\nexport function resolveApiKey(options: {\n\tapiKey?: string;\n\tprofile?: string;\n\tenv?: NodeJS.ProcessEnv;\n\t/** Where to say that an exported key displaced an exported profile. Defaults to stderr. */\n\twarn?: (message: string) => void;\n}): string | undefined {\n\tconst env = options.env ?? process.env;\n\tconst selection = selectCredential({\n\t\t...(options.apiKey !== undefined ? { apiKeyFlag: options.apiKey } : {}),\n\t\t...(options.profile !== undefined\n\t\t\t? { profileFlag: options.profile }\n\t\t\t: {}),\n\t\t...(env.NEON_API_KEY !== undefined\n\t\t\t? { apiKeyEnv: env.NEON_API_KEY }\n\t\t\t: {}),\n\t\t...(env.NEON_PROFILE !== undefined\n\t\t\t? { profileEnv: env.NEON_PROFILE }\n\t\t\t: {}),\n\t});\n\n\t// Sharing the decision is only half of it. The disclosure is the half that keeps a\n\t// displaced profile from being the original bug in a quieter form: `NEON_API_KEY=…\n\t// NEON_PROFILE=work neon-env run` legitimately uses the key, and saying nothing leaves the\n\t// user believing they ran as `work`. `neon` has warned here from the start; this did not.\n\tconst displaced = displacedProfileWarning(selection);\n\tif (displaced !== null) {\n\t\tconst warn =\n\t\t\toptions.warn ??\n\t\t\t((message: string) => process.stderr.write(`${message}\\n`));\n\t\twarn(displaced);\n\t}\n\n\tif (selection.source !== \"profile\") return selection.apiKey;\n\treturn readStoredCredential(selection, env);\n}\n\n/**\n * The credential stored for the selected profile.\n *\n * Two different situations, deliberately not merged. A **missing** credential under `DEFAULT` is\n * the ordinary not-signed-in state and resolves to no key; under a profile the user named it is\n * an error, because reporting a missing credential would hide that the real problem is the name\n * they typed. A **damaged** credential is always an error: the file is there, it is not an\n * absence, and no amount of signing in elsewhere explains it.\n */\nfunction readStoredCredential(\n\tselection: { profile: string; explicit: boolean },\n\tenv: NodeJS.ProcessEnv,\n): string | undefined {\n\tconst { profile, explicit } = selection;\n\t/**\n\t * An *absence* is only an error when the user named the profile. Not being signed in under\n\t * `DEFAULT` is the ordinary state, and the library's `PLATFORM_MISSING_API_KEY` says it\n\t * better than a stack trace.\n\t */\n\tconst absent = (reason: string): undefined => {\n\t\tif (explicit) throw new Error(reason);\n\t\treturn undefined;\n\t};\n\n\tlet path: string;\n\ttry {\n\t\tpath =\n\t\t\tprofile === DEFAULT_PROFILE\n\t\t\t\t? // Per *file*, so an install predating the rename still finds its\n\t\t\t\t\t// `credentials.json` in the legacy `neonctl` directory, in place.\n\t\t\t\t\tresolveConfigFile(\"credentials.json\", { env }).path\n\t\t\t\t: // From the config root, not from wherever `credentials.json` happens to live:\n\t\t\t\t\t// that file can still be in `neonctl/` while `profiles.json` is in `neon/`,\n\t\t\t\t\t// and deriving one from the other loses every named profile.\n\t\t\t\t\tresolveProfile(configDir({ env }), profile).credentialsPath;\n\t} catch (err) {\n\t\t// An unknown profile name is a naming error whoever typed it can fix, so it is fatal\n\t\t// either way — it cannot be reported as \"not signed in\".\n\t\tthrow err instanceof Error ? err : new Error(String(err));\n\t}\n\n\tconst read = inspectCredentials(path);\n\tif (read.kind === \"absent\") {\n\t\treturn absent(\n\t\t\t`Profile \"${profile}\" has no stored credential at ${path}. Sign in with \\`neon profile create ${profile}\\`.`,\n\t\t);\n\t}\n\n\t// A file that exists but cannot be read is never an absence, named or not. Returning\n\t// `undefined` here reported \"no API key\" for a credential that is present and broken,\n\t// which sends the reader looking for a missing login instead of at the damaged file — and\n\t// under `neon` the same file is a hard error, so the two CLIs disagreed about it.\n\tif (read.kind === \"unusable\") {\n\t\tthrow new Error(\n\t\t\t`${read.reason}. Replace it deliberately with \\`neon profile create ${profile} --force\\`, or delete the file.`,\n\t\t);\n\t}\n\n\tconst credential = interpretCredentials(read.credentials, {\n\t\tpath,\n\t\tprofile,\n\t});\n\tif (credential.kind === \"api_key\") return credential.apiKey;\n\tconst token = read.credentials.access_token;\n\tif (typeof token === \"string\" && token.trim() !== \"\") return token.trim();\n\tthrow new Error(\n\t\t`Profile \"${profile}\" holds a browser sign-in with no usable token at ${path}. Sign in again with \\`neon auth --profile ${profile}\\`.`,\n\t);\n}\n\nexport { DEFAULT_PROFILE };\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AA0BA,SAAgB,cAAc,SAMP;CACtB,MAAM,MAAM,QAAQ,OAAO,QAAQ;CACnC,MAAM,YAAY,iBAAiB;EAClC,GAAI,QAAQ,WAAW,KAAA,IAAY,EAAE,YAAY,QAAQ,OAAO,IAAI,CAAC;EACrE,GAAI,QAAQ,YAAY,KAAA,IACrB,EAAE,aAAa,QAAQ,QAAQ,IAC/B,CAAC;EACJ,GAAI,IAAI,iBAAiB,KAAA,IACtB,EAAE,WAAW,IAAI,aAAa,IAC9B,CAAC;EACJ,GAAI,IAAI,iBAAiB,KAAA,IACtB,EAAE,YAAY,IAAI,aAAa,IAC/B,CAAC;CACL,CAAC;CAMD,MAAM,YAAY,wBAAwB,SAAS;CACnD,IAAI,cAAc,MAIjB,CAFC,QAAQ,UACN,YAAoB,QAAQ,OAAO,MAAM,GAAG,QAAQ,GAAG,GAAA,CACrD,SAAS;CAGf,IAAI,UAAU,WAAW,WAAW,OAAO,UAAU;CACrD,OAAO,qBAAqB,WAAW,GAAG;AAC3C;;;;;;;;;;AAWA,SAAS,qBACR,WACA,KACqB;CACrB,MAAM,EAAE,SAAS,aAAa;;;;;;CAM9B,MAAM,UAAU,WAA8B;EAC7C,IAAI,UAAU,MAAM,IAAI,MAAM,MAAM;CAErC;CAEA,IAAI;CACJ,IAAI;EACH,OACC,YAAA,YAGE,kBAAkB,oBAAoB,EAAE,IAAI,CAAC,CAAC,CAAC,OAI/C,eAAe,UAAU,EAAE,IAAI,CAAC,GAAG,OAAO,CAAC,CAAC;CAChD,SAAS,KAAK;EAGb,MAAM,eAAe,QAAQ,MAAM,IAAI,MAAM,OAAO,GAAG,CAAC;CACzD;CAEA,MAAM,OAAO,mBAAmB,IAAI;CACpC,IAAI,KAAK,SAAS,UACjB,OAAO,OACN,YAAY,QAAQ,gCAAgC,KAAK,uCAAuC,QAAQ,IACzG;CAOD,IAAI,KAAK,SAAS,YACjB,MAAM,IAAI,MACT,GAAG,KAAK,OAAO,uDAAuD,QAAQ,gCAC/E;CAGD,MAAM,aAAa,qBAAqB,KAAK,aAAa;EACzD;EACA;CACD,CAAC;CACD,IAAI,WAAW,SAAS,WAAW,OAAO,WAAW;CACrD,MAAM,QAAQ,KAAK,YAAY;CAC/B,IAAI,OAAO,UAAU,YAAY,MAAM,KAAK,MAAM,IAAI,OAAO,MAAM,KAAK;CACxE,MAAM,IAAI,MACT,YAAY,QAAQ,oDAAoD,KAAK,6CAA6C,QAAQ,IACnI;AACD"}
@@ -15,8 +15,8 @@ function resolveContext(options) {
15
15
  const projectId = nonEmpty(options.projectId) ?? nonEmpty(env.NEON_PROJECT_ID) ?? file?.projectId;
16
16
  const branch = nonEmpty(options.branch) ?? nonEmpty(env.NEON_BRANCH) ?? nonEmpty(env.NEON_BRANCH_ID) ?? file?.branch;
17
17
  const missing = [];
18
- if (!projectId) missing.push("project id — pass `--project-id`, set `NEON_PROJECT_ID`, or add `projectId` to `.neon` (run `npx neonctl link`).");
19
- if (!branch) missing.push("branch — pass `--branch`, set `NEON_BRANCH`/`NEON_BRANCH_ID`, or add `branch` to `.neon` (run `npx neonctl link` / `neonctl checkout <branch>`).");
18
+ if (!projectId) missing.push("project id — pass `--project-id`, set `NEON_PROJECT_ID`, or add `projectId` to `.neon` (run `npx neon link`).");
19
+ if (!branch) missing.push("branch — pass `--branch`, set `NEON_BRANCH`/`NEON_BRANCH_ID`, or add `branch` to `.neon` (run `npx neon link` / `neon checkout <branch>`).");
20
20
  if (!projectId || !branch) return {
21
21
  ok: false,
22
22
  missing
@@ -1 +1 @@
1
- {"version":3,"file":"resolve-context.js","names":[],"sources":["../../../src/lib/cli/resolve-context.ts"],"sourcesContent":["import { existsSync, readFileSync, statSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { dirname, resolve } from \"node:path\";\n\n/**\n * Resolved project + branch context for the `neon-env` CLI. The CLI owns this resolution\n * (flags → `NEON_*` env → `.neon[/project.json]` file) so the `@neon/env` library\n * functions can stay filesystem- and env-agnostic.\n */\nexport interface ResolvedContext {\n\tprojectId: string;\n\t/** Branch ref — a name (preferred for readability) or an id (`br-…`). */\n\tbranch: string;\n}\n\nexport interface ResolveContextOptions {\n\tprojectId?: string;\n\tbranch?: string;\n\tcwd: string;\n\tenv?: NodeJS.ProcessEnv;\n}\n\n/**\n * Resolve `projectId` and `branch` for a CLI invocation. Precedence (each wins over the\n * next): explicit flag → `NEON_*` env var → `.neon[/project.json]` walked up from `cwd`.\n *\n * Returns the resolved values plus a list of human-readable reasons for any field that\n * could not be resolved (so the caller can render one combined error).\n */\nexport function resolveContext(\n\toptions: ResolveContextOptions,\n): { ok: true; context: ResolvedContext } | { ok: false; missing: string[] } {\n\tconst env = options.env ?? process.env;\n\tconst file = findNeonFile(options.cwd);\n\n\tconst projectId =\n\t\tnonEmpty(options.projectId) ??\n\t\tnonEmpty(env.NEON_PROJECT_ID) ??\n\t\tfile?.projectId;\n\n\t// A branch ref — name (preferred) or id. `NEON_BRANCH` carries the name; `NEON_BRANCH_ID`\n\t// is the legacy id-only var. The `.neon` file pins `branch` (name) via `neonctl link`,\n\t// with legacy `branchId` still honored. fetchEnv resolves either form by name or id.\n\tconst branch =\n\t\tnonEmpty(options.branch) ??\n\t\tnonEmpty(env.NEON_BRANCH) ??\n\t\tnonEmpty(env.NEON_BRANCH_ID) ??\n\t\tfile?.branch;\n\n\tconst missing: string[] = [];\n\tif (!projectId) {\n\t\tmissing.push(\n\t\t\t\"project id — pass `--project-id`, set `NEON_PROJECT_ID`, or add `projectId` to `.neon` (run `npx neonctl link`).\",\n\t\t);\n\t}\n\tif (!branch) {\n\t\tmissing.push(\n\t\t\t\"branch — pass `--branch`, set `NEON_BRANCH`/`NEON_BRANCH_ID`, or add `branch` to `.neon` (run `npx neonctl link` / `neonctl checkout <branch>`).\",\n\t\t);\n\t}\n\tif (!projectId || !branch) return { ok: false, missing };\n\n\treturn {\n\t\tok: true,\n\t\tcontext: { projectId, branch },\n\t};\n}\n\ninterface NeonFile {\n\tprojectId?: string;\n\t/** Branch ref — name (preferred) or id. Reads `branch`, falling back to legacy `branchId`. */\n\tbranch?: string;\n}\n\n/**\n * Walk up from `cwd` looking for `.neon/project.json` (preferred) or `.neon` (neonctl\n * convention). Stops at the first `.git` directory or the home directory. Read-only.\n */\nfunction findNeonFile(cwd: string): NeonFile | null {\n\tlet current = resolve(cwd);\n\tconst stop = resolve(homedir());\n\tlet lastSeen: string | null = null;\n\n\twhile (true) {\n\t\tconst parsed =\n\t\t\treadNeonFileAt(resolve(current, \".neon\", \"project.json\")) ??\n\t\t\treadNeonFileAt(resolve(current, \".neon\"));\n\t\tif (parsed) return parsed;\n\n\t\tif (current === stop) return null;\n\t\tif (existsSync(resolve(current, \".git\"))) return null;\n\n\t\tconst parent = dirname(current);\n\t\tif (parent === current || parent === lastSeen) return null;\n\t\tlastSeen = current;\n\t\tcurrent = parent;\n\t}\n}\n\nfunction readNeonFileAt(path: string): NeonFile | null {\n\tif (!isFile(path)) return null;\n\tlet raw: string;\n\ttry {\n\t\traw = readFileSync(path, \"utf-8\");\n\t} catch {\n\t\treturn null;\n\t}\n\tlet parsed: unknown;\n\ttry {\n\t\tparsed = JSON.parse(raw);\n\t} catch {\n\t\treturn null;\n\t}\n\tif (parsed === null || typeof parsed !== \"object\" || Array.isArray(parsed))\n\t\treturn null;\n\tconst obj = parsed as Record<string, unknown>;\n\tconst out: NeonFile = {};\n\tif (typeof obj.projectId === \"string\" && obj.projectId !== \"\")\n\t\tout.projectId = obj.projectId;\n\t// Prefer the `branch` field (name or id, written by `neonctl link`); fall back to the\n\t// legacy id-only `branchId`.\n\tconst branch =\n\t\ttypeof obj.branch === \"string\" && obj.branch !== \"\"\n\t\t\t? obj.branch\n\t\t\t: typeof obj.branchId === \"string\" && obj.branchId !== \"\"\n\t\t\t\t? obj.branchId\n\t\t\t\t: undefined;\n\tif (branch) out.branch = branch;\n\treturn out;\n}\n\nfunction isFile(path: string): boolean {\n\ttry {\n\t\treturn statSync(path).isFile();\n\t} catch {\n\t\treturn false;\n\t}\n}\n\nfunction nonEmpty(value: string | undefined): string | undefined {\n\tif (typeof value !== \"string\") return undefined;\n\tconst trimmed = value.trim();\n\treturn trimmed === \"\" ? undefined : trimmed;\n}\n"],"mappings":";;;;;;;;;;;AA6BA,SAAgB,eACf,SAC4E;CAC5E,MAAM,MAAM,QAAQ,OAAO,QAAQ;CACnC,MAAM,OAAO,aAAa,QAAQ,GAAG;CAErC,MAAM,YACL,SAAS,QAAQ,SAAS,KAC1B,SAAS,IAAI,eAAe,KAC5B,MAAM;CAKP,MAAM,SACL,SAAS,QAAQ,MAAM,KACvB,SAAS,IAAI,WAAW,KACxB,SAAS,IAAI,cAAc,KAC3B,MAAM;CAEP,MAAM,UAAoB,CAAC;CAC3B,IAAI,CAAC,WACJ,QAAQ,KACP,kHACD;CAED,IAAI,CAAC,QACJ,QAAQ,KACP,kJACD;CAED,IAAI,CAAC,aAAa,CAAC,QAAQ,OAAO;EAAE,IAAI;EAAO;CAAQ;CAEvD,OAAO;EACN,IAAI;EACJ,SAAS;GAAE;GAAW;EAAO;CAC9B;AACD;;;;;AAYA,SAAS,aAAa,KAA8B;CACnD,IAAI,UAAU,QAAQ,GAAG;CACzB,MAAM,OAAO,QAAQ,QAAQ,CAAC;CAC9B,IAAI,WAA0B;CAE9B,OAAO,MAAM;EACZ,MAAM,SACL,eAAe,QAAQ,SAAS,SAAS,cAAc,CAAC,KACxD,eAAe,QAAQ,SAAS,OAAO,CAAC;EACzC,IAAI,QAAQ,OAAO;EAEnB,IAAI,YAAY,MAAM,OAAO;EAC7B,IAAI,WAAW,QAAQ,SAAS,MAAM,CAAC,GAAG,OAAO;EAEjD,MAAM,SAAS,QAAQ,OAAO;EAC9B,IAAI,WAAW,WAAW,WAAW,UAAU,OAAO;EACtD,WAAW;EACX,UAAU;CACX;AACD;AAEA,SAAS,eAAe,MAA+B;CACtD,IAAI,CAAC,OAAO,IAAI,GAAG,OAAO;CAC1B,IAAI;CACJ,IAAI;EACH,MAAM,aAAa,MAAM,OAAO;CACjC,QAAQ;EACP,OAAO;CACR;CACA,IAAI;CACJ,IAAI;EACH,SAAS,KAAK,MAAM,GAAG;CACxB,QAAQ;EACP,OAAO;CACR;CACA,IAAI,WAAW,QAAQ,OAAO,WAAW,YAAY,MAAM,QAAQ,MAAM,GACxE,OAAO;CACR,MAAM,MAAM;CACZ,MAAM,MAAgB,CAAC;CACvB,IAAI,OAAO,IAAI,cAAc,YAAY,IAAI,cAAc,IAC1D,IAAI,YAAY,IAAI;CAGrB,MAAM,SACL,OAAO,IAAI,WAAW,YAAY,IAAI,WAAW,KAC9C,IAAI,SACJ,OAAO,IAAI,aAAa,YAAY,IAAI,aAAa,KACpD,IAAI,WACJ,KAAA;CACL,IAAI,QAAQ,IAAI,SAAS;CACzB,OAAO;AACR;AAEA,SAAS,OAAO,MAAuB;CACtC,IAAI;EACH,OAAO,SAAS,IAAI,CAAC,CAAC,OAAO;CAC9B,QAAQ;EACP,OAAO;CACR;AACD;AAEA,SAAS,SAAS,OAA+C;CAChE,IAAI,OAAO,UAAU,UAAU,OAAO,KAAA;CACtC,MAAM,UAAU,MAAM,KAAK;CAC3B,OAAO,YAAY,KAAK,KAAA,IAAY;AACrC"}
1
+ {"version":3,"file":"resolve-context.js","names":[],"sources":["../../../src/lib/cli/resolve-context.ts"],"sourcesContent":["import { existsSync, readFileSync, statSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { dirname, resolve } from \"node:path\";\n\n/**\n * Resolved project + branch context for the `neon-env` CLI. The CLI owns this resolution\n * (flags → `NEON_*` env → `.neon[/project.json]` file) so the `@neon/env` library\n * functions can stay filesystem- and env-agnostic.\n */\nexport interface ResolvedContext {\n\tprojectId: string;\n\t/** Branch ref — a name (preferred for readability) or an id (`br-…`). */\n\tbranch: string;\n}\n\nexport interface ResolveContextOptions {\n\tprojectId?: string;\n\tbranch?: string;\n\tcwd: string;\n\tenv?: NodeJS.ProcessEnv;\n}\n\n/**\n * Resolve `projectId` and `branch` for a CLI invocation. Precedence (each wins over the\n * next): explicit flag → `NEON_*` env var → `.neon[/project.json]` walked up from `cwd`.\n *\n * Returns the resolved values plus a list of human-readable reasons for any field that\n * could not be resolved (so the caller can render one combined error).\n */\nexport function resolveContext(\n\toptions: ResolveContextOptions,\n): { ok: true; context: ResolvedContext } | { ok: false; missing: string[] } {\n\tconst env = options.env ?? process.env;\n\tconst file = findNeonFile(options.cwd);\n\n\tconst projectId =\n\t\tnonEmpty(options.projectId) ??\n\t\tnonEmpty(env.NEON_PROJECT_ID) ??\n\t\tfile?.projectId;\n\n\t// A branch ref — name (preferred) or id. `NEON_BRANCH` carries the name; `NEON_BRANCH_ID`\n\t// is the legacy id-only var. The `.neon` file pins `branch` (name) via `neonctl link`,\n\t// with legacy `branchId` still honored. fetchEnv resolves either form by name or id.\n\tconst branch =\n\t\tnonEmpty(options.branch) ??\n\t\tnonEmpty(env.NEON_BRANCH) ??\n\t\tnonEmpty(env.NEON_BRANCH_ID) ??\n\t\tfile?.branch;\n\n\tconst missing: string[] = [];\n\tif (!projectId) {\n\t\tmissing.push(\n\t\t\t\"project id — pass `--project-id`, set `NEON_PROJECT_ID`, or add `projectId` to `.neon` (run `npx neon link`).\",\n\t\t);\n\t}\n\tif (!branch) {\n\t\tmissing.push(\n\t\t\t\"branch — pass `--branch`, set `NEON_BRANCH`/`NEON_BRANCH_ID`, or add `branch` to `.neon` (run `npx neon link` / `neon checkout <branch>`).\",\n\t\t);\n\t}\n\tif (!projectId || !branch) return { ok: false, missing };\n\n\treturn {\n\t\tok: true,\n\t\tcontext: { projectId, branch },\n\t};\n}\n\ninterface NeonFile {\n\tprojectId?: string;\n\t/** Branch ref — name (preferred) or id. Reads `branch`, falling back to legacy `branchId`. */\n\tbranch?: string;\n}\n\n/**\n * Walk up from `cwd` looking for `.neon/project.json` (preferred) or `.neon` (neonctl\n * convention). Stops at the first `.git` directory or the home directory. Read-only.\n */\nfunction findNeonFile(cwd: string): NeonFile | null {\n\tlet current = resolve(cwd);\n\tconst stop = resolve(homedir());\n\tlet lastSeen: string | null = null;\n\n\twhile (true) {\n\t\tconst parsed =\n\t\t\treadNeonFileAt(resolve(current, \".neon\", \"project.json\")) ??\n\t\t\treadNeonFileAt(resolve(current, \".neon\"));\n\t\tif (parsed) return parsed;\n\n\t\tif (current === stop) return null;\n\t\tif (existsSync(resolve(current, \".git\"))) return null;\n\n\t\tconst parent = dirname(current);\n\t\tif (parent === current || parent === lastSeen) return null;\n\t\tlastSeen = current;\n\t\tcurrent = parent;\n\t}\n}\n\nfunction readNeonFileAt(path: string): NeonFile | null {\n\tif (!isFile(path)) return null;\n\tlet raw: string;\n\ttry {\n\t\traw = readFileSync(path, \"utf-8\");\n\t} catch {\n\t\treturn null;\n\t}\n\tlet parsed: unknown;\n\ttry {\n\t\tparsed = JSON.parse(raw);\n\t} catch {\n\t\treturn null;\n\t}\n\tif (parsed === null || typeof parsed !== \"object\" || Array.isArray(parsed))\n\t\treturn null;\n\tconst obj = parsed as Record<string, unknown>;\n\tconst out: NeonFile = {};\n\tif (typeof obj.projectId === \"string\" && obj.projectId !== \"\")\n\t\tout.projectId = obj.projectId;\n\t// Prefer the `branch` field (name or id, written by `neonctl link`); fall back to the\n\t// legacy id-only `branchId`.\n\tconst branch =\n\t\ttypeof obj.branch === \"string\" && obj.branch !== \"\"\n\t\t\t? obj.branch\n\t\t\t: typeof obj.branchId === \"string\" && obj.branchId !== \"\"\n\t\t\t\t? obj.branchId\n\t\t\t\t: undefined;\n\tif (branch) out.branch = branch;\n\treturn out;\n}\n\nfunction isFile(path: string): boolean {\n\ttry {\n\t\treturn statSync(path).isFile();\n\t} catch {\n\t\treturn false;\n\t}\n}\n\nfunction nonEmpty(value: string | undefined): string | undefined {\n\tif (typeof value !== \"string\") return undefined;\n\tconst trimmed = value.trim();\n\treturn trimmed === \"\" ? undefined : trimmed;\n}\n"],"mappings":";;;;;;;;;;;AA6BA,SAAgB,eACf,SAC4E;CAC5E,MAAM,MAAM,QAAQ,OAAO,QAAQ;CACnC,MAAM,OAAO,aAAa,QAAQ,GAAG;CAErC,MAAM,YACL,SAAS,QAAQ,SAAS,KAC1B,SAAS,IAAI,eAAe,KAC5B,MAAM;CAKP,MAAM,SACL,SAAS,QAAQ,MAAM,KACvB,SAAS,IAAI,WAAW,KACxB,SAAS,IAAI,cAAc,KAC3B,MAAM;CAEP,MAAM,UAAoB,CAAC;CAC3B,IAAI,CAAC,WACJ,QAAQ,KACP,+GACD;CAED,IAAI,CAAC,QACJ,QAAQ,KACP,4IACD;CAED,IAAI,CAAC,aAAa,CAAC,QAAQ,OAAO;EAAE,IAAI;EAAO;CAAQ;CAEvD,OAAO;EACN,IAAI;EACJ,SAAS;GAAE;GAAW;EAAO;CAC9B;AACD;;;;;AAYA,SAAS,aAAa,KAA8B;CACnD,IAAI,UAAU,QAAQ,GAAG;CACzB,MAAM,OAAO,QAAQ,QAAQ,CAAC;CAC9B,IAAI,WAA0B;CAE9B,OAAO,MAAM;EACZ,MAAM,SACL,eAAe,QAAQ,SAAS,SAAS,cAAc,CAAC,KACxD,eAAe,QAAQ,SAAS,OAAO,CAAC;EACzC,IAAI,QAAQ,OAAO;EAEnB,IAAI,YAAY,MAAM,OAAO;EAC7B,IAAI,WAAW,QAAQ,SAAS,MAAM,CAAC,GAAG,OAAO;EAEjD,MAAM,SAAS,QAAQ,OAAO;EAC9B,IAAI,WAAW,WAAW,WAAW,UAAU,OAAO;EACtD,WAAW;EACX,UAAU;CACX;AACD;AAEA,SAAS,eAAe,MAA+B;CACtD,IAAI,CAAC,OAAO,IAAI,GAAG,OAAO;CAC1B,IAAI;CACJ,IAAI;EACH,MAAM,aAAa,MAAM,OAAO;CACjC,QAAQ;EACP,OAAO;CACR;CACA,IAAI;CACJ,IAAI;EACH,SAAS,KAAK,MAAM,GAAG;CACxB,QAAQ;EACP,OAAO;CACR;CACA,IAAI,WAAW,QAAQ,OAAO,WAAW,YAAY,MAAM,QAAQ,MAAM,GACxE,OAAO;CACR,MAAM,MAAM;CACZ,MAAM,MAAgB,CAAC;CACvB,IAAI,OAAO,IAAI,cAAc,YAAY,IAAI,cAAc,IAC1D,IAAI,YAAY,IAAI;CAGrB,MAAM,SACL,OAAO,IAAI,WAAW,YAAY,IAAI,WAAW,KAC9C,IAAI,SACJ,OAAO,IAAI,aAAa,YAAY,IAAI,aAAa,KACpD,IAAI,WACJ,KAAA;CACL,IAAI,QAAQ,IAAI,SAAS;CACzB,OAAO;AACR;AAEA,SAAS,OAAO,MAAuB;CACtC,IAAI;EACH,OAAO,SAAS,IAAI,CAAC,CAAC,OAAO;CAC9B,QAAQ;EACP,OAAO;CACR;AACD;AAEA,SAAS,SAAS,OAA+C;CAChE,IAAI,OAAO,UAAU,UAAU,OAAO,KAAA;CACtC,MAAM,UAAU,MAAM,KAAK;CAC3B,OAAO,YAAY,KAAK,KAAA,IAAY;AACrC"}
package/dist/lib/env.js CHANGED
@@ -120,7 +120,7 @@ async function fetchEnvKeys(config, options, keys) {
120
120
  if (Object.keys(postgres).length > 0) result.postgres = postgres;
121
121
  if (wants(K.branch.name)) result.branch = { name: branch.name };
122
122
  if (wantsAuth) {
123
- if (!authSnapshot) throw new PlatformError(ErrorCode.NotFound, [`fetchEnv: branch policy enables auth but no Neon Auth integration is enabled on branch ${branch.name} (${branch.id}).`, "Enable it via `apply(config, { projectId, branchId })` (or `npx neonctl …`), in the Neon Console — then re-run fetchEnv. Or return auth.enabled=false."].join(" "), { details: {
123
+ if (!authSnapshot) throw new PlatformError(ErrorCode.NotFound, [`fetchEnv: branch policy enables auth but no Neon Auth integration is enabled on branch ${branch.name} (${branch.id}).`, "Enable it via `apply(config, { projectId, branchId })` (or `npx neon …`), in the Neon Console — then re-run fetchEnv. Or return auth.enabled=false."].join(" "), { details: {
124
124
  projectId,
125
125
  branchId: branch.id
126
126
  } });