@neon/config 0.12.0 → 0.13.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/dist/paths.d.ts +79 -0
- package/dist/paths.d.ts.map +1 -0
- package/dist/paths.js +111 -0
- package/dist/paths.js.map +1 -0
- package/package.json +6 -1
package/dist/paths.d.ts
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
//#region src/paths.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* # `@neon/config/paths` — where the Neon CLIs keep their files on disk
|
|
4
|
+
*
|
|
5
|
+
* **Implementor-only, and deliberately impure.** This subpath reads environment variables
|
|
6
|
+
* and touches the filesystem, which the root `@neon/config` export must never do — the same
|
|
7
|
+
* split as `@neon/env` (pure) versus `@neon/env/runtime` (stateful). Import it from a CLI,
|
|
8
|
+
* never from a `neon.ts` policy. It imports nothing from the rest of the package, so pulling
|
|
9
|
+
* it in costs one module.
|
|
10
|
+
*
|
|
11
|
+
* It exists because three separate readers each grew their own answer to "where is the
|
|
12
|
+
* config directory", and all three disagreed: `packages/cli` honoured `XDG_CONFIG_HOME` but
|
|
13
|
+
* not `NEONCTL_CONFIG_DIR`, `packages/env` honoured the env var but not XDG, and
|
|
14
|
+
* `packages/init` hardcoded `~/.config/neonctl`. With `XDG_CONFIG_HOME` set, the CLI wrote
|
|
15
|
+
* credentials somewhere the other two never looked.
|
|
16
|
+
*
|
|
17
|
+
* ## The directory
|
|
18
|
+
*
|
|
19
|
+
* `neon` is the current name; `neonctl` is the legacy one, kept readable forever. Resolution,
|
|
20
|
+
* each entry winning over the next:
|
|
21
|
+
*
|
|
22
|
+
* 1. An explicit directory (a `--config-dir` flag) — **exact**, no legacy fallback.
|
|
23
|
+
* 2. `NEON_CONFIG_DIR` — exact.
|
|
24
|
+
* 3. `NEONCTL_CONFIG_DIR` (legacy name) — exact.
|
|
25
|
+
* 4. `$XDG_CONFIG_HOME/neon`, else `<home>/.config/neon`.
|
|
26
|
+
*
|
|
27
|
+
* An explicitly chosen directory is never paired with a fallback: `--config-dir /tmp/ci` that
|
|
28
|
+
* quietly read `~/.config/neonctl` would defeat the point of passing it.
|
|
29
|
+
*
|
|
30
|
+
* ## The files
|
|
31
|
+
*
|
|
32
|
+
* {@link resolveConfigFile} answers "which path should I use for this file", and it is the
|
|
33
|
+
* same answer for reading and writing:
|
|
34
|
+
*
|
|
35
|
+
* - Present in `neon/` → use it.
|
|
36
|
+
* - Present only in `neonctl/` → **use it there, in place.** An existing credentials file is
|
|
37
|
+
* never copied or moved, so nothing is left behind to go stale and no other tool starts
|
|
38
|
+
* reading an abandoned token.
|
|
39
|
+
* - Present in neither → the new location. New files only ever appear under `neon/`.
|
|
40
|
+
*/
|
|
41
|
+
/** Current directory name. New files are created here. */
|
|
42
|
+
declare const CONFIG_DIR_NAME = "neon";
|
|
43
|
+
/** Legacy directory name, read forever so existing installs keep working untouched. */
|
|
44
|
+
declare const LEGACY_CONFIG_DIR_NAME = "neonctl";
|
|
45
|
+
interface ConfigPathOptions {
|
|
46
|
+
/**
|
|
47
|
+
* An explicit directory, e.g. from a `--config-dir` flag. Used exactly as given: no
|
|
48
|
+
* environment variables are consulted and the legacy directory is never searched.
|
|
49
|
+
*/
|
|
50
|
+
dir?: string;
|
|
51
|
+
/** Environment to read. Defaults to `process.env`. Injectable for tests. */
|
|
52
|
+
env?: NodeJS.ProcessEnv;
|
|
53
|
+
}
|
|
54
|
+
/** Where files are created. See the module docs for the precedence. */
|
|
55
|
+
declare function configDir(options?: ConfigPathOptions): string;
|
|
56
|
+
/**
|
|
57
|
+
* The legacy directory, or `undefined` when the location was chosen explicitly (in which
|
|
58
|
+
* case there is no legacy counterpart to fall back to).
|
|
59
|
+
*/
|
|
60
|
+
declare function legacyConfigDir(options?: ConfigPathOptions): string | undefined;
|
|
61
|
+
interface ResolvedConfigFile {
|
|
62
|
+
/** The path to use, for both reading and writing. */
|
|
63
|
+
path: string;
|
|
64
|
+
/** The directory `path` lives in. */
|
|
65
|
+
dir: string;
|
|
66
|
+
/** True when the file was found in the legacy `neonctl` directory. */
|
|
67
|
+
isLegacy: boolean;
|
|
68
|
+
/** Whether the file exists at `path` right now. */
|
|
69
|
+
exists: boolean;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Resolve one file inside the config directory. Prefers the current location, falls back to
|
|
73
|
+
* an existing legacy file **in place**, and otherwise points at the current location so new
|
|
74
|
+
* files are created there.
|
|
75
|
+
*/
|
|
76
|
+
declare function resolveConfigFile(fileName: string, options?: ConfigPathOptions): ResolvedConfigFile;
|
|
77
|
+
//#endregion
|
|
78
|
+
export { CONFIG_DIR_NAME, ConfigPathOptions, LEGACY_CONFIG_DIR_NAME, ResolvedConfigFile, configDir, legacyConfigDir, resolveConfigFile };
|
|
79
|
+
//# sourceMappingURL=paths.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"paths.d.ts","names":[],"sources":["../src/paths.ts"],"mappings":";;AA4CA;AAGA;AAEA;AAWA;AAUA;AAOA;AAgBA;AAAiC;AAEvB;AACP;AAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cApDR,eAAA;;cAGA,sBAAA;UAEI,iBAAA;;;;;;;QAOV,MAAA,CAAO;;;iBAIE,SAAA,WAAmB;;;;;iBAUnB,eAAA,WACN;UAMO,kBAAA;;;;;;;;;;;;;;;iBAgBD,iBAAA,6BAEN,oBACP"}
|
package/dist/paths.js
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { join, resolve } from "node:path";
|
|
3
|
+
//#region src/paths.ts
|
|
4
|
+
/**
|
|
5
|
+
* # `@neon/config/paths` — where the Neon CLIs keep their files on disk
|
|
6
|
+
*
|
|
7
|
+
* **Implementor-only, and deliberately impure.** This subpath reads environment variables
|
|
8
|
+
* and touches the filesystem, which the root `@neon/config` export must never do — the same
|
|
9
|
+
* split as `@neon/env` (pure) versus `@neon/env/runtime` (stateful). Import it from a CLI,
|
|
10
|
+
* never from a `neon.ts` policy. It imports nothing from the rest of the package, so pulling
|
|
11
|
+
* it in costs one module.
|
|
12
|
+
*
|
|
13
|
+
* It exists because three separate readers each grew their own answer to "where is the
|
|
14
|
+
* config directory", and all three disagreed: `packages/cli` honoured `XDG_CONFIG_HOME` but
|
|
15
|
+
* not `NEONCTL_CONFIG_DIR`, `packages/env` honoured the env var but not XDG, and
|
|
16
|
+
* `packages/init` hardcoded `~/.config/neonctl`. With `XDG_CONFIG_HOME` set, the CLI wrote
|
|
17
|
+
* credentials somewhere the other two never looked.
|
|
18
|
+
*
|
|
19
|
+
* ## The directory
|
|
20
|
+
*
|
|
21
|
+
* `neon` is the current name; `neonctl` is the legacy one, kept readable forever. Resolution,
|
|
22
|
+
* each entry winning over the next:
|
|
23
|
+
*
|
|
24
|
+
* 1. An explicit directory (a `--config-dir` flag) — **exact**, no legacy fallback.
|
|
25
|
+
* 2. `NEON_CONFIG_DIR` — exact.
|
|
26
|
+
* 3. `NEONCTL_CONFIG_DIR` (legacy name) — exact.
|
|
27
|
+
* 4. `$XDG_CONFIG_HOME/neon`, else `<home>/.config/neon`.
|
|
28
|
+
*
|
|
29
|
+
* An explicitly chosen directory is never paired with a fallback: `--config-dir /tmp/ci` that
|
|
30
|
+
* quietly read `~/.config/neonctl` would defeat the point of passing it.
|
|
31
|
+
*
|
|
32
|
+
* ## The files
|
|
33
|
+
*
|
|
34
|
+
* {@link resolveConfigFile} answers "which path should I use for this file", and it is the
|
|
35
|
+
* same answer for reading and writing:
|
|
36
|
+
*
|
|
37
|
+
* - Present in `neon/` → use it.
|
|
38
|
+
* - Present only in `neonctl/` → **use it there, in place.** An existing credentials file is
|
|
39
|
+
* never copied or moved, so nothing is left behind to go stale and no other tool starts
|
|
40
|
+
* reading an abandoned token.
|
|
41
|
+
* - Present in neither → the new location. New files only ever appear under `neon/`.
|
|
42
|
+
*/
|
|
43
|
+
/** Current directory name. New files are created here. */
|
|
44
|
+
const CONFIG_DIR_NAME = "neon";
|
|
45
|
+
/** Legacy directory name, read forever so existing installs keep working untouched. */
|
|
46
|
+
const LEGACY_CONFIG_DIR_NAME = "neonctl";
|
|
47
|
+
/** Where files are created. See the module docs for the precedence. */
|
|
48
|
+
function configDir(options = {}) {
|
|
49
|
+
const explicit = explicitDir(options);
|
|
50
|
+
if (explicit) return explicit;
|
|
51
|
+
return join(configHome(options.env ?? process.env), CONFIG_DIR_NAME);
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* The legacy directory, or `undefined` when the location was chosen explicitly (in which
|
|
55
|
+
* case there is no legacy counterpart to fall back to).
|
|
56
|
+
*/
|
|
57
|
+
function legacyConfigDir(options = {}) {
|
|
58
|
+
if (explicitDir(options)) return void 0;
|
|
59
|
+
return join(configHome(options.env ?? process.env), LEGACY_CONFIG_DIR_NAME);
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Resolve one file inside the config directory. Prefers the current location, falls back to
|
|
63
|
+
* an existing legacy file **in place**, and otherwise points at the current location so new
|
|
64
|
+
* files are created there.
|
|
65
|
+
*/
|
|
66
|
+
function resolveConfigFile(fileName, options = {}) {
|
|
67
|
+
const dir = configDir(options);
|
|
68
|
+
const current = resolve(dir, fileName);
|
|
69
|
+
if (existsSync(current)) return {
|
|
70
|
+
path: current,
|
|
71
|
+
dir,
|
|
72
|
+
isLegacy: false,
|
|
73
|
+
exists: true
|
|
74
|
+
};
|
|
75
|
+
const legacyDir = legacyConfigDir(options);
|
|
76
|
+
if (legacyDir) {
|
|
77
|
+
const legacy = resolve(legacyDir, fileName);
|
|
78
|
+
if (existsSync(legacy)) return {
|
|
79
|
+
path: legacy,
|
|
80
|
+
dir: legacyDir,
|
|
81
|
+
isLegacy: true,
|
|
82
|
+
exists: true
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
return {
|
|
86
|
+
path: current,
|
|
87
|
+
dir,
|
|
88
|
+
isLegacy: false,
|
|
89
|
+
exists: false
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
/** `$XDG_CONFIG_HOME`, else `<home>/.config`. Falls back to a relative `.config` with no home. */
|
|
93
|
+
function configHome(env) {
|
|
94
|
+
const xdg = nonEmpty(env.XDG_CONFIG_HOME);
|
|
95
|
+
if (xdg) return xdg;
|
|
96
|
+
const home = nonEmpty(env.HOME) ?? nonEmpty(env.USERPROFILE);
|
|
97
|
+
return home ? join(home, ".config") : ".config";
|
|
98
|
+
}
|
|
99
|
+
function explicitDir(options) {
|
|
100
|
+
const env = options.env ?? process.env;
|
|
101
|
+
return nonEmpty(options.dir) ?? nonEmpty(env.NEON_CONFIG_DIR) ?? nonEmpty(env.NEONCTL_CONFIG_DIR);
|
|
102
|
+
}
|
|
103
|
+
function nonEmpty(value) {
|
|
104
|
+
if (typeof value !== "string") return void 0;
|
|
105
|
+
const trimmed = value.trim();
|
|
106
|
+
return trimmed === "" ? void 0 : trimmed;
|
|
107
|
+
}
|
|
108
|
+
//#endregion
|
|
109
|
+
export { CONFIG_DIR_NAME, LEGACY_CONFIG_DIR_NAME, configDir, legacyConfigDir, resolveConfigFile };
|
|
110
|
+
|
|
111
|
+
//# sourceMappingURL=paths.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"paths.js","names":[],"sources":["../src/paths.ts"],"sourcesContent":["/**\n * # `@neon/config/paths` — where the Neon CLIs keep their files on disk\n *\n * **Implementor-only, and deliberately impure.** This subpath reads environment variables\n * and touches the filesystem, which the root `@neon/config` export must never do — the same\n * split as `@neon/env` (pure) versus `@neon/env/runtime` (stateful). Import it from a CLI,\n * never from a `neon.ts` policy. It imports nothing from the rest of the package, so pulling\n * it in costs one module.\n *\n * It exists because three separate readers each grew their own answer to \"where is the\n * config directory\", and all three disagreed: `packages/cli` honoured `XDG_CONFIG_HOME` but\n * not `NEONCTL_CONFIG_DIR`, `packages/env` honoured the env var but not XDG, and\n * `packages/init` hardcoded `~/.config/neonctl`. With `XDG_CONFIG_HOME` set, the CLI wrote\n * credentials somewhere the other two never looked.\n *\n * ## The directory\n *\n * `neon` is the current name; `neonctl` is the legacy one, kept readable forever. Resolution,\n * each entry winning over the next:\n *\n * 1. An explicit directory (a `--config-dir` flag) — **exact**, no legacy fallback.\n * 2. `NEON_CONFIG_DIR` — exact.\n * 3. `NEONCTL_CONFIG_DIR` (legacy name) — exact.\n * 4. `$XDG_CONFIG_HOME/neon`, else `<home>/.config/neon`.\n *\n * An explicitly chosen directory is never paired with a fallback: `--config-dir /tmp/ci` that\n * quietly read `~/.config/neonctl` would defeat the point of passing it.\n *\n * ## The files\n *\n * {@link resolveConfigFile} answers \"which path should I use for this file\", and it is the\n * same answer for reading and writing:\n *\n * - Present in `neon/` → use it.\n * - Present only in `neonctl/` → **use it there, in place.** An existing credentials file is\n * never copied or moved, so nothing is left behind to go stale and no other tool starts\n * reading an abandoned token.\n * - Present in neither → the new location. New files only ever appear under `neon/`.\n */\n\nimport { existsSync } from \"node:fs\";\nimport { join, resolve } from \"node:path\";\n\n/** Current directory name. New files are created here. */\nexport const CONFIG_DIR_NAME = \"neon\";\n\n/** Legacy directory name, read forever so existing installs keep working untouched. */\nexport const LEGACY_CONFIG_DIR_NAME = \"neonctl\";\n\nexport interface ConfigPathOptions {\n\t/**\n\t * An explicit directory, e.g. from a `--config-dir` flag. Used exactly as given: no\n\t * environment variables are consulted and the legacy directory is never searched.\n\t */\n\tdir?: string;\n\t/** Environment to read. Defaults to `process.env`. Injectable for tests. */\n\tenv?: NodeJS.ProcessEnv;\n}\n\n/** Where files are created. See the module docs for the precedence. */\nexport function configDir(options: ConfigPathOptions = {}): string {\n\tconst explicit = explicitDir(options);\n\tif (explicit) return explicit;\n\treturn join(configHome(options.env ?? process.env), CONFIG_DIR_NAME);\n}\n\n/**\n * The legacy directory, or `undefined` when the location was chosen explicitly (in which\n * case there is no legacy counterpart to fall back to).\n */\nexport function legacyConfigDir(\n\toptions: ConfigPathOptions = {},\n): string | undefined {\n\tif (explicitDir(options)) return undefined;\n\treturn join(configHome(options.env ?? process.env), LEGACY_CONFIG_DIR_NAME);\n}\n\nexport interface ResolvedConfigFile {\n\t/** The path to use, for both reading and writing. */\n\tpath: string;\n\t/** The directory `path` lives in. */\n\tdir: string;\n\t/** True when the file was found in the legacy `neonctl` directory. */\n\tisLegacy: boolean;\n\t/** Whether the file exists at `path` right now. */\n\texists: boolean;\n}\n\n/**\n * Resolve one file inside the config directory. Prefers the current location, falls back to\n * an existing legacy file **in place**, and otherwise points at the current location so new\n * files are created there.\n */\nexport function resolveConfigFile(\n\tfileName: string,\n\toptions: ConfigPathOptions = {},\n): ResolvedConfigFile {\n\tconst dir = configDir(options);\n\tconst current = resolve(dir, fileName);\n\tif (existsSync(current))\n\t\treturn { path: current, dir, isLegacy: false, exists: true };\n\n\tconst legacyDir = legacyConfigDir(options);\n\tif (legacyDir) {\n\t\tconst legacy = resolve(legacyDir, fileName);\n\t\tif (existsSync(legacy))\n\t\t\treturn {\n\t\t\t\tpath: legacy,\n\t\t\t\tdir: legacyDir,\n\t\t\t\tisLegacy: true,\n\t\t\t\texists: true,\n\t\t\t};\n\t}\n\n\treturn { path: current, dir, isLegacy: false, exists: false };\n}\n\n/** `$XDG_CONFIG_HOME`, else `<home>/.config`. Falls back to a relative `.config` with no home. */\nfunction configHome(env: NodeJS.ProcessEnv): string {\n\tconst xdg = nonEmpty(env.XDG_CONFIG_HOME);\n\tif (xdg) return xdg;\n\tconst home = nonEmpty(env.HOME) ?? nonEmpty(env.USERPROFILE);\n\treturn home ? join(home, \".config\") : \".config\";\n}\n\nfunction explicitDir(options: ConfigPathOptions): string | undefined {\n\tconst env = options.env ?? process.env;\n\treturn (\n\t\tnonEmpty(options.dir) ??\n\t\tnonEmpty(env.NEON_CONFIG_DIR) ??\n\t\tnonEmpty(env.NEONCTL_CONFIG_DIR)\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":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4CA,MAAa,kBAAkB;;AAG/B,MAAa,yBAAyB;;AAatC,SAAgB,UAAU,UAA6B,CAAC,GAAW;CAClE,MAAM,WAAW,YAAY,OAAO;CACpC,IAAI,UAAU,OAAO;CACrB,OAAO,KAAK,WAAW,QAAQ,OAAO,QAAQ,GAAG,GAAG,eAAe;AACpE;;;;;AAMA,SAAgB,gBACf,UAA6B,CAAC,GACT;CACrB,IAAI,YAAY,OAAO,GAAG,OAAO,KAAA;CACjC,OAAO,KAAK,WAAW,QAAQ,OAAO,QAAQ,GAAG,GAAG,sBAAsB;AAC3E;;;;;;AAkBA,SAAgB,kBACf,UACA,UAA6B,CAAC,GACT;CACrB,MAAM,MAAM,UAAU,OAAO;CAC7B,MAAM,UAAU,QAAQ,KAAK,QAAQ;CACrC,IAAI,WAAW,OAAO,GACrB,OAAO;EAAE,MAAM;EAAS;EAAK,UAAU;EAAO,QAAQ;CAAK;CAE5D,MAAM,YAAY,gBAAgB,OAAO;CACzC,IAAI,WAAW;EACd,MAAM,SAAS,QAAQ,WAAW,QAAQ;EAC1C,IAAI,WAAW,MAAM,GACpB,OAAO;GACN,MAAM;GACN,KAAK;GACL,UAAU;GACV,QAAQ;EACT;CACF;CAEA,OAAO;EAAE,MAAM;EAAS;EAAK,UAAU;EAAO,QAAQ;CAAM;AAC7D;;AAGA,SAAS,WAAW,KAAgC;CACnD,MAAM,MAAM,SAAS,IAAI,eAAe;CACxC,IAAI,KAAK,OAAO;CAChB,MAAM,OAAO,SAAS,IAAI,IAAI,KAAK,SAAS,IAAI,WAAW;CAC3D,OAAO,OAAO,KAAK,MAAM,SAAS,IAAI;AACvC;AAEA,SAAS,YAAY,SAAgD;CACpE,MAAM,MAAM,QAAQ,OAAO,QAAQ;CACnC,OACC,SAAS,QAAQ,GAAG,KACpB,SAAS,IAAI,eAAe,KAC5B,SAAS,IAAI,kBAAkB;AAEjC;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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@neon/config",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.0",
|
|
4
4
|
"description": "Config-as-Code for Neon. Define a `neon.ts` policy and inspect/diff/deploy it against the Neon API as plain TypeScript functions.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"neon",
|
|
@@ -32,6 +32,11 @@
|
|
|
32
32
|
"types": "./dist/v1.d.ts",
|
|
33
33
|
"import": "./dist/v1.js",
|
|
34
34
|
"default": "./dist/v1.js"
|
|
35
|
+
},
|
|
36
|
+
"./paths": {
|
|
37
|
+
"types": "./dist/paths.d.ts",
|
|
38
|
+
"import": "./dist/paths.js",
|
|
39
|
+
"default": "./dist/paths.js"
|
|
35
40
|
}
|
|
36
41
|
},
|
|
37
42
|
"files": [
|