@neondatabase/env 1.0.0 → 1.0.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.
Files changed (59) hide show
  1. package/README.md +13 -0
  2. package/dist/cli.js +971 -6
  3. package/dist/cli.js.map +1 -1
  4. package/dist/{_shared/env-core/env.js → env.js} +62 -41
  5. package/dist/env.js.map +1 -0
  6. package/dist/index.d.ts +528 -3
  7. package/dist/index.d.ts.map +1 -0
  8. package/dist/index.js +197 -2
  9. package/dist/{lib/parse-env.js.map → index.js.map} +1 -1
  10. package/package.json +8 -6
  11. package/dist/_shared/auth_selection.d.ts +0 -95
  12. package/dist/_shared/auth_selection.d.ts.map +0 -1
  13. package/dist/_shared/auth_selection.js +0 -85
  14. package/dist/_shared/auth_selection.js.map +0 -1
  15. package/dist/_shared/credentials.d.ts +0 -186
  16. package/dist/_shared/credentials.d.ts.map +0 -1
  17. package/dist/_shared/credentials.js +0 -190
  18. package/dist/_shared/credentials.js.map +0 -1
  19. package/dist/_shared/env-core/env.d.ts +0 -424
  20. package/dist/_shared/env-core/env.d.ts.map +0 -1
  21. package/dist/_shared/env-core/env.js.map +0 -1
  22. package/dist/_shared/env-core/reuse-secrets.d.ts +0 -95
  23. package/dist/_shared/env-core/reuse-secrets.d.ts.map +0 -1
  24. package/dist/_shared/env-core/reuse-secrets.js +0 -181
  25. package/dist/_shared/env-core/reuse-secrets.js.map +0 -1
  26. package/dist/_shared/paths.d.ts +0 -116
  27. package/dist/_shared/paths.d.ts.map +0 -1
  28. package/dist/_shared/paths.js +0 -153
  29. package/dist/_shared/paths.js.map +0 -1
  30. package/dist/_shared/profiles.d.ts +0 -145
  31. package/dist/_shared/profiles.d.ts.map +0 -1
  32. package/dist/_shared/profiles.js +0 -228
  33. package/dist/_shared/profiles.js.map +0 -1
  34. package/dist/_shared/secure_file.d.ts +0 -25
  35. package/dist/_shared/secure_file.d.ts.map +0 -1
  36. package/dist/_shared/secure_file.js +0 -43
  37. package/dist/_shared/secure_file.js.map +0 -1
  38. package/dist/config/dist/lib/define-config.d.ts +0 -20
  39. package/dist/config/dist/lib/define-config.d.ts.map +0 -1
  40. package/dist/config/dist/lib/neon-api.d.ts +0 -375
  41. package/dist/config/dist/lib/neon-api.d.ts.map +0 -1
  42. package/dist/config/dist/lib/types.d.ts +0 -603
  43. package/dist/config/dist/lib/types.d.ts.map +0 -1
  44. package/dist/config/dist/v1.d.ts +0 -5
  45. package/dist/lib/cli/commands.d.ts +0 -68
  46. package/dist/lib/cli/commands.d.ts.map +0 -1
  47. package/dist/lib/cli/commands.js +0 -233
  48. package/dist/lib/cli/commands.js.map +0 -1
  49. package/dist/lib/cli/resolve-api-key.d.ts +0 -29
  50. package/dist/lib/cli/resolve-api-key.d.ts.map +0 -1
  51. package/dist/lib/cli/resolve-api-key.js +0 -74
  52. package/dist/lib/cli/resolve-api-key.js.map +0 -1
  53. package/dist/lib/cli/resolve-context.d.ts +0 -34
  54. package/dist/lib/cli/resolve-context.d.ts.map +0 -1
  55. package/dist/lib/cli/resolve-context.js +0 -88
  56. package/dist/lib/cli/resolve-context.js.map +0 -1
  57. package/dist/lib/parse-env.d.ts +0 -95
  58. package/dist/lib/parse-env.d.ts.map +0 -1
  59. package/dist/lib/parse-env.js +0 -198
@@ -1,88 +0,0 @@
1
- import { existsSync, readFileSync, statSync } from "node:fs";
2
- import { dirname, resolve } from "node:path";
3
- import { homedir } from "node:os";
4
- //#region src/lib/cli/resolve-context.ts
5
- /**
6
- * Resolve `projectId` and `branch` for a CLI invocation. Precedence (each wins over the
7
- * next): explicit flag → `NEON_*` env var → `.neon[/project.json]` walked up from `cwd`.
8
- *
9
- * Returns the resolved values plus a list of human-readable reasons for any field that
10
- * could not be resolved (so the caller can render one combined error).
11
- */
12
- function resolveContext(options) {
13
- const env = options.env ?? process.env;
14
- const file = findNeonFile(options.cwd);
15
- const projectId = nonEmpty(options.projectId) ?? nonEmpty(env.NEON_PROJECT_ID) ?? file?.projectId;
16
- const branch = nonEmpty(options.branch) ?? nonEmpty(env.NEON_BRANCH) ?? nonEmpty(env.NEON_BRANCH_ID) ?? file?.branch;
17
- const missing = [];
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
- if (!projectId || !branch) return {
21
- ok: false,
22
- missing
23
- };
24
- return {
25
- ok: true,
26
- context: {
27
- projectId,
28
- branch
29
- }
30
- };
31
- }
32
- /**
33
- * Walk up from `cwd` looking for `.neon/project.json` (preferred) or `.neon` (neonctl
34
- * convention). Stops at the first `.git` directory or the home directory. Read-only.
35
- */
36
- function findNeonFile(cwd) {
37
- let current = resolve(cwd);
38
- const stop = resolve(homedir());
39
- let lastSeen = null;
40
- while (true) {
41
- const parsed = readNeonFileAt(resolve(current, ".neon", "project.json")) ?? readNeonFileAt(resolve(current, ".neon"));
42
- if (parsed) return parsed;
43
- if (current === stop) return null;
44
- if (existsSync(resolve(current, ".git"))) return null;
45
- const parent = dirname(current);
46
- if (parent === current || parent === lastSeen) return null;
47
- lastSeen = current;
48
- current = parent;
49
- }
50
- }
51
- function readNeonFileAt(path) {
52
- if (!isFile(path)) return null;
53
- let raw;
54
- try {
55
- raw = readFileSync(path, "utf-8");
56
- } catch {
57
- return null;
58
- }
59
- let parsed;
60
- try {
61
- parsed = JSON.parse(raw);
62
- } catch {
63
- return null;
64
- }
65
- if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) return null;
66
- const obj = parsed;
67
- const out = {};
68
- if (typeof obj.projectId === "string" && obj.projectId !== "") out.projectId = obj.projectId;
69
- const branch = typeof obj.branch === "string" && obj.branch !== "" ? obj.branch : typeof obj.branchId === "string" && obj.branchId !== "" ? obj.branchId : void 0;
70
- if (branch) out.branch = branch;
71
- return out;
72
- }
73
- function isFile(path) {
74
- try {
75
- return statSync(path).isFile();
76
- } catch {
77
- return false;
78
- }
79
- }
80
- function nonEmpty(value) {
81
- if (typeof value !== "string") return void 0;
82
- const trimmed = value.trim();
83
- return trimmed === "" ? void 0 : trimmed;
84
- }
85
- //#endregion
86
- export { resolveContext };
87
-
88
- //# sourceMappingURL=resolve-context.js.map
@@ -1 +0,0 @@
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"}
@@ -1,95 +0,0 @@
1
- import { Config } from "../config/dist/lib/types.js";
2
- import "../config/dist/v1.js";
3
- import { FilteredNeonEnv, NeonEnv, SelectableEnvKey } from "../_shared/env-core/env.js";
4
-
5
- //#region src/lib/parse-env.d.ts
6
-
7
- /** The static `preview.functions` record of a config, or an empty record when absent. */
8
- type PreviewFunctionsOf<C extends Config> = NonNullable<C["preview"]> extends {
9
- functions: infer F;
10
- } ? F : Record<never, never>;
11
- /** The declared function slugs of a config (record keys), as a string union. */
12
- type FunctionSlugOf<C extends Config> = Extract<keyof PreviewFunctionsOf<C>, string>;
13
- /**
14
- * Human-readable hint surfaced as the **expected type** of `parseEnv`'s `scope` argument when
15
- * the policy declares no functions at all. Without it the argument's expected type is the bare
16
- * `never` {@link FunctionSlugOf} yields, and TypeScript reports the opaque `Type '"x"' is not
17
- * assignable to type 'never'`; the literal turns that into a sentence naming the fix (and the
18
- * editor offers it as the single completion, so the empty completion list is explained rather
19
- * than just empty). Mirrors `NeonAuthRequiredHint` in `@neon/config`.
20
- */
21
- type NoFunctionScopeHint = "this policy declares no `preview.functions`, so there is no function scope to read. Declare the function in `neon.ts` first, or omit the scope to read the branch env";
22
- /**
23
- * The expected type of `parseEnv`'s function-slug `scope` argument: the caller's inferred slug
24
- * `S` normally, and the {@link NoFunctionScopeHint} message when the policy declares no
25
- * functions. Keeping `S` (rather than `FunctionSlugOf<C>`) in the enabled branch is what makes
26
- * the returned `function` namespace exact — it stays the one function's env keys instead of
27
- * widening to every declared function's.
28
- */
29
- type FunctionScopeField<C extends Config, S extends string> = [FunctionSlugOf<C>] extends [never] ? NoFunctionScopeHint : S;
30
- /** The declared env-var keys of one function `S`, as a string union. */
31
- type FunctionEnvKeysOf<C extends Config, S extends string> = S extends keyof PreviewFunctionsOf<C> ? NonNullable<PreviewFunctionsOf<C>[S]> extends {
32
- env: infer E;
33
- } ? Extract<keyof E, string> : never : never;
34
- /**
35
- * The extra `function` namespace added to `parseEnv`'s result when called with a function
36
- * slug scope: the declared env-var keys for that function, each resolved to a `string`.
37
- */
38
- type NeonFunctionEnv<C extends Config, S extends string> = {
39
- function: Record<FunctionEnvKeysOf<C, S>, string>;
40
- };
41
- /**
42
- * Synchronous, network-free counterpart to {@link fetchEnv}. Reads `process.env`, validates
43
- * the required Neon env vars with zod, and returns the same {@link NeonEnv} shape — so the
44
- * rest of your app touches `env.postgres.databaseUrl` instead of stringly-typed
45
- * `process.env.DATABASE_URL` lookups.
46
- *
47
- * Designed for the **"env-vars-already-injected"** path:
48
- * - You wrapped your dev command with `neon-env run -- <cmd>` or `neon dev`.
49
- * - Your platform (Vercel, Fly, Railway, …) injected the vars via its own integration.
50
- * - You are **inside a deployed Neon Function**, whose env was uploaded at `config apply`.
51
- *
52
- * Unlike the old API, `parseEnv` does **not** take a branch name: the secret set is now
53
- * static (top-level `config.auth` / `config.dataApi`), so it reads those directly without
54
- * evaluating the per-branch closure.
55
- *
56
- * The second argument is a **scope** or a **key filter**:
57
- * - omitted — *external* scope (app bootstrap, build scripts, your dev machine). Returns the
58
- * full `{ postgres, auth?, dataApi?, … }` the policy enables.
59
- * - a **function slug** (a key of `config.preview.functions`) — *function* scope: you are
60
- * running inside that function. Returns the same branch secrets **plus** a typed
61
- * `function` namespace with the function's declared env-var keys. The slug autocompletes
62
- * from the policy ({@link FunctionSlugOf}) and an undeclared one is a type error.
63
- * - an **array of OS-level env-var keys** (e.g. `["DATABASE_URL", "NEON_AUTH_BASE_URL"]`) —
64
- * *filtered* mode: only those vars are required and returned, as a narrowed namespaced
65
- * shape. The keys autocomplete from the policy ({@link SelectableEnvKey}), so you can only
66
- * pick vars the policy actually enables. Use this when a process needs just a subset (a
67
- * Next.js app that reads `DATABASE_URL` but not `DATABASE_URL_UNPOOLED`, say) and you don't
68
- * want `parseEnv` to throw over vars you never use.
69
- *
70
- * Throws `PlatformError(EnvNotInjected)` listing every missing/invalid var when the env
71
- * isn't fully populated, with a fix hint pointing back at `neon dev` / `neon-env run`.
72
- *
73
- * ```ts
74
- * import config from "../neon";
75
- * import { parseEnv } from "@neon/env";
76
- *
77
- * // External (app / build):
78
- * const env = parseEnv(config);
79
- * const db = drizzle(neon(env.postgres.databaseUrl), { schema });
80
- *
81
- * // Inside the "hello" function:
82
- * const env = parseEnv(config, "hello");
83
- * env.function.resendApiKey; // typed from hello's declared env keys
84
- *
85
- * // Filtered: only enforce + return the pooled URL.
86
- * const { postgres } = parseEnv(config, ["DATABASE_URL"]);
87
- * postgres.databaseUrl; // string — `databaseUrlUnpooled` is absent
88
- * ```
89
- */
90
- declare function parseEnv<const C extends Config>(config: C): NeonEnv<C>;
91
- declare function parseEnv<const C extends Config, const S extends FunctionSlugOf<C>>(config: C, scope: FunctionScopeField<C, S>): NeonEnv<C> & NeonFunctionEnv<C, S>;
92
- declare function parseEnv<const C extends Config, const K extends SelectableEnvKey<C>>(config: C, keys: readonly K[]): FilteredNeonEnv<K>;
93
- //#endregion
94
- export { FunctionSlugOf, NeonFunctionEnv, NoFunctionScopeHint, parseEnv };
95
- //# sourceMappingURL=parse-env.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"parse-env.d.ts","names":[],"sources":["../../src/lib/parse-env.ts"],"mappings":";;;;;;AAqCI;AAAM,KALL,kBAKK,CAAA,UALwB,MAKxB,CAAA,GAJT,WAIS,CAJG,CAIH,CAAA,SAAA,CAAA,CAAA,SAAA;EAGE,SAAA,EAAA,KAAc,EAAA;AAAA,CAAA,GAJtB,CAIsB,GAHtB,MAGsB,CAAA,KAAA,EAAA,KAAA,CAAA;AAAW;AACX,KADd,cACc,CAAA,UADW,MACX,CAAA,GADqB,OACrB,CAAA,MAAnB,kBAAmB,CAAA,CAAA,CAAA,EAAA,MAAA,CAAA;AAAnB;AADwC;AAAO;AAetD;AACyK;AASlJ;AAAW;AAClB;AAAf,KAXW,mBAAA,GAWX,uKAAA;AAEE;AACA;AAAC;AAAA;AAGkB;AACX;AAEP;AAAmC,KAVlC,kBAUkC,CAAA,UAVL,MAUK,EAAA,UAAA,MAAA,CAAA,GAAA,CATtC,cASmB,CATJ,CASI,CAAA,CACc,SAAA,CAAA,KAAA,CAAA,GAR/B,mBAQ+B,GAP/B,CAO+B;AAAnB;AAAsB,KAJhC,iBAIgC,CAAA,UAH1B,MAG0B,EAAA,UAAA,MAAA,CAAA,GADjC,CACiC,SAAA,MADjB,kBACiB,CADE,CACF,CAAA,GAAlC,WAAkC,CAAtB,kBAAsB,CAAH,CAAG,CAAA,CAAA,CAAA,CAAA,CAAA,SAAA;EAAlC,GAAA,EAAA,KAAA,EAAA;AACe,CAAA,GAAd,OAAc,CAAA,MAAA,CAAA,EAAA,MAAA,CAAA,GAAA,KAAA,GAAA,KAAA;AAAd;AAAO;AAQX;AAA2B;AAAW,KAA1B,eAA0B,CAAA,UAAA,MAAA,EAAA,UAAA,MAAA,CAAA,GAAA;EACF,QAAA,EAAzB,MAAyB,CAAlB,iBAAkB,CAAA,CAAA,EAAG,CAAH,CAAA,EAAA,MAAA,CAAA;AAAG,CAAA;AAArB;AAAP;AAAM;AAiIjB;AAAwB;AAAiB;AAAgB;AAAY;AAAR;AAAO;AASpE;AAAwB;AACP;AACe;AAAf;AAER;AACkB;AAAG;AAAtB;AACG;AAAR;AAA6B;AAAG;AAAnB;AAAe;AAC/B;AAAwB;AACP;AACiB;AAAjB;AACP;AAAkB;AAAsB;AAAhB;AAAe;;;;;;;;;;;;;;;iBAnBjC,yBAAyB,gBAAgB,IAAI,QAAQ;iBASrD,yBACC,wBACA,eAAe,YAEvB,UACD,mBAAmB,GAAG,KAC3B,QAAQ,KAAK,gBAAgB,GAAG;iBACnB,yBACC,wBACA,iBAAiB,YACxB,kBAAkB,MAAM,gBAAgB"}
@@ -1,198 +0,0 @@
1
- import { NEON_ENV_VAR_KEYS } from "../_shared/env-core/env.js";
2
- import { ErrorCode, PlatformError } from "@neon/config/v1";
3
- import { z } from "zod";
4
- //#region src/lib/parse-env.ts
5
- /**
6
- * `parseEnv` — the synchronous, network-free counterpart to `fetchEnv`: read the Neon env vars
7
- * already injected into `process.env`, validate them against the policy, and return them in the
8
- * same namespaced shape.
9
- *
10
- * Lives in this package rather than in `shared/env-core` because nothing else needs it. The
11
- * `neon` CLI resolves env from the API and injects it; it never reads it back. Keeping it here
12
- * also keeps `zod` out of the shared tree, and so out of every consumer that copies it.
13
- */
14
- /**
15
- * Per-namespace zod schemas. Each defines exactly the OS-level keys parsed from
16
- * `process.env` for its namespace. Keep in sync with {@link NEON_ENV_VAR_KEYS}.
17
- *
18
- * `z.string().url()` would be tighter than `min(1)` but Postgres URIs that include
19
- * URL-illegal characters in the password (rare but legal in Neon's connection-string
20
- * format) fail the WHATWG `URL` parse, so we settle for "non-empty string".
21
- */
22
- const postgresEnvSchema = z.object({
23
- DATABASE_URL: z.string({ message: "DATABASE_URL is missing" }).min(1, "DATABASE_URL must not be empty"),
24
- DATABASE_URL_UNPOOLED: z.string({ message: "DATABASE_URL_UNPOOLED is missing" }).min(1, "DATABASE_URL_UNPOOLED must not be empty")
25
- });
26
- const authEnvSchema = z.object({
27
- NEON_AUTH_BASE_URL: z.string({ message: "NEON_AUTH_BASE_URL is missing" }).min(1, "NEON_AUTH_BASE_URL must not be empty"),
28
- NEON_AUTH_JWKS_URL: z.string({ message: "NEON_AUTH_JWKS_URL is missing" }).min(1, "NEON_AUTH_JWKS_URL must not be empty")
29
- });
30
- const dataApiEnvSchema = z.object({ NEON_DATA_API_URL: z.string({ message: "NEON_DATA_API_URL is missing" }).min(1, "NEON_DATA_API_URL must not be empty") });
31
- const storageEnvSchema = z.object({
32
- AWS_ACCESS_KEY_ID: z.string({ message: "AWS_ACCESS_KEY_ID is missing" }).min(1, "AWS_ACCESS_KEY_ID must not be empty"),
33
- AWS_SECRET_ACCESS_KEY: z.string({ message: "AWS_SECRET_ACCESS_KEY is missing" }).min(1, "AWS_SECRET_ACCESS_KEY must not be empty"),
34
- AWS_ENDPOINT_URL_S3: z.string({ message: "AWS_ENDPOINT_URL_S3 is missing" }).min(1, "AWS_ENDPOINT_URL_S3 must not be empty"),
35
- AWS_REGION: z.string({ message: "AWS_REGION is missing" }).min(1, "AWS_REGION must not be empty")
36
- });
37
- const aiGatewayEnvSchema = z.object({
38
- NEON_AI_GATEWAY_TOKEN: z.string({ message: "NEON_AI_GATEWAY_TOKEN is missing" }).min(1, "NEON_AI_GATEWAY_TOKEN must not be empty"),
39
- NEON_AI_GATEWAY_BASE_URL: z.string({ message: "NEON_AI_GATEWAY_BASE_URL is missing" }).min(1, "NEON_AI_GATEWAY_BASE_URL must not be empty")
40
- });
41
- /** Whether a **static** policy declares object storage (`preview.buckets`). No network. */
42
- function configWantsStorage(config) {
43
- return Object.keys(config.preview?.buckets ?? {}).length > 0;
44
- }
45
- /** Whether a **static** policy enables the AI Gateway (`preview.aiGateway`). No network. */
46
- function configWantsAiGateway(config) {
47
- return isServiceEnabledInput(config.preview?.aiGateway);
48
- }
49
- /** Static-toggle helper mirroring `config`'s `isServiceEnabled` for the env reader. */
50
- function isServiceEnabledInput(toggle) {
51
- if (toggle === void 0) return false;
52
- if (typeof toggle === "boolean") return toggle;
53
- return toggle.enabled !== false;
54
- }
55
- function parseEnv(config, scopeOrKeys) {
56
- const source = process.env;
57
- if (Array.isArray(scopeOrKeys)) return parseFilteredEnv(source, scopeOrKeys);
58
- const scope = typeof scopeOrKeys === "string" ? scopeOrKeys : void 0;
59
- const issues = [];
60
- const result = {};
61
- const pg = postgresEnvSchema.safeParse({
62
- DATABASE_URL: source.DATABASE_URL,
63
- DATABASE_URL_UNPOOLED: source.DATABASE_URL_UNPOOLED
64
- });
65
- if (pg.success) result.postgres = {
66
- databaseUrl: pg.data.DATABASE_URL,
67
- databaseUrlUnpooled: pg.data.DATABASE_URL_UNPOOLED
68
- };
69
- else for (const issue of pg.error.issues) issues.push(issue.message);
70
- const branchName = source[NEON_ENV_VAR_KEYS.branch.name];
71
- if (branchName !== void 0 && branchName !== "") result.branch = { name: branchName };
72
- if (isServiceEnabledInput(config.auth)) {
73
- const auth = authEnvSchema.safeParse({
74
- NEON_AUTH_BASE_URL: source.NEON_AUTH_BASE_URL,
75
- NEON_AUTH_JWKS_URL: source.NEON_AUTH_JWKS_URL
76
- });
77
- if (auth.success) result.auth = {
78
- baseUrl: auth.data.NEON_AUTH_BASE_URL,
79
- jwksUrl: auth.data.NEON_AUTH_JWKS_URL
80
- };
81
- else for (const issue of auth.error.issues) issues.push(issue.message);
82
- }
83
- if (isServiceEnabledInput(config.dataApi)) {
84
- const dataApi = dataApiEnvSchema.safeParse({ NEON_DATA_API_URL: source.NEON_DATA_API_URL });
85
- if (dataApi.success) result.dataApi = { url: dataApi.data.NEON_DATA_API_URL };
86
- else for (const issue of dataApi.error.issues) issues.push(issue.message);
87
- }
88
- if (configWantsStorage(config)) {
89
- const storage = storageEnvSchema.safeParse({
90
- AWS_ACCESS_KEY_ID: source.AWS_ACCESS_KEY_ID,
91
- AWS_SECRET_ACCESS_KEY: source.AWS_SECRET_ACCESS_KEY,
92
- AWS_ENDPOINT_URL_S3: source.AWS_ENDPOINT_URL_S3,
93
- AWS_REGION: source.AWS_REGION
94
- });
95
- if (storage.success) result.storage = {
96
- accessKeyId: storage.data.AWS_ACCESS_KEY_ID,
97
- secretAccessKey: storage.data.AWS_SECRET_ACCESS_KEY,
98
- endpoint: storage.data.AWS_ENDPOINT_URL_S3,
99
- region: storage.data.AWS_REGION
100
- };
101
- else for (const issue of storage.error.issues) issues.push(issue.message);
102
- }
103
- if (configWantsAiGateway(config)) {
104
- const aiGateway = aiGatewayEnvSchema.safeParse({
105
- NEON_AI_GATEWAY_TOKEN: source.NEON_AI_GATEWAY_TOKEN,
106
- NEON_AI_GATEWAY_BASE_URL: source.NEON_AI_GATEWAY_BASE_URL
107
- });
108
- if (aiGateway.success) result.aiGateway = {
109
- apiKey: aiGateway.data.NEON_AI_GATEWAY_TOKEN,
110
- baseUrl: aiGateway.data.NEON_AI_GATEWAY_BASE_URL
111
- };
112
- else for (const issue of aiGateway.error.issues) issues.push(issue.message);
113
- }
114
- if (scope !== void 0) {
115
- const fn = config.preview?.functions?.[scope];
116
- if (!fn) throw new PlatformError(ErrorCode.EnvNotInjected, [`parseEnv: no function "${scope}" is declared in this policy's preview.functions.`, "Pass a declared function slug (or omit the scope to read external env)."].join("\n"), { details: { scope } });
117
- const envOut = {};
118
- for (const key of Object.keys(fn.env ?? {})) {
119
- const value = source[key];
120
- if (value === void 0) issues.push(`${key} is missing (function "${scope}")`);
121
- else envOut[key] = value;
122
- }
123
- result.function = envOut;
124
- }
125
- if (issues.length > 0) throw new PlatformError(ErrorCode.EnvNotInjected, [
126
- "parseEnv: the required Neon env variables are not present in process.env.",
127
- ...issues.map((i) => ` - ${i}`),
128
- "Inject them via one of:",
129
- " - `neon dev` / `neon-env run -- <your dev command>` (wraps the command with the vars injected)",
130
- " - your hosting platform's Neon integration (Vercel, Fly, Railway, …)",
131
- " - for the `function` namespace: deploy the function (`neon deploy` / `config apply`) so its env is uploaded.",
132
- "Or switch the call to `await fetchEnv(config, …)` if you're in a context that can do async I/O."
133
- ].join("\n"), { details: { missing: issues } });
134
- return result;
135
- }
136
- /**
137
- * Runtime reverse map for filtered `parseEnv`: OS-level env-var key → `[namespace, property]`
138
- * in the {@link NeonEnv} shape. The compile-time mirror is {@link EnvKeysByNamespace} /
139
- * {@link EnvKeyToProp}; keep all three in sync. Only input vars appear (no output-only
140
- * aliases).
141
- */
142
- const FILTERABLE_ENV_KEYS = {
143
- DATABASE_URL: ["postgres", "databaseUrl"],
144
- DATABASE_URL_UNPOOLED: ["postgres", "databaseUrlUnpooled"],
145
- NEON_BRANCH: ["branch", "name"],
146
- NEON_AUTH_BASE_URL: ["auth", "baseUrl"],
147
- NEON_AUTH_JWKS_URL: ["auth", "jwksUrl"],
148
- NEON_DATA_API_URL: ["dataApi", "url"],
149
- AWS_ACCESS_KEY_ID: ["storage", "accessKeyId"],
150
- AWS_SECRET_ACCESS_KEY: ["storage", "secretAccessKey"],
151
- AWS_ENDPOINT_URL_S3: ["storage", "endpoint"],
152
- AWS_REGION: ["storage", "region"],
153
- NEON_AI_GATEWAY_TOKEN: ["aiGateway", "apiKey"],
154
- NEON_AI_GATEWAY_BASE_URL: ["aiGateway", "baseUrl"]
155
- };
156
- /**
157
- * Filtered counterpart to the {@link parseEnv} body: validate and return only the explicitly
158
- * selected OS-level env-var keys, projected back into the narrowed namespaced shape. Unlike
159
- * the full reader it never consults the policy — the selection alone decides what's required —
160
- * so vars the caller didn't ask for (e.g. `DATABASE_URL_UNPOOLED`) can be absent without
161
- * throwing. Mirrors the same non-empty constraint and {@link PlatformError} aggregation.
162
- */
163
- function parseFilteredEnv(source, keys) {
164
- const issues = [];
165
- const result = {};
166
- for (const key of keys) {
167
- if (!Object.hasOwn(FILTERABLE_ENV_KEYS, key)) {
168
- issues.push(`${key} is not a selectable Neon env variable`);
169
- continue;
170
- }
171
- const value = source[key];
172
- if (value === void 0) {
173
- issues.push(`${key} is missing`);
174
- continue;
175
- }
176
- if (value === "") {
177
- issues.push(`${key} must not be empty`);
178
- continue;
179
- }
180
- const [namespace, property] = FILTERABLE_ENV_KEYS[key];
181
- const bucket = result[namespace] ?? {};
182
- bucket[property] = value;
183
- result[namespace] = bucket;
184
- }
185
- if (issues.length > 0) throw new PlatformError(ErrorCode.EnvNotInjected, [
186
- "parseEnv: the required Neon env variables are not present in process.env.",
187
- ...issues.map((i) => ` - ${i}`),
188
- "Inject them via one of:",
189
- " - `neon dev` / `neon-env run -- <your dev command>` (wraps the command with the vars injected)",
190
- " - your hosting platform's Neon integration (Vercel, Fly, Railway, …)",
191
- "Or switch the call to `await fetchEnv(config, …)` if you're in a context that can do async I/O."
192
- ].join("\n"), { details: { missing: issues } });
193
- return result;
194
- }
195
- //#endregion
196
- export { parseEnv };
197
-
198
- //# sourceMappingURL=parse-env.js.map