@clicadigital/vault-sdk 0.1.0 → 0.3.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/README.md +179 -152
- package/dist/cli.js +15 -13
- package/dist/cli.js.map +1 -1
- package/dist/client.d.ts +8 -1
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +41 -9
- package/dist/client.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/package.json +43 -43
- package/src/cli.ts +134 -132
- package/src/client.ts +109 -74
- package/src/dotenv.ts +22 -22
- package/src/index.ts +177 -167
- package/src/types.ts +14 -14
package/package.json
CHANGED
|
@@ -1,43 +1,43 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@clicadigital/vault-sdk",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "SDK + CLI to
|
|
5
|
-
"type": "module",
|
|
6
|
-
"license": "UNLICENSED",
|
|
7
|
-
"main": "./dist/index.js",
|
|
8
|
-
"types": "./dist/index.d.ts",
|
|
9
|
-
"bin": {
|
|
10
|
-
"vault-sdk": "./dist/cli.js"
|
|
11
|
-
},
|
|
12
|
-
"exports": {
|
|
13
|
-
".": {
|
|
14
|
-
"types": "./dist/index.d.ts",
|
|
15
|
-
"bun": "./src/index.ts",
|
|
16
|
-
"import": "./dist/index.js",
|
|
17
|
-
"default": "./dist/index.js"
|
|
18
|
-
}
|
|
19
|
-
},
|
|
20
|
-
"files": [
|
|
21
|
-
"dist",
|
|
22
|
-
"src"
|
|
23
|
-
],
|
|
24
|
-
"scripts": {
|
|
25
|
-
"build": "tsc -p tsconfig.json",
|
|
26
|
-
"prepublishOnly": "bun run build",
|
|
27
|
-
"vault": "bun run src/cli.ts"
|
|
28
|
-
},
|
|
29
|
-
"engines": {
|
|
30
|
-
"node": ">=18"
|
|
31
|
-
},
|
|
32
|
-
"keywords": [
|
|
33
|
-
"vault",
|
|
34
|
-
"env",
|
|
35
|
-
"secrets",
|
|
36
|
-
"kubernetes",
|
|
37
|
-
"clicadigital"
|
|
38
|
-
],
|
|
39
|
-
"devDependencies": {
|
|
40
|
-
"@types/node": "^22.16.5",
|
|
41
|
-
"typescript": "^5.8.3"
|
|
42
|
-
}
|
|
43
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@clicadigital/vault-sdk",
|
|
3
|
+
"version": "0.3.0",
|
|
4
|
+
"description": "SDK + CLI to load vault envs via cluster ServiceAccount (k3s/k8s)",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "UNLICENSED",
|
|
7
|
+
"main": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"bin": {
|
|
10
|
+
"vault-sdk": "./dist/cli.js"
|
|
11
|
+
},
|
|
12
|
+
"exports": {
|
|
13
|
+
".": {
|
|
14
|
+
"types": "./dist/index.d.ts",
|
|
15
|
+
"bun": "./src/index.ts",
|
|
16
|
+
"import": "./dist/index.js",
|
|
17
|
+
"default": "./dist/index.js"
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
"files": [
|
|
21
|
+
"dist",
|
|
22
|
+
"src"
|
|
23
|
+
],
|
|
24
|
+
"scripts": {
|
|
25
|
+
"build": "tsc -p tsconfig.json",
|
|
26
|
+
"prepublishOnly": "bun run build",
|
|
27
|
+
"vault": "bun run src/cli.ts"
|
|
28
|
+
},
|
|
29
|
+
"engines": {
|
|
30
|
+
"node": ">=18"
|
|
31
|
+
},
|
|
32
|
+
"keywords": [
|
|
33
|
+
"vault",
|
|
34
|
+
"env",
|
|
35
|
+
"secrets",
|
|
36
|
+
"kubernetes",
|
|
37
|
+
"clicadigital"
|
|
38
|
+
],
|
|
39
|
+
"devDependencies": {
|
|
40
|
+
"@types/node": "^22.16.5",
|
|
41
|
+
"typescript": "^5.8.3"
|
|
42
|
+
}
|
|
43
|
+
}
|
package/src/cli.ts
CHANGED
|
@@ -1,132 +1,134 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import { createClientFromEnv } from "./client.js";
|
|
3
|
-
import { createEnv, ensureEnvironment, pullVault } from "./index.js";
|
|
4
|
-
|
|
5
|
-
type Flags = Record<string, string | boolean | string[]>;
|
|
6
|
-
|
|
7
|
-
function parseArgs(argv: string[]): { cmd: string; flags: Flags } {
|
|
8
|
-
const [cmd = "help", ...rest] = argv;
|
|
9
|
-
const flags: Flags = {};
|
|
10
|
-
for (let i = 0; i < rest.length; i++) {
|
|
11
|
-
const token = rest[i];
|
|
12
|
-
if (!token?.startsWith("--")) continue;
|
|
13
|
-
const key = token.slice(2);
|
|
14
|
-
const next = rest[i + 1];
|
|
15
|
-
if (!next || next.startsWith("--")) {
|
|
16
|
-
flags[key] = true;
|
|
17
|
-
continue;
|
|
18
|
-
}
|
|
19
|
-
if (key === "var") {
|
|
20
|
-
const list = Array.isArray(flags.var) ? flags.var : [];
|
|
21
|
-
list.push(next);
|
|
22
|
-
flags.var = list;
|
|
23
|
-
} else {
|
|
24
|
-
flags[key] = next;
|
|
25
|
-
}
|
|
26
|
-
i++;
|
|
27
|
-
}
|
|
28
|
-
return { cmd, flags };
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
function str(flags: Flags, key: string, fallback?: string): string {
|
|
32
|
-
const v = flags[key];
|
|
33
|
-
if (typeof v === "string" && v.length > 0) return v;
|
|
34
|
-
if (fallback !== undefined) return fallback;
|
|
35
|
-
throw new Error(`Missing --${key}`);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
function printHelp(): void {
|
|
39
|
-
console.log(`@clicadigital/vault-sdk — cria e carrega envs via URL do vault na VPN
|
|
40
|
-
|
|
41
|
-
Usage:
|
|
42
|
-
vault-sdk create --project <name> --env <name> [--url <vpn-url>] [--var KEY=VAL]...
|
|
43
|
-
vault-sdk ensure --project <name> --env <name> [--url <vpn-url>]
|
|
44
|
-
vault-sdk pull --project <name> --env <name> [--out .env] [--no-inject] [--url <vpn-url>]
|
|
45
|
-
|
|
46
|
-
Env:
|
|
47
|
-
VAULT_URL
|
|
48
|
-
VAULT_PROJECT
|
|
49
|
-
VAULT_ENV
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
const
|
|
83
|
-
const
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
const
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { createClientFromEnv } from "./client.js";
|
|
3
|
+
import { createEnv, ensureEnvironment, pullVault } from "./index.js";
|
|
4
|
+
|
|
5
|
+
type Flags = Record<string, string | boolean | string[]>;
|
|
6
|
+
|
|
7
|
+
function parseArgs(argv: string[]): { cmd: string; flags: Flags } {
|
|
8
|
+
const [cmd = "help", ...rest] = argv;
|
|
9
|
+
const flags: Flags = {};
|
|
10
|
+
for (let i = 0; i < rest.length; i++) {
|
|
11
|
+
const token = rest[i];
|
|
12
|
+
if (!token?.startsWith("--")) continue;
|
|
13
|
+
const key = token.slice(2);
|
|
14
|
+
const next = rest[i + 1];
|
|
15
|
+
if (!next || next.startsWith("--")) {
|
|
16
|
+
flags[key] = true;
|
|
17
|
+
continue;
|
|
18
|
+
}
|
|
19
|
+
if (key === "var") {
|
|
20
|
+
const list = Array.isArray(flags.var) ? flags.var : [];
|
|
21
|
+
list.push(next);
|
|
22
|
+
flags.var = list;
|
|
23
|
+
} else {
|
|
24
|
+
flags[key] = next;
|
|
25
|
+
}
|
|
26
|
+
i++;
|
|
27
|
+
}
|
|
28
|
+
return { cmd, flags };
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function str(flags: Flags, key: string, fallback?: string): string {
|
|
32
|
+
const v = flags[key];
|
|
33
|
+
if (typeof v === "string" && v.length > 0) return v;
|
|
34
|
+
if (fallback !== undefined) return fallback;
|
|
35
|
+
throw new Error(`Missing --${key}`);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function printHelp(): void {
|
|
39
|
+
console.log(`@clicadigital/vault-sdk — cria e carrega envs via URL do vault na VPN
|
|
40
|
+
|
|
41
|
+
Usage:
|
|
42
|
+
vault-sdk create --project <name> --env <name> [--url <vpn-url>] [--var KEY=VAL]...
|
|
43
|
+
vault-sdk ensure --project <name> --env <name> [--url <vpn-url>]
|
|
44
|
+
vault-sdk pull --project <name> --env <name> [--out .env] [--no-inject] [--url <vpn-url>]
|
|
45
|
+
|
|
46
|
+
Env:
|
|
47
|
+
VAULT_URL opcional; default k8s: http://vault-api-service.vault.svc.cluster.local:3001
|
|
48
|
+
VAULT_PROJECT default --project
|
|
49
|
+
VAULT_ENV default --env
|
|
50
|
+
VAULT_SA_TOKEN_PATH path do token SA (default: /var/run/secrets/tokens/vault/token)
|
|
51
|
+
|
|
52
|
+
Auth no k3s: ServiceAccount token (audience=vault) + NetworkPolicy.
|
|
53
|
+
Local (VAULT_AUTH=none na API): sem token.
|
|
54
|
+
`);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function applyUrl(flags: Flags): void {
|
|
58
|
+
if (typeof flags.url === "string") process.env.VAULT_URL = flags.url;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function parseVarList(flags: Flags): Record<string, string> {
|
|
62
|
+
const out: Record<string, string> = {};
|
|
63
|
+
const list = Array.isArray(flags.var) ? flags.var : [];
|
|
64
|
+
for (const pair of list) {
|
|
65
|
+
const eq = pair.indexOf("=");
|
|
66
|
+
if (eq < 1) throw new Error(`Invalid --var ${pair} (use KEY=VALUE)`);
|
|
67
|
+
out[pair.slice(0, eq)] = pair.slice(eq + 1);
|
|
68
|
+
}
|
|
69
|
+
return out;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
async function main(): Promise<void> {
|
|
73
|
+
const { cmd, flags } = parseArgs(process.argv.slice(2));
|
|
74
|
+
|
|
75
|
+
if (cmd === "help" || flags.help === true) {
|
|
76
|
+
printHelp();
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
applyUrl(flags);
|
|
81
|
+
|
|
82
|
+
const project = str(flags, "project", process.env.VAULT_PROJECT);
|
|
83
|
+
const env = str(flags, "env", process.env.VAULT_ENV);
|
|
84
|
+
const url = typeof flags.url === "string" ? flags.url : undefined;
|
|
85
|
+
const client = url
|
|
86
|
+
? createClientFromEnv({ ...process.env, VAULT_URL: url })
|
|
87
|
+
: undefined;
|
|
88
|
+
|
|
89
|
+
if (cmd === "create") {
|
|
90
|
+
const vars = parseVarList(flags);
|
|
91
|
+
const result = await createEnv({ project, env, url, vars, client });
|
|
92
|
+
console.log(JSON.stringify({ project, env, ...result }, null, 2));
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (cmd === "ensure") {
|
|
97
|
+
const result = await ensureEnvironment({ project, env, client });
|
|
98
|
+
console.log(
|
|
99
|
+
JSON.stringify(
|
|
100
|
+
{
|
|
101
|
+
project,
|
|
102
|
+
env,
|
|
103
|
+
projectId: result.projectId,
|
|
104
|
+
environmentId: result.environmentId,
|
|
105
|
+
createdProject: result.createdProject,
|
|
106
|
+
createdEnvironment: result.createdEnvironment,
|
|
107
|
+
},
|
|
108
|
+
null,
|
|
109
|
+
2,
|
|
110
|
+
),
|
|
111
|
+
);
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (cmd === "pull") {
|
|
116
|
+
const out = typeof flags.out === "string" ? flags.out : undefined;
|
|
117
|
+
const inject = flags["no-inject"] !== true;
|
|
118
|
+
const result = await pullVault({ project, env, out, inject, client });
|
|
119
|
+
console.log(
|
|
120
|
+
`pulled ${Object.keys(result.vars).length} vars for ${project}/${env}` +
|
|
121
|
+
(result.out ? ` → ${result.out}` : "") +
|
|
122
|
+
(inject ? " (injected into process.env)" : ""),
|
|
123
|
+
);
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
printHelp();
|
|
128
|
+
process.exit(1);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
main().catch((err) => {
|
|
132
|
+
console.error(err instanceof Error ? err.message : err);
|
|
133
|
+
process.exit(1);
|
|
134
|
+
});
|
package/src/client.ts
CHANGED
|
@@ -1,74 +1,109 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
|
|
1
|
+
import { readFileSync, existsSync } from "node:fs";
|
|
2
|
+
import type { EnvVar, ProjectTree } from "./types.js";
|
|
3
|
+
|
|
4
|
+
const DEFAULT_TOKEN_PATHS = [
|
|
5
|
+
"/var/run/secrets/tokens/vault/token",
|
|
6
|
+
"/var/run/secrets/kubernetes.io/serviceaccount/token",
|
|
7
|
+
];
|
|
8
|
+
|
|
9
|
+
/** DNS interno do Service no k3s (namespace `vault`). Override com VAULT_URL. */
|
|
10
|
+
export const DEFAULT_VAULT_URL =
|
|
11
|
+
"http://vault-api-service.vault.svc.cluster.local:3001";
|
|
12
|
+
|
|
13
|
+
export type VaultClientOptions = {
|
|
14
|
+
/** Base URL da API do vault na VPN. Default: DEFAULT_VAULT_URL */
|
|
15
|
+
baseUrl: string;
|
|
16
|
+
/** Bearer token (ServiceAccount projected). Se omitido, tenta ler do filesystem do pod. */
|
|
17
|
+
token?: string;
|
|
18
|
+
/** Paths para procurar o token SA (ordem de prioridade). */
|
|
19
|
+
tokenPaths?: string[];
|
|
20
|
+
fetch?: typeof globalThis.fetch;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
function resolveToken(opts: VaultClientOptions): string | undefined {
|
|
24
|
+
if (opts.token) return opts.token;
|
|
25
|
+
const paths = opts.tokenPaths ?? DEFAULT_TOKEN_PATHS;
|
|
26
|
+
for (const path of paths) {
|
|
27
|
+
if (!existsSync(path)) continue;
|
|
28
|
+
try {
|
|
29
|
+
const token = readFileSync(path, "utf8").trim();
|
|
30
|
+
if (token) return token;
|
|
31
|
+
} catch {
|
|
32
|
+
// ignore unreadable path
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return undefined;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export class VaultClient {
|
|
39
|
+
readonly baseUrl: string;
|
|
40
|
+
private readonly token: string | undefined;
|
|
41
|
+
private readonly fetchFn: typeof globalThis.fetch;
|
|
42
|
+
|
|
43
|
+
constructor(opts: VaultClientOptions) {
|
|
44
|
+
this.baseUrl = opts.baseUrl.replace(/\/+$/, "");
|
|
45
|
+
this.token = resolveToken(opts);
|
|
46
|
+
this.fetchFn = opts.fetch ?? globalThis.fetch.bind(globalThis);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
private async request<T>(path: string, init?: RequestInit): Promise<T> {
|
|
50
|
+
const headers: Record<string, string> = {
|
|
51
|
+
"content-type": "application/json",
|
|
52
|
+
...(init?.headers as Record<string, string> | undefined),
|
|
53
|
+
};
|
|
54
|
+
if (this.token) headers.authorization = `Bearer ${this.token}`;
|
|
55
|
+
|
|
56
|
+
const res = await this.fetchFn(`${this.baseUrl}${path}`, {
|
|
57
|
+
...init,
|
|
58
|
+
headers,
|
|
59
|
+
});
|
|
60
|
+
if (!res.ok) {
|
|
61
|
+
const body = (await res.json().catch(() => null)) as { error?: string } | null;
|
|
62
|
+
throw new Error(body?.error ?? `Vault API ${res.status} ${path}`);
|
|
63
|
+
}
|
|
64
|
+
if (res.status === 204) return undefined as T;
|
|
65
|
+
return (await res.json()) as T;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
listProjects(): Promise<ProjectTree[]> {
|
|
69
|
+
return this.request("/v1/projects");
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
createProject(name: string): Promise<{ id: string; name: string }> {
|
|
73
|
+
return this.request("/v1/projects", {
|
|
74
|
+
method: "POST",
|
|
75
|
+
body: JSON.stringify({ name }),
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
createEnvironment(projectId: string, name: string): Promise<{ ok: boolean }> {
|
|
80
|
+
return this.request(`/v1/projects/${projectId}/environments`, {
|
|
81
|
+
method: "POST",
|
|
82
|
+
body: JSON.stringify({ name }),
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
listVars(environmentId: string): Promise<EnvVar[]> {
|
|
87
|
+
return this.request(`/v1/environments/${environmentId}/vars`);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
upsertVar(environmentId: string, key: string, value: string): Promise<{ ok: boolean }> {
|
|
91
|
+
return this.request(`/v1/environments/${environmentId}/vars`, {
|
|
92
|
+
method: "PUT",
|
|
93
|
+
body: JSON.stringify({ key, value }),
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export function createClientFromEnv(
|
|
99
|
+
env: NodeJS.ProcessEnv = process.env,
|
|
100
|
+
): VaultClient {
|
|
101
|
+
const baseUrl =
|
|
102
|
+
env.VAULT_URL ?? env.VAULT_API_URL ?? env.API_URL ?? DEFAULT_VAULT_URL;
|
|
103
|
+
const tokenPath = env.VAULT_SA_TOKEN_PATH;
|
|
104
|
+
return new VaultClient({
|
|
105
|
+
baseUrl,
|
|
106
|
+
token: env.VAULT_SA_TOKEN,
|
|
107
|
+
tokenPaths: tokenPath ? [tokenPath, ...DEFAULT_TOKEN_PATHS] : undefined,
|
|
108
|
+
});
|
|
109
|
+
}
|
package/src/dotenv.ts
CHANGED
|
@@ -1,22 +1,22 @@
|
|
|
1
|
-
export function toDotEnv(vars: Record<string, string>): string {
|
|
2
|
-
return (
|
|
3
|
-
Object.entries(vars)
|
|
4
|
-
.sort(([a], [b]) => a.localeCompare(b))
|
|
5
|
-
.map(([key, value]) => `${key}=${escapeEnvValue(value)}`)
|
|
6
|
-
.join("\n") + "\n"
|
|
7
|
-
);
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
function escapeEnvValue(value: string): string {
|
|
11
|
-
if (value === "") return '""';
|
|
12
|
-
if (/^[A-Za-z0-9_./:@%+=,-]+$/.test(value)) return value;
|
|
13
|
-
return JSON.stringify(value);
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export function varsToRecord(
|
|
17
|
-
rows: { key: string; value: string }[],
|
|
18
|
-
): Record<string, string> {
|
|
19
|
-
const out: Record<string, string> = {};
|
|
20
|
-
for (const row of rows) out[row.key] = row.value;
|
|
21
|
-
return out;
|
|
22
|
-
}
|
|
1
|
+
export function toDotEnv(vars: Record<string, string>): string {
|
|
2
|
+
return (
|
|
3
|
+
Object.entries(vars)
|
|
4
|
+
.sort(([a], [b]) => a.localeCompare(b))
|
|
5
|
+
.map(([key, value]) => `${key}=${escapeEnvValue(value)}`)
|
|
6
|
+
.join("\n") + "\n"
|
|
7
|
+
);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function escapeEnvValue(value: string): string {
|
|
11
|
+
if (value === "") return '""';
|
|
12
|
+
if (/^[A-Za-z0-9_./:@%+=,-]+$/.test(value)) return value;
|
|
13
|
+
return JSON.stringify(value);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function varsToRecord(
|
|
17
|
+
rows: { key: string; value: string }[],
|
|
18
|
+
): Record<string, string> {
|
|
19
|
+
const out: Record<string, string> = {};
|
|
20
|
+
for (const row of rows) out[row.key] = row.value;
|
|
21
|
+
return out;
|
|
22
|
+
}
|