envilder
Version:
A CLI and GitHub Action that securely centralizes your environment variables from AWS SSM or Azure Key Vault as a single source of truth
168 lines • 6.29 kB
JavaScript
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
import { readFile } from 'node:fs/promises';
import { createSecretProvider } from '../infrastructure/secret-provider-factory.js';
import { EnvilderClient } from './envilder-client.js';
import { MapFileParser } from './map-file-parser.js';
/**
* Facade for loading secrets from cloud providers.
*
* Supports loading from a single map file or from an
* environment-based mapping that routes each environment name
* to its own map file (or `null` to skip).
*
* @example
* ```typescript
* // One-liner — resolve + inject into process.env:
* await Envilder.load('envilder.json');
*
* // Resolve without injecting:
* const secrets = await Envilder.resolveFile('envilder.json');
*
* // Fluent builder with provider override:
* const secrets = await Envilder.fromMapFile('envilder.json')
* .withProvider(SecretProviderType.Azure)
* .withVaultUrl('https://my-vault.vault.azure.net')
* .inject();
* ```
*/
export class Envilder {
constructor(filePath) {
this.options = {};
this.filePath = filePath;
}
/**
* Returns a fluent builder bound to the given map file.
*
* Chain `.withProvider()`, `.withVaultUrl()`, or `.withProfile()`
* before calling `.resolve()` or `.inject()`.
*/
static fromMapFile(filePath) {
validateFilePath(filePath);
return new Envilder(filePath.trim());
}
/**
* Resolves secrets and injects them into `process.env`.
*
* Can be called in two ways:
* - `load(filePath)` — load from a single map file
* - `load(env, envMapping)` — look up env in the mapping
*/
static load(filePathOrEnv, envMapping) {
return __awaiter(this, void 0, void 0, function* () {
if (envMapping !== undefined) {
const source = resolveEnvSource(filePathOrEnv, envMapping);
if (source === null) {
return new Map();
}
return new Envilder(source).inject();
}
validateFilePath(filePathOrEnv);
return new Envilder(filePathOrEnv.trim()).inject();
});
}
/**
* Resolves secrets without injecting into `process.env`.
*
* Can be called in two ways:
* - `resolveFile(filePath)` — resolve from a single map file
* - `resolveFile(env, envMapping)` — look up env in the mapping
*/
static resolveFile(filePathOrEnv, envMapping) {
return __awaiter(this, void 0, void 0, function* () {
if (envMapping !== undefined) {
const source = resolveEnvSource(filePathOrEnv, envMapping);
if (source === null) {
return new Map();
}
return new Envilder(source).resolve();
}
validateFilePath(filePathOrEnv);
return new Envilder(filePathOrEnv.trim()).resolve();
});
}
/** Override the secret provider (AWS or Azure). */
withProvider(provider) {
this.options.provider = provider;
return this;
}
/** Override the Azure Key Vault URL. */
withVaultUrl(vaultUrl) {
this.options.vaultUrl = vaultUrl;
return this;
}
/** Override the AWS named profile. */
withProfile(profile) {
this.options.profile = profile;
return this;
}
/** Resolve secrets and return them as a Map. */
resolve() {
return __awaiter(this, void 0, void 0, function* () {
const mapFile = yield this.parseFile();
const options = this.buildOptions();
const provider = createSecretProvider(mapFile.config, options);
const client = new EnvilderClient(provider);
return client.resolveSecrets(mapFile);
});
}
/** Resolve secrets, inject into `process.env`, and return them. */
inject() {
return __awaiter(this, void 0, void 0, function* () {
const secrets = yield this.resolve();
EnvilderClient.injectIntoEnvironment(secrets);
return secrets;
});
}
parseFile() {
return __awaiter(this, void 0, void 0, function* () {
let json;
try {
json = yield readFile(this.filePath, 'utf-8');
}
catch (err) {
if (err instanceof Error && 'code' in err && err.code === 'ENOENT') {
throw new Error(`Map file not found: ${this.filePath}`);
}
throw err;
}
return new MapFileParser().parse(json);
});
}
buildOptions() {
const hasOverrides = this.options.provider !== undefined ||
this.options.vaultUrl !== undefined ||
this.options.profile !== undefined;
return hasOverrides ? this.options : undefined;
}
}
function validateFilePath(filePath) {
if (!(filePath === null || filePath === void 0 ? void 0 : filePath.trim())) {
throw new Error('file path cannot be empty');
}
}
function resolveEnvSource(env, envMapping) {
if (!(env === null || env === void 0 ? void 0 : env.trim())) {
throw new Error('env cannot be empty');
}
const normalized = env.trim();
if (!Object.hasOwn(envMapping, normalized)) {
return null;
}
const source = envMapping[normalized];
if (source === null) {
return null;
}
if (!source.trim()) {
throw new Error(`envMapping contains an empty file path for environment '${normalized}'.`);
}
return source.trim();
}
//# sourceMappingURL=envilder.js.map