@neondatabase/env 0.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,83 @@
1
+ import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
2
+ import { tmpdir } from "node:os";
3
+ import { dirname, join } from "node:path";
4
+ import { vi } from "vitest";
5
+
6
+ /**
7
+ * Every Neon env var (and adjacent OS env var) the platform package reads at runtime.
8
+ * Used by {@link stubCleanNeonEnv} to give each test a deterministic, empty starting
9
+ * env regardless of the developer's local `~/.config/neonctl`, exported `NEON_*` vars,
10
+ * or anything else carried over from the parent shell.
11
+ */
12
+ const NEON_AND_RELATED_ENV_KEYS = [
13
+ "NEON_API_KEY",
14
+ "NEON_PROJECT_ID",
15
+ "NEON_BRANCH_ID",
16
+ "NEON_ORG_ID",
17
+ "NEON_AUTH_BASE_URL",
18
+ "NEON_DATA_API_URL",
19
+ "DATABASE_URL",
20
+ "DATABASE_URL_UNPOOLED",
21
+ "NEONCTL_CONFIG_DIR",
22
+ "HOME",
23
+ "USERPROFILE",
24
+ ] as const;
25
+
26
+ /**
27
+ * Stub every Neon-related env var to `undefined` (so they look unset to the code under
28
+ * test). Tests can override individual keys with `vi.stubEnv(key, value)` after calling
29
+ * this. `vitest.config.ts` sets `unstubEnvs: true` so each test's stubs are auto-reset.
30
+ *
31
+ * Call this from a `beforeEach` in any test file that touches `process.env`-driven
32
+ * resolution (api key / context / connection-string env vars).
33
+ */
34
+ export function stubCleanNeonEnv(): void {
35
+ for (const key of NEON_AND_RELATED_ENV_KEYS) {
36
+ // vitest 3.x: passing `undefined` deletes the env var (rather than setting it
37
+ // to the literal string "undefined") — exactly what we want, because empty
38
+ // strings would still trip code that reads `env.HOME` to build paths.
39
+ vi.stubEnv(key, undefined);
40
+ }
41
+ }
42
+
43
+ /**
44
+ * Build a transient project tree under the OS temp directory.
45
+ *
46
+ * Returns the absolute root path plus a `cleanup()` that removes the tree. Tests should
47
+ * call `cleanup()` from an `afterEach`/`afterAll` hook.
48
+ *
49
+ * `files` is a flat map of relative paths → contents; intermediate directories are created
50
+ * automatically. Directories themselves can be created by passing `null` as the value.
51
+ *
52
+ * A `.git/HEAD` marker is seeded at the root by default so the platform's upward walkers
53
+ * (which stop at `.git`) don't escape the synthetic repo and read the developer's real
54
+ * `~/.neon`. Pass an explicit `.git` entry in `files` to override or position it elsewhere
55
+ * (e.g. for tests that exercise the boundary behaviour itself).
56
+ */
57
+ export function makeTempRepo(files: Record<string, string | null>): {
58
+ root: string;
59
+ cleanup: () => void;
60
+ } {
61
+ const root = mkdtempSync(join(tmpdir(), "neon-ts-test-"));
62
+ const callerSpecifiesGit = Object.keys(files).some(
63
+ (p) => p === ".git" || p.startsWith(".git/"),
64
+ );
65
+ const entries: Array<[string, string | null]> = callerSpecifiesGit
66
+ ? Object.entries(files)
67
+ : [[".git/HEAD", "ref: refs/heads/main\n"], ...Object.entries(files)];
68
+ for (const [relPath, contents] of entries) {
69
+ const abs = join(root, relPath);
70
+ mkdirSync(dirname(abs), { recursive: true });
71
+ if (contents !== null) {
72
+ writeFileSync(abs, contents, "utf-8");
73
+ } else {
74
+ mkdirSync(abs, { recursive: true });
75
+ }
76
+ }
77
+ return {
78
+ root,
79
+ cleanup: () => {
80
+ rmSync(root, { recursive: true, force: true });
81
+ },
82
+ };
83
+ }
package/src/v1.ts ADDED
@@ -0,0 +1,32 @@
1
+ /**
2
+ * `@neondatabase/env/v1` — resolve and inject Neon connection strings for the branch
3
+ * selected by your `neon.ts` policy.
4
+ *
5
+ * - `fetchEnv(config)` — async; resolves the branch + calls the Neon API for live
6
+ * connection strings. Use in build scripts / top-level await.
7
+ * - `parseEnv(config)` — sync; reads already-injected `process.env` and validates it.
8
+ * Use in app bootstrap (Drizzle config, Next.js, Vite, …).
9
+ * - `toEntries(env)` — project a resolved env into `{ KEY: value }` pairs.
10
+ *
11
+ * The branch policy type (`Config`) and `defineConfig` come from `@neondatabase/config`.
12
+ */
13
+
14
+ export type {
15
+ FetchEnvOptions,
16
+ NeonAuthEnv,
17
+ NeonDataApiEnv,
18
+ NeonEnv,
19
+ NeonPostgresEnv,
20
+ } from "./lib/env.js";
21
+ export {
22
+ fetchEnv,
23
+ NEON_ENV_VAR_KEYS,
24
+ parseEnv,
25
+ toEntries,
26
+ } from "./lib/env.js";
27
+
28
+ // The branch policy type (`Config`) and `defineConfig` live in `@neondatabase/config`.
29
+ // Import them from there directly:
30
+ // import { defineConfig } from "@neondatabase/config/v1";
31
+ // They are intentionally not re-exported here to keep this package's surface focused on
32
+ // env resolution and to avoid coupling the two packages' type-declaration bundles.
package/tsconfig.json ADDED
@@ -0,0 +1,4 @@
1
+ {
2
+ "extends": "../../tsconfig.base.json",
3
+ "include": ["src", "e2e", "vitest.config.ts", "vitest.e2e.config.ts", "tsdown.config.ts"]
4
+ }
@@ -0,0 +1,20 @@
1
+ import { defineConfig } from "tsdown";
2
+
3
+ export default defineConfig({
4
+ name: "@neondatabase/env",
5
+ bundle: false,
6
+ clean: true,
7
+ dts: true,
8
+ entry: [
9
+ "src/index.ts",
10
+ "src/v1.ts",
11
+ "src/cli.ts",
12
+ "src/lib/**/*.ts",
13
+ "!src/**/*.test.*",
14
+ "!src/lib/fake-neon-api.ts",
15
+ "!src/lib/test-utils.ts",
16
+ ],
17
+ format: "esm",
18
+ outDir: "dist",
19
+ treeshake: true,
20
+ });
@@ -0,0 +1,19 @@
1
+ import { defineConfig } from "vitest/config";
2
+
3
+ export default defineConfig({
4
+ test: {
5
+ clearMocks: true,
6
+ mockReset: true,
7
+ unstubEnvs: true,
8
+ coverage: {
9
+ all: true,
10
+ include: ["src"],
11
+ exclude: ["src/**/*.test.ts"],
12
+ reporter: ["html", "lcov"],
13
+ },
14
+ // Skip e2e tests (which talk to the real Neon API) in the standard suite — they
15
+ // run via `pnpm test:e2e` against `vitest.e2e.config.ts`.
16
+ exclude: ["lib", "node_modules", "dist", "**/*.e2e.test.ts", "e2e/**"],
17
+ setupFiles: ["console-fail-test/setup"],
18
+ },
19
+ });
@@ -0,0 +1,29 @@
1
+ import { defineConfig } from "vitest/config";
2
+
3
+ /**
4
+ * Vitest config for end-to-end tests that hit the **real** Neon API.
5
+ *
6
+ * - Matches `**\/*.e2e.test.ts` only — the standard `test:ci` config explicitly excludes
7
+ * this pattern so the two suites never collide.
8
+ * - Uses long per-test timeouts because real project creation / deletion can take 10+s.
9
+ * - Forces single-threaded execution to avoid quota / rate-limit interference between
10
+ * tests (`pool: "forks"` with `singleFork: true`).
11
+ * - Loads `.env` via Vitest's built-in dotenv support so `NEON_API_KEY` (and optionally
12
+ * `NEON_ORG_ID`) become `process.env` entries inside the tests.
13
+ */
14
+ export default defineConfig({
15
+ test: {
16
+ include: ["src/**/*.e2e.test.ts", "e2e/**/*.test.ts"],
17
+ exclude: ["node_modules", "dist"],
18
+ setupFiles: ["./e2e/load-env.ts", "./e2e/setup.ts"],
19
+ testTimeout: 120_000,
20
+ hookTimeout: 120_000,
21
+ pool: "forks",
22
+ poolOptions: {
23
+ forks: {
24
+ singleFork: true,
25
+ },
26
+ },
27
+ reporters: ["verbose"],
28
+ },
29
+ });