@adonisjs/env
Version:
Environment variable manager for Node.js
235 lines (234 loc) • 7.42 kB
JavaScript
import { n as debug_default, t as EnvLoader } from "./loader-KsjAiZ3e.js";
import "node:module";
import { parseEnv } from "node:util";
import { readFile } from "node:fs/promises";
import { Exception, RuntimeException, createError } from "@poppinss/utils/exception";
import { Secret } from "@poppinss/utils";
import { schema } from "@poppinss/validator-lite";
var __defProp = Object.defineProperty;
var __exportAll = (all, no_symbols) => {
let target = {};
for (var name in all) __defProp(target, name, {
get: all[name],
enumerable: true
});
if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
return target;
};
var errors_exports = /* @__PURE__ */ __exportAll({
E_IDENTIFIER_ALREADY_DEFINED: () => E_IDENTIFIER_ALREADY_DEFINED,
E_INVALID_ENV_VARIABLES: () => E_INVALID_ENV_VARIABLES
});
const E_INVALID_ENV_VARIABLES = class EnvValidationException extends Exception {
static message = "Validation failed for one or more environment variables";
static code = "E_INVALID_ENV_VARIABLES";
help = "";
};
const E_IDENTIFIER_ALREADY_DEFINED = createError("The identifier \"%s\" is already defined", "E_IDENTIFIER_ALREADY_DEFINED", 500);
var EnvParser = class EnvParser {
#envContents;
#appRoot;
#preferProcessEnv = true;
static #identifiers = { async file(value, key, appRoot) {
const filePath = new URL(value, appRoot);
try {
return await readFile(filePath, "utf-8");
} catch (error) {
if (error.code === "ENOENT") throw new RuntimeException(`Cannot process "${key}" env variable. Unable to locate file "${filePath}"`, { cause: error });
throw error;
}
} };
constructor(envContents, appRoot, options) {
if (options?.ignoreProcessEnv) this.#preferProcessEnv = false;
this.#envContents = envContents;
this.#appRoot = appRoot;
}
static identifier(name, callback) {
EnvParser.defineIdentifier(name, callback);
}
static defineIdentifier(name, callback) {
if (this.#identifiers[name]) throw new E_IDENTIFIER_ALREADY_DEFINED([name]);
this.#identifiers[name] = callback;
}
static defineIdentifierIfMissing(name, callback) {
if (typeof this.#identifiers[name] === "undefined") this.#identifiers[name] = callback;
}
static removeIdentifier(name) {
delete this.#identifiers[name];
}
#getValue(key, parsed) {
if (this.#preferProcessEnv && process.env[key]) return process.env[key];
if (parsed[key]) return this.#interpolate(parsed[key], parsed);
return process.env[key] || "";
}
#interpolateMustache(token, parsed) {
const closingBrace = token.indexOf("}");
if (closingBrace === -1) return token;
const varReference = token.slice(1, closingBrace).trim();
return `${this.#getValue(varReference, parsed)}${token.slice(closingBrace + 1)}`;
}
#interpolateVariable(token, parsed) {
return token.replace(/[a-zA-Z0-9_]+/, (key) => {
return this.#getValue(key, parsed);
});
}
#interpolate(value, parsed) {
const tokens = value.split("$");
let newValue = "";
let skipNextToken = true;
tokens.forEach((token) => {
if (token === "\\") {
newValue += "$";
skipNextToken = true;
return;
}
if (skipNextToken) {
newValue += token.replace(/\\$/, "$");
if (token.endsWith("\\")) return;
} else {
if (token.startsWith("{")) {
newValue += this.#interpolateMustache(token, parsed);
return;
}
newValue += this.#interpolateVariable(token, parsed);
}
skipNextToken = false;
});
return newValue;
}
async parse() {
const envCollection = parseEnv(this.#envContents.trim());
const identifiers = Object.keys(EnvParser.#identifiers);
let result = {};
$keyLoop: for (const key in envCollection) {
const value = this.#getValue(key, envCollection);
if (value.includes(":")) {
for (const identifier of identifiers) {
if (value.startsWith(`${identifier}:`)) {
result[key] = await EnvParser.#identifiers[identifier](value.substring(identifier.length + 1), key, this.#appRoot);
continue $keyLoop;
}
if (value.startsWith(`${identifier}\\:`)) {
result[key] = identifier + value.substring(identifier.length + 1);
continue $keyLoop;
}
}
result[key] = value;
} else result[key] = value;
}
return result;
}
};
var EnvValidator = class {
#schema;
#error;
constructor(schema) {
this.#schema = schema;
this.#error = new E_INVALID_ENV_VARIABLES();
}
validate(values) {
const help = [];
const validated = Object.keys(this.#schema).reduce((result, key) => {
const value = process.env[key] || values[key];
try {
result[key] = this.#schema[key](key, value);
} catch (error) {
help.push(`- ${error.message}`);
}
return result;
}, { ...values });
if (help.length) {
this.#error.help = help.join("\n");
throw this.#error;
}
return validated;
}
};
var EnvProcessor = class {
#appRoot;
constructor(appRoot) {
this.#appRoot = appRoot;
}
async #processContents(envContents, store) {
if (!envContents.trim()) return store;
const values = await new EnvParser(envContents, this.#appRoot).parse();
Object.keys(values).forEach((key) => {
let value = process.env[key];
if (value === void 0) {
value = values[key];
process.env[key] = values[key];
}
if (key in store === false) store[key] = value;
});
return store;
}
async #loadAndProcessDotFiles() {
const envFiles = await new EnvLoader(this.#appRoot).load();
if (debug_default.enabled) debug_default("processing .env files (priority from top to bottom) %O", envFiles.map((file) => file.path));
const envValues = {};
await Promise.all(envFiles.map(({ contents }) => this.#processContents(contents, envValues)));
return envValues;
}
async process() {
return this.#loadAndProcessDotFiles();
}
};
function secret(options) {
return function validate(key, value) {
if (!value) throw new Error(options?.message ?? `Missing environment variable "${key}"`);
return new Secret(value);
};
}
secret.optional = function optionalString() {
return function validate(_, value) {
if (!value) return;
return new Secret(value);
};
};
secret.optionalWhen = function optionalWhenString(condition, options) {
return function validate(key, value) {
if (typeof condition === "function" ? condition(key, value) : condition) return secret.optional()(key, value);
return secret(options)(key, value);
};
};
const schema$1 = {
...schema,
secret
};
var Env = class Env {
#values;
constructor(values) {
this.#values = values;
}
static async create(appRoot, schema) {
const values = await new EnvProcessor(appRoot).process();
return new Env(this.rules(schema).validate(values));
}
static identifier(name, callback) {
return EnvParser.defineIdentifier(name, callback);
}
static defineIdentifier(name, callback) {
EnvParser.defineIdentifier(name, callback);
}
static defineIdentifierIfMissing(name, callback) {
EnvParser.defineIdentifierIfMissing(name, callback);
}
static removeIdentifier(name) {
EnvParser.removeIdentifier(name);
}
static schema = schema$1;
static rules(schema) {
return new EnvValidator(schema);
}
get(key, defaultValue) {
if (this.#values[key] !== void 0) return this.#values[key];
const envValue = process.env[key];
if (envValue) return envValue;
return defaultValue;
}
set(key, value) {
this.#values[key] = value;
process.env[key] = value;
}
};
export { Env, EnvLoader, EnvParser, EnvProcessor, errors_exports as errors };