@aprovan/node 0.1.0 → 0.1.1-dev.53db668
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/chunk-XZEPXBX5.js +326 -0
- package/dist/chunk-XZEPXBX5.js.map +1 -0
- package/dist/cli.js +1 -1
- package/dist/index.d.ts +65 -1
- package/dist/index.js +3 -1
- package/package.json +1 -1
- package/dist/chunk-QVRIXCGD.js +0 -89
- package/dist/chunk-QVRIXCGD.js.map +0 -1
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __export = (target, all) => {
|
|
3
|
+
for (var name in all)
|
|
4
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
// src/index.ts
|
|
8
|
+
import { GetParameterCommand, SSMClient } from "@aws-sdk/client-ssm";
|
|
9
|
+
|
|
10
|
+
// src/dotenv.ts
|
|
11
|
+
var dotenv_exports = {};
|
|
12
|
+
__export(dotenv_exports, {
|
|
13
|
+
config: () => config,
|
|
14
|
+
decrypt: () => decrypt,
|
|
15
|
+
parse: () => parse,
|
|
16
|
+
populate: () => populate
|
|
17
|
+
});
|
|
18
|
+
import fs from "fs";
|
|
19
|
+
import path from "path";
|
|
20
|
+
import os from "os";
|
|
21
|
+
import crypto from "crypto";
|
|
22
|
+
var createError = (message, code) => {
|
|
23
|
+
const err = new Error(message);
|
|
24
|
+
err.code = code;
|
|
25
|
+
return err;
|
|
26
|
+
};
|
|
27
|
+
var parseBoolean = (value) => typeof value === "string" ? !["false", "0", "no", "off", ""].includes(value.toLowerCase()) : Boolean(value);
|
|
28
|
+
var supportsAnsi = () => process.stdout.isTTY;
|
|
29
|
+
var dim = (text) => supportsAnsi() ? `\x1B[2m${text}\x1B[0m` : text;
|
|
30
|
+
var log = (msg) => console.log(`[dotenv] ${msg}`);
|
|
31
|
+
var debug = (msg) => console.debug(`[dotenv:debug] ${msg}`);
|
|
32
|
+
var warn = (msg) => console.warn(`[dotenv:warn] ${msg}`);
|
|
33
|
+
var resolveHome = (envPath) => envPath.startsWith("~") ? path.join(os.homedir(), envPath.slice(1)) : envPath;
|
|
34
|
+
var LINE_REGEX = /(?:^|^)\s*(?:export\s+)?([\w.-]+)(?:\s*=\s*?|:\s+?)(\s*'(?:\\'|[^'])*'|\s*"(?:\\"|[^"])*"|\s*`(?:\\`|[^`])*`|[^#\r\n]+)?\s*(?:#.*)?(?:$|$)/gm;
|
|
35
|
+
var parse = (src) => {
|
|
36
|
+
const lines = src.toString().replace(/\r\n?/gm, "\n");
|
|
37
|
+
const result = {};
|
|
38
|
+
let match;
|
|
39
|
+
while ((match = LINE_REGEX.exec(lines)) !== null) {
|
|
40
|
+
const key = match[1];
|
|
41
|
+
if (!key) continue;
|
|
42
|
+
let value = (match[2] ?? "").trim();
|
|
43
|
+
const quote = value[0];
|
|
44
|
+
value = value.replace(/^(['"`])([\s\S]*)\1$/gm, "$2");
|
|
45
|
+
if (quote === '"') {
|
|
46
|
+
value = value.replace(/\\n/g, "\n").replace(/\\r/g, "\r");
|
|
47
|
+
}
|
|
48
|
+
result[key] = value;
|
|
49
|
+
}
|
|
50
|
+
return result;
|
|
51
|
+
};
|
|
52
|
+
var decrypt = (encrypted, keyStr) => {
|
|
53
|
+
const key = Buffer.from(keyStr.slice(-64), "hex");
|
|
54
|
+
const cipherBuffer = Buffer.from(encrypted, "base64");
|
|
55
|
+
const nonce = cipherBuffer.subarray(0, 12);
|
|
56
|
+
const authTag = cipherBuffer.subarray(-16);
|
|
57
|
+
const ciphertext = cipherBuffer.subarray(12, -16);
|
|
58
|
+
try {
|
|
59
|
+
const decipher = crypto.createDecipheriv("aes-256-gcm", key, nonce);
|
|
60
|
+
decipher.setAuthTag(authTag);
|
|
61
|
+
return decipher.update(ciphertext).toString() + decipher.final().toString();
|
|
62
|
+
} catch (error) {
|
|
63
|
+
const err = error;
|
|
64
|
+
const isRange = error instanceof RangeError;
|
|
65
|
+
const invalidKeyLength = err.message === "Invalid key length";
|
|
66
|
+
const decryptionFailed = err.message === "Unsupported state or unable to authenticate data";
|
|
67
|
+
if (isRange || invalidKeyLength) {
|
|
68
|
+
throw createError(
|
|
69
|
+
"INVALID_DOTENV_KEY: It must be 64 characters long (or more)",
|
|
70
|
+
"INVALID_DOTENV_KEY"
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
if (decryptionFailed) {
|
|
74
|
+
throw createError(
|
|
75
|
+
"DECRYPTION_FAILED: Please check your DOTENV_KEY",
|
|
76
|
+
"DECRYPTION_FAILED"
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
throw error;
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
var populate = (target, source, options = {}) => {
|
|
83
|
+
const { debug: showDebug = false, override = false } = options;
|
|
84
|
+
const populated = {};
|
|
85
|
+
if (typeof source !== "object" || source === null) {
|
|
86
|
+
throw createError(
|
|
87
|
+
"OBJECT_REQUIRED: Please check the argument being passed to populate",
|
|
88
|
+
"OBJECT_REQUIRED"
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
for (const [key, value] of Object.entries(source)) {
|
|
92
|
+
const exists = Object.prototype.hasOwnProperty.call(target, key);
|
|
93
|
+
if (exists && !override) {
|
|
94
|
+
if (showDebug) debug(`"${key}" already defined, not overwritten`);
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
if (exists && showDebug) {
|
|
98
|
+
debug(`"${key}" already defined, overwritten`);
|
|
99
|
+
}
|
|
100
|
+
target[key] = value;
|
|
101
|
+
populated[key] = value;
|
|
102
|
+
}
|
|
103
|
+
return populated;
|
|
104
|
+
};
|
|
105
|
+
var getDotenvKey = (options) => options?.DOTENV_KEY || process.env["DOTENV_KEY"] || "";
|
|
106
|
+
var findVaultPath = (options) => {
|
|
107
|
+
const toVaultPath = (p) => p.endsWith(".vault") ? p : `${p}.vault`;
|
|
108
|
+
if (options?.path) {
|
|
109
|
+
const paths = Array.isArray(options.path) ? options.path : [options.path];
|
|
110
|
+
const found = paths.find((p) => fs.existsSync(p));
|
|
111
|
+
if (found) {
|
|
112
|
+
const vaultPath = toVaultPath(found);
|
|
113
|
+
return fs.existsSync(vaultPath) ? vaultPath : null;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
const defaultPath = path.resolve(process.cwd(), ".env.vault");
|
|
117
|
+
return fs.existsSync(defaultPath) ? defaultPath : null;
|
|
118
|
+
};
|
|
119
|
+
var parseVaultKey = (dotenvKey, parsed) => {
|
|
120
|
+
let uri;
|
|
121
|
+
try {
|
|
122
|
+
uri = new URL(dotenvKey);
|
|
123
|
+
} catch (error) {
|
|
124
|
+
const err = error;
|
|
125
|
+
if (err.code === "ERR_INVALID_URL") {
|
|
126
|
+
throw createError(
|
|
127
|
+
"INVALID_DOTENV_KEY: Wrong format. Must be valid URI like dotenv://:key_1234@dotenvx.com/vault/.env.vault?environment=development",
|
|
128
|
+
"INVALID_DOTENV_KEY"
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
throw error;
|
|
132
|
+
}
|
|
133
|
+
const key = uri.password;
|
|
134
|
+
if (!key) {
|
|
135
|
+
throw createError(
|
|
136
|
+
"INVALID_DOTENV_KEY: Missing key part",
|
|
137
|
+
"INVALID_DOTENV_KEY"
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
const environment = uri.searchParams.get("environment");
|
|
141
|
+
if (!environment) {
|
|
142
|
+
throw createError(
|
|
143
|
+
"INVALID_DOTENV_KEY: Missing environment part",
|
|
144
|
+
"INVALID_DOTENV_KEY"
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
const envKey = `DOTENV_VAULT_${environment.toUpperCase()}`;
|
|
148
|
+
const ciphertext = parsed[envKey];
|
|
149
|
+
if (!ciphertext) {
|
|
150
|
+
throw createError(
|
|
151
|
+
`NOT_FOUND_DOTENV_ENVIRONMENT: Cannot locate ${envKey} in .env.vault`,
|
|
152
|
+
"NOT_FOUND_DOTENV_ENVIRONMENT"
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
return { ciphertext, key };
|
|
156
|
+
};
|
|
157
|
+
var parseVault = (options) => {
|
|
158
|
+
const vaultPath = findVaultPath(options);
|
|
159
|
+
const vaultResult = configDotenv({
|
|
160
|
+
...options,
|
|
161
|
+
path: vaultPath ?? void 0
|
|
162
|
+
});
|
|
163
|
+
if (!vaultResult.parsed || Object.keys(vaultResult.parsed).length === 0) {
|
|
164
|
+
throw createError(
|
|
165
|
+
`MISSING_DATA: Cannot parse ${vaultPath} for an unknown reason`,
|
|
166
|
+
"MISSING_DATA"
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
const keys = getDotenvKey(options).split(",");
|
|
170
|
+
let lastError = null;
|
|
171
|
+
for (const rawKey of keys) {
|
|
172
|
+
try {
|
|
173
|
+
const { ciphertext, key } = parseVaultKey(
|
|
174
|
+
rawKey.trim(),
|
|
175
|
+
vaultResult.parsed
|
|
176
|
+
);
|
|
177
|
+
return parse(decrypt(ciphertext, key));
|
|
178
|
+
} catch (error) {
|
|
179
|
+
lastError = error;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
throw lastError ?? createError("DECRYPTION_FAILED", "DECRYPTION_FAILED");
|
|
183
|
+
};
|
|
184
|
+
var configVault = (options) => {
|
|
185
|
+
const showDebug = parseBoolean(
|
|
186
|
+
process.env["DOTENV_CONFIG_DEBUG"] ?? options.debug
|
|
187
|
+
);
|
|
188
|
+
const quiet = parseBoolean(
|
|
189
|
+
process.env["DOTENV_CONFIG_QUIET"] ?? options.quiet
|
|
190
|
+
);
|
|
191
|
+
if (showDebug || !quiet) {
|
|
192
|
+
log("Loading env from encrypted .env.vault");
|
|
193
|
+
}
|
|
194
|
+
const parsed = parseVault(options);
|
|
195
|
+
const target = options.processEnv ?? process.env;
|
|
196
|
+
populate(target, parsed, options);
|
|
197
|
+
return { parsed };
|
|
198
|
+
};
|
|
199
|
+
var configDotenv = (options) => {
|
|
200
|
+
const target = options?.processEnv ?? process.env;
|
|
201
|
+
const encoding = options?.encoding ?? "utf8";
|
|
202
|
+
let showDebug = parseBoolean(
|
|
203
|
+
target["DOTENV_CONFIG_DEBUG"] ?? options?.debug ?? false
|
|
204
|
+
);
|
|
205
|
+
let quiet = parseBoolean(
|
|
206
|
+
target["DOTENV_CONFIG_QUIET"] ?? options?.quiet ?? false
|
|
207
|
+
);
|
|
208
|
+
const defaultPath = path.resolve(process.cwd(), ".env");
|
|
209
|
+
const paths = options?.path ? (Array.isArray(options.path) ? options.path : [options.path]).map(
|
|
210
|
+
resolveHome
|
|
211
|
+
) : [defaultPath];
|
|
212
|
+
const parsedAll = {};
|
|
213
|
+
let lastError;
|
|
214
|
+
for (const filePath of paths) {
|
|
215
|
+
try {
|
|
216
|
+
const content = fs.readFileSync(filePath, { encoding });
|
|
217
|
+
const parsed = parse(content);
|
|
218
|
+
populate(parsedAll, parsed, options);
|
|
219
|
+
} catch (e) {
|
|
220
|
+
if (showDebug)
|
|
221
|
+
debug(`Failed to load ${filePath}: ${e.message}`);
|
|
222
|
+
lastError = e;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
const populated = populate(target, parsedAll, options);
|
|
226
|
+
showDebug = parseBoolean(target["DOTENV_CONFIG_DEBUG"] ?? showDebug);
|
|
227
|
+
quiet = parseBoolean(target["DOTENV_CONFIG_QUIET"] ?? quiet);
|
|
228
|
+
if (showDebug || !quiet) {
|
|
229
|
+
const count = Object.keys(populated).length;
|
|
230
|
+
const shortPaths = paths.map((p) => {
|
|
231
|
+
try {
|
|
232
|
+
return path.relative(process.cwd(), p);
|
|
233
|
+
} catch {
|
|
234
|
+
return p;
|
|
235
|
+
}
|
|
236
|
+
}).join(", ");
|
|
237
|
+
log(
|
|
238
|
+
`injected ${count} env var(s) from ${shortPaths} ${dim(
|
|
239
|
+
"-- tip: set DOTENV_CONFIG_QUIET=true to silence"
|
|
240
|
+
)}`
|
|
241
|
+
);
|
|
242
|
+
}
|
|
243
|
+
return lastError ? { parsed: parsedAll, error: lastError } : { parsed: parsedAll };
|
|
244
|
+
};
|
|
245
|
+
var config = (options) => {
|
|
246
|
+
const dotenvKey = getDotenvKey(options);
|
|
247
|
+
if (!dotenvKey) {
|
|
248
|
+
return configDotenv(options);
|
|
249
|
+
}
|
|
250
|
+
const vaultPath = findVaultPath(options);
|
|
251
|
+
if (!vaultPath) {
|
|
252
|
+
warn(
|
|
253
|
+
`DOTENV_KEY is set but .env.vault file not found. Did you forget to build it?`
|
|
254
|
+
);
|
|
255
|
+
return configDotenv(options);
|
|
256
|
+
}
|
|
257
|
+
return configVault(options ?? {});
|
|
258
|
+
};
|
|
259
|
+
|
|
260
|
+
// src/index.ts
|
|
261
|
+
var aprovanEnvParameterName = (environment = "prd") => `/aprovan/${environment}/env`;
|
|
262
|
+
async function getAprovanEnv(environment = "prd", client = new SSMClient({})) {
|
|
263
|
+
const response = await client.send(
|
|
264
|
+
new GetParameterCommand({
|
|
265
|
+
Name: aprovanEnvParameterName(environment),
|
|
266
|
+
WithDecryption: true
|
|
267
|
+
})
|
|
268
|
+
);
|
|
269
|
+
const raw = response.Parameter?.Value;
|
|
270
|
+
if (raw === void 0) {
|
|
271
|
+
throw new Error(`Aprovan environment parameter ${environment} has no value`);
|
|
272
|
+
}
|
|
273
|
+
return { raw, values: parse(raw) };
|
|
274
|
+
}
|
|
275
|
+
async function loadAprovanEnv(environment = "prd", options = { overwrite: true }) {
|
|
276
|
+
const { values } = await getAprovanEnv(environment, options.client);
|
|
277
|
+
for (const [key, value] of Object.entries(values)) {
|
|
278
|
+
if (options.overwrite !== false || process.env[key] === void 0) {
|
|
279
|
+
process.env[key] = value;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
return values;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
export {
|
|
286
|
+
dotenv_exports,
|
|
287
|
+
aprovanEnvParameterName,
|
|
288
|
+
getAprovanEnv,
|
|
289
|
+
loadAprovanEnv
|
|
290
|
+
};
|
|
291
|
+
/**
|
|
292
|
+
* Dotenv - Load environment variables from .env files
|
|
293
|
+
*
|
|
294
|
+
* Adapted from dotenv by motdotla
|
|
295
|
+
* @see https://github.com/motdotla/dotenv/blob/master/lib/main.js
|
|
296
|
+
* @license BSD-2-Clause
|
|
297
|
+
*
|
|
298
|
+
* @example
|
|
299
|
+
* // Basic usage - loads from .env in current directory
|
|
300
|
+
* import { config } from './dotenv';
|
|
301
|
+
* config();
|
|
302
|
+
*
|
|
303
|
+
* @example
|
|
304
|
+
* // Custom path
|
|
305
|
+
* config({ path: '/custom/path/.env' });
|
|
306
|
+
*
|
|
307
|
+
* @example
|
|
308
|
+
* // Multiple files (loaded in order, later values don't override)
|
|
309
|
+
* config({ path: ['.env.local', '.env'] });
|
|
310
|
+
*
|
|
311
|
+
* @example
|
|
312
|
+
* // Override existing env vars
|
|
313
|
+
* config({ override: true });
|
|
314
|
+
*
|
|
315
|
+
* @example
|
|
316
|
+
* // Encrypted vault (requires DOTENV_KEY env var)
|
|
317
|
+
* // Automatically loads from .env.vault when DOTENV_KEY is set
|
|
318
|
+
* config();
|
|
319
|
+
*
|
|
320
|
+
* @example
|
|
321
|
+
* // Parse a string directly
|
|
322
|
+
* import { parse } from './dotenv';
|
|
323
|
+
* const env = parse('FOO=bar\nBAZ=qux');
|
|
324
|
+
* // => { FOO: 'bar', BAZ: 'qux' }
|
|
325
|
+
*/
|
|
326
|
+
//# sourceMappingURL=chunk-XZEPXBX5.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/dotenv.ts"],"sourcesContent":["import { GetParameterCommand, SSMClient } from \"@aws-sdk/client-ssm\";\nimport * as dotenv from './dotenv';\n\nexport const aprovanEnvParameterName = (environment = \"prd\"): string =>\n `/aprovan/${environment}/env`;\n\nexport interface LoadAprovanEnvOptions {\n client?: SSMClient;\n overwrite?: boolean;\n}\n\nexport async function getAprovanEnv(\n environment = \"prd\",\n client = new SSMClient({}),\n): Promise<{ raw: string; values: Record<string, string> }> {\n const response = await client.send(\n new GetParameterCommand({\n Name: aprovanEnvParameterName(environment),\n WithDecryption: true,\n }),\n );\n const raw = response.Parameter?.Value;\n if (raw === undefined) {\n throw new Error(`Aprovan environment parameter ${environment} has no value`);\n }\n return { raw, values: dotenv.parse(raw) };\n}\n\nexport async function loadAprovanEnv(\n environment = \"prd\",\n options: LoadAprovanEnvOptions = { overwrite: true },\n): Promise<Record<string, string>> {\n const { values } = await getAprovanEnv(environment, options.client);\n for (const [key, value] of Object.entries(values)) {\n if (options.overwrite !== false || process.env[key] === undefined) {\n process.env[key] = value;\n }\n }\n return values;\n}\n\nexport { dotenv };\n","/**\n * Dotenv - Load environment variables from .env files\n *\n * Adapted from dotenv by motdotla\n * @see https://github.com/motdotla/dotenv/blob/master/lib/main.js\n * @license BSD-2-Clause\n *\n * @example\n * // Basic usage - loads from .env in current directory\n * import { config } from './dotenv';\n * config();\n *\n * @example\n * // Custom path\n * config({ path: '/custom/path/.env' });\n *\n * @example\n * // Multiple files (loaded in order, later values don't override)\n * config({ path: ['.env.local', '.env'] });\n *\n * @example\n * // Override existing env vars\n * config({ override: true });\n *\n * @example\n * // Encrypted vault (requires DOTENV_KEY env var)\n * // Automatically loads from .env.vault when DOTENV_KEY is set\n * config();\n *\n * @example\n * // Parse a string directly\n * import { parse } from './dotenv';\n * const env = parse('FOO=bar\\nBAZ=qux');\n * // => { FOO: 'bar', BAZ: 'qux' }\n */\n\nimport fs from 'fs';\nimport path from 'path';\nimport os from 'os';\nimport crypto from 'crypto';\n\ntype EnvRecord = Record<string, string>;\ntype ProcessEnv = NodeJS.ProcessEnv | EnvRecord;\n\ninterface DotenvError extends Error {\n code: string;\n}\n\ninterface DotenvOptions {\n path?: string | string[];\n encoding?: BufferEncoding;\n debug?: boolean;\n quiet?: boolean;\n override?: boolean;\n processEnv?: ProcessEnv;\n DOTENV_KEY?: string;\n}\n\ninterface DotenvResult {\n parsed: EnvRecord;\n error?: Error;\n}\n\nconst createError = (message: string, code: string): DotenvError => {\n const err = new Error(message) as DotenvError;\n err.code = code;\n return err;\n};\n\nconst parseBoolean = (value: unknown): boolean =>\n typeof value === 'string'\n ? !['false', '0', 'no', 'off', ''].includes(value.toLowerCase())\n : Boolean(value);\n\nconst supportsAnsi = (): boolean => process.stdout.isTTY;\n\nconst dim = (text: string): string =>\n supportsAnsi() ? `\\x1b[2m${text}\\x1b[0m` : text;\n\nconst log = (msg: string): void => console.log(`[dotenv] ${msg}`);\nconst debug = (msg: string): void => console.debug(`[dotenv:debug] ${msg}`);\nconst warn = (msg: string): void => console.warn(`[dotenv:warn] ${msg}`);\n\nconst resolveHome = (envPath: string): string =>\n envPath.startsWith('~') ? path.join(os.homedir(), envPath.slice(1)) : envPath;\n\nconst LINE_REGEX =\n /(?:^|^)\\s*(?:export\\s+)?([\\w.-]+)(?:\\s*=\\s*?|:\\s+?)(\\s*'(?:\\\\'|[^'])*'|\\s*\"(?:\\\\\"|[^\"])*\"|\\s*`(?:\\\\`|[^`])*`|[^#\\r\\n]+)?\\s*(?:#.*)?(?:$|$)/gm;\n\nconst parse = (src: string | Buffer): EnvRecord => {\n const lines = src.toString().replace(/\\r\\n?/gm, '\\n');\n const result: EnvRecord = {};\n let match: RegExpExecArray | null;\n\n while ((match = LINE_REGEX.exec(lines)) !== null) {\n const key = match[1];\n if (!key) continue;\n\n let value = (match[2] ?? '').trim();\n const quote = value[0];\n\n // Remove surrounding quotes\n value = value.replace(/^(['\"`])([\\s\\S]*)\\1$/gm, '$2');\n\n // Expand escape sequences for double-quoted strings\n if (quote === '\"') {\n value = value.replace(/\\\\n/g, '\\n').replace(/\\\\r/g, '\\r');\n }\n\n result[key] = value;\n }\n\n return result;\n};\n\nconst decrypt = (encrypted: string, keyStr: string): string => {\n const key = Buffer.from(keyStr.slice(-64), 'hex');\n const cipherBuffer = Buffer.from(encrypted, 'base64');\n\n const nonce = cipherBuffer.subarray(0, 12);\n const authTag = cipherBuffer.subarray(-16);\n const ciphertext = cipherBuffer.subarray(12, -16);\n\n try {\n const decipher = crypto.createDecipheriv('aes-256-gcm', key, nonce);\n decipher.setAuthTag(authTag);\n return decipher.update(ciphertext).toString() + decipher.final().toString();\n } catch (error) {\n const err = error as Error;\n const isRange = error instanceof RangeError;\n const invalidKeyLength = err.message === 'Invalid key length';\n const decryptionFailed =\n err.message === 'Unsupported state or unable to authenticate data';\n\n if (isRange || invalidKeyLength) {\n throw createError(\n 'INVALID_DOTENV_KEY: It must be 64 characters long (or more)',\n 'INVALID_DOTENV_KEY',\n );\n }\n if (decryptionFailed) {\n throw createError(\n 'DECRYPTION_FAILED: Please check your DOTENV_KEY',\n 'DECRYPTION_FAILED',\n );\n }\n throw error;\n }\n};\n\nconst populate = (\n target: ProcessEnv,\n source: EnvRecord,\n options: Pick<DotenvOptions, 'debug' | 'override'> = {},\n): EnvRecord => {\n const { debug: showDebug = false, override = false } = options;\n const populated: EnvRecord = {};\n\n if (typeof source !== 'object' || source === null) {\n throw createError(\n 'OBJECT_REQUIRED: Please check the argument being passed to populate',\n 'OBJECT_REQUIRED',\n );\n }\n\n for (const [key, value] of Object.entries(source)) {\n const exists = Object.prototype.hasOwnProperty.call(target, key);\n\n if (exists && !override) {\n if (showDebug) debug(`\"${key}\" already defined, not overwritten`);\n continue;\n }\n\n if (exists && showDebug) {\n debug(`\"${key}\" already defined, overwritten`);\n }\n\n target[key] = value;\n populated[key] = value;\n }\n\n return populated;\n};\n\nconst getDotenvKey = (options?: DotenvOptions): string =>\n options?.DOTENV_KEY || process.env['DOTENV_KEY'] || '';\n\nconst findVaultPath = (options?: DotenvOptions): string | null => {\n const toVaultPath = (p: string): string =>\n p.endsWith('.vault') ? p : `${p}.vault`;\n\n if (options?.path) {\n const paths = Array.isArray(options.path) ? options.path : [options.path];\n const found = paths.find((p) => fs.existsSync(p));\n if (found) {\n const vaultPath = toVaultPath(found);\n return fs.existsSync(vaultPath) ? vaultPath : null;\n }\n }\n\n const defaultPath = path.resolve(process.cwd(), '.env.vault');\n return fs.existsSync(defaultPath) ? defaultPath : null;\n};\n\nconst parseVaultKey = (\n dotenvKey: string,\n parsed: EnvRecord,\n): { ciphertext: string; key: string } => {\n let uri: URL;\n try {\n uri = new URL(dotenvKey);\n } catch (error) {\n const err = error as NodeJS.ErrnoException;\n if (err.code === 'ERR_INVALID_URL') {\n throw createError(\n 'INVALID_DOTENV_KEY: Wrong format. Must be valid URI like dotenv://:key_1234@dotenvx.com/vault/.env.vault?environment=development',\n 'INVALID_DOTENV_KEY',\n );\n }\n throw error;\n }\n\n const key = uri.password;\n if (!key) {\n throw createError(\n 'INVALID_DOTENV_KEY: Missing key part',\n 'INVALID_DOTENV_KEY',\n );\n }\n\n const environment = uri.searchParams.get('environment');\n if (!environment) {\n throw createError(\n 'INVALID_DOTENV_KEY: Missing environment part',\n 'INVALID_DOTENV_KEY',\n );\n }\n\n const envKey = `DOTENV_VAULT_${environment.toUpperCase()}`;\n const ciphertext = parsed[envKey];\n if (!ciphertext) {\n throw createError(\n `NOT_FOUND_DOTENV_ENVIRONMENT: Cannot locate ${envKey} in .env.vault`,\n 'NOT_FOUND_DOTENV_ENVIRONMENT',\n );\n }\n\n return { ciphertext, key };\n};\n\nconst parseVault = (options: DotenvOptions): EnvRecord => {\n const vaultPath = findVaultPath(options);\n const vaultResult = configDotenv({\n ...options,\n path: vaultPath ?? undefined,\n });\n\n if (!vaultResult.parsed || Object.keys(vaultResult.parsed).length === 0) {\n throw createError(\n `MISSING_DATA: Cannot parse ${vaultPath} for an unknown reason`,\n 'MISSING_DATA',\n );\n }\n\n const keys = getDotenvKey(options).split(',');\n let lastError: Error | null = null;\n\n for (const rawKey of keys) {\n try {\n const { ciphertext, key } = parseVaultKey(\n rawKey.trim(),\n vaultResult.parsed,\n );\n return parse(decrypt(ciphertext, key));\n } catch (error) {\n lastError = error as Error;\n }\n }\n\n throw lastError ?? createError('DECRYPTION_FAILED', 'DECRYPTION_FAILED');\n};\n\nconst configVault = (options: DotenvOptions): DotenvResult => {\n const showDebug = parseBoolean(\n process.env['DOTENV_CONFIG_DEBUG'] ?? options.debug,\n );\n const quiet = parseBoolean(\n process.env['DOTENV_CONFIG_QUIET'] ?? options.quiet,\n );\n\n if (showDebug || !quiet) {\n log('Loading env from encrypted .env.vault');\n }\n\n const parsed = parseVault(options);\n const target = options.processEnv ?? process.env;\n populate(target, parsed, options);\n\n return { parsed };\n};\n\nconst configDotenv = (options?: DotenvOptions): DotenvResult => {\n const target = options?.processEnv ?? process.env;\n const encoding: BufferEncoding = options?.encoding ?? 'utf8';\n let showDebug = parseBoolean(\n target['DOTENV_CONFIG_DEBUG'] ?? options?.debug ?? false,\n );\n let quiet = parseBoolean(\n target['DOTENV_CONFIG_QUIET'] ?? options?.quiet ?? false,\n );\n\n const defaultPath = path.resolve(process.cwd(), '.env');\n const paths = options?.path\n ? (Array.isArray(options.path) ? options.path : [options.path]).map(\n resolveHome,\n )\n : [defaultPath];\n\n const parsedAll: EnvRecord = {};\n let lastError: Error | undefined;\n\n for (const filePath of paths) {\n try {\n const content = fs.readFileSync(filePath, { encoding });\n const parsed = parse(content);\n populate(parsedAll, parsed, options);\n } catch (e) {\n if (showDebug)\n debug(`Failed to load ${filePath}: ${(e as Error).message}`);\n lastError = e as Error;\n }\n }\n\n const populated = populate(target, parsedAll, options);\n\n // Re-check settings from loaded .env\n showDebug = parseBoolean(target['DOTENV_CONFIG_DEBUG'] ?? showDebug);\n quiet = parseBoolean(target['DOTENV_CONFIG_QUIET'] ?? quiet);\n\n if (showDebug || !quiet) {\n const count = Object.keys(populated).length;\n const shortPaths = paths\n .map((p) => {\n try {\n return path.relative(process.cwd(), p);\n } catch {\n return p;\n }\n })\n .join(', ');\n log(\n `injected ${count} env var(s) from ${shortPaths} ${dim(\n '-- tip: set DOTENV_CONFIG_QUIET=true to silence',\n )}`,\n );\n }\n\n return lastError\n ? { parsed: parsedAll, error: lastError }\n : { parsed: parsedAll };\n};\n\n/** Load environment variables from .env file(s) */\nexport const config = (options?: DotenvOptions): DotenvResult => {\n const dotenvKey = getDotenvKey(options);\n\n if (!dotenvKey) {\n return configDotenv(options);\n }\n\n const vaultPath = findVaultPath(options);\n if (!vaultPath) {\n warn(\n `DOTENV_KEY is set but .env.vault file not found. Did you forget to build it?`,\n );\n return configDotenv(options);\n }\n\n return configVault(options ?? {});\n};\n\nexport { parse, populate, decrypt };\n"],"mappings":"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","names":[]}
|
package/dist/cli.js
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,69 @@
|
|
|
1
1
|
import { SSMClient } from '@aws-sdk/client-ssm';
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Dotenv - Load environment variables from .env files
|
|
5
|
+
*
|
|
6
|
+
* Adapted from dotenv by motdotla
|
|
7
|
+
* @see https://github.com/motdotla/dotenv/blob/master/lib/main.js
|
|
8
|
+
* @license BSD-2-Clause
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* // Basic usage - loads from .env in current directory
|
|
12
|
+
* import { config } from './dotenv';
|
|
13
|
+
* config();
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* // Custom path
|
|
17
|
+
* config({ path: '/custom/path/.env' });
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* // Multiple files (loaded in order, later values don't override)
|
|
21
|
+
* config({ path: ['.env.local', '.env'] });
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* // Override existing env vars
|
|
25
|
+
* config({ override: true });
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* // Encrypted vault (requires DOTENV_KEY env var)
|
|
29
|
+
* // Automatically loads from .env.vault when DOTENV_KEY is set
|
|
30
|
+
* config();
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* // Parse a string directly
|
|
34
|
+
* import { parse } from './dotenv';
|
|
35
|
+
* const env = parse('FOO=bar\nBAZ=qux');
|
|
36
|
+
* // => { FOO: 'bar', BAZ: 'qux' }
|
|
37
|
+
*/
|
|
38
|
+
type EnvRecord = Record<string, string>;
|
|
39
|
+
type ProcessEnv = NodeJS.ProcessEnv | EnvRecord;
|
|
40
|
+
interface DotenvOptions {
|
|
41
|
+
path?: string | string[];
|
|
42
|
+
encoding?: BufferEncoding;
|
|
43
|
+
debug?: boolean;
|
|
44
|
+
quiet?: boolean;
|
|
45
|
+
override?: boolean;
|
|
46
|
+
processEnv?: ProcessEnv;
|
|
47
|
+
DOTENV_KEY?: string;
|
|
48
|
+
}
|
|
49
|
+
interface DotenvResult {
|
|
50
|
+
parsed: EnvRecord;
|
|
51
|
+
error?: Error;
|
|
52
|
+
}
|
|
53
|
+
declare const parse: (src: string | Buffer) => EnvRecord;
|
|
54
|
+
declare const decrypt: (encrypted: string, keyStr: string) => string;
|
|
55
|
+
declare const populate: (target: ProcessEnv, source: EnvRecord, options?: Pick<DotenvOptions, "debug" | "override">) => EnvRecord;
|
|
56
|
+
/** Load environment variables from .env file(s) */
|
|
57
|
+
declare const config: (options?: DotenvOptions) => DotenvResult;
|
|
58
|
+
|
|
59
|
+
declare const dotenv_config: typeof config;
|
|
60
|
+
declare const dotenv_decrypt: typeof decrypt;
|
|
61
|
+
declare const dotenv_parse: typeof parse;
|
|
62
|
+
declare const dotenv_populate: typeof populate;
|
|
63
|
+
declare namespace dotenv {
|
|
64
|
+
export { dotenv_config as config, dotenv_decrypt as decrypt, dotenv_parse as parse, dotenv_populate as populate };
|
|
65
|
+
}
|
|
66
|
+
|
|
3
67
|
declare const aprovanEnvParameterName: (environment?: string) => string;
|
|
4
68
|
interface LoadAprovanEnvOptions {
|
|
5
69
|
client?: SSMClient;
|
|
@@ -11,4 +75,4 @@ declare function getAprovanEnv(environment?: string, client?: SSMClient): Promis
|
|
|
11
75
|
}>;
|
|
12
76
|
declare function loadAprovanEnv(environment?: string, options?: LoadAprovanEnvOptions): Promise<Record<string, string>>;
|
|
13
77
|
|
|
14
|
-
export { type LoadAprovanEnvOptions, aprovanEnvParameterName, getAprovanEnv, loadAprovanEnv };
|
|
78
|
+
export { type LoadAprovanEnvOptions, aprovanEnvParameterName, dotenv, getAprovanEnv, loadAprovanEnv };
|
package/dist/index.js
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
2
|
aprovanEnvParameterName,
|
|
3
|
+
dotenv_exports,
|
|
3
4
|
getAprovanEnv,
|
|
4
5
|
loadAprovanEnv
|
|
5
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-XZEPXBX5.js";
|
|
6
7
|
export {
|
|
7
8
|
aprovanEnvParameterName,
|
|
9
|
+
dotenv_exports as dotenv,
|
|
8
10
|
getAprovanEnv,
|
|
9
11
|
loadAprovanEnv
|
|
10
12
|
};
|
package/package.json
CHANGED
package/dist/chunk-QVRIXCGD.js
DELETED
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
// src/index.ts
|
|
2
|
-
import { GetParameterCommand, SSMClient } from "@aws-sdk/client-ssm";
|
|
3
|
-
|
|
4
|
-
// src/dotenv.ts
|
|
5
|
-
var LINE_REGEX = /(?:^|^)\s*(?:export\s+)?([\w.-]+)(?:\s*=\s*?|:\s+?)(\s*'(?:\\'|[^'])*'|\s*"(?:\\"|[^"])*"|\s*`(?:\\`|[^`])*`|[^#\r\n]+)?\s*(?:#.*)?(?:$|$)/gm;
|
|
6
|
-
var parse = (src) => {
|
|
7
|
-
const lines = src.toString().replace(/\r\n?/gm, "\n");
|
|
8
|
-
const result = {};
|
|
9
|
-
let match;
|
|
10
|
-
while ((match = LINE_REGEX.exec(lines)) !== null) {
|
|
11
|
-
const key = match[1];
|
|
12
|
-
if (!key) continue;
|
|
13
|
-
let value = (match[2] ?? "").trim();
|
|
14
|
-
const quote = value[0];
|
|
15
|
-
value = value.replace(/^(['"`])([\s\S]*)\1$/gm, "$2");
|
|
16
|
-
if (quote === '"') {
|
|
17
|
-
value = value.replace(/\\n/g, "\n").replace(/\\r/g, "\r");
|
|
18
|
-
}
|
|
19
|
-
result[key] = value;
|
|
20
|
-
}
|
|
21
|
-
return result;
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
// src/index.ts
|
|
25
|
-
var aprovanEnvParameterName = (environment = "prd") => `/aprovan/${environment}/env`;
|
|
26
|
-
async function getAprovanEnv(environment = "prd", client = new SSMClient({})) {
|
|
27
|
-
const response = await client.send(
|
|
28
|
-
new GetParameterCommand({
|
|
29
|
-
Name: aprovanEnvParameterName(environment),
|
|
30
|
-
WithDecryption: true
|
|
31
|
-
})
|
|
32
|
-
);
|
|
33
|
-
const raw = response.Parameter?.Value;
|
|
34
|
-
if (raw === void 0) {
|
|
35
|
-
throw new Error(`Aprovan environment parameter ${environment} has no value`);
|
|
36
|
-
}
|
|
37
|
-
return { raw, values: parse(raw) };
|
|
38
|
-
}
|
|
39
|
-
async function loadAprovanEnv(environment = "prd", options = { overwrite: true }) {
|
|
40
|
-
const { values } = await getAprovanEnv(environment, options.client);
|
|
41
|
-
for (const [key, value] of Object.entries(values)) {
|
|
42
|
-
if (options.overwrite !== false || process.env[key] === void 0) {
|
|
43
|
-
process.env[key] = value;
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
return values;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
export {
|
|
50
|
-
aprovanEnvParameterName,
|
|
51
|
-
getAprovanEnv,
|
|
52
|
-
loadAprovanEnv
|
|
53
|
-
};
|
|
54
|
-
/**
|
|
55
|
-
* Dotenv - Load environment variables from .env files
|
|
56
|
-
*
|
|
57
|
-
* Adapted from dotenv by motdotla
|
|
58
|
-
* @see https://github.com/motdotla/dotenv/blob/master/lib/main.js
|
|
59
|
-
* @license BSD-2-Clause
|
|
60
|
-
*
|
|
61
|
-
* @example
|
|
62
|
-
* // Basic usage - loads from .env in current directory
|
|
63
|
-
* import { config } from './dotenv';
|
|
64
|
-
* config();
|
|
65
|
-
*
|
|
66
|
-
* @example
|
|
67
|
-
* // Custom path
|
|
68
|
-
* config({ path: '/custom/path/.env' });
|
|
69
|
-
*
|
|
70
|
-
* @example
|
|
71
|
-
* // Multiple files (loaded in order, later values don't override)
|
|
72
|
-
* config({ path: ['.env.local', '.env'] });
|
|
73
|
-
*
|
|
74
|
-
* @example
|
|
75
|
-
* // Override existing env vars
|
|
76
|
-
* config({ override: true });
|
|
77
|
-
*
|
|
78
|
-
* @example
|
|
79
|
-
* // Encrypted vault (requires DOTENV_KEY env var)
|
|
80
|
-
* // Automatically loads from .env.vault when DOTENV_KEY is set
|
|
81
|
-
* config();
|
|
82
|
-
*
|
|
83
|
-
* @example
|
|
84
|
-
* // Parse a string directly
|
|
85
|
-
* import { parse } from './dotenv';
|
|
86
|
-
* const env = parse('FOO=bar\nBAZ=qux');
|
|
87
|
-
* // => { FOO: 'bar', BAZ: 'qux' }
|
|
88
|
-
*/
|
|
89
|
-
//# sourceMappingURL=chunk-QVRIXCGD.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/dotenv.ts"],"sourcesContent":["import { GetParameterCommand, SSMClient } from \"@aws-sdk/client-ssm\";\nimport { parse } from \"./dotenv\";\n\nexport const aprovanEnvParameterName = (environment = \"prd\"): string =>\n `/aprovan/${environment}/env`;\n\nexport interface LoadAprovanEnvOptions {\n client?: SSMClient;\n overwrite?: boolean;\n}\n\nexport async function getAprovanEnv(\n environment = \"prd\",\n client = new SSMClient({}),\n): Promise<{ raw: string; values: Record<string, string> }> {\n const response = await client.send(\n new GetParameterCommand({\n Name: aprovanEnvParameterName(environment),\n WithDecryption: true,\n }),\n );\n const raw = response.Parameter?.Value;\n if (raw === undefined) {\n throw new Error(`Aprovan environment parameter ${environment} has no value`);\n }\n return { raw, values: parse(raw) };\n}\n\nexport async function loadAprovanEnv(\n environment = \"prd\",\n options: LoadAprovanEnvOptions = { overwrite: true },\n): Promise<Record<string, string>> {\n const { values } = await getAprovanEnv(environment, options.client);\n for (const [key, value] of Object.entries(values)) {\n if (options.overwrite !== false || process.env[key] === undefined) {\n process.env[key] = value;\n }\n }\n return values;\n}\n","/**\n * Dotenv - Load environment variables from .env files\n *\n * Adapted from dotenv by motdotla\n * @see https://github.com/motdotla/dotenv/blob/master/lib/main.js\n * @license BSD-2-Clause\n *\n * @example\n * // Basic usage - loads from .env in current directory\n * import { config } from './dotenv';\n * config();\n *\n * @example\n * // Custom path\n * config({ path: '/custom/path/.env' });\n *\n * @example\n * // Multiple files (loaded in order, later values don't override)\n * config({ path: ['.env.local', '.env'] });\n *\n * @example\n * // Override existing env vars\n * config({ override: true });\n *\n * @example\n * // Encrypted vault (requires DOTENV_KEY env var)\n * // Automatically loads from .env.vault when DOTENV_KEY is set\n * config();\n *\n * @example\n * // Parse a string directly\n * import { parse } from './dotenv';\n * const env = parse('FOO=bar\\nBAZ=qux');\n * // => { FOO: 'bar', BAZ: 'qux' }\n */\n\nimport fs from 'fs';\nimport path from 'path';\nimport os from 'os';\nimport crypto from 'crypto';\n\ntype EnvRecord = Record<string, string>;\ntype ProcessEnv = NodeJS.ProcessEnv | EnvRecord;\n\ninterface DotenvError extends Error {\n code: string;\n}\n\ninterface DotenvOptions {\n path?: string | string[];\n encoding?: BufferEncoding;\n debug?: boolean;\n quiet?: boolean;\n override?: boolean;\n processEnv?: ProcessEnv;\n DOTENV_KEY?: string;\n}\n\ninterface DotenvResult {\n parsed: EnvRecord;\n error?: Error;\n}\n\nconst createError = (message: string, code: string): DotenvError => {\n const err = new Error(message) as DotenvError;\n err.code = code;\n return err;\n};\n\nconst parseBoolean = (value: unknown): boolean =>\n typeof value === 'string'\n ? !['false', '0', 'no', 'off', ''].includes(value.toLowerCase())\n : Boolean(value);\n\nconst supportsAnsi = (): boolean => process.stdout.isTTY;\n\nconst dim = (text: string): string =>\n supportsAnsi() ? `\\x1b[2m${text}\\x1b[0m` : text;\n\nconst log = (msg: string): void => console.log(`[dotenv] ${msg}`);\nconst debug = (msg: string): void => console.debug(`[dotenv:debug] ${msg}`);\nconst warn = (msg: string): void => console.warn(`[dotenv:warn] ${msg}`);\n\nconst resolveHome = (envPath: string): string =>\n envPath.startsWith('~') ? path.join(os.homedir(), envPath.slice(1)) : envPath;\n\nconst LINE_REGEX =\n /(?:^|^)\\s*(?:export\\s+)?([\\w.-]+)(?:\\s*=\\s*?|:\\s+?)(\\s*'(?:\\\\'|[^'])*'|\\s*\"(?:\\\\\"|[^\"])*\"|\\s*`(?:\\\\`|[^`])*`|[^#\\r\\n]+)?\\s*(?:#.*)?(?:$|$)/gm;\n\nconst parse = (src: string | Buffer): EnvRecord => {\n const lines = src.toString().replace(/\\r\\n?/gm, '\\n');\n const result: EnvRecord = {};\n let match: RegExpExecArray | null;\n\n while ((match = LINE_REGEX.exec(lines)) !== null) {\n const key = match[1];\n if (!key) continue;\n\n let value = (match[2] ?? '').trim();\n const quote = value[0];\n\n // Remove surrounding quotes\n value = value.replace(/^(['\"`])([\\s\\S]*)\\1$/gm, '$2');\n\n // Expand escape sequences for double-quoted strings\n if (quote === '\"') {\n value = value.replace(/\\\\n/g, '\\n').replace(/\\\\r/g, '\\r');\n }\n\n result[key] = value;\n }\n\n return result;\n};\n\nconst decrypt = (encrypted: string, keyStr: string): string => {\n const key = Buffer.from(keyStr.slice(-64), 'hex');\n const cipherBuffer = Buffer.from(encrypted, 'base64');\n\n const nonce = cipherBuffer.subarray(0, 12);\n const authTag = cipherBuffer.subarray(-16);\n const ciphertext = cipherBuffer.subarray(12, -16);\n\n try {\n const decipher = crypto.createDecipheriv('aes-256-gcm', key, nonce);\n decipher.setAuthTag(authTag);\n return decipher.update(ciphertext).toString() + decipher.final().toString();\n } catch (error) {\n const err = error as Error;\n const isRange = error instanceof RangeError;\n const invalidKeyLength = err.message === 'Invalid key length';\n const decryptionFailed =\n err.message === 'Unsupported state or unable to authenticate data';\n\n if (isRange || invalidKeyLength) {\n throw createError(\n 'INVALID_DOTENV_KEY: It must be 64 characters long (or more)',\n 'INVALID_DOTENV_KEY',\n );\n }\n if (decryptionFailed) {\n throw createError(\n 'DECRYPTION_FAILED: Please check your DOTENV_KEY',\n 'DECRYPTION_FAILED',\n );\n }\n throw error;\n }\n};\n\nconst populate = (\n target: ProcessEnv,\n source: EnvRecord,\n options: Pick<DotenvOptions, 'debug' | 'override'> = {},\n): EnvRecord => {\n const { debug: showDebug = false, override = false } = options;\n const populated: EnvRecord = {};\n\n if (typeof source !== 'object' || source === null) {\n throw createError(\n 'OBJECT_REQUIRED: Please check the argument being passed to populate',\n 'OBJECT_REQUIRED',\n );\n }\n\n for (const [key, value] of Object.entries(source)) {\n const exists = Object.prototype.hasOwnProperty.call(target, key);\n\n if (exists && !override) {\n if (showDebug) debug(`\"${key}\" already defined, not overwritten`);\n continue;\n }\n\n if (exists && showDebug) {\n debug(`\"${key}\" already defined, overwritten`);\n }\n\n target[key] = value;\n populated[key] = value;\n }\n\n return populated;\n};\n\nconst getDotenvKey = (options?: DotenvOptions): string =>\n options?.DOTENV_KEY || process.env.DOTENV_KEY || '';\n\nconst findVaultPath = (options?: DotenvOptions): string | null => {\n const toVaultPath = (p: string): string =>\n p.endsWith('.vault') ? p : `${p}.vault`;\n\n if (options?.path) {\n const paths = Array.isArray(options.path) ? options.path : [options.path];\n const found = paths.find((p) => fs.existsSync(p));\n if (found) {\n const vaultPath = toVaultPath(found);\n return fs.existsSync(vaultPath) ? vaultPath : null;\n }\n }\n\n const defaultPath = path.resolve(process.cwd(), '.env.vault');\n return fs.existsSync(defaultPath) ? defaultPath : null;\n};\n\nconst parseVaultKey = (\n dotenvKey: string,\n parsed: EnvRecord,\n): { ciphertext: string; key: string } => {\n let uri: URL;\n try {\n uri = new URL(dotenvKey);\n } catch (error) {\n const err = error as NodeJS.ErrnoException;\n if (err.code === 'ERR_INVALID_URL') {\n throw createError(\n 'INVALID_DOTENV_KEY: Wrong format. Must be valid URI like dotenv://:key_1234@dotenvx.com/vault/.env.vault?environment=development',\n 'INVALID_DOTENV_KEY',\n );\n }\n throw error;\n }\n\n const key = uri.password;\n if (!key) {\n throw createError(\n 'INVALID_DOTENV_KEY: Missing key part',\n 'INVALID_DOTENV_KEY',\n );\n }\n\n const environment = uri.searchParams.get('environment');\n if (!environment) {\n throw createError(\n 'INVALID_DOTENV_KEY: Missing environment part',\n 'INVALID_DOTENV_KEY',\n );\n }\n\n const envKey = `DOTENV_VAULT_${environment.toUpperCase()}`;\n const ciphertext = parsed[envKey];\n if (!ciphertext) {\n throw createError(\n `NOT_FOUND_DOTENV_ENVIRONMENT: Cannot locate ${envKey} in .env.vault`,\n 'NOT_FOUND_DOTENV_ENVIRONMENT',\n );\n }\n\n return { ciphertext, key };\n};\n\nconst parseVault = (options: DotenvOptions): EnvRecord => {\n const vaultPath = findVaultPath(options);\n const vaultResult = configDotenv({\n ...options,\n path: vaultPath ?? undefined,\n });\n\n if (!vaultResult.parsed || Object.keys(vaultResult.parsed).length === 0) {\n throw createError(\n `MISSING_DATA: Cannot parse ${vaultPath} for an unknown reason`,\n 'MISSING_DATA',\n );\n }\n\n const keys = getDotenvKey(options).split(',');\n let lastError: Error | null = null;\n\n for (const rawKey of keys) {\n try {\n const { ciphertext, key } = parseVaultKey(\n rawKey.trim(),\n vaultResult.parsed,\n );\n return parse(decrypt(ciphertext, key));\n } catch (error) {\n lastError = error as Error;\n }\n }\n\n throw lastError ?? createError('DECRYPTION_FAILED', 'DECRYPTION_FAILED');\n};\n\nconst configVault = (options: DotenvOptions): DotenvResult => {\n const showDebug = parseBoolean(\n process.env.DOTENV_CONFIG_DEBUG ?? options.debug,\n );\n const quiet = parseBoolean(process.env.DOTENV_CONFIG_QUIET ?? options.quiet);\n\n if (showDebug || !quiet) {\n log('Loading env from encrypted .env.vault');\n }\n\n const parsed = parseVault(options);\n const target = options.processEnv ?? process.env;\n populate(target, parsed, options);\n\n return { parsed };\n};\n\nconst configDotenv = (options?: DotenvOptions): DotenvResult => {\n const target = options?.processEnv ?? process.env;\n const encoding: BufferEncoding = options?.encoding ?? 'utf8';\n let showDebug = parseBoolean(\n target.DOTENV_CONFIG_DEBUG ?? options?.debug ?? false,\n );\n let quiet = parseBoolean(\n target.DOTENV_CONFIG_QUIET ?? options?.quiet ?? false,\n );\n\n const defaultPath = path.resolve(process.cwd(), '.env');\n const paths = options?.path\n ? (Array.isArray(options.path) ? options.path : [options.path]).map(\n resolveHome,\n )\n : [defaultPath];\n\n const parsedAll: EnvRecord = {};\n let lastError: Error | undefined;\n\n for (const filePath of paths) {\n try {\n const content = fs.readFileSync(filePath, { encoding });\n const parsed = parse(content);\n populate(parsedAll, parsed, options);\n } catch (e) {\n if (showDebug)\n debug(`Failed to load ${filePath}: ${(e as Error).message}`);\n lastError = e as Error;\n }\n }\n\n const populated = populate(target, parsedAll, options);\n\n // Re-check settings from loaded .env\n showDebug = parseBoolean(target.DOTENV_CONFIG_DEBUG ?? showDebug);\n quiet = parseBoolean(target.DOTENV_CONFIG_QUIET ?? quiet);\n\n if (showDebug || !quiet) {\n const count = Object.keys(populated).length;\n const shortPaths = paths\n .map((p) => {\n try {\n return path.relative(process.cwd(), p);\n } catch {\n return p;\n }\n })\n .join(', ');\n log(\n `injected ${count} env var(s) from ${shortPaths} ${dim(\n '-- tip: set DOTENV_CONFIG_QUIET=true to silence',\n )}`,\n );\n }\n\n return lastError\n ? { parsed: parsedAll, error: lastError }\n : { parsed: parsedAll };\n};\n\n/** Load environment variables from .env file(s) */\nexport const config = (options?: DotenvOptions): DotenvResult => {\n const dotenvKey = getDotenvKey(options);\n\n if (!dotenvKey) {\n return configDotenv(options);\n }\n\n const vaultPath = findVaultPath(options);\n if (!vaultPath) {\n warn(\n `DOTENV_KEY is set but .env.vault file not found. Did you forget to build it?`,\n );\n return configDotenv(options);\n }\n\n return configVault(options ?? {});\n};\n\nexport { parse, populate, decrypt };\n"],"mappings":";AAAA,SAAS,qBAAqB,iBAAiB;;;ACsF/C,IAAM,aACJ;AAEF,IAAM,QAAQ,CAAC,QAAoC;AACjD,QAAM,QAAQ,IAAI,SAAS,EAAE,QAAQ,WAAW,IAAI;AACpD,QAAM,SAAoB,CAAC;AAC3B,MAAI;AAEJ,UAAQ,QAAQ,WAAW,KAAK,KAAK,OAAO,MAAM;AAChD,UAAM,MAAM,MAAM,CAAC;AACnB,QAAI,CAAC,IAAK;AAEV,QAAI,SAAS,MAAM,CAAC,KAAK,IAAI,KAAK;AAClC,UAAM,QAAQ,MAAM,CAAC;AAGrB,YAAQ,MAAM,QAAQ,0BAA0B,IAAI;AAGpD,QAAI,UAAU,KAAK;AACjB,cAAQ,MAAM,QAAQ,QAAQ,IAAI,EAAE,QAAQ,QAAQ,IAAI;AAAA,IAC1D;AAEA,WAAO,GAAG,IAAI;AAAA,EAChB;AAEA,SAAO;AACT;;;AD9GO,IAAM,0BAA0B,CAAC,cAAc,UACpD,YAAY,WAAW;AAOzB,eAAsB,cACpB,cAAc,OACd,SAAS,IAAI,UAAU,CAAC,CAAC,GACiC;AAC1D,QAAM,WAAW,MAAM,OAAO;AAAA,IAC5B,IAAI,oBAAoB;AAAA,MACtB,MAAM,wBAAwB,WAAW;AAAA,MACzC,gBAAgB;AAAA,IAClB,CAAC;AAAA,EACH;AACA,QAAM,MAAM,SAAS,WAAW;AAChC,MAAI,QAAQ,QAAW;AACrB,UAAM,IAAI,MAAM,iCAAiC,WAAW,eAAe;AAAA,EAC7E;AACA,SAAO,EAAE,KAAK,QAAQ,MAAM,GAAG,EAAE;AACnC;AAEA,eAAsB,eACpB,cAAc,OACd,UAAiC,EAAE,WAAW,KAAK,GAClB;AACjC,QAAM,EAAE,OAAO,IAAI,MAAM,cAAc,aAAa,QAAQ,MAAM;AAClE,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,MAAM,GAAG;AACjD,QAAI,QAAQ,cAAc,SAAS,QAAQ,IAAI,GAAG,MAAM,QAAW;AACjE,cAAQ,IAAI,GAAG,IAAI;AAAA,IACrB;AAAA,EACF;AACA,SAAO;AACT;","names":[]}
|