@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.
- package/.env.example +5 -0
- package/README.md +66 -0
- package/e2e/env.e2e.test.ts +36 -0
- package/e2e/helpers.ts +188 -0
- package/e2e/load-env.ts +29 -0
- package/e2e/setup.ts +24 -0
- package/package.json +22 -0
- package/src/cli.ts +107 -0
- package/src/index.ts +5 -0
- package/src/lib/cli/commands.test.ts +101 -0
- package/src/lib/cli/commands.ts +267 -0
- package/src/lib/cli/resolve-context.test.ts +242 -0
- package/src/lib/cli/resolve-context.ts +142 -0
- package/src/lib/env.test.ts +172 -0
- package/src/lib/env.ts +610 -0
- package/src/lib/fake-neon-api.ts +782 -0
- package/src/lib/test-utils.ts +83 -0
- package/src/v1.ts +32 -0
- package/tsconfig.json +4 -0
- package/tsdown.config.ts +20 -0
- package/vitest.config.ts +19 -0
- package/vitest.e2e.config.ts +29 -0
|
@@ -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
package/tsdown.config.ts
ADDED
|
@@ -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
|
+
});
|
package/vitest.config.ts
ADDED
|
@@ -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
|
+
});
|