@neondatabase/config 0.4.1 → 0.4.2
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/lib/auth.d.ts +4 -0
- package/dist/lib/auth.d.ts.map +1 -1
- package/dist/lib/auth.js +15 -1
- package/dist/lib/auth.js.map +1 -1
- package/package.json +1 -1
package/dist/lib/auth.d.ts
CHANGED
|
@@ -50,6 +50,9 @@ declare function resolveApiKey(options?: {
|
|
|
50
50
|
* `~/.config/neonctl/credentials.json`) and construct a real {@link NeonApi} adapter from
|
|
51
51
|
* it, or throw a uniform `PLATFORM_MISSING_API_KEY` error if no key can be found.
|
|
52
52
|
*
|
|
53
|
+
* The API host is resolved via: `options.apiHost` → `NEON_API_HOST` env → production
|
|
54
|
+
* default (`https://console.neon.tech/api/v2`).
|
|
55
|
+
*
|
|
53
56
|
* Used by `pullConfig`, `pushConfig`, `fetchEnv`, and `branch` to build their default
|
|
54
57
|
* `NeonApi` when the caller doesn't inject one. `operation` is the calling function's
|
|
55
58
|
* name (e.g. `"pushConfig"`, `"branch"`) — it's prepended to the error message so users
|
|
@@ -57,6 +60,7 @@ declare function resolveApiKey(options?: {
|
|
|
57
60
|
*/
|
|
58
61
|
declare function createNeonApiFromOptions(operation: string, options?: {
|
|
59
62
|
apiKey?: string;
|
|
63
|
+
apiHost?: string;
|
|
60
64
|
}): NeonApi;
|
|
61
65
|
//#endregion
|
|
62
66
|
export { NeonctlCredentials, createNeonApiFromOptions, readNeonctlCredentials, resolveApiKey };
|
package/dist/lib/auth.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.d.ts","names":[],"sources":["../../src/lib/auth.ts"],"mappings":";;;;;;AAWA;AAkBA;AA8CA;
|
|
1
|
+
{"version":3,"file":"auth.d.ts","names":[],"sources":["../../src/lib/auth.ts"],"mappings":";;;;;;AAWA;AAkBA;AA8CA;AAsCgB,UAtGC,kBAAA,CAsGuB;;;;;;;;;;;;;;;;;iBApFxB,sBAAA;;IAEb;;;;;;;;;;;;iBA4Ca,aAAA;;;;;;;;;;;;;;;;;;;;iBAsCA,wBAAA;;;IAMb"}
|
package/dist/lib/auth.js
CHANGED
|
@@ -67,11 +67,19 @@ function resolveApiKey(options = {}) {
|
|
|
67
67
|
};
|
|
68
68
|
return null;
|
|
69
69
|
}
|
|
70
|
+
/** Trim trailing slashes and surrounding whitespace; treat empty as unset. */
|
|
71
|
+
function normalizeApiHost(url) {
|
|
72
|
+
const trimmed = url?.trim().replace(/\/+$/, "");
|
|
73
|
+
return trimmed ? trimmed : void 0;
|
|
74
|
+
}
|
|
70
75
|
/**
|
|
71
76
|
* Resolve the Neon API key via the standard chain (option → `NEON_API_KEY` env →
|
|
72
77
|
* `~/.config/neonctl/credentials.json`) and construct a real {@link NeonApi} adapter from
|
|
73
78
|
* it, or throw a uniform `PLATFORM_MISSING_API_KEY` error if no key can be found.
|
|
74
79
|
*
|
|
80
|
+
* The API host is resolved via: `options.apiHost` → `NEON_API_HOST` env → production
|
|
81
|
+
* default (`https://console.neon.tech/api/v2`).
|
|
82
|
+
*
|
|
75
83
|
* Used by `pullConfig`, `pushConfig`, `fetchEnv`, and `branch` to build their default
|
|
76
84
|
* `NeonApi` when the caller doesn't inject one. `operation` is the calling function's
|
|
77
85
|
* name (e.g. `"pushConfig"`, `"branch"`) — it's prepended to the error message so users
|
|
@@ -79,7 +87,13 @@ function resolveApiKey(options = {}) {
|
|
|
79
87
|
*/
|
|
80
88
|
function createNeonApiFromOptions(operation, options = {}) {
|
|
81
89
|
const resolved = resolveApiKey(options.apiKey ? { apiKey: options.apiKey } : {});
|
|
82
|
-
if (resolved)
|
|
90
|
+
if (resolved) {
|
|
91
|
+
const baseUrl = normalizeApiHost(options.apiHost) ?? normalizeApiHost(process.env.NEON_API_HOST);
|
|
92
|
+
return createRealNeonApi({
|
|
93
|
+
apiKey: resolved.token,
|
|
94
|
+
...baseUrl ? { baseUrl } : {}
|
|
95
|
+
});
|
|
96
|
+
}
|
|
83
97
|
throw new PlatformError(ErrorCode.MissingApiKey, [
|
|
84
98
|
`${operation} has no Neon API key to work with.`,
|
|
85
99
|
"Tried (in order): `apiKey` option, NEON_API_KEY env, and `~/.config/neonctl/credentials.json`.",
|
package/dist/lib/auth.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.js","names":[],"sources":["../../src/lib/auth.ts"],"sourcesContent":["import { existsSync, readFileSync } from \"node:fs\";\nimport { resolve } from \"node:path\";\nimport { ErrorCode, PlatformError } from \"./errors.js\";\nimport type { NeonApi } from \"./neon-api.js\";\nimport { createRealNeonApi } from \"./neon-api-real.js\";\n\n/**\n * Minimal shape of `~/.config/neonctl/credentials.json` we read. `neonctl` writes more\n * fields (refresh_token, expires_at, …) but only `access_token` is what we need — it's a\n * Bearer token the Neon API accepts on the same endpoints `napi_*` API keys do.\n */\nexport interface NeonctlCredentials {\n\taccess_token: string;\n\t[key: string]: unknown;\n}\n\n/**\n * Locate and read the OAuth credentials neonctl writes after `neon auth`.\n *\n * Resolution:\n * 1. `options.configDir` (explicit override — mirrors neonctl's `--config-dir` flag).\n * 2. `NEONCTL_CONFIG_DIR` environment variable.\n * 3. `<home>/.config/neonctl/credentials.json` (the neonctl default; `home` reads\n * `HOME`, falling back to `USERPROFILE` for Windows parity).\n *\n * Returns `null` (never throws) when the file is missing, unreadable, malformed, or has\n * no `access_token` — so callers can use this as a quiet fallback in a resolution chain\n * without try/catch noise.\n */\nexport function readNeonctlCredentials(\n\toptions: { configDir?: string } = {},\n): NeonctlCredentials | null {\n\tconst home = process.env.HOME ?? process.env.USERPROFILE;\n\tconst configDir =\n\t\toptions.configDir ??\n\t\tprocess.env.NEONCTL_CONFIG_DIR ??\n\t\t(home ? resolve(home, \".config\", \"neonctl\") : undefined);\n\tif (!configDir) return null;\n\n\tconst credentialsPath = resolve(configDir, \"credentials.json\");\n\tif (!existsSync(credentialsPath)) return null;\n\n\tlet raw: string;\n\ttry {\n\t\traw = readFileSync(credentialsPath, \"utf-8\");\n\t} catch {\n\t\treturn null;\n\t}\n\n\tlet parsed: unknown;\n\ttry {\n\t\tparsed = JSON.parse(raw);\n\t} catch {\n\t\treturn null;\n\t}\n\n\tif (parsed === null || typeof parsed !== \"object\" || Array.isArray(parsed))\n\t\treturn null;\n\tconst obj = parsed as Record<string, unknown>;\n\tif (typeof obj.access_token !== \"string\" || obj.access_token === \"\")\n\t\treturn null;\n\treturn obj as NeonctlCredentials;\n}\n\n/**\n * Resolution chain for the Bearer token sent to the Neon API. Each entry wins over the\n * next:\n *\n * 1. `options.apiKey` (explicit).\n * 2. `NEON_API_KEY` environment variable.\n * 3. `access_token` from `~/.config/neonctl/credentials.json` (or `NEONCTL_CONFIG_DIR`).\n *\n * Returns `null` when no source provides one. Callers wrap the null case in a\n * `PLATFORM_MISSING_API_KEY` error with a message tailored to the operation.\n */\nexport function resolveApiKey(\n\toptions: { apiKey?: string; configDir?: string } = {},\n): { token: string; source: \"option\" | \"env\" | \"neonctl\" } | null {\n\tif (options.apiKey && options.apiKey.trim() !== \"\") {\n\t\treturn { token: options.apiKey.trim(), source: \"option\" };\n\t}\n\tconst envKey = process.env.NEON_API_KEY;\n\tif (typeof envKey === \"string\" && envKey.trim() !== \"\") {\n\t\treturn { token: envKey.trim(), source: \"env\" };\n\t}\n\tconst creds = readNeonctlCredentials(\n\t\toptions.configDir ? { configDir: options.configDir } : {},\n\t);\n\tif (creds) {\n\t\treturn { token: creds.access_token, source: \"neonctl\" };\n\t}\n\treturn null;\n}\n\n/**\n * Resolve the Neon API key via the standard chain (option → `NEON_API_KEY` env →\n * `~/.config/neonctl/credentials.json`) and construct a real {@link NeonApi} adapter from\n * it, or throw a uniform `PLATFORM_MISSING_API_KEY` error if no key can be found.\n *\n * Used by `pullConfig`, `pushConfig`, `fetchEnv`, and `branch` to build their default\n * `NeonApi` when the caller doesn't inject one. `operation` is the calling function's\n * name (e.g. `\"pushConfig\"`, `\"branch\"`) — it's prepended to the error message so users\n * can tell which call surfaced the missing key.\n */\nexport function createNeonApiFromOptions(\n\toperation: string,\n\toptions: {\n\t\tapiKey?: string;\n\t} = {},\n): NeonApi {\n\tconst resolved = resolveApiKey(\n\t\toptions.apiKey ? { apiKey: options.apiKey } : {},\n\t);\n\tif (resolved)
|
|
1
|
+
{"version":3,"file":"auth.js","names":[],"sources":["../../src/lib/auth.ts"],"sourcesContent":["import { existsSync, readFileSync } from \"node:fs\";\nimport { resolve } from \"node:path\";\nimport { ErrorCode, PlatformError } from \"./errors.js\";\nimport type { NeonApi } from \"./neon-api.js\";\nimport { createRealNeonApi } from \"./neon-api-real.js\";\n\n/**\n * Minimal shape of `~/.config/neonctl/credentials.json` we read. `neonctl` writes more\n * fields (refresh_token, expires_at, …) but only `access_token` is what we need — it's a\n * Bearer token the Neon API accepts on the same endpoints `napi_*` API keys do.\n */\nexport interface NeonctlCredentials {\n\taccess_token: string;\n\t[key: string]: unknown;\n}\n\n/**\n * Locate and read the OAuth credentials neonctl writes after `neon auth`.\n *\n * Resolution:\n * 1. `options.configDir` (explicit override — mirrors neonctl's `--config-dir` flag).\n * 2. `NEONCTL_CONFIG_DIR` environment variable.\n * 3. `<home>/.config/neonctl/credentials.json` (the neonctl default; `home` reads\n * `HOME`, falling back to `USERPROFILE` for Windows parity).\n *\n * Returns `null` (never throws) when the file is missing, unreadable, malformed, or has\n * no `access_token` — so callers can use this as a quiet fallback in a resolution chain\n * without try/catch noise.\n */\nexport function readNeonctlCredentials(\n\toptions: { configDir?: string } = {},\n): NeonctlCredentials | null {\n\tconst home = process.env.HOME ?? process.env.USERPROFILE;\n\tconst configDir =\n\t\toptions.configDir ??\n\t\tprocess.env.NEONCTL_CONFIG_DIR ??\n\t\t(home ? resolve(home, \".config\", \"neonctl\") : undefined);\n\tif (!configDir) return null;\n\n\tconst credentialsPath = resolve(configDir, \"credentials.json\");\n\tif (!existsSync(credentialsPath)) return null;\n\n\tlet raw: string;\n\ttry {\n\t\traw = readFileSync(credentialsPath, \"utf-8\");\n\t} catch {\n\t\treturn null;\n\t}\n\n\tlet parsed: unknown;\n\ttry {\n\t\tparsed = JSON.parse(raw);\n\t} catch {\n\t\treturn null;\n\t}\n\n\tif (parsed === null || typeof parsed !== \"object\" || Array.isArray(parsed))\n\t\treturn null;\n\tconst obj = parsed as Record<string, unknown>;\n\tif (typeof obj.access_token !== \"string\" || obj.access_token === \"\")\n\t\treturn null;\n\treturn obj as NeonctlCredentials;\n}\n\n/**\n * Resolution chain for the Bearer token sent to the Neon API. Each entry wins over the\n * next:\n *\n * 1. `options.apiKey` (explicit).\n * 2. `NEON_API_KEY` environment variable.\n * 3. `access_token` from `~/.config/neonctl/credentials.json` (or `NEONCTL_CONFIG_DIR`).\n *\n * Returns `null` when no source provides one. Callers wrap the null case in a\n * `PLATFORM_MISSING_API_KEY` error with a message tailored to the operation.\n */\nexport function resolveApiKey(\n\toptions: { apiKey?: string; configDir?: string } = {},\n): { token: string; source: \"option\" | \"env\" | \"neonctl\" } | null {\n\tif (options.apiKey && options.apiKey.trim() !== \"\") {\n\t\treturn { token: options.apiKey.trim(), source: \"option\" };\n\t}\n\tconst envKey = process.env.NEON_API_KEY;\n\tif (typeof envKey === \"string\" && envKey.trim() !== \"\") {\n\t\treturn { token: envKey.trim(), source: \"env\" };\n\t}\n\tconst creds = readNeonctlCredentials(\n\t\toptions.configDir ? { configDir: options.configDir } : {},\n\t);\n\tif (creds) {\n\t\treturn { token: creds.access_token, source: \"neonctl\" };\n\t}\n\treturn null;\n}\n\n/** Trim trailing slashes and surrounding whitespace; treat empty as unset. */\nfunction normalizeApiHost(url: string | undefined): string | undefined {\n\tconst trimmed = url?.trim().replace(/\\/+$/, \"\");\n\treturn trimmed ? trimmed : undefined;\n}\n\n/**\n * Resolve the Neon API key via the standard chain (option → `NEON_API_KEY` env →\n * `~/.config/neonctl/credentials.json`) and construct a real {@link NeonApi} adapter from\n * it, or throw a uniform `PLATFORM_MISSING_API_KEY` error if no key can be found.\n *\n * The API host is resolved via: `options.apiHost` → `NEON_API_HOST` env → production\n * default (`https://console.neon.tech/api/v2`).\n *\n * Used by `pullConfig`, `pushConfig`, `fetchEnv`, and `branch` to build their default\n * `NeonApi` when the caller doesn't inject one. `operation` is the calling function's\n * name (e.g. `\"pushConfig\"`, `\"branch\"`) — it's prepended to the error message so users\n * can tell which call surfaced the missing key.\n */\nexport function createNeonApiFromOptions(\n\toperation: string,\n\toptions: {\n\t\tapiKey?: string;\n\t\tapiHost?: string;\n\t} = {},\n): NeonApi {\n\tconst resolved = resolveApiKey(\n\t\toptions.apiKey ? { apiKey: options.apiKey } : {},\n\t);\n\tif (resolved) {\n\t\tconst baseUrl =\n\t\t\tnormalizeApiHost(options.apiHost) ??\n\t\t\tnormalizeApiHost(process.env.NEON_API_HOST);\n\t\treturn createRealNeonApi({\n\t\t\tapiKey: resolved.token,\n\t\t\t...(baseUrl ? { baseUrl } : {}),\n\t\t});\n\t}\n\tthrow new PlatformError(\n\t\tErrorCode.MissingApiKey,\n\t\t[\n\t\t\t`${operation} has no Neon API key to work with.`,\n\t\t\t\"Tried (in order): `apiKey` option, NEON_API_KEY env, and `~/.config/neonctl/credentials.json`.\",\n\t\t\t\"Either pass `apiKey` directly, set NEON_API_KEY, run `npx neonctl auth` to populate the credentials file, or pass a custom `api` adapter (e.g. an in-memory fake for tests).\",\n\t\t\t\"Generate a key at https://console.neon.tech/app/settings/api-keys.\",\n\t\t].join(\" \"),\n\t);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AA6BA,SAAgB,uBACf,UAAkC,CAAC,GACP;CAC5B,MAAM,OAAO,QAAQ,IAAI,QAAQ,QAAQ,IAAI;CAC7C,MAAM,YACL,QAAQ,aACR,QAAQ,IAAI,uBACX,OAAO,QAAQ,MAAM,WAAW,SAAS,IAAI,KAAA;CAC/C,IAAI,CAAC,WAAW,OAAO;CAEvB,MAAM,kBAAkB,QAAQ,WAAW,kBAAkB;CAC7D,IAAI,CAAC,WAAW,eAAe,GAAG,OAAO;CAEzC,IAAI;CACJ,IAAI;EACH,MAAM,aAAa,iBAAiB,OAAO;CAC5C,QAAQ;EACP,OAAO;CACR;CAEA,IAAI;CACJ,IAAI;EACH,SAAS,KAAK,MAAM,GAAG;CACxB,QAAQ;EACP,OAAO;CACR;CAEA,IAAI,WAAW,QAAQ,OAAO,WAAW,YAAY,MAAM,QAAQ,MAAM,GACxE,OAAO;CACR,MAAM,MAAM;CACZ,IAAI,OAAO,IAAI,iBAAiB,YAAY,IAAI,iBAAiB,IAChE,OAAO;CACR,OAAO;AACR;;;;;;;;;;;;AAaA,SAAgB,cACf,UAAmD,CAAC,GACa;CACjE,IAAI,QAAQ,UAAU,QAAQ,OAAO,KAAK,MAAM,IAC/C,OAAO;EAAE,OAAO,QAAQ,OAAO,KAAK;EAAG,QAAQ;CAAS;CAEzD,MAAM,SAAS,QAAQ,IAAI;CAC3B,IAAI,OAAO,WAAW,YAAY,OAAO,KAAK,MAAM,IACnD,OAAO;EAAE,OAAO,OAAO,KAAK;EAAG,QAAQ;CAAM;CAE9C,MAAM,QAAQ,uBACb,QAAQ,YAAY,EAAE,WAAW,QAAQ,UAAU,IAAI,CAAC,CACzD;CACA,IAAI,OACH,OAAO;EAAE,OAAO,MAAM;EAAc,QAAQ;CAAU;CAEvD,OAAO;AACR;;AAGA,SAAS,iBAAiB,KAA6C;CACtE,MAAM,UAAU,KAAK,KAAK,EAAE,QAAQ,QAAQ,EAAE;CAC9C,OAAO,UAAU,UAAU,KAAA;AAC5B;;;;;;;;;;;;;;AAeA,SAAgB,yBACf,WACA,UAGI,CAAC,GACK;CACV,MAAM,WAAW,cAChB,QAAQ,SAAS,EAAE,QAAQ,QAAQ,OAAO,IAAI,CAAC,CAChD;CACA,IAAI,UAAU;EACb,MAAM,UACL,iBAAiB,QAAQ,OAAO,KAChC,iBAAiB,QAAQ,IAAI,aAAa;EAC3C,OAAO,kBAAkB;GACxB,QAAQ,SAAS;GACjB,GAAI,UAAU,EAAE,QAAQ,IAAI,CAAC;EAC9B,CAAC;CACF;CACA,MAAM,IAAI,cACT,UAAU,eACV;EACC,GAAG,UAAU;EACb;EACA;EACA;CACD,EAAE,KAAK,GAAG,CACX;AACD"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@neondatabase/config",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.2",
|
|
4
4
|
"description": "Config-as-Code for the Neon Platform. Define a `neon.ts` policy and inspect/diff/deploy it against the Neon API as plain TypeScript functions.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"neon",
|