Big News: Socket raises $60M Series C at a $1B valuation to secure software supply chains for AI-driven development.Announcement
Sign In

@avanio/variable-util

Package Overview
Dependencies
Maintainers
3
Versions
66
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@avanio/variable-util - npm Package Compare versions

Comparing version
1.2.1
to
1.2.2
+1704
dist/index.cjs
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
let _luolapeikko_ts_common = require("@luolapeikko/ts-common");
let _luolapeikko_result_option = require("@luolapeikko/result-option");
let events = require("events");
//#region src/types/RequestNotReady.ts
/**
* Type guard for RequestNotReady
* @param {unknown} obj - object to check if it is a RequestNotReady payload
* @returns {boolean} boolean indicating if the object is a RequestNotReady payload
* @category Utils
* @since v0.2.8
*/
function isRequestNotReadMessage(obj) {
return typeof obj === "object" && obj !== null && "_type" in obj && obj._type === "RequestNotReady" && "message" in obj && typeof obj.message === "string";
}
/**
* function to create a RequestNotReady payload to indicate that the request is not ready to be loaded yet. (e.g. the user is not logged in)
* @param {string} message - reason why the request is not ready yet
* @returns {RequestNotReady} RequestNotReady payload
* @category Utils
* @since v0.2.8
*/
function createRequestNotReady(message) {
return {
message,
_type: "RequestNotReady"
};
}
//#endregion
//#region src/logger.ts
let logger;
/**
* Set the logger to be used by the module
* @category Utils
* @param {ILoggerLike} newLogger - logger to be used
* @since v0.2.18
*/
function setLogger(newLogger) {
logger = newLogger;
}
/**
* Get current logger instance
* @category Utils
* @returns {ILoggerLike | undefined} - current logger
* @since v0.5.9
*/
function getLogger() {
return logger;
}
/**
* resolve logger: undefined = global logger, null = no logger else it's ILoggerLike
* @param {ILoggerLike | undefined | null} logger - logger to resolve
* @category Utils
* @returns {ILoggerLike | undefined} - resolved logger
* @since v0.5.9
*/
function resolveLogger(logger) {
if (logger === void 0) return getLogger();
return logger ?? void 0;
}
//#endregion
//#region src/ConfigOptions.ts
/**
* Build options from partial options
* @param {Partial<ConfigOptions>} options - partial config options
* @returns {SolvedConfigOptions} - solved config options
* @category Config
* @since v0.6.0
*/
function buildOptions({ logger, namespace } = {}) {
return {
logger: resolveLogger(logger),
namespace
};
}
//#endregion
//#region src/lib/seenUtils.ts
/**
* Function to check if we have seen a value before and update the seenMap
* @param {Map<string, string>} seenMap - Map of seen values
* @param {string} key - key to check
* @param {string | undefined} value - value to check
* @returns {boolean} - true if already seen value, false if not
* @since v1.0.0
*/
function handleSeen(seenMap, key, value) {
if (value === void 0) return false;
const seen = value === seenMap.get(key);
if (!seen) seenMap.set(key, value);
return seen;
}
//#endregion
//#region src/lib/formatUtils.ts
/**
* Sanitizes a URL by replacing the username and password with asterisks.
* @param {string} value - The URL to sanitize.
* @param {ILoggerLike} [logger] - An optional logger to use for logging warnings.
* @returns {string} The sanitized URL.
* @category Utils
* @since v0.2.5
*/
function urlSanitize(value, logger) {
try {
const url = new URL(value);
url.password = "*".repeat(url.password.length);
url.username = "*".repeat(url.username.length);
return url.href;
} catch (err) {
logger?.warn("variables:", err);
return value;
}
}
/**
* Returns a formatted string representation of a value, enclosed in square brackets.
* @param {string | undefined} value - The value to format.
* @param {FormatParameters | undefined} config - An optional configuration object.
* @returns {string} The formatted string representation of the value.
* @category Utils
* @since v0.2.5
*/
function printValue(value, config) {
if (!value || !config) return "";
return ` [${buildHiddenValue(value, config.showValue)}]`;
}
/**
* Constructs a hidden value string based on the specified visibility option.
* @param {string} value - The original string value to be hidden.
* @param {ShowValueType | undefined} show - Determines how the value should be displayed:
* - `true`: shows the full value
* - `undefined` or `false`: hides the value completely with asterisks
* - `PartialHiddenValueStringType`: shows part of the value (prefix, suffix, or both)
* @returns {string} The resulting string with the specified visibility.
* @since v0.2.5
*/
function buildHiddenValue(value, show) {
if (show === true) return value;
if (!show) return buildHiddenAsterisksValueString(value);
return buildPartialHiddenValueString(value, show);
}
/**
* Builds a hidden value string, replacing each character with an asterisk.
* @param {string} value - The value to be hidden.
* @returns {string} The hidden value string with asterisks.
* @since v0.2.5
*/
function buildHiddenAsterisksValueString(value) {
return "*".repeat(value.length);
}
/**
* Show only 1-3 characters of the secret value based on length of the value.
* @param {string} value - The value to partially hide.
* @param {PartialHiddenValueStringType} type - The type of partial hiding to apply ('prefix', 'suffix', or 'prefix-suffix').
* @returns {string} The partially hidden value string.
* @since v0.2.5
*/
function buildPartialHiddenValueString(value, type) {
const visibleCharacters = Math.min(3, Math.max(1, Math.floor(value.length * .2)));
switch (type) {
case "prefix": return `${value.slice(0, visibleCharacters)}${"*".repeat(value.length - visibleCharacters)}`;
case "suffix": return `${"*".repeat(value.length - visibleCharacters)}${value.slice(-visibleCharacters)}`;
case "prefix-suffix": {
const halfOfVisibleCharacters = Math.max(1, Math.ceil(visibleCharacters / 2));
return `${value.slice(0, halfOfVisibleCharacters)}${"*".repeat(value.length - halfOfVisibleCharacters * 2)}${value.slice(-halfOfVisibleCharacters)}`;
}
}
}
//#endregion
//#region src/VariableError.ts
/**
* Custom error class for variable errors.
* @class VariableError
* @augments Error
* @param {string} message - The error message.
* @category Errors
* @since v0.2.2
* @example
* throw new VariableError('Variable not found');
*/
var VariableError = class extends Error {
/**
* Create a new VariableError
* @param {string} message - The error message
* @param {ErrorOptions} [options] - The error options
*/
constructor(message, options) {
super(message, options);
this.name = "VariableError";
}
};
//#endregion
//#region src/loaderUtils.ts
/**
* function to log variable output
* @param {SolvedConfigOptions} options - options to use
* @param {string} type - type of variable
* @param {string} key - key of variable
* @param {string} value - value of variable
* @param {string} path - path of variable
* @param {FormatParameters} [params] - optional format parameters
* @returns {void}
* @category Utils
* @since v1.0.0
*/
function printLog({ logger, namespace }, type, key, value, path, params) {
const namespaceString = namespace ? `:${namespace}` : "";
logger?.info(`ConfigVariables${namespaceString}[${type}]: ${key}${printValue(value, params)} from ${path}`);
}
/**
* Rebuild Error as raw VariableError
* @param {unknown} err - error to rebuild
* @returns {VariableError} - rebuilt error
* @category Utils
* @since v0.12.0
*/
function handleAsVariableError(err) {
if (err instanceof VariableError) return err;
if (err instanceof Error) {
const varError = new VariableError(err.message);
varError.stack = err.stack;
return varError;
}
return new VariableError(`Unknown error ${JSON.stringify(err)}`);
}
/**
* Rebuild Error as VariableError
* @param {string} value - value of variable
* @param {Error} error - error to rebuild
* @param {IConfigLoader} loader - loader to use
* @param {IConfigParser<unknown, unknown>} parser - parser used
* @param {FormatParameters} [params] - optional format parameters
* @returns {VariableError} - rebuilt error
* @category Errors
* @since v1.0.0
*/
function rebuildAsVariableError(value, error, loader, parser, params) {
if (error instanceof VariableError) return error;
else {
const varError = new VariableError(`variables[${loader.loaderType}](${parser.name}):${printValue(value, params)} ${error.message}`);
varError.stack = error.stack;
return varError;
}
}
/**
* build error message for preValidate step
* @param {string} value - value of variable
* @param {unknown} err - error to rebuild
* @param {IConfigLoader} loader - loader to use
* @param {IConfigParser<unknown, unknown>} parser - parser used
* @param {FormatParameters} [params] - optional format parameters
* @returns {VariableError} - error message
* @category Errors
* @since v1.0.0
*/
function buildPreValidateErrorMessage(value, err, loader, parser, params) {
if (err instanceof Error) return rebuildAsVariableError(value, err, loader, parser, params);
else return new VariableError(`variables[${loader.loaderType}](${parser.name}):${printValue(value, params)} unknown preValidate error`);
}
/**
* build error message for parser step
* @param {string} value - value of variable
* @param {unknown} err - error to rebuild
* @param {IConfigLoader} loader - loader to use
* @param {IConfigParser<unknown, unknown>} parser - parser used
* @param {FormatParameters} [params] - optional format parameters
* @returns {VariableError} - error message
* @category Errors
* @since v1.0.0
*/
function buildParserErrorMessage(value, err, loader, parser, params) {
if (err instanceof Error) return rebuildAsVariableError(value, err, loader, parser, params);
else return new VariableError(`variables[${loader.loaderType}](${parser.name}):${printValue(value, params)} unknown parse error`);
}
/**
* build error message for postValidate step
* @param {string} value - value of variable
* @param {unknown} err - error to rebuild
* @param {IConfigLoader} loader - loader to use
* @param {IConfigParser<unknown, unknown>} parser - parser used
* @param {FormatParameters} [params] - optional format parameters
* @returns {VariableError} - error message
* @category Errors
* @since v1.0.0
*/
function buildPostValidateErrorMessage(value, err, loader, parser, params) {
if (err instanceof Error) return rebuildAsVariableError(value, err, loader, parser, params);
else return new VariableError(`variables[${loader.loaderType}](${parser.name}):${printValue(value, params)} unknown postValidate error`);
}
/**
* handle function for loader
* @template Input - Type of input value
* @template Output - Type of output value
* @param {string} rootKey - key of variable
* @param {IConfigLoader} loader - loader to use
* @param {IConfigParser<Output, RawOutput>} parser - parser to use
* @param {FormatParameters} [params] - optional format parameters
* @param {SolvedConfigOptions} options - options to use
* @param {EncodeOptions} [encodeOptions] - optional encode options
* @returns {Promise<{type: string; value: Output | undefined} | undefined>} - parsed value
* @since v1.0.0
*/
async function handleLoader(rootKey, loader, parser, params, options, encodeOptions) {
try {
if (await loader.isLoaderDisabled()) return;
const result = await loader.getLoaderResult(rootKey);
if (!result) return;
const { value, path, seen } = result;
if (value) {
/**
* parser pre-validate
*/
try {
await parser.preValidate?.({
key: rootKey,
value,
loader
});
} catch (err) {
throw buildPreValidateErrorMessage(value, err, loader, parser, params);
}
/**
* parse value
*/
let rawOutput;
try {
rawOutput = await parser.parse({
key: rootKey,
value,
loader
});
} catch (err) {
throw buildParserErrorMessage(value, err, loader, parser, params);
}
/**
* parser post-validate
*/
let output = rawOutput;
try {
const validateData = await parser.postValidate?.({
key: rootKey,
value: rawOutput,
loader
});
if (validateData) output = validateData;
} catch (err) {
throw buildPostValidateErrorMessage(value, err, loader, parser, params);
}
/**
* print log
*/
const stringValue = parser.toString(output, encodeOptions);
if (!seen && !encodeOptions?.silent) {
const logValue = parser.toLogString?.(output) ?? stringValue;
printLog(options, loader.loaderType, rootKey, logValue, path, params);
}
return {
namespace: options.namespace,
stringValue,
type: loader.loaderType,
value: output
};
}
} catch (err) {
options.logger?.error(err);
}
}
//#endregion
//#region src/getConfigObject.ts
/**
* Map of seen default values
*/
const defaultValueSeenMap = /* @__PURE__ */ new Map();
/**
* Clear the seen map for default values (for unit testing purposes)
* @since v0.2.5
*/
function clearDefaultValueSeenMap() {
defaultValueSeenMap.clear();
}
async function getConfigObject(rootKey, loaders, parser, defaultValueLoadable, params, options, encodeOptions) {
const currentOptions = buildOptions(options);
let defaultValue;
let type;
/**
* get default value before loaders (to throw error before loaders)
*/
if (defaultValueLoadable !== void 0) try {
defaultValue = await _luolapeikko_ts_common.LoadableCore.resolve(defaultValueLoadable);
type = "default";
} catch (err) {
currentOptions.logger?.error(err);
throw handleAsVariableError(err);
}
for (const loader of loaders) {
let output;
try {
output = await handleLoader(rootKey, loader, parser, params, currentOptions, encodeOptions);
} catch (err) {
currentOptions.logger?.error(err);
}
if (output !== void 0) return output;
}
let stringValue;
if (defaultValue !== void 0) {
stringValue = parser.toString(defaultValue, encodeOptions);
if (!handleSeen(defaultValueSeenMap, rootKey, stringValue) && !encodeOptions?.silent) printLog(currentOptions, "default", rootKey, stringValue, "default", params);
return {
namespace: currentOptions.namespace,
stringValue,
type: "default",
value: defaultValue
};
}
return {
namespace: currentOptions.namespace,
stringValue,
type,
value: defaultValue
};
}
//#endregion
//#region src/getConfigVariable.ts
async function getConfigVariable(rootKey, loaders, parser, defaultValueLoadable, params, options, encodeOptions) {
return (await getConfigObject(rootKey, loaders, parser, defaultValueLoadable, params, options, encodeOptions)).value;
}
//#endregion
//#region src/getConfigVariableResult.ts
async function getConfigVariableResult(rootKey, loaders, parser, defaultValueLoadable, params, options) {
try {
return (0, _luolapeikko_result_option.Ok)(await getConfigVariable(rootKey, loaders, parser, defaultValueLoadable, params, options));
} catch (err) {
return (0, _luolapeikko_result_option.Err)(err);
}
}
//#endregion
//#region src/getConfigObjectResult.ts
async function getConfigObjectResult(rootKey, loaders, parser, defaultValueLoadable, params, options) {
try {
return (0, _luolapeikko_result_option.Ok)(await getConfigObject(rootKey, loaders, parser, defaultValueLoadable, params, options));
} catch (err) {
return (0, _luolapeikko_result_option.Err)(err);
}
}
//#endregion
//#region src/lib/semicolonUtils.ts
/**
* Make the first character of a string lowercase
* @param {string} data String to make lowercase
* @returns {string} String with first character lowercase
* @category Utils
* @example
* lcFirst('Hello') // 'hello'
*/
function lcFirst(data) {
return data.length > 0 ? data.charAt(0).toLowerCase() + data.slice(1) : data;
}
/**
* Parse a semicolon separated string into a config object
* @param {string} config Semicolon separated string
* @param {boolean} [keepCase] Keep the case of the keys, default true
* @returns {Record<string, string>} Config object
* @category Utils
* @since v1.0.0
* @example
* parseSemicolonConfig('a=b;c=d') // {a: 'b', c: 'd'}
*/
function parseSemicolonConfig(config, keepCase = true) {
return config.split(";").reduce((last, c) => {
const [k, v] = c.split("=", 2);
if (k && v) {
const key = keepCase ? k.trim() : lcFirst(k.trim());
if (key) last[key] = decodeURIComponent(v.trim());
}
return last;
}, {});
}
/**
* Stringify a config object to a semicolon separated string
* @param {Record<string, string>} config Object to stringify
* @param {boolean} [uriEncode] Use URI encoding for string outputs
* @returns {string} Stringified config
* @category Utils
* @since v1.0.0
* @example
* stringifySemicolonConfig({a: 'b', c: 'd'}) // 'a=b;c=d'
*/
function stringifySemicolonConfig(config, uriEncode = true) {
return Object.entries(config).reduce((last, [key, value]) => {
if (value !== void 0) {
const encodedValue = uriEncode ? encodeURIComponent(String(value)) : String(value);
last.push(`${key}=${encodedValue}`);
}
return last;
}, []).join(";");
}
/**
* Stringify a config object to a semicolon separated string for logging
* @template Out - Type of output
* @param {Record<string, string>} config Object to stringify
* @param {ShowValueType} showProtectedKeys How to show protected keys
* @param {string[]} protectedKeys list of protected keys
* @returns {string} Stringified config
* @category Utils
* @since v1.0.0
* @example
* logStringifySemicolonConfig({a: 'b', c: 'd'}) // 'a=b;c=d'
*/
function logStringifySemicolonConfig(config, showProtectedKeys, protectedKeys) {
return Object.entries(config).reduce((last, [key, value]) => {
if (value !== void 0) if (protectedKeys.has(key)) {
const hiddenValue = buildHiddenValue(String(value), showProtectedKeys);
last.push(`${key}=${hiddenValue}`);
} else last.push(`${key}=${String(value)}`);
return last;
}, []).join(";");
}
//#endregion
//#region src/lib/objectUtils.ts
/**
* validate if a value is a valid object
* @param {unknown} value - value to validate
* @returns {value is Record<string, unknown>} - true if value is a valid object
* @since v0.2.5
*/
function isValidObject(value) {
return typeof value === "object" && value !== null && !Array.isArray(value);
}
/**
* Check if a value has a toJSON method
* @param {unknown} value - value to check
* @returns {value is {toJSON: () => string}} - true if value has a toJSON method
*/
function haveToJSON(value) {
return typeof value === "object" && value !== null && "toJSON" in value;
}
/**
* Stringify a value to a string
* @param {unknown} value - value to stringify
* @returns {string} - stringified value
*/
function stringifyValue(value) {
if (isValidObject(value)) return haveToJSON(value) ? value.toJSON() : JSON.stringify(value);
return String(value);
}
/**
* Convert an object to a string value object
* @param {Record<string, unknown>} obj - object to convert
* @returns {Record<string, string | undefined>} - object with string values
* @since v0.2.5
*/
function buildStringObject(obj) {
return Object.entries(obj).reduce((last, [key, value]) => {
if (value) last[key] = stringifyValue(value);
return last;
}, {});
}
/**
* Convert an object to a Map<string, string>
* @param {Record<string, unknown>} obj - object to convert
* @returns {Map<string, string>} - Map with string values
* @since v0.2.5
*/
function buildStringMap(obj) {
return Object.entries(obj).reduce((acc, [key, value]) => {
if (value) acc.set(key, stringifyValue(value));
return acc;
}, /* @__PURE__ */ new Map());
}
/**
* Apply a Record<string, string> to a Map<string, string>
* @param {Record<string, string | undefined>} obj - object to apply
* @param {Map<string, string>} target - Map to apply to
* @returns {Map<string, string>} - target with updates
* @since v0.2.5
*/
function applyStringMap(obj, target) {
return Object.entries(obj).reduce((acc, [key, value]) => {
if (value) acc.set(key, value);
return acc;
}, target);
}
//#endregion
//#region src/loaders/ConfigLoader.ts
/**
* Abstract base class for config loaders
* @template Props - the type of the props
* @template OverrideMap - the type of the override key map
* @category Loaders
* @abstract
* @since v1.0.0
*/
var ConfigLoader = class extends events.EventEmitter {
constructor(props = {}, overrideKeys = {}) {
super();
this.valueSeen = /* @__PURE__ */ new Map();
this.options = props;
this.overrideKeys = overrideKeys;
}
async getLoaderResult(lookupKey) {
const loaderValue = await this.handleLoaderValue(this.overrideKeys[lookupKey] ?? lookupKey);
if (!loaderValue) return;
return {
value: loaderValue.value,
path: loaderValue.path,
seen: handleSeen(this.valueSeen, lookupKey, loaderValue.value)
};
}
async isLoaderDisabled() {
const loadableDisabled = (await this.getOptions()).disabled;
return _luolapeikko_ts_common.LoadableCore.resolve(loadableDisabled);
}
setDisabled(disabled) {
return this.setOption("disabled", disabled);
}
/**
* Get options from loader and merge with default options
* @returns {Promise<DefaultProps>} - Promise of DefaultProps & Props
*/
async getOptions() {
const resolvedOptions = await (typeof this.options === "function" ? this.options() : this.options);
return Object.assign({}, this.defaultOptions, resolvedOptions);
}
async setOption(key, value) {
this.options = Object.assign({}, await this.getOptions(), { [key]: value });
}
/**
* Build error string `ConfigVariables[<type>]: <message>`
* @param {string} message - error message
* @returns {string} - error string
*/
buildErrorStr(message) {
return `ConfigLoader[${this.loaderType}]: ${message}`;
}
};
//#endregion
//#region src/loaders/RecordConfigLoader.ts
/**
* RecordConfigLoader is a class that extends ConfigLoader and adds the ability to reload the data.
* @template Props - the type of the options for the loader
* @template OverrideMap - the type of the override key map
* @since v1.0.0
*/
var RecordConfigLoader = class extends ConfigLoader {
constructor(..._args) {
super(..._args);
this._isLoaded = false;
}
/**
* reloads the data
*/
async reload() {
this.valueSeen.clear();
await this.loadData();
}
/**
* is the data loaded
* @returns {boolean} - true if the data is loaded, false otherwise
*/
isLoaded() {
return this._isLoaded;
}
/**
* load data from the loader. If the data is loaded successfully
* an "updated" event will be emitted.
* @returns {Promise<void>}
*/
async loadData() {
this.dataPromise = this.handleData();
await this.dataPromise;
this.emit("updated");
}
};
//#endregion
//#region src/loaders/EnvConfigLoader.ts
/**
* env loader class is used to load env variables from process.env
* @template OverrideMap - the type of the override key map
* @param {string} [overrideKey] - optional override key for lookup
* @returns {IConfigLoader} - IConfigLoader object
* @category Loaders
* @since v1.0.0
*/
var EnvConfigLoader = class extends ConfigLoader {
constructor(..._args) {
super(..._args);
this.loaderType = "env";
this.defaultOptions = { disabled: false };
}
handleLoaderValue(lookupKey) {
return {
value: process.env[lookupKey],
path: `process.env.${lookupKey}`
};
}
};
//#endregion
//#region src/loaders/ReactEnvConfigLoader.ts
/**
* React env loader class is used to load env variables from process.env.REACT_APP_*
* @template OverrideMap - the type of the override key map
* @param {Partial<OverrideMap>} [override] - optional override key for lookup
* @returns {IConfigLoader} - IConfigLoader object
* @category Loaders
* @since v1.0.0
*/
var ReactEnvConfigLoader = class extends ConfigLoader {
constructor(..._args) {
super(..._args);
this.loaderType = "react-env";
this.defaultOptions = { disabled: false };
}
handleLoaderValue(lookupKey) {
const targetKey = `REACT_APP_${lookupKey}`;
return {
value: process.env[targetKey],
path: `process.env.${targetKey}`
};
}
};
//#endregion
//#region src/loaders/MapConfigLoader.ts
/**
* MapConfigLoader is a class that extends ConfigLoader and adds the ability to reload the data.
* @template Props - the type of the options for the loader
* @template OverrideMap - the type of the override key map
* @since v1.0.0
* @category Loaders
*/
var MapConfigLoader = class extends ConfigLoader {
constructor(..._args) {
super(..._args);
this._isLoaded = false;
this.data = /* @__PURE__ */ new Map();
}
/**
* clear maps and reloads the data
*/
async reload() {
this.data.clear();
this.valueSeen.clear();
await this.loadData();
}
/**
* is the data loaded
* @returns {boolean} Whether the data is loaded
*/
isLoaded() {
return this._isLoaded;
}
/**
* load data from the loader. If the data is loaded successfully
* an "updated" event will be emitted.
* @returns {Promise<void>}
*/
async loadData() {
if (await this.handleLoadData()) this.emit("updated");
}
};
//#endregion
//#region src/loaders/FetchConfigLoader.ts
/**
* FetchConfigLoader is used to load config from a fetch request
* @template OverrideMap - the type of the override key map
* @category Loaders
* @since v1.0.0
*/
var FetchConfigLoader = class extends MapConfigLoader {
/**
* Constructor for FetchConfigLoader
* @param {Loadable<ConfigRequest>} request - callback that returns a fetch request or a message object that the request is not ready
* @param {Loadable<Partial<FetchConfigLoaderOptions>>} options - optional options for FetchConfigLoader
* @param {Partial<OverrideMap>} overrideKeys - optional override keys for FetchConfigLoader
* @param {Lowercase<string>} type - optional name type for FetchConfigLoader (default: 'fetch')
*/
constructor(request, options = {}, overrideKeys = {}, type = "fetch") {
super(options, overrideKeys);
this.path = "undefined";
this.defaultOptions = {
cache: void 0,
cacheHitHttpCode: 304,
disabled: false,
fetchClient: typeof window === "object" ? fetch.bind(window) : fetch,
isSilent: false,
logger: void 0,
payload: "json",
validate: void 0
};
this.request = request;
this.loaderType = type;
}
async handleLoaderValue(lookupKey) {
if (!this._isLoaded) {
await this.loadData();
this._isLoaded = true;
}
return {
value: this.data.get(lookupKey),
path: this.path
};
}
async handleLoadData() {
const { logger, cache, isSilent, cacheHitHttpCode, payload } = await this.getOptions();
const req = await this.getRequest();
if (isRequestNotReadMessage(req)) {
logger?.debug(`FetchEnvConfig: ${req.message}`);
return false;
}
this.path = urlSanitize(req.url);
logger?.debug(`fetching config from ${this.path}`);
let res = await this.fetchRequestOrCacheResponse(req);
if (!res) {
logger?.info(`client is offline and not have cached response for FetchEnvConfig`);
return false;
}
if (res.ok && cache) {
logger?.debug(`storing response in cache for FetchEnvConfig`);
await cache.storeRequest(req, res);
}
if (res.status >= 400) {
res = await this.checkIfValidCacheResponse(req, res);
if (res.status >= 400) {
if (isSilent) {
logger?.info(`http error ${res.status.toString()} from FetchEnvConfig`);
return false;
}
throw new VariableError(`http error ${res.status.toString()} from FetchEnvConfig`);
}
}
if (res.status === cacheHitHttpCode) res = await this.handleNotModifiedCache(req, res);
const contentType = res.headers.get("content-type");
if (contentType?.startsWith("application/json") && payload === "json") {
applyStringMap(await this.handleJson(res), this.data);
logger?.debug("successfully loaded config from FetchEnvConfig");
return true;
}
if (isSilent) {
logger?.info(`unsupported content-type ${String(contentType)} from FetchEnvConfig`);
return false;
}
throw new VariableError(`unsupported content-type ${String(contentType)} from FetchEnvConfig`);
}
/**
* if client is offline, we will try return the cached response else add cache validation (ETag) and try get the response from the fetch request
* @param {Request} req - request to fetch
* @returns {Promise<Response | undefined>} - response or undefined
*/
async fetchRequestOrCacheResponse(req) {
const { logger, cache, fetchClient } = await this.getOptions();
if (cache) {
const cacheRes = await cache.fetchRequest(req);
if (!cache.isOnline()) {
if (cacheRes) {
logger?.debug(`client is offline, returned cached response for FetchEnvConfig`);
return cacheRes;
}
return;
}
if (cacheRes) {
const etag = cacheRes.headers.get("etag");
if (etag) req.headers.set("If-None-Match", etag);
}
}
return fetchClient(req);
}
/**
* on error, check if we have a valid cached response
* @param {Request} req - request to fetch
* @param {Response} res - response from fetch
* @returns {Promise<Response>} - response
*/
async checkIfValidCacheResponse(req, res) {
const { cache } = await this.getOptions();
if (cache) {
const cacheRes = await cache.fetchRequest(req);
if (cacheRes) return cacheRes;
}
return res;
}
/**
* if we get a 304, get the cached response
* @param {Request} req - request to fetch
* @param {Response} res - response from fetch
* @returns {Promise<Response>} - response
*/
async handleNotModifiedCache(req, res) {
const { cache, logger } = await this.getOptions();
if (cache) {
const cacheRes = await cache.fetchRequest(req);
if (cacheRes) {
logger?.debug(`returned cached response for FetchEnvConfig`);
return cacheRes;
}
throw new VariableError(`http error ${res.status.toString()} from FetchEnvConfig (using cached version)`);
}
return res;
}
async handleJson(res) {
const { validate, isSilent, logger } = await this.getOptions();
try {
const contentType = res.headers.get("content-type");
if (!contentType?.startsWith("application/json")) throw new Error(`unsupported content-type ${String(contentType)}`);
const rawData = await res.json();
if (!isValidObject(rawData)) throw new Error(`is not valid JSON object`);
const data = buildStringObject(rawData);
if (validate) return await validate(data);
return data;
} catch (error) {
const err = error instanceof Error ? error : /* @__PURE__ */ new Error("unknown error");
if (isSilent) {
logger?.info(`FetchEnvConfig JSON error: ${err.message}`);
return Promise.resolve({});
}
throw new VariableError(`FetchEnvConfig JSON error: ${err.message}`);
}
}
async getRequest() {
return typeof this.request === "function" ? this.request() : this.request;
}
};
//#endregion
//#region src/loaders/MemoryConfigLoader.ts
/**
* Config loader with in-memory data which can be set and retrieved variables on the fly.
* - Useful for temporary controlled overrides or testing
* @template MemoryMap - the type of the memory map
* @template OverrideMap - the type of the override key map
* @since v1.0.0
* @category Loaders
*/
var MemoryConfigLoader = class extends ConfigLoader {
constructor(initialData, options = {}, overrideKeys = {}, type = "memory") {
super(options, overrideKeys);
this.defaultOptions = {
disabled: false,
logger: void 0
};
this.data = new Map(Object.entries(initialData));
this.loaderType = type;
}
async set(key, value) {
(await this.getOptions()).logger?.debug(this.buildErrorStr(`setting key ${String(key)} to '${String(value)}'`));
if (this.data.get(key) !== value) this.valueSeen.delete(String(key));
this.data.set(key, value);
this.emit("updated");
}
async get(key) {
(await this.getOptions()).logger?.debug(this.buildErrorStr(`getting key ${String(key)}`));
return this.data.get(key);
}
handleLoaderValue(lookupKey) {
return {
value: this.data.get(lookupKey),
path: `key:${lookupKey}`
};
}
};
//#endregion
//#region src/loaders/SwitchLoader.ts
/**
* SwitchLoader, this loader will switch between different configurations based on the active key.
* @example
* type TestConfigMapEnv = { DEMO: string; ANOTHER: string; };
* const switchLoader = new SwitchLoader<TestEnv, 'switch1' | 'switch2'>({
* switch1: { DEMO: 'value' },
* switch2: { DEMO: 'value2' },
* });
* const switcher = switchLoader.getLoader; // use this on the config map loaders: [switcher(), dotenv(), env(), ...]
* await switchLoader.activateSwitch('switch1'); // when you want enable switch1 values
* @template Config - The configuration map
* @template Key - The key to switch between
* @since v1.0.0
* @category Loaders
*/
var SwitchLoader = class extends ConfigLoader {
constructor(configs, props = {}, type = "switch") {
super(props);
this.keys = /* @__PURE__ */ new Set();
this.defaultOptions = {
disabled: false,
logger: void 0
};
this.config = configs;
this.loaderType = type;
}
async activateSwitch(key) {
(await this.getOptions()).logger?.debug(this.buildErrorStr(`activating key '${String(key)}' => [${this.getConfigKeys(key).join(", ")}]`));
this.keys.add(key);
this.emit("updated");
}
async deactivateSwitch(key) {
(await this.getOptions()).logger?.debug(this.buildErrorStr(`deactivating key '${String(key)}' => [${this.getConfigKeys(key).join(", ")}]`));
this.keys.delete(key);
this.emit("updated");
}
getCurrentKeys() {
return this.keys;
}
handleLoaderValue(lookupKey) {
let output;
for (const key of Array.from(this.keys)) {
const currentValue = this.config[key][lookupKey];
if (currentValue) output = {
value: currentValue,
path: `switch:${String(key)}:${lookupKey}`
};
}
return output;
}
getConfigKeys(key) {
return Object.keys(this.config[key]);
}
};
//#endregion
//#region src/parsers/JsonConfigParser.ts
/**
* Config parser to parse JSON string as object
* @example
* const objectSchema = z.object({
* foo: z.string(),
* baz: z.string(),
* secret: z.string(),
* });
* // parses '{"foo": "bar", "baz": "qux", "secret": "secret"}' string to {foo: "bar", baz: "qux", secret: "secret"}
* const fooBarJsonParser = new JsonConfigParser({
* validate: (value) => objectSchema.parse(value),
* protectedKeys: ['secret'],
* showProtectedKeys: 'prefix-suffix', // shows secret value with few characters from start and end on logging
* });
* @template Out - the type of the output object
* @implements {IConfigParser<Out, object>}
* @category Parsers
* @since v1.0.0
*/
var JsonConfigParser = class {
constructor({ protectedKeys, validate, showProtectedKeys } = {}) {
this.name = "jsonConfigParser";
this.protectedKeys = new Set(protectedKeys);
this.validate = validate;
this.showProtectedKeys = showProtectedKeys;
}
parse({ value }) {
return this.buildBaseRecord(JSON.parse(value));
}
async postValidate({ value }) {
return await this.validate?.(value);
}
toString(config) {
return JSON.stringify(Object.entries(config).reduce((last, [key, value]) => {
if (value) last[key] = value;
return last;
}, {}));
}
toLogString(config) {
return JSON.stringify(Object.entries(config).reduce((last, [key, value]) => {
if (value) if (this.protectedKeys.has(key)) last[key] = buildHiddenValue(String(value), this.showProtectedKeys);
else last[key] = value;
return last;
}, {}));
}
/**
* Build a string record from the given data
* @param {unknown} data - to be validated as object
* @returns {object} A record with string values
*/
buildBaseRecord(data) {
if (typeof data !== "object" || data === null || Array.isArray(data)) return {};
return data;
}
};
//#endregion
//#region src/parsers/UrlParser.ts
/**
* UrlParser class is used to parse and validate env variables of type URL.
* @class UrlParser
* @implements {IConfigParser<URL, URL>}
* @category Parsers
* @since v0.2.5
*/
var UrlParser = class {
/**
* Create a new UrlParser
* @param {UrlParserProps} properties - Properties for the UrlParser
*/
constructor({ urlSanitize } = {}) {
this.name = "urlParser";
this.urlSanitize = urlSanitize ?? false;
}
parse({ value }) {
try {
return new URL(value);
} catch (err) {
throw _luolapeikko_ts_common.ErrorCore.from(err);
}
}
toString(value) {
return value.href;
}
toLogString(value) {
if (this.urlSanitize) return this.handleUrlSanitize(value.href);
return value.toString();
}
/**
* Build a URL object from a string and sanitize the username and password
* @param {string} value string to parse
* @returns {URL} URL object with sanitized username and password
*/
handleUrlSanitize(value) {
const url = new URL(value);
url.password = "*".repeat(url.password.length);
url.username = "*".repeat(url.username.length);
return url.href;
}
};
//#endregion
//#region src/lib/primitiveUtils.ts
/**
* Get an integer from a string
* @param {string} value - string to parse
* @returns {IResult<number, TypeError>} - Ok if value is an integer string, Err if not
* @since v0.2.5
*/
function getInteger(value) {
const parsed = parseInt(value, 10);
if (isNaN(parsed)) return (0, _luolapeikko_result_option.Err)(/* @__PURE__ */ new TypeError(`${value} is not an integer string`));
return (0, _luolapeikko_result_option.Ok)(parsed);
}
const booleanTrueStringValues = [
"true",
"1",
"yes",
"y",
"on"
];
const allBooleanStringValues = [...[
"false",
"0",
"no",
"n",
"off"
], ...booleanTrueStringValues];
/**
* Parse a string or boolean into a boolean value.
* @param {string | boolean} value - The value to parse.
* @returns {IResult<boolean, TypeError>} - Ok with boolean if successful, Err with TypeError if not.
* @since v0.2.5
*/
function getBoolean(value) {
if (typeof value === "boolean") return (0, _luolapeikko_result_option.Ok)(value);
const output = value.toLowerCase();
if (!allBooleanStringValues.includes(output)) return (0, _luolapeikko_result_option.Err)(/* @__PURE__ */ new TypeError(`${value} is not a boolean string`));
return (0, _luolapeikko_result_option.Ok)(booleanTrueStringValues.includes(output));
}
/**
* Simply returns the input string as a successful result.
* @param {string} value - input string
* @returns {IResult<string, TypeError>} - Ok with input string, never Err
* @since v0.2.5
*/
function getString(value) {
return (0, _luolapeikko_result_option.Ok)(value);
}
/**
* Parse a string into a float value.
* @param {string} value - The value to parse.
* @returns {IResult<number, TypeError>} - Ok with float if successful, Err with TypeError if not.
* @since v0.2.5
*/
function getFloat(value) {
const parsed = parseFloat(value);
if (isNaN(parsed)) return (0, _luolapeikko_result_option.Err)(/* @__PURE__ */ new TypeError(`${value} is not a float string`));
return (0, _luolapeikko_result_option.Ok)(parsed);
}
/**
* Parse a string into a bigint value.
* @param {string} value - The value to parse.
* @returns {IResult<bigint, TypeError>} - Ok with bigint if successful, Err with TypeError if not.
* @since v0.2.5
*/
function getBigInt(value) {
try {
return (0, _luolapeikko_result_option.Ok)(BigInt(value));
} catch (_err) {
return (0, _luolapeikko_result_option.Err)(/* @__PURE__ */ new TypeError(`${value} is not a bigint string`));
}
}
//#endregion
//#region src/parsers/stringParser.ts
/**
* Build parser and have optional post validation (as example for literal values)
* @template Output - Type of output, defaults to string
* @param {TypeGuardValidate<Output>} [validate] - optional post validation
* @returns {IConfigParser<string, Output>} - parser
* @category Parsers
* @since v1.0.0
*/
function stringParser(validate) {
return {
name: "stringParser",
parse: ({ value, key }) => {
return getString(value).mapErr((cause) => new TypeError(`value for key ${key} is not a string`, { cause })).unwrap();
},
postValidate: async (props) => {
if (!(await validate)?.(props.value)) return;
return props.value;
},
preValidate: ({ key, value }) => {
if (typeof value !== "string") throw new TypeError(`value for key ${key} is not a string`);
},
toString: (value) => {
return value;
}
};
}
//#endregion
//#region src/parsers/SemicolonConfigParser.ts
/**
* Config parser to parse semicolon separated string key=value pairs as object
* @example
* const objectSchema = z.object({
* foo: z.string(),
* baz: z.string(),
* secret: z.string(),
* });
* // parses 'foo=bar;baz=qux;secret=secret' string to {foo: "bar", baz: "qux", secret: "secret"}
* const fooBarJsonParser = new SemicolonConfigParser({
* validate: (value) => objectSchema.parse(value),
* protectedKeys: ['secret'],
* showProtectedKeys: 'prefix-suffix', // shows secret value with few characters from start and end on logging
* });
* @template Out - the type of the output object
* @implements {IConfigParser<Out, Record<string, string>>}
* @category Parsers
* @since v1.0.0
*/
var SemicolonConfigParser = class {
constructor({ keepCase, protectedKeys, validate, showProtectedKeys } = {}) {
this.name = "semicolonConfigParser";
this.protectedKeys = new Set(protectedKeys);
this.validate = validate;
this.keepCase = keepCase ?? true;
this.showProtectedKeys = showProtectedKeys;
}
parse({ value }) {
return Promise.resolve(parseSemicolonConfig(value, this.keepCase));
}
async postValidate({ value }) {
return await this.validate?.(value);
}
toString(config, options) {
return stringifySemicolonConfig(config, options?.uriEncode);
}
toLogString(config) {
return logStringifySemicolonConfig(config, this.showProtectedKeys, this.protectedKeys);
}
};
//#endregion
//#region src/parsers/booleanParser.ts
/**
* Build parser and have optional post validation (as example for literal values)
*
* supports the following string ___true___ values:
*
* ```['true', '1', 'yes', 'y', 'on']```
*
* supports the following string ___false___ values:
*
* ```['false', '0', 'no', 'n', 'off']```
* @template Output - Type of output, defaults to number
* @param {TypeGuardValidate<Output>} [validate] - optional post validation
* @returns {IConfigParser<boolean, Output>} - parser
* @category Parsers
* @since v1.0.0
*/
function booleanParser(validate) {
return {
name: "booleanParser",
parse: ({ key, value }) => {
return getBoolean(value).mapErr((cause) => new TypeError(`value for key ${key} is not a boolean string`, { cause })).unwrap();
},
postValidate: async (props) => {
if (!(await validate)?.(props.value)) return;
return props.value;
},
preValidate: ({ key, value }) => {
if (typeof value === "boolean") return;
if (typeof value !== "string") throw new TypeError(`value for key ${key} is not a string`);
},
toString: (value) => {
return value.toString();
}
};
}
//#endregion
//#region src/parsers/floatParser.ts
/**
* Build parser and have optional post validation (as example for literal values)
* @template Output - Type of output, defaults to number
* @param {TypeGuardValidate<Output>} [validate] - optional post validation
* @returns {IConfigParser<number, Output>} - parser
* @category Parsers
* @since v1.0.0
*/
function floatParser(validate) {
return {
name: "floatParser",
parse: ({ key, value }) => {
return getFloat(value).mapErr((cause) => new TypeError(`value for key ${key} is not a float string`, { cause })).unwrap();
},
postValidate: async (props) => {
if (!(await validate)?.(props.value)) return;
return props.value;
},
preValidate: ({ key, value }) => {
if (typeof value !== "string") throw new TypeError(`value for key ${key} is not a string`);
},
toString: (value) => {
return value.toString();
}
};
}
//#endregion
//#region src/parsers/integerParser.ts
/**
* Build parser and have optional post validation (as example for literal values)
* @template Output - Type of output, defaults to number
* @param {TypeGuardValidate<Output>} [validate] - optional post validation
* @returns {IConfigParser<number, Output>} - parser
* @category Parsers
* @since v1.0.0
*/
function integerParser(validate) {
return {
name: "integerParser",
parse: ({ key, value }) => {
return getInteger(value).mapErr((cause) => new TypeError(`value for key ${key} is not an integer string`, { cause })).unwrap();
},
postValidate: async (props) => {
if (!(await validate)?.(props.value)) return;
return props.value;
},
toString: (value) => {
return value.toString();
}
};
}
//#endregion
//#region src/parsers/arrayParser.ts
/**
* Build parser for array of values
* @template Input - Type of input
* @template Output - Type of output
* @param {IConfigParser<Input, Output>} parse parser for the array values
* @param {string} separator separator for the array values, defaults to ';'
* @param {TypeGuardValidate<Output> | undefined} validate optional post validation
* @returns {IConfigParser<Output[], Input[]>} Parser for array of values
* @category Parsers
* @since v1.0.0
*/
function arrayParser(parse, separator = ";", validate) {
return {
name: "arraySeparatorParser",
parse: (props) => {
return Promise.all(props.value.split(separator).map((v) => parse.parse({
...props,
value: v
})));
},
postValidate: async (props) => {
if (!validate) return;
return (await Promise.all(props.value.map(async (v) => {
if (!(await validate)?.(v)) return;
return v;
}))).filter((v) => v !== void 0);
},
toString: (value) => {
return value.map((v) => parse.toString(v)).join(separator);
}
};
}
//#endregion
//#region src/parsers/bigIntParser.ts
/**
* Build parser and have optional post validation (as example for literal values)
* @template Output - Type of output, defaults to bigint
* @param {TypeGuardValidate<Output>} [validate] - optional post validation
* @returns {IConfigParser<bigint, Output>} - parser
* @category Parsers
* @since v1.0.0
*/
function bigIntParser(validate) {
return {
name: "bigIntParser",
parse: ({ key, value }) => {
return getBigInt(value).mapErr((cause) => new TypeError(`value for key ${key} is not an integer string`, { cause })).unwrap();
},
postValidate: async (props) => {
if (!(await validate)?.(props.value)) return;
return props.value;
},
toString: (value) => {
return value.toString();
}
};
}
//#endregion
//#region src/VariableLookupError.ts
/**
* Custom error class for variable lookup errors.
* @class VariableLookupError
* @augments VariableError
* @category Errors
* @since v0.2.2
*/
var VariableLookupError = class extends VariableError {
/**
* Create a new VariableLookupError
* @param {string} variableKey - The variable key.
* @param {string} message - The error message.
* @param {ErrorOptions} [options] - The error options
*/
constructor(variableKey, message, options) {
super(message, options);
this.variableKey = variableKey;
this.name = "VariableLookupError";
}
};
//#endregion
//#region src/ConfigMap.ts
/**
* ConfigMap
* @example
* type ConfigEnv = {
* PORT: number;
* HOST: string;
* DEBUG: boolean;
* URL: URL;
* };
* const config = new ConfigMap<ConfigEnv>({
* DEBUG: {loaders: [env()], parser: booleanParser, defaultValue: false},
* HOST: {loaders: [env()], parser: stringParser, defaultValue: 'localhost'},
* PORT: {loaders: [env()], parser: integerParser, defaultValue: 3000},
* URL: {loaders: [env()], parser: new UrlParser({urlSanitize: true}), defaultValue: new URL('http://localhost:3000')},
* });
* console.log('port', await config.get('PORT'));
* @template Data - type of config map
* @since v1.1.0
*/
var ConfigMap = class {
/**
* ConfigMap constructor
* @param {EnvMapSchema<Data>} schema - schema of config map
* @param {Iterable<IConfigLoader>} loaders - iterable of config loaders
* @param {ConfigOptions} options - optional config options (logger, namespace)
*/
constructor(schema, loaders, options = {
logger: void 0,
namespace: void 0
}) {
this.schema = schema;
this.options = options;
this.loaders = loaders;
}
/**
* Set logger value
* @param {ILoggerLike | undefined} logger - logger like object
*/
setLogger(logger) {
this.options.logger = logger;
}
/**
* get env object from config map
* @returns {Promise<TypeValueRecords<Data>>} Promise of env object or undefined
* @example
* const valueObject: LoaderTypeValue<number> = await config.getObject('PORT');
* console.log(valueObject.type, valueObject.value); // 'env', 3000
* @param {string} key - key of config map
* @param {EncodeOptions} [encodeOptions] - optional encode options
*/
async getObject(key, encodeOptions) {
if (typeof key !== "string") throw new VariableError(`ConfigMap key ${String(key)} is not a string`);
const entry = this.schema[key];
if (!entry) throw new VariableLookupError(key, `ConfigMap key ${String(key)} not found in config map`);
const { parser, defaultValue, params, undefinedThrowsError, undefinedErrorMessage } = entry;
const configObject = await getConfigObject(key, Array.from(await _luolapeikko_ts_common.LoadableCore.resolve(this.loaders)), parser, defaultValue, params, this.options, encodeOptions);
if (undefinedThrowsError && configObject.value === void 0) {
buildOptions(this.options).logger?.info(`ConfigMap key ${String(key)} is undefined (expect to throw error)`);
throw new VariableError(undefinedErrorMessage ?? `ConfigMap key ${String(key)} is undefined`);
}
return configObject;
}
/**
* get env object from config map as Result
* @returns {Promise<Result<TypeValueRecords<Data>>>} Result Promise of env object or undefined
* @example
* const valueObject: Result<LoaderTypeValue<number>> = await config.getObjectResult('PORT');
* if (valueObject.isOk()) {
* const {type, value} = valueObject.ok();
* console.log(type, value); // 'env', 3000
* }
* @param {string} key - key of config map
* @param {EncodeOptions} [encodeOptions] - optional encode options
*/
async getObjectResult(key, encodeOptions) {
try {
return (0, _luolapeikko_result_option.Ok)(await this.getObject(key, encodeOptions));
} catch (err) {
return (0, _luolapeikko_result_option.Err)(err);
}
}
/**
* get env value from config map
* @returns {Promise<Data[Key]>} Promise of value or undefined
* @example
* const port: number = await config.get('PORT');
* @param {string} key - key of config map
* @param {EncodeOptions} [encodeOptions] - optional encode options
*/
async get(key, encodeOptions) {
return (await this.getObject(key, encodeOptions)).value;
}
/**
* get env value as string from config map
* @returns {Promise<string | undefined>} Promise of string value or undefined
* @example
* const port: string = await config.getString('PORT');
* @param {string} key - key of config map
* @param {EncodeOptions} [encodeOptions] - optional encode options
*/
async getString(key, encodeOptions) {
return (await this.getObject(key, encodeOptions)).stringValue;
}
/**
* get env value from config map as Result
* @returns {Promise<Result<Data[Key]>>} Result Promise of value or undefined
* @example
* const port: Result<number> = await config.getResult('PORT');
* if (port.isOk()) {
* console.log('port', port.ok());
* }
* @param {string} key - key of config map
* @param {EncodeOptions} [encodeOptions] - optional encode options
*/
async getResult(key, encodeOptions) {
try {
return (0, _luolapeikko_result_option.Ok)(await this.get(key, encodeOptions));
} catch (err) {
return (0, _luolapeikko_result_option.Err)(err);
}
}
/**
* get env value as string from config map as Result
* @returns {Promise<Result<string | undefined>>} Result Promise of string value or undefined
* @example
* const port: Result<string> = await config.getStringResult('PORT');
* if (port.isOk()) {
* console.log('port', port.ok());
* }
* @param {string} key - key of config map
* @param {EncodeOptions} [encodeOptions] - optional encode options
*/
async getStringResult(key, encodeOptions) {
try {
return (0, _luolapeikko_result_option.Ok)(await this.getString(key, encodeOptions));
} catch (err) {
return (0, _luolapeikko_result_option.Err)(err);
}
}
/**
* get all env value objects from config map
* @returns {Promise<TypeValueRecords<Data>>} Promise of all values
* @example
* const values: TypeValueRecords<Data> = await config.getAll();
* console.log(values.PORT.type, values.PORT.value); // 'env', 3000
*/
async getAllObjects() {
return (await this.getAllPromises()).reduce((result, [key, value]) => {
result[key] = value;
return result;
}, {});
}
/**
* get all env values from config map
* @returns {Promise<Data>} Promise of all values
* @example
* const values: Data = await config.getAllValues();
* console.log('PORT', values.PORT); // 3000 (number)
*/
async getAllValues() {
return (await this.getAllPromises()).reduce((result, [key, value]) => {
result[key] = value.value;
return result;
}, {});
}
/**
* get all env values from config map as string
* @returns {Promise<Record<keyof Data, string>>} Promise of all values as string
* @example
* const values: Record<keyof Data, string> = await config.getAllStringValues();
* console.log('PORT', values.PORT); // '3000' (string)
* @param {EncodeOptions} [encodeOptions] - optional encode options
*/
async getAllStringValues(encodeOptions) {
return (await this.getAllPromises(encodeOptions)).reduce((result, [key, value]) => {
result[key] = value.stringValue;
return result;
}, {});
}
/**
* Validate all env values from config map, expect to throw error if error exists
* @param {(data: Data) => void} callback callback function
*/
async validateAll(callback) {
callback(await this.getAllValues());
}
/**
* run lookup to all keys and return all promises
* @param {EncodeOptions} [encodeOptions] - optional encode options
* @returns {Promise<[keyof Data, LoaderTypeValueStrict<Data[keyof Data]>][]>} Promise of all values
*/
getAllPromises(encodeOptions) {
return Promise.all(Object.keys(this.schema).map(async (key) => {
return [key, await this.getObject(key, encodeOptions)];
}));
}
};
//#endregion
exports.ConfigLoader = ConfigLoader;
exports.ConfigMap = ConfigMap;
exports.EnvConfigLoader = EnvConfigLoader;
exports.FetchConfigLoader = FetchConfigLoader;
exports.JsonConfigParser = JsonConfigParser;
exports.MapConfigLoader = MapConfigLoader;
exports.MemoryConfigLoader = MemoryConfigLoader;
exports.ReactEnvConfigLoader = ReactEnvConfigLoader;
exports.RecordConfigLoader = RecordConfigLoader;
exports.SemicolonConfigParser = SemicolonConfigParser;
exports.SwitchLoader = SwitchLoader;
exports.UrlParser = UrlParser;
exports.VariableError = VariableError;
exports.VariableLookupError = VariableLookupError;
exports.applyStringMap = applyStringMap;
exports.arrayParser = arrayParser;
exports.bigIntParser = bigIntParser;
exports.booleanParser = booleanParser;
exports.buildHiddenAsterisksValueString = buildHiddenAsterisksValueString;
exports.buildHiddenValue = buildHiddenValue;
exports.buildOptions = buildOptions;
exports.buildPartialHiddenValueString = buildPartialHiddenValueString;
exports.buildStringMap = buildStringMap;
exports.buildStringObject = buildStringObject;
exports.clearDefaultValueSeenMap = clearDefaultValueSeenMap;
exports.createRequestNotReady = createRequestNotReady;
exports.floatParser = floatParser;
exports.getConfigObject = getConfigObject;
exports.getConfigObjectResult = getConfigObjectResult;
exports.getConfigVariable = getConfigVariable;
exports.getConfigVariableResult = getConfigVariableResult;
exports.getLogger = getLogger;
exports.handleSeen = handleSeen;
exports.integerParser = integerParser;
exports.isRequestNotReadMessage = isRequestNotReadMessage;
exports.isValidObject = isValidObject;
exports.logStringifySemicolonConfig = logStringifySemicolonConfig;
exports.parseSemicolonConfig = parseSemicolonConfig;
exports.printValue = printValue;
exports.resolveLogger = resolveLogger;
exports.setLogger = setLogger;
exports.stringParser = stringParser;
exports.stringifySemicolonConfig = stringifySemicolonConfig;
exports.urlSanitize = urlSanitize;
//# sourceMappingURL=index.cjs.map

Sorry, the diff of this file is too big to display

import { Loadable } from "@luolapeikko/ts-common";
import { ILoggerLike, ISetOptionalLogger } from "@avanio/logger-like";
import { IResult } from "@luolapeikko/result-option";
import { EventEmitter } from "events";
//#region src/interfaces/IConfigLoader.d.ts
/**
* Represents the result of loading a configuration value.
* @property value - The value associated with the configuration key, or `undefined` if not found.
* @property path - The source path from which the value was loaded.
* @property seen - Indicates whether the key and value have already been encountered (used for logging purposes).
* @since v0.8.0
* @category Loaders
*/
type LoaderValueResult = {
/** this is shown on logs `ConfigVariables[type]: KEY [___VALUE___] from {path}` if showValue is true */value: string | undefined; /** this is shown on logs `ConfigVariables[type]: KEY [VALUE] from {___path___}` */
path: string; /** is key and value already seen (for logging) */
seen: boolean;
};
/**
* Interface for config loaders
* @since v1.0.0
* @category Loaders
*/
interface IConfigLoader {
/** this is shown on logs `ConfigVariables[___type___]: KEY [VALUE] from {path}` */
readonly loaderType: Lowercase<string>;
/**
* get loader result for lookupKey
* @param {string} lookupKey - key to lookup
* @returns {undefined | LoaderValueResult | Promise<undefined | LoaderValueResult>} - Promise of LoaderValueResult or undefined
*/
getLoaderResult(lookupKey: string): undefined | LoaderValueResult | Promise<undefined | LoaderValueResult>;
/**
* Check if loader is disabled
* @returns {boolean | undefined | Promise<boolean | undefined>} - Promise of boolean or undefined
*/
isLoaderDisabled(): boolean | undefined | Promise<boolean | undefined>;
}
/**
* Helper type to write override keys to config loaders
* @since v1.0.0
* @category Loaders
* @example
* type OverrideKeyMap = InferOverrideKeyMap<MainEnv & TestEnv>;
* // Example usage of OverrideKeyMap, where the keys are the original config keys and the values are the override keys
* const env = new EnvConfigLoader<OverrideKeyMap>(undefined, {PORT: 'HTTP_PORT'}); // get PORT value from process.env.HTTP_PORT
*/
type OverrideKeyMap = Record<string, string>;
/**
* Helper infer type to write override keys to config loaders
* @template T - The type of the config object.
* @since v1.0.0
* @category Loaders
* @example
* type OverrideKeyMap = InferOverrideKeyMap<MainEnv & TestEnv>;
* // Example usage of OverrideKeyMap, where the keys are the original config keys and the values are the override keys
* const env = new EnvConfigLoader<OverrideKeyMap>(undefined, {PORT: 'HTTP_PORT'}); // get PORT value from process.env.HTTP_PORT
*/
type InferOverrideKeyMap<T> = Record<keyof T, string>;
//#endregion
//#region src/interfaces/IConfigParser.d.ts
/**
* Parser method props
* @since v0.11.0
*/
type ParserProps = {
loader: IConfigLoader;
key: string;
value: string;
};
/**
* PreValidate method props
* @since v0.11.0
*/
type PreValidateProps = {
loader: IConfigLoader;
key: string;
value: unknown;
};
/**
* PostValidate method props
* @template T - Type of value
* @since v0.11.0
*/
type PostValidateProps<T> = {
loader: IConfigLoader;
key: string;
value: T;
};
/**
* PreValidate function
* @template Output - Type of output value
* @since v1.0.0
*/
type TypeGuardValidate<Output> = ((value: unknown) => value is Output) | Promise<(value: unknown) => value is Output>;
/**
* String encoder options for parsers
* @since v0.11.0
*/
type EncodeOptions = {
/**
* use URI encoding for string outputs (used by semicolon parser)
*/
uriEncode?: boolean;
/**
* not logging the value
*/
silent?: boolean;
};
/**
* Interface for config parsers
* @template Input - Type of raw input value
* @template Output - Type of output value
* @since v1.0.0
*/
interface IConfigParser<Input, Output> {
/**
* name of the parser
*/
name: string;
/**
* Config parser function
* @throws Error if parsing fails
*/
parse(parserProps: ParserProps): Input | Promise<Input>;
/**
* Optional raw string value validation before parsing.
* @throws Error if validation fails
*/
preValidate?(preValidateProps: PreValidateProps): void | Promise<void>;
/**
* Optional value validation after parsing
* @throws Error if validation fails
*/
postValidate?(postValidateProps: PostValidateProps<Input>): Output | undefined | Promise<Output | undefined>;
/**
* Build readable string from value
*/
toString(config: Output, options?: EncodeOptions): string;
/**
* Optional build readable string from value for log (can hide sensitive part from logs) else toString is used
* @param config - value to log
*/
toLogString?(config: Output): string;
}
//#endregion
//#region src/interfaces/IValidate.d.ts
/**
* Interface for validation function
* @template Input - Type of raw input
* @template Output - Type of output
* @since v1.0.0
*/
type ValidateCallback<Input, Output> = (data: Input) => Output | Promise<Output>;
//#endregion
//#region src/interfaces/IRequestCache.d.ts
/**
* interface for a request cache
* @category Utils
* @example
* const exampleCache: IRequestCache = {
* isOnline() {
* return (typeof window !== 'undefined' && window.navigator && window.navigator.onLine) || true;
* },
* async fetchRequest(req: Request) {
* if (typeof window !== 'undefined' && window.caches) {
* const cache = await window.caches.open('fetch');
* return cache.match(req);
* }
* return undefined;
* },
* async storeRequest(req: Request, res: Response) {
* if (typeof window !== 'undefined' && window.caches && res.ok) {
* const cache = await window.caches.open('fetch');
* req.headers.delete('Authorization');
* await cache.put(req, res.clone());
* }
* },
* };
* @since v0.2.8
*/
interface IRequestCache {
/**
* check if the client is connected to the internet
*/
isOnline(): boolean;
/**
* get the cached response for a request
*/
fetchRequest(req: Request): Promise<Response | undefined>;
/**
* store the response for a request
*/
storeRequest(req: Request, res: Response): Promise<void>;
}
//#endregion
//#region src/types/RequestNotReady.d.ts
/**
* RequestNotReady indicates that the request is not ready to be loaded yet. (e.g. the user is not logged in)
* @category Utils
* @since v0.2.8
*/
interface RequestNotReady {
_type: 'RequestNotReady';
message: string;
}
/**
* Type guard for RequestNotReady
* @param {unknown} obj - object to check if it is a RequestNotReady payload
* @returns {boolean} boolean indicating if the object is a RequestNotReady payload
* @category Utils
* @since v0.2.8
*/
declare function isRequestNotReadMessage(obj: unknown): obj is RequestNotReady;
/**
* function to create a RequestNotReady payload to indicate that the request is not ready to be loaded yet. (e.g. the user is not logged in)
* @param {string} message - reason why the request is not ready yet
* @returns {RequestNotReady} RequestNotReady payload
* @category Utils
* @since v0.2.8
*/
declare function createRequestNotReady(message: string): RequestNotReady;
//#endregion
//#region src/lib/formatUtils.d.ts
type PartialHiddenValueStringType = /** show only prefix of secret "j43****" */'prefix' /** show only suffix of secret "****7hd" */ | 'suffix' /** show both prefix and suffix of secret "j43****7hd" */ | 'prefix-suffix';
type ShowValueType = boolean | PartialHiddenValueStringType;
/**
* Format parameters for the variables
* @since v0.2.5
*/
interface FormatParameters {
/**
* Whether to show the value in the output, defaults to undefined
* - undefined: hide the value
* - true: show the actual value
* - false: show the value with asterisks
* - 'prefix': show only prefix of value (1-3 characters based on length of the value)
* - 'suffix': show only suffix of value (1-3 characters based on length of the value)
* - 'both': show both prefix and suffix of value (1-2 characters on suffix and prefix based on length of the value)
* @default false
*/
showValue?: ShowValueType;
}
/**
* Sanitizes a URL by replacing the username and password with asterisks.
* @param {string} value - The URL to sanitize.
* @param {ILoggerLike} [logger] - An optional logger to use for logging warnings.
* @returns {string} The sanitized URL.
* @category Utils
* @since v0.2.5
*/
declare function urlSanitize$1(value: string, logger?: ILoggerLike): string;
/**
* Returns a formatted string representation of a value, enclosed in square brackets.
* @param {string | undefined} value - The value to format.
* @param {FormatParameters | undefined} config - An optional configuration object.
* @returns {string} The formatted string representation of the value.
* @category Utils
* @since v0.2.5
*/
declare function printValue(value: string | undefined, config: FormatParameters | undefined): string;
/**
* Constructs a hidden value string based on the specified visibility option.
* @param {string} value - The original string value to be hidden.
* @param {ShowValueType | undefined} show - Determines how the value should be displayed:
* - `true`: shows the full value
* - `undefined` or `false`: hides the value completely with asterisks
* - `PartialHiddenValueStringType`: shows part of the value (prefix, suffix, or both)
* @returns {string} The resulting string with the specified visibility.
* @since v0.2.5
*/
declare function buildHiddenValue(value: string, show: ShowValueType | undefined): string;
/**
* Builds a hidden value string, replacing each character with an asterisk.
* @param {string} value - The value to be hidden.
* @returns {string} The hidden value string with asterisks.
* @since v0.2.5
*/
declare function buildHiddenAsterisksValueString(value: string): string;
/**
* Show only 1-3 characters of the secret value based on length of the value.
* @param {string} value - The value to partially hide.
* @param {PartialHiddenValueStringType} type - The type of partial hiding to apply ('prefix', 'suffix', or 'prefix-suffix').
* @returns {string} The partially hidden value string.
* @since v0.2.5
*/
declare function buildPartialHiddenValueString(value: string, type: PartialHiddenValueStringType): string;
//#endregion
//#region src/types/EnvMapSchema.d.ts
/**
* Optional environment entry
* @template Value - type of value
* @since v1.1.0
*/
type OptionalEnvEntry<Value> = {
/**
* The parser to use to parse the value
*/
parser: IConfigParser<unknown, Value>;
/**
* The default value to use if the variable is not defined
*/
defaultValue?: Loadable<Value>;
/**
* The format parameters to use to format the value
*/
params?: FormatParameters;
/**
* Whether to throw an error if the variable is undefined
*/
undefinedThrowsError?: boolean;
/**
* Replaces the default throw error message with this message
*/
undefinedErrorMessage?: string;
};
/**
* Required environment entry
* @template Value - type of value
* @since v1.1.0
*/
type RequiredEnvEntry<Value> = {
/**
* The parser to use to parse the value
*/
parser: IConfigParser<unknown, Value>;
/**
* The default value to use if the variable is not defined
*/
defaultValue: Loadable<Value>;
/**
* The format parameters to use to format the value
*/
params?: FormatParameters;
/**
* Whether to throw an error if the variable is undefined
*/
undefinedThrowsError?: boolean;
/**
* Replaces the default throw error message with this message
*/
undefinedErrorMessage?: string;
};
/**
* Required environment entry with undefinedThrowsError
* @template Value - type of value
* @since v1.1.0
*/
type RequiredUndefinedThrowEntry<Value> = {
/**
* The parser to use to parse the value
*/
parser: IConfigParser<unknown, Value>;
/**
* The default value to use if the variable is not defined
*/
defaultValue?: Loadable<Value>;
/**
* The format parameters to use to format the value
*/
params?: FormatParameters;
/**
* Whether to throw an error if the variable is undefined
*/
undefinedThrowsError: true;
/**
* Replaces the default throw error message with this message
*/
undefinedErrorMessage?: string;
};
/**
* Environment map schema
* @template Output - type of output value
* @since v0.2.15
*/
type EnvMapSchema<Output extends Record<string, unknown>> = { [K in keyof Required<Output>]: undefined extends Output[K] ? OptionalEnvEntry<Output[K]> : RequiredEnvEntry<Output[K]> | RequiredUndefinedThrowEntry<Output[K]> };
//#endregion
//#region src/types/TypeValue.d.ts
/**
* Loader type value
* @template T - type of value
* @since 0.2.18
*/
type LoaderTypeValue<T> = {
/** type of loader output (default, env,...) */type: string | undefined; /** output value */
value: T | undefined; /** string value of output */
stringValue: string | undefined; /** variable namespace */
namespace: string | undefined;
};
/**
* Loader type strict value
* @template T - type of value
* @since 0.2.18
*/
type LoaderTypeValueStrict<T> = {
/** type of loader output (default, env,...) */type: string | undefined; /** output value */
value: T; /** string value of output */
stringValue: string; /** variable namespace */
namespace: string | undefined;
};
//#endregion
//#region src/ConfigOptions.d.ts
type ConfigOptions = {
/** undefined = global logger, null = no logger else it's ILoggerLike */logger?: ILoggerLike | null; /** optional namespace added to logs */
namespace?: string;
};
type SolvedConfigOptions = {
/** optional logger instance */logger: ILoggerLike | undefined; /** optional namespace added to logs */
namespace: string | undefined;
};
/**
* Build options from partial options
* @param {Partial<ConfigOptions>} options - partial config options
* @returns {SolvedConfigOptions} - solved config options
* @category Config
* @since v0.6.0
*/
declare function buildOptions({
logger,
namespace
}?: Partial<ConfigOptions>): SolvedConfigOptions;
//#endregion
//#region src/getConfigVariable.d.ts
/**
* get config variable from loaders
* @example
* // from "@avanio/variable-util-node"
* const fileEnv = new FileConfigLoader({fileName: './settings.json', type: 'json'}).getLoader;
*
* const port: Promise<string> = await getConfigVariable('PORT', [env(), fileEnv()], stringParser, '8080', {showValue: true});
* // with override key
* const port: Promise<string> = await getConfigVariable('PORT', [env('HTTP_PORT', fileEnv())], stringParser, '8080', {showValue: true});
* @template Output - Type of output
* @param {string} rootKey - root key of config variable
* @param {IConfigLoader[]} loaders - loaders to use
* @param {IConfigParser<unknown, Output>} parser - parser to use
* @param {Loadable<Output>} [defaultValueLoadable] - default value to use
* @param {FormatParameters} [params] - optional format parameters
* @param {ConfigOptions} [options] - optional config options
* @param {EncodeOptions} [encodeOptions] - optional encode options
* @returns {Promise<Output>} - config variable
* @since v0.2.5
*/
declare function getConfigVariable<Output>(rootKey: string, loaders: IConfigLoader[], parser: IConfigParser<unknown, Output>, defaultValueLoadable: Loadable<Output>, params?: FormatParameters, options?: ConfigOptions, encodeOptions?: EncodeOptions): Promise<Output>;
declare function getConfigVariable<Output>(rootKey: string, loaders: IConfigLoader[], parser: IConfigParser<unknown, Output>, defaultValueLoadable?: Loadable<Output>, params?: FormatParameters, options?: ConfigOptions, encodeOptions?: EncodeOptions): Promise<Output | undefined>;
//#endregion
//#region src/getConfigVariableResult.d.ts
/**
* get config variable from loaders
* @example
* // from "@avanio/variable-util-node"
* const portResult: Result<string> = await getConfigVariableResult('PORT', [env(), fileEnv()], stringParser, '8080', {showValue: true});
*
* const value: string = portResult.unwrap(); // get value or throw error
* const value: string | undefined = portResult.ok(); // get value or undefined
* @template Output - Type of output
* @param {string} rootKey - root key of config variable
* @param {IConfigLoader[]} loaders - loaders to use
* @param {IConfigParser<unknown, Output>} parser - parser to use
* @param {Loadable<Output>} [defaultValueLoadable] - default value to use
* @param {FormatParameters} [params] - optional format parameters
* @param {ConfigOptions} [options] - optional config options
* @returns {Promise<IResult<Output | undefined>>} - result with value or error
* @since v1.0.0
*/
declare function getConfigVariableResult<Output>(rootKey: string, loaders: IConfigLoader[], parser: IConfigParser<unknown, Output>, defaultValueLoadable: Loadable<Output>, params?: FormatParameters, options?: ConfigOptions): Promise<IResult<Output>>;
declare function getConfigVariableResult<Output>(rootKey: string, loaders: IConfigLoader[], parser: IConfigParser<unknown, Output>, defaultValueLoadable?: Loadable<Output>, params?: FormatParameters, options?: ConfigOptions): Promise<IResult<Output | undefined>>;
//#endregion
//#region src/getConfigObject.d.ts
/**
* Clear the seen map for default values (for unit testing purposes)
* @since v0.2.5
*/
declare function clearDefaultValueSeenMap(): void;
/**
* get config object which contains value and type of loader
* @example
* // from "@avanio/variable-util-node"
* const fileEnv = new FileConfigLoader({fileName: './settings.json', type: 'json'}).getLoader;
* const portConfig: {type: string | undefined; value: string} = await getConfigObject('PORT', [env(), fileEnv()], stringParser, '8080', {showValue: true});
* const value: string = portConfig.value;
* const type: string | undefined = portConfig.type; // loader type name
* @template Output - Type of output
* @param {string} rootKey root key of config object
* @param {IConfigLoader[]} loaders array of loaders
* @param {IConfigParser<unknown, Output>} parser parser for value
* @param {Loadable<Output>} defaultValueLoadable optional default value
* @param {FormatParameters} params optional format parameters
* @param {ConfigOptions} options optional config options
* @param {EncodeOptions} [encodeOptions] optional encode options
* @returns {Promise<LoaderTypeValue<Output>>} config object
* @since v0.2.5
*/
declare function getConfigObject<Output>(rootKey: string, loaders: IConfigLoader[], parser: IConfigParser<unknown, Output>, defaultValueLoadable: Loadable<Output>, params?: FormatParameters, options?: ConfigOptions, encodeOptions?: EncodeOptions): Promise<LoaderTypeValueStrict<Output>>;
declare function getConfigObject<Output>(rootKey: string, loaders: IConfigLoader[], parser: IConfigParser<unknown, Output>, defaultValueLoadable?: Loadable<Output>, params?: FormatParameters, options?: ConfigOptions, encodeOptions?: EncodeOptions): Promise<LoaderTypeValue<Output>>;
//#endregion
//#region src/getConfigObjectResult.d.ts
/**
* Wrapper around getConfigObject that returns a Result
* @example
* // from "@avanio/variable-util-node"
* const fileEnv = new FileConfigLoader({fileName: './settings.json', type: 'json'}).getLoader;
* const portConfig: Result<{type: string | undefined; value: string}> = await getConfigObject('PORT', [env(), fileEnv()], stringParser, '8080', {showValue: true});
* if ( portConfig.isOk() ) {
* const {value, type} = portConfig.unwrap();
* } else {
* // handle error
* }
* @template Output - Type of output
* @param {string} rootKey root key of config object
* @param {IConfigLoader[]} loaders array of loaders
* @param {IConfigParser<unknown, Output>} parser parser for value
* @param {Loadable<Output>} defaultValueLoadable optional default value
* @param {FormatParameters} params optional format parameters
* @param {ConfigOptions} options optional config options
* @returns {Promise<IResult<{type: string | undefined; value: Output}>>} result
* @since v0.2.5
*/
declare function getConfigObjectResult<Output>(rootKey: string, loaders: IConfigLoader[], parser: IConfigParser<unknown, Output>, defaultValueLoadable: Loadable<Output>, params?: FormatParameters, options?: ConfigOptions): Promise<IResult<{
type: string | undefined;
value: Output;
}>>;
declare function getConfigObjectResult<Output>(rootKey: string, loaders: IConfigLoader[], parser: IConfigParser<unknown, Output>, defaultValueLoadable?: Loadable<Output>, params?: FormatParameters, options?: ConfigOptions): Promise<IResult<{
type: string | undefined;
value: Output | undefined;
}>>;
//#endregion
//#region src/loaders/ConfigLoader.d.ts
/**
* ConfigLoaderEventMap is the event map for the ConfigLoader
* @category Loaders
* @since v0.11.1
*/
type ConfigLoaderEventMap = {
/** notify when loader data is updated */updated: [];
};
/**
* IConfigLoaderProps is the interface for ConfigLoader props
* @category Loaders
* @since v0.8.0
*/
interface IConfigLoaderProps {
disabled?: Loadable<boolean>;
}
type LoaderValue = {
value: string | undefined;
path: string;
};
/**
* Abstract base class for config loaders
* @template Props - the type of the props
* @template OverrideMap - the type of the override key map
* @category Loaders
* @abstract
* @since v1.0.0
*/
declare abstract class ConfigLoader<Props extends IConfigLoaderProps, OverrideMap extends OverrideKeyMap = OverrideKeyMap> extends EventEmitter<ConfigLoaderEventMap> implements IConfigLoader {
abstract loaderType: Lowercase<string>;
protected abstract defaultOptions: Props;
protected options: Loadable<Partial<Props>>;
protected overrideKeys: Partial<OverrideMap>;
protected valueSeen: Map<string, string>;
constructor(props?: Loadable<Partial<Props>>, overrideKeys?: Partial<OverrideMap>);
getLoaderResult(lookupKey: string): Promise<{
value: string | undefined;
path: string;
seen: boolean;
} | undefined>;
isLoaderDisabled(): Promise<boolean | undefined>;
setDisabled(disabled: Loadable<boolean>): Promise<void>;
/**
* Get options from loader and merge with default options
* @returns {Promise<DefaultProps>} - Promise of DefaultProps & Props
*/
protected getOptions(): Promise<Props>;
protected setOption<Key extends keyof Props>(key: Key, value: Props[Key]): Promise<void>;
/**
* Build error string `ConfigVariables[<type>]: <message>`
* @param {string} message - error message
* @returns {string} - error string
*/
protected buildErrorStr(message: string): string;
/**
* implementation of config loader function
* @param lookupKey - key to lookup in config
* @param params - optional passing params for handleLoader (i.e. lookup key override, settings etc.)
* @returns {LoaderValue | Promise<LoaderValue>} - Promise of LoaderValue
*/
protected abstract handleLoaderValue(lookupKey: string): undefined | LoaderValue | Promise<undefined | LoaderValue>;
}
//#endregion
//#region src/loaders/RecordConfigLoader.d.ts
/**
* RecordConfigLoader is a class that extends ConfigLoader and adds the ability to reload the data.
* @template Props - the type of the options for the loader
* @template OverrideMap - the type of the override key map
* @since v1.0.0
*/
declare abstract class RecordConfigLoader<Props extends IConfigLoaderProps, OverrideMap extends OverrideKeyMap = OverrideKeyMap> extends ConfigLoader<Props, OverrideMap> {
protected abstract defaultOptions: Props;
protected _isLoaded: boolean;
protected dataPromise: Promise<Record<string, string>> | undefined;
/**
* reloads the data
*/
reload(): Promise<void>;
/**
* is the data loaded
* @returns {boolean} - true if the data is loaded, false otherwise
*/
isLoaded(): boolean;
/**
* load data from the loader. If the data is loaded successfully
* an "updated" event will be emitted.
* @returns {Promise<void>}
*/
protected loadData(): Promise<void>;
protected abstract handleData(): Promise<Record<string, string>>;
}
//#endregion
//#region src/loaders/EnvConfigLoader.d.ts
/**
* env loader class is used to load env variables from process.env
* @template OverrideMap - the type of the override key map
* @param {string} [overrideKey] - optional override key for lookup
* @returns {IConfigLoader} - IConfigLoader object
* @category Loaders
* @since v1.0.0
*/
declare class EnvConfigLoader<OverrideMap extends OverrideKeyMap = OverrideKeyMap> extends ConfigLoader<IConfigLoaderProps, OverrideMap> {
readonly loaderType = "env";
defaultOptions: IConfigLoaderProps;
protected handleLoaderValue(lookupKey: string): {
value: string | undefined;
path: string;
};
}
//#endregion
//#region src/loaders/ReactEnvConfigLoader.d.ts
/**
* React env loader class is used to load env variables from process.env.REACT_APP_*
* @template OverrideMap - the type of the override key map
* @param {Partial<OverrideMap>} [override] - optional override key for lookup
* @returns {IConfigLoader} - IConfigLoader object
* @category Loaders
* @since v1.0.0
*/
declare class ReactEnvConfigLoader<OverrideMap extends OverrideKeyMap = OverrideKeyMap> extends ConfigLoader<IConfigLoaderProps, OverrideMap> {
readonly loaderType = "react-env";
defaultOptions: IConfigLoaderProps;
protected handleLoaderValue(lookupKey: string): {
value: string | undefined;
path: string;
};
}
//#endregion
//#region src/loaders/MapConfigLoader.d.ts
/**
* MapConfigLoader is a class that extends ConfigLoader and adds the ability to reload the data.
* @template Props - the type of the options for the loader
* @template OverrideMap - the type of the override key map
* @since v1.0.0
* @category Loaders
*/
declare abstract class MapConfigLoader<Props extends IConfigLoaderProps, OverrideMap extends OverrideKeyMap = OverrideKeyMap> extends ConfigLoader<Props, OverrideMap> {
protected abstract defaultOptions: Props;
protected _isLoaded: boolean;
protected data: Map<string, string>;
/**
* clear maps and reloads the data
*/
reload(): Promise<void>;
/**
* is the data loaded
* @returns {boolean} Whether the data is loaded
*/
isLoaded(): boolean;
/**
* load data from the loader. If the data is loaded successfully
* an "updated" event will be emitted.
* @returns {Promise<void>}
*/
protected loadData(): Promise<void>;
protected abstract handleLoadData(): Promise<boolean>;
}
//#endregion
//#region src/loaders/FetchConfigLoader.d.ts
/**
* Options for the FetchConfigLoader
* @since v1.0.0
*/
interface FetchConfigLoaderOptions extends IConfigLoaderProps {
fetchClient: typeof fetch;
/** this prevents Error to be thrown if have http error */
isSilent: boolean;
payload: 'json';
validate: ValidateCallback<Record<string, string | undefined>, Record<string, string | undefined>> | undefined;
logger: ILoggerLike | undefined;
cache: IRequestCache | undefined;
/** if we get a cache hit code (defaults 304), we use the cached response instead */
cacheHitHttpCode: number;
}
type ConfigRequest = Request | RequestNotReady;
/**
* FetchConfigLoader is used to load config from a fetch request
* @template OverrideMap - the type of the override key map
* @category Loaders
* @since v1.0.0
*/
declare class FetchConfigLoader<OverrideMap extends OverrideKeyMap = OverrideKeyMap> extends MapConfigLoader<FetchConfigLoaderOptions, OverrideMap> {
readonly loaderType: Lowercase<string>;
private request;
private path;
protected defaultOptions: FetchConfigLoaderOptions;
/**
* Constructor for FetchConfigLoader
* @param {Loadable<ConfigRequest>} request - callback that returns a fetch request or a message object that the request is not ready
* @param {Loadable<Partial<FetchConfigLoaderOptions>>} options - optional options for FetchConfigLoader
* @param {Partial<OverrideMap>} overrideKeys - optional override keys for FetchConfigLoader
* @param {Lowercase<string>} type - optional name type for FetchConfigLoader (default: 'fetch')
*/
constructor(request: Loadable<ConfigRequest>, options?: Loadable<Partial<FetchConfigLoaderOptions>>, overrideKeys?: Partial<OverrideMap>, type?: Lowercase<string>);
protected handleLoaderValue(lookupKey: string): Promise<{
value: string | undefined;
path: string;
}>;
protected handleLoadData(): Promise<boolean>;
/**
* if client is offline, we will try return the cached response else add cache validation (ETag) and try get the response from the fetch request
* @param {Request} req - request to fetch
* @returns {Promise<Response | undefined>} - response or undefined
*/
private fetchRequestOrCacheResponse;
/**
* on error, check if we have a valid cached response
* @param {Request} req - request to fetch
* @param {Response} res - response from fetch
* @returns {Promise<Response>} - response
*/
private checkIfValidCacheResponse;
/**
* if we get a 304, get the cached response
* @param {Request} req - request to fetch
* @param {Response} res - response from fetch
* @returns {Promise<Response>} - response
*/
private handleNotModifiedCache;
private handleJson;
private getRequest;
}
//#endregion
//#region src/loaders/MemoryConfigLoader.d.ts
interface IMemoryConfigLoaderProps extends IConfigLoaderProps {
logger: ILoggerLike | undefined;
}
/**
* Config loader with in-memory data which can be set and retrieved variables on the fly.
* - Useful for temporary controlled overrides or testing
* @template MemoryMap - the type of the memory map
* @template OverrideMap - the type of the override key map
* @since v1.0.0
* @category Loaders
*/
declare class MemoryConfigLoader<MemoryMap extends Record<string, string | undefined>, OverrideMap extends OverrideKeyMap = OverrideKeyMap> extends ConfigLoader<IMemoryConfigLoaderProps, OverrideMap> {
readonly loaderType: Lowercase<string>;
private data;
protected defaultOptions: IMemoryConfigLoaderProps;
constructor(initialData: MemoryMap, options?: Loadable<Partial<IMemoryConfigLoaderProps>>, overrideKeys?: Partial<OverrideMap>, type?: Lowercase<string>);
set(key: keyof MemoryMap, value: string | undefined): Promise<void>;
get(key: keyof MemoryMap): Promise<string | undefined>;
protected handleLoaderValue(lookupKey: string): {
value: string | undefined;
path: string;
};
}
//#endregion
//#region src/loaders/SwitchLoader.d.ts
interface ISwitchLoaderProps extends IConfigLoaderProps {
logger?: ILoggerLike;
}
/**
* ConfigMap, this is a helper type to define the configuration map for the switch loader.
* @template Key - The key to switch between
* @template Map - The configuration map
* @since v0.10.1
* @example
* type TestConfigMapEnv = { DEMO: string; ANOTHER: string; };
* const switchConfigMap: SwitchConfigMap<TestConfigMapEnv, 'switch1' | 'switch2'> = {
* switch1: { DEMO: 'value' },
* switch2: { DEMO: 'value2' },
* };
*/
type SwitchConfigMap<Map extends Record<string, unknown>, Key extends string> = Record<Key, Partial<Record<keyof Map, string>>>;
/**
* SwitchLoader, this loader will switch between different configurations based on the active key.
* @example
* type TestConfigMapEnv = { DEMO: string; ANOTHER: string; };
* const switchLoader = new SwitchLoader<TestEnv, 'switch1' | 'switch2'>({
* switch1: { DEMO: 'value' },
* switch2: { DEMO: 'value2' },
* });
* const switcher = switchLoader.getLoader; // use this on the config map loaders: [switcher(), dotenv(), env(), ...]
* await switchLoader.activateSwitch('switch1'); // when you want enable switch1 values
* @template Config - The configuration map
* @template Key - The key to switch between
* @since v1.0.0
* @category Loaders
*/
declare class SwitchLoader<Config extends Record<string, unknown>, Key extends string> extends ConfigLoader<ISwitchLoaderProps> {
readonly loaderType: Lowercase<string>;
private readonly config;
private readonly keys;
protected defaultOptions: ISwitchLoaderProps;
constructor(configs: SwitchConfigMap<Config, Key>, props?: Loadable<Partial<ISwitchLoaderProps>>, type?: Lowercase<string>);
activateSwitch(key: Key): Promise<void>;
deactivateSwitch(key: Key): Promise<void>;
getCurrentKeys(): Readonly<Set<Key>>;
protected handleLoaderValue(lookupKey: string): LoaderValue | undefined;
protected getConfigKeys(key: Key): string[];
}
//#endregion
//#region src/parsers/JsonConfigParser.d.ts
type JsonConfigParserOptions<Out extends Record<string, unknown>> = {
validate?: ValidateCallback<object, Out>; /** keys to hide or partially hide */
protectedKeys?: Iterable<keyof Out>; /** if and how to show protected keys */
showProtectedKeys?: ShowValueType;
};
/**
* Config parser to parse JSON string as object
* @example
* const objectSchema = z.object({
* foo: z.string(),
* baz: z.string(),
* secret: z.string(),
* });
* // parses '{"foo": "bar", "baz": "qux", "secret": "secret"}' string to {foo: "bar", baz: "qux", secret: "secret"}
* const fooBarJsonParser = new JsonConfigParser({
* validate: (value) => objectSchema.parse(value),
* protectedKeys: ['secret'],
* showProtectedKeys: 'prefix-suffix', // shows secret value with few characters from start and end on logging
* });
* @template Out - the type of the output object
* @implements {IConfigParser<Out, object>}
* @category Parsers
* @since v1.0.0
*/
declare class JsonConfigParser<Out extends Record<string, unknown>> implements IConfigParser<object, Out> {
name: string;
private validate;
private showProtectedKeys?;
private protectedKeys;
constructor({
protectedKeys,
validate,
showProtectedKeys
}?: JsonConfigParserOptions<Out>);
parse({
value
}: ParserProps): object;
postValidate({
value
}: PostValidateProps<Record<string, unknown>>): Promise<Out | undefined>;
toString(config: Out): string;
toLogString(config: Out): string;
/**
* Build a string record from the given data
* @param {unknown} data - to be validated as object
* @returns {object} A record with string values
*/
private buildBaseRecord;
}
//#endregion
//#region src/parsers/UrlParser.d.ts
/**
* Properties for the UrlParser
* @since v0.2.5
*/
interface UrlParserProps {
urlSanitize?: boolean;
}
/**
* UrlParser class is used to parse and validate env variables of type URL.
* @class UrlParser
* @implements {IConfigParser<URL, URL>}
* @category Parsers
* @since v0.2.5
*/
declare class UrlParser implements IConfigParser<URL, URL> {
name: string;
/**
* Should the username and password be sanitized
*/
private urlSanitize;
/**
* Create a new UrlParser
* @param {UrlParserProps} properties - Properties for the UrlParser
*/
constructor({
urlSanitize
}?: UrlParserProps);
parse({
value
}: ParserProps): URL;
toString(value: URL): string;
toLogString(value: URL): string;
/**
* Build a URL object from a string and sanitize the username and password
* @param {string} value string to parse
* @returns {URL} URL object with sanitized username and password
*/
private handleUrlSanitize;
}
//#endregion
//#region src/parsers/stringParser.d.ts
/**
* Build parser and have optional post validation (as example for literal values)
* @template Output - Type of output, defaults to string
* @param {TypeGuardValidate<Output>} [validate] - optional post validation
* @returns {IConfigParser<string, Output>} - parser
* @category Parsers
* @since v1.0.0
*/
declare function stringParser<Output extends string = string>(validate?: TypeGuardValidate<Output>): IConfigParser<string, Output>;
//#endregion
//#region src/lib/semicolonUtils.d.ts
/**
* Parse a semicolon separated string into a config object
* @param {string} config Semicolon separated string
* @param {boolean} [keepCase] Keep the case of the keys, default true
* @returns {Record<string, string>} Config object
* @category Utils
* @since v1.0.0
* @example
* parseSemicolonConfig('a=b;c=d') // {a: 'b', c: 'd'}
*/
declare function parseSemicolonConfig(config: string, keepCase?: boolean): Record<string, string>;
/**
* Stringify a config object to a semicolon separated string
* @param {Record<string, string>} config Object to stringify
* @param {boolean} [uriEncode] Use URI encoding for string outputs
* @returns {string} Stringified config
* @category Utils
* @since v1.0.0
* @example
* stringifySemicolonConfig({a: 'b', c: 'd'}) // 'a=b;c=d'
*/
declare function stringifySemicolonConfig(config: Record<string, unknown>, uriEncode?: boolean): string;
/**
* Stringify a config object to a semicolon separated string for logging
* @template Out - Type of output
* @param {Record<string, string>} config Object to stringify
* @param {ShowValueType} showProtectedKeys How to show protected keys
* @param {string[]} protectedKeys list of protected keys
* @returns {string} Stringified config
* @category Utils
* @since v1.0.0
* @example
* logStringifySemicolonConfig({a: 'b', c: 'd'}) // 'a=b;c=d'
*/
declare function logStringifySemicolonConfig<Out extends Record<string, unknown>>(config: Out, showProtectedKeys: ShowValueType | undefined, protectedKeys: Set<keyof Out>): string;
//#endregion
//#region src/lib/objectUtils.d.ts
/**
* validate if a value is a valid object
* @param {unknown} value - value to validate
* @returns {value is Record<string, unknown>} - true if value is a valid object
* @since v0.2.5
*/
declare function isValidObject(value: unknown): value is Record<string, unknown>;
/**
* Convert an object to a string value object
* @param {Record<string, unknown>} obj - object to convert
* @returns {Record<string, string | undefined>} - object with string values
* @since v0.2.5
*/
declare function buildStringObject(obj: Record<string, unknown>): Record<string, string>;
/**
* Convert an object to a Map<string, string>
* @param {Record<string, unknown>} obj - object to convert
* @returns {Map<string, string>} - Map with string values
* @since v0.2.5
*/
declare function buildStringMap(obj: Record<string, unknown>): Map<string, string>;
/**
* Apply a Record<string, string> to a Map<string, string>
* @param {Record<string, string | undefined>} obj - object to apply
* @param {Map<string, string>} target - Map to apply to
* @returns {Map<string, string>} - target with updates
* @since v0.2.5
*/
declare function applyStringMap(obj: Record<string, string | undefined>, target: Map<string, string>): Map<string, string>;
//#endregion
//#region src/lib/seenUtils.d.ts
/**
* Function to check if we have seen a value before and update the seenMap
* @param {Map<string, string>} seenMap - Map of seen values
* @param {string} key - key to check
* @param {string | undefined} value - value to check
* @returns {boolean} - true if already seen value, false if not
* @since v1.0.0
*/
declare function handleSeen(seenMap: Map<string, string>, key: string, value: string | undefined): boolean;
//#endregion
//#region src/parsers/SemicolonConfigParser.d.ts
interface SemicolonConfigParserOptions<OutType extends Record<string, unknown> = Record<string, unknown>> {
validate?: ValidateCallback<Record<string, string>, OutType>;
/** keys to hide or partially hide */
protectedKeys?: Iterable<keyof OutType>;
/** if and how to show protected keys */
showProtectedKeys?: ShowValueType;
/** keep case of keys, if set as false then will convert keys first letters to lower case (Js Style) */
keepCase?: boolean;
}
/**
* Config parser to parse semicolon separated string key=value pairs as object
* @example
* const objectSchema = z.object({
* foo: z.string(),
* baz: z.string(),
* secret: z.string(),
* });
* // parses 'foo=bar;baz=qux;secret=secret' string to {foo: "bar", baz: "qux", secret: "secret"}
* const fooBarJsonParser = new SemicolonConfigParser({
* validate: (value) => objectSchema.parse(value),
* protectedKeys: ['secret'],
* showProtectedKeys: 'prefix-suffix', // shows secret value with few characters from start and end on logging
* });
* @template Out - the type of the output object
* @implements {IConfigParser<Out, Record<string, string>>}
* @category Parsers
* @since v1.0.0
*/
declare class SemicolonConfigParser<Out extends Record<string, unknown> = Record<string, unknown>> implements IConfigParser<Record<string, string>, Out> {
name: string;
private validate;
private keepCase;
private showProtectedKeys;
private protectedKeys;
constructor({
keepCase,
protectedKeys,
validate,
showProtectedKeys
}?: SemicolonConfigParserOptions<Out>);
parse({
value
}: ParserProps): Promise<Record<string, string>>;
postValidate({
value
}: PostValidateProps<Record<string, string>>): Promise<Out | undefined>;
toString(config: Out, options?: EncodeOptions): string;
toLogString(config: Out): string;
}
//#endregion
//#region src/parsers/booleanParser.d.ts
/**
* Build parser and have optional post validation (as example for literal values)
*
* supports the following string ___true___ values:
*
* ```['true', '1', 'yes', 'y', 'on']```
*
* supports the following string ___false___ values:
*
* ```['false', '0', 'no', 'n', 'off']```
* @template Output - Type of output, defaults to number
* @param {TypeGuardValidate<Output>} [validate] - optional post validation
* @returns {IConfigParser<boolean, Output>} - parser
* @category Parsers
* @since v1.0.0
*/
declare function booleanParser<Output extends boolean = boolean>(validate?: TypeGuardValidate<Output>): IConfigParser<boolean, Output>;
//#endregion
//#region src/parsers/floatParser.d.ts
/**
* Build parser and have optional post validation (as example for literal values)
* @template Output - Type of output, defaults to number
* @param {TypeGuardValidate<Output>} [validate] - optional post validation
* @returns {IConfigParser<number, Output>} - parser
* @category Parsers
* @since v1.0.0
*/
declare function floatParser<Output extends number = number>(validate?: TypeGuardValidate<Output>): IConfigParser<number, Output>;
//#endregion
//#region src/parsers/integerParser.d.ts
/**
* Build parser and have optional post validation (as example for literal values)
* @template Output - Type of output, defaults to number
* @param {TypeGuardValidate<Output>} [validate] - optional post validation
* @returns {IConfigParser<number, Output>} - parser
* @category Parsers
* @since v1.0.0
*/
declare function integerParser<Output extends number = number>(validate?: TypeGuardValidate<Output>): IConfigParser<number, Output>;
//#endregion
//#region src/parsers/arrayParser.d.ts
/**
* Build parser for array of values
* @template Input - Type of input
* @template Output - Type of output
* @param {IConfigParser<Input, Output>} parse parser for the array values
* @param {string} separator separator for the array values, defaults to ';'
* @param {TypeGuardValidate<Output> | undefined} validate optional post validation
* @returns {IConfigParser<Output[], Input[]>} Parser for array of values
* @category Parsers
* @since v1.0.0
*/
declare function arrayParser<Input, Output>(parse: IConfigParser<Input, Output>, separator?: string, validate?: TypeGuardValidate<Output>): IConfigParser<Input[], Output[]>;
//#endregion
//#region src/parsers/bigIntParser.d.ts
/**
* Build parser and have optional post validation (as example for literal values)
* @template Output - Type of output, defaults to bigint
* @param {TypeGuardValidate<Output>} [validate] - optional post validation
* @returns {IConfigParser<bigint, Output>} - parser
* @category Parsers
* @since v1.0.0
*/
declare function bigIntParser<Output extends bigint = bigint>(validate?: TypeGuardValidate<Output>): IConfigParser<bigint, Output>;
//#endregion
//#region src/VariableError.d.ts
/**
* Custom error class for variable errors.
* @class VariableError
* @augments Error
* @param {string} message - The error message.
* @category Errors
* @since v0.2.2
* @example
* throw new VariableError('Variable not found');
*/
declare class VariableError extends Error {
/**
* Create a new VariableError
* @param {string} message - The error message
* @param {ErrorOptions} [options] - The error options
*/
constructor(message: string, options?: ErrorOptions);
}
//#endregion
//#region src/VariableLookupError.d.ts
/**
* Custom error class for variable lookup errors.
* @class VariableLookupError
* @augments VariableError
* @category Errors
* @since v0.2.2
*/
declare class VariableLookupError extends VariableError {
readonly variableKey: string;
/**
* Create a new VariableLookupError
* @param {string} variableKey - The variable key.
* @param {string} message - The error message.
* @param {ErrorOptions} [options] - The error options
*/
constructor(variableKey: string, message: string, options?: ErrorOptions);
}
//#endregion
//#region src/ConfigMap.d.ts
/**
* TypeValueRecords
* @template T - type of config map
* @since v0.6.0
*/
type TypeValueRecords<T> = Record<keyof T, LoaderTypeValueStrict<T[keyof T]>>;
/**
* ConfigMap
* @example
* type ConfigEnv = {
* PORT: number;
* HOST: string;
* DEBUG: boolean;
* URL: URL;
* };
* const config = new ConfigMap<ConfigEnv>({
* DEBUG: {loaders: [env()], parser: booleanParser, defaultValue: false},
* HOST: {loaders: [env()], parser: stringParser, defaultValue: 'localhost'},
* PORT: {loaders: [env()], parser: integerParser, defaultValue: 3000},
* URL: {loaders: [env()], parser: new UrlParser({urlSanitize: true}), defaultValue: new URL('http://localhost:3000')},
* });
* console.log('port', await config.get('PORT'));
* @template Data - type of config map
* @since v1.1.0
*/
declare class ConfigMap<Data extends Record<string, unknown>> implements ISetOptionalLogger {
private schema;
private options;
private loaders;
/**
* ConfigMap constructor
* @param {EnvMapSchema<Data>} schema - schema of config map
* @param {Iterable<IConfigLoader>} loaders - iterable of config loaders
* @param {ConfigOptions} options - optional config options (logger, namespace)
*/
constructor(schema: EnvMapSchema<Data>, loaders: Loadable<Iterable<IConfigLoader>>, options?: ConfigOptions);
/**
* Set logger value
* @param {ILoggerLike | undefined} logger - logger like object
*/
setLogger(logger: ILoggerLike | undefined): void;
/**
* get env object from config map
* @returns {Promise<TypeValueRecords<Data>>} Promise of env object or undefined
* @example
* const valueObject: LoaderTypeValue<number> = await config.getObject('PORT');
* console.log(valueObject.type, valueObject.value); // 'env', 3000
* @param {string} key - key of config map
* @param {EncodeOptions} [encodeOptions] - optional encode options
*/
getObject<Key extends keyof Data = keyof Data>(key: Key, encodeOptions?: EncodeOptions): Promise<LoaderTypeValueStrict<Data[Key]>>;
/**
* get env object from config map as Result
* @returns {Promise<Result<TypeValueRecords<Data>>>} Result Promise of env object or undefined
* @example
* const valueObject: Result<LoaderTypeValue<number>> = await config.getObjectResult('PORT');
* if (valueObject.isOk()) {
* const {type, value} = valueObject.ok();
* console.log(type, value); // 'env', 3000
* }
* @param {string} key - key of config map
* @param {EncodeOptions} [encodeOptions] - optional encode options
*/
getObjectResult<Key extends keyof Data = keyof Data>(key: Key, encodeOptions?: EncodeOptions): Promise<IResult<LoaderTypeValueStrict<Data[Key]>>>;
/**
* get env value from config map
* @returns {Promise<Data[Key]>} Promise of value or undefined
* @example
* const port: number = await config.get('PORT');
* @param {string} key - key of config map
* @param {EncodeOptions} [encodeOptions] - optional encode options
*/
get<Key extends keyof Data = keyof Data>(key: Key, encodeOptions?: EncodeOptions): Promise<Data[Key]>;
/**
* get env value as string from config map
* @returns {Promise<string | undefined>} Promise of string value or undefined
* @example
* const port: string = await config.getString('PORT');
* @param {string} key - key of config map
* @param {EncodeOptions} [encodeOptions] - optional encode options
*/
getString<Key extends keyof Data = keyof Data>(key: Key, encodeOptions?: EncodeOptions): Promise<undefined extends Data[Key] ? string | undefined : string>;
/**
* get env value from config map as Result
* @returns {Promise<Result<Data[Key]>>} Result Promise of value or undefined
* @example
* const port: Result<number> = await config.getResult('PORT');
* if (port.isOk()) {
* console.log('port', port.ok());
* }
* @param {string} key - key of config map
* @param {EncodeOptions} [encodeOptions] - optional encode options
*/
getResult<Key extends keyof Data = keyof Data>(key: Key, encodeOptions?: EncodeOptions): Promise<IResult<Data[Key]>>;
/**
* get env value as string from config map as Result
* @returns {Promise<Result<string | undefined>>} Result Promise of string value or undefined
* @example
* const port: Result<string> = await config.getStringResult('PORT');
* if (port.isOk()) {
* console.log('port', port.ok());
* }
* @param {string} key - key of config map
* @param {EncodeOptions} [encodeOptions] - optional encode options
*/
getStringResult<Key extends keyof Data = keyof Data>(key: Key, encodeOptions?: EncodeOptions): Promise<IResult<undefined extends Data[Key] ? string | undefined : string>>;
/**
* get all env value objects from config map
* @returns {Promise<TypeValueRecords<Data>>} Promise of all values
* @example
* const values: TypeValueRecords<Data> = await config.getAll();
* console.log(values.PORT.type, values.PORT.value); // 'env', 3000
*/
getAllObjects(): Promise<TypeValueRecords<Data>>;
/**
* get all env values from config map
* @returns {Promise<Data>} Promise of all values
* @example
* const values: Data = await config.getAllValues();
* console.log('PORT', values.PORT); // 3000 (number)
*/
getAllValues(): Promise<Data>;
/**
* get all env values from config map as string
* @returns {Promise<Record<keyof Data, string>>} Promise of all values as string
* @example
* const values: Record<keyof Data, string> = await config.getAllStringValues();
* console.log('PORT', values.PORT); // '3000' (string)
* @param {EncodeOptions} [encodeOptions] - optional encode options
*/
getAllStringValues(encodeOptions?: EncodeOptions): Promise<Record<keyof Data, string>>;
/**
* Validate all env values from config map, expect to throw error if error exists
* @param {(data: Data) => void} callback callback function
*/
validateAll(callback: (data: Data) => void): Promise<void>;
/**
* run lookup to all keys and return all promises
* @param {EncodeOptions} [encodeOptions] - optional encode options
* @returns {Promise<[keyof Data, LoaderTypeValueStrict<Data[keyof Data]>][]>} Promise of all values
*/
private getAllPromises;
}
//#endregion
//#region src/logger.d.ts
/**
* Set the logger to be used by the module
* @category Utils
* @param {ILoggerLike} newLogger - logger to be used
* @since v0.2.18
*/
declare function setLogger(newLogger: ILoggerLike): void;
/**
* Get current logger instance
* @category Utils
* @returns {ILoggerLike | undefined} - current logger
* @since v0.5.9
*/
declare function getLogger(): ILoggerLike | undefined;
/**
* resolve logger: undefined = global logger, null = no logger else it's ILoggerLike
* @param {ILoggerLike | undefined | null} logger - logger to resolve
* @category Utils
* @returns {ILoggerLike | undefined} - resolved logger
* @since v0.5.9
*/
declare function resolveLogger(logger: undefined | null | ILoggerLike): ILoggerLike | undefined;
//#endregion
export { ConfigLoader, ConfigLoaderEventMap, ConfigMap, ConfigOptions, ConfigRequest, EncodeOptions, EnvConfigLoader, EnvMapSchema, FetchConfigLoader, FetchConfigLoaderOptions, FormatParameters, IConfigLoader, IConfigLoaderProps, IConfigParser, IMemoryConfigLoaderProps, IRequestCache, ISwitchLoaderProps, InferOverrideKeyMap, JsonConfigParser, JsonConfigParserOptions, LoaderTypeValue, LoaderTypeValueStrict, LoaderValue, LoaderValueResult, MapConfigLoader, MemoryConfigLoader, OptionalEnvEntry, OverrideKeyMap, ParserProps, PartialHiddenValueStringType, PostValidateProps, PreValidateProps, ReactEnvConfigLoader, RecordConfigLoader, RequestNotReady, RequiredEnvEntry, RequiredUndefinedThrowEntry, SemicolonConfigParser, SemicolonConfigParserOptions, ShowValueType, SolvedConfigOptions, SwitchConfigMap, SwitchLoader, TypeGuardValidate, TypeValueRecords, UrlParser, UrlParserProps, ValidateCallback, VariableError, VariableLookupError, applyStringMap, arrayParser, bigIntParser, booleanParser, buildHiddenAsterisksValueString, buildHiddenValue, buildOptions, buildPartialHiddenValueString, buildStringMap, buildStringObject, clearDefaultValueSeenMap, createRequestNotReady, floatParser, getConfigObject, getConfigObjectResult, getConfigVariable, getConfigVariableResult, getLogger, handleSeen, integerParser, isRequestNotReadMessage, isValidObject, logStringifySemicolonConfig, parseSemicolonConfig, printValue, resolveLogger, setLogger, stringParser, stringifySemicolonConfig, urlSanitize$1 as urlSanitize };
//# sourceMappingURL=index.d.cts.map
+611
-575

@@ -1,6 +0,7 @@

import { Loadable } from '@luolapeikko/ts-common';
import { ILoggerLike, ISetOptionalLogger } from '@avanio/logger-like';
import { IResult } from '@luolapeikko/result-option';
import { EventEmitter } from 'events';
import { Loadable } from "@luolapeikko/ts-common";
import { IResult } from "@luolapeikko/result-option";
import { EventEmitter } from "events";
import { ILoggerLike, ISetOptionalLogger } from "@avanio/logger-like";
//#region src/interfaces/IConfigLoader.d.ts
/**

@@ -15,8 +16,5 @@ * Represents the result of loading a configuration value.

type LoaderValueResult = {
/** this is shown on logs `ConfigVariables[type]: KEY [___VALUE___] from {path}` if showValue is true */
value: string | undefined;
/** this is shown on logs `ConfigVariables[type]: KEY [VALUE] from {___path___}` */
path: string;
/** is key and value already seen (for logging) */
seen: boolean;
/** this is shown on logs `ConfigVariables[type]: KEY [___VALUE___] from {path}` if showValue is true */value: string | undefined; /** this is shown on logs `ConfigVariables[type]: KEY [VALUE] from {___path___}` */
path: string; /** is key and value already seen (for logging) */
seen: boolean;
};

@@ -29,15 +27,15 @@ /**

interface IConfigLoader {
/** this is shown on logs `ConfigVariables[___type___]: KEY [VALUE] from {path}` */
readonly loaderType: Lowercase<string>;
/**
* get loader result for lookupKey
* @param {string} lookupKey - key to lookup
* @returns {undefined | LoaderValueResult | Promise<undefined | LoaderValueResult>} - Promise of LoaderValueResult or undefined
*/
getLoaderResult(lookupKey: string): undefined | LoaderValueResult | Promise<undefined | LoaderValueResult>;
/**
* Check if loader is disabled
* @returns {boolean | undefined | Promise<boolean | undefined>} - Promise of boolean or undefined
*/
isLoaderDisabled(): boolean | undefined | Promise<boolean | undefined>;
/** this is shown on logs `ConfigVariables[___type___]: KEY [VALUE] from {path}` */
readonly loaderType: Lowercase<string>;
/**
* get loader result for lookupKey
* @param {string} lookupKey - key to lookup
* @returns {undefined | LoaderValueResult | Promise<undefined | LoaderValueResult>} - Promise of LoaderValueResult or undefined
*/
getLoaderResult(lookupKey: string): undefined | LoaderValueResult | Promise<undefined | LoaderValueResult>;
/**
* Check if loader is disabled
* @returns {boolean | undefined | Promise<boolean | undefined>} - Promise of boolean or undefined
*/
isLoaderDisabled(): boolean | undefined | Promise<boolean | undefined>;
}

@@ -65,3 +63,4 @@ /**

type InferOverrideKeyMap<T> = Record<keyof T, string>;
//#endregion
//#region src/interfaces/IConfigParser.d.ts
/**

@@ -72,5 +71,5 @@ * Parser method props

type ParserProps = {
loader: IConfigLoader;
key: string;
value: string;
loader: IConfigLoader;
key: string;
value: string;
};

@@ -82,5 +81,5 @@ /**

type PreValidateProps = {
loader: IConfigLoader;
key: string;
value: unknown;
loader: IConfigLoader;
key: string;
value: unknown;
};

@@ -93,5 +92,5 @@ /**

type PostValidateProps<T> = {
loader: IConfigLoader;
key: string;
value: T;
loader: IConfigLoader;
key: string;
value: T;
};

@@ -109,10 +108,10 @@ /**

type EncodeOptions = {
/**
* use URI encoding for string outputs (used by semicolon parser)
*/
uriEncode?: boolean;
/**
* not logging the value
*/
silent?: boolean;
/**
* use URI encoding for string outputs (used by semicolon parser)
*/
uriEncode?: boolean;
/**
* not logging the value
*/
silent?: boolean;
};

@@ -126,32 +125,33 @@ /**

interface IConfigParser<Input, Output> {
/**
* name of the parser
*/
name: string;
/**
* Config parser function
* @throws Error if parsing fails
*/
parse(parserProps: ParserProps): Input | Promise<Input>;
/**
* Optional raw string value validation before parsing.
* @throws Error if validation fails
*/
preValidate?(preValidateProps: PreValidateProps): void | Promise<void>;
/**
* Optional value validation after parsing
* @throws Error if validation fails
*/
postValidate?(postValidateProps: PostValidateProps<Input>): Output | undefined | Promise<Output | undefined>;
/**
* Build readable string from value
*/
toString(config: Output, options?: EncodeOptions): string;
/**
* Optional build readable string from value for log (can hide sensitive part from logs) else toString is used
* @param config - value to log
*/
toLogString?(config: Output): string;
/**
* name of the parser
*/
name: string;
/**
* Config parser function
* @throws Error if parsing fails
*/
parse(parserProps: ParserProps): Input | Promise<Input>;
/**
* Optional raw string value validation before parsing.
* @throws Error if validation fails
*/
preValidate?(preValidateProps: PreValidateProps): void | Promise<void>;
/**
* Optional value validation after parsing
* @throws Error if validation fails
*/
postValidate?(postValidateProps: PostValidateProps<Input>): Output | undefined | Promise<Output | undefined>;
/**
* Build readable string from value
*/
toString(config: Output, options?: EncodeOptions): string;
/**
* Optional build readable string from value for log (can hide sensitive part from logs) else toString is used
* @param config - value to log
*/
toLogString?(config: Output): string;
}
//#endregion
//#region src/interfaces/IValidate.d.ts
/**

@@ -164,3 +164,4 @@ * Interface for validation function

type ValidateCallback<Input, Output> = (data: Input) => Output | Promise<Output>;
//#endregion
//#region src/interfaces/IRequestCache.d.ts
/**

@@ -192,16 +193,17 @@ * interface for a request cache

interface IRequestCache {
/**
* check if the client is connected to the internet
*/
isOnline(): boolean;
/**
* get the cached response for a request
*/
fetchRequest(req: Request): Promise<Response | undefined>;
/**
* store the response for a request
*/
storeRequest(req: Request, res: Response): Promise<void>;
/**
* check if the client is connected to the internet
*/
isOnline(): boolean;
/**
* get the cached response for a request
*/
fetchRequest(req: Request): Promise<Response | undefined>;
/**
* store the response for a request
*/
storeRequest(req: Request, res: Response): Promise<void>;
}
//#endregion
//#region src/types/RequestNotReady.d.ts
/**

@@ -213,4 +215,4 @@ * RequestNotReady indicates that the request is not ready to be loaded yet. (e.g. the user is not logged in)

interface RequestNotReady {
_type: 'RequestNotReady';
message: string;
_type: 'RequestNotReady';
message: string;
}

@@ -233,10 +235,5 @@ /**

declare function createRequestNotReady(message: string): RequestNotReady;
type PartialHiddenValueStringType =
/** show only prefix of secret "j43****" */
'prefix'
/** show only suffix of secret "****7hd" */
| 'suffix'
/** show both prefix and suffix of secret "j43****7hd" */
| 'prefix-suffix';
//#endregion
//#region src/lib/formatUtils.d.ts
type PartialHiddenValueStringType = /** show only prefix of secret "j43****" */'prefix' /** show only suffix of secret "****7hd" */ | 'suffix' /** show both prefix and suffix of secret "j43****7hd" */ | 'prefix-suffix';
type ShowValueType = boolean | PartialHiddenValueStringType;

@@ -248,13 +245,13 @@ /**

interface FormatParameters {
/**
* Whether to show the value in the output, defaults to undefined
* - undefined: hide the value
* - true: show the actual value
* - false: show the value with asterisks
* - 'prefix': show only prefix of value (1-3 characters based on length of the value)
* - 'suffix': show only suffix of value (1-3 characters based on length of the value)
* - 'both': show both prefix and suffix of value (1-2 characters on suffix and prefix based on length of the value)
* @default false
*/
showValue?: ShowValueType;
/**
* Whether to show the value in the output, defaults to undefined
* - undefined: hide the value
* - true: show the actual value
* - false: show the value with asterisks
* - 'prefix': show only prefix of value (1-3 characters based on length of the value)
* - 'suffix': show only suffix of value (1-3 characters based on length of the value)
* - 'both': show both prefix and suffix of value (1-2 characters on suffix and prefix based on length of the value)
* @default false
*/
showValue?: ShowValueType;
}

@@ -269,3 +266,3 @@ /**

*/
declare function urlSanitize(value: string, logger?: ILoggerLike): string;
declare function urlSanitize$1(value: string, logger?: ILoggerLike): string;
/**

@@ -306,3 +303,4 @@ * Returns a formatted string representation of a value, enclosed in square brackets.

declare function buildPartialHiddenValueString(value: string, type: PartialHiddenValueStringType): string;
//#endregion
//#region src/types/EnvMapSchema.d.ts
/**

@@ -314,22 +312,22 @@ * Optional environment entry

type OptionalEnvEntry<Value> = {
/**
* The parser to use to parse the value
*/
parser: IConfigParser<unknown, Value>;
/**
* The default value to use if the variable is not defined
*/
defaultValue?: Loadable<Value>;
/**
* The format parameters to use to format the value
*/
params?: FormatParameters;
/**
* Whether to throw an error if the variable is undefined
*/
undefinedThrowsError?: boolean;
/**
* Replaces the default throw error message with this message
*/
undefinedErrorMessage?: string;
/**
* The parser to use to parse the value
*/
parser: IConfigParser<unknown, Value>;
/**
* The default value to use if the variable is not defined
*/
defaultValue?: Loadable<Value>;
/**
* The format parameters to use to format the value
*/
params?: FormatParameters;
/**
* Whether to throw an error if the variable is undefined
*/
undefinedThrowsError?: boolean;
/**
* Replaces the default throw error message with this message
*/
undefinedErrorMessage?: string;
};

@@ -342,22 +340,22 @@ /**

type RequiredEnvEntry<Value> = {
/**
* The parser to use to parse the value
*/
parser: IConfigParser<unknown, Value>;
/**
* The default value to use if the variable is not defined
*/
defaultValue: Loadable<Value>;
/**
* The format parameters to use to format the value
*/
params?: FormatParameters;
/**
* Whether to throw an error if the variable is undefined
*/
undefinedThrowsError?: boolean;
/**
* Replaces the default throw error message with this message
*/
undefinedErrorMessage?: string;
/**
* The parser to use to parse the value
*/
parser: IConfigParser<unknown, Value>;
/**
* The default value to use if the variable is not defined
*/
defaultValue: Loadable<Value>;
/**
* The format parameters to use to format the value
*/
params?: FormatParameters;
/**
* Whether to throw an error if the variable is undefined
*/
undefinedThrowsError?: boolean;
/**
* Replaces the default throw error message with this message
*/
undefinedErrorMessage?: string;
};

@@ -370,22 +368,22 @@ /**

type RequiredUndefinedThrowEntry<Value> = {
/**
* The parser to use to parse the value
*/
parser: IConfigParser<unknown, Value>;
/**
* The default value to use if the variable is not defined
*/
defaultValue?: Loadable<Value>;
/**
* The format parameters to use to format the value
*/
params?: FormatParameters;
/**
* Whether to throw an error if the variable is undefined
*/
undefinedThrowsError: true;
/**
* Replaces the default throw error message with this message
*/
undefinedErrorMessage?: string;
/**
* The parser to use to parse the value
*/
parser: IConfigParser<unknown, Value>;
/**
* The default value to use if the variable is not defined
*/
defaultValue?: Loadable<Value>;
/**
* The format parameters to use to format the value
*/
params?: FormatParameters;
/**
* Whether to throw an error if the variable is undefined
*/
undefinedThrowsError: true;
/**
* Replaces the default throw error message with this message
*/
undefinedErrorMessage?: string;
};

@@ -397,6 +395,5 @@ /**

*/
type EnvMapSchema<Output extends Record<string, unknown>> = {
[K in keyof Required<Output>]: undefined extends Output[K] ? OptionalEnvEntry<Output[K]> : RequiredEnvEntry<Output[K]> | RequiredUndefinedThrowEntry<Output[K]>;
};
type EnvMapSchema<Output extends Record<string, unknown>> = { [K in keyof Required<Output>]: undefined extends Output[K] ? OptionalEnvEntry<Output[K]> : RequiredEnvEntry<Output[K]> | RequiredUndefinedThrowEntry<Output[K]> };
//#endregion
//#region src/types/TypeValue.d.ts
/**

@@ -408,10 +405,6 @@ * Loader type value

type LoaderTypeValue<T> = {
/** type of loader output (default, env,...) */
type: string | undefined;
/** output value */
value: T | undefined;
/** string value of output */
stringValue: string | undefined;
/** variable namespace */
namespace: string | undefined;
/** type of loader output (default, env,...) */type: string | undefined; /** output value */
value: T | undefined; /** string value of output */
stringValue: string | undefined; /** variable namespace */
namespace: string | undefined;
};

@@ -424,23 +417,16 @@ /**

type LoaderTypeValueStrict<T> = {
/** type of loader output (default, env,...) */
type: string | undefined;
/** output value */
value: T;
/** string value of output */
stringValue: string;
/** variable namespace */
namespace: string | undefined;
/** type of loader output (default, env,...) */type: string | undefined; /** output value */
value: T; /** string value of output */
stringValue: string; /** variable namespace */
namespace: string | undefined;
};
//#endregion
//#region src/ConfigOptions.d.ts
type ConfigOptions = {
/** undefined = global logger, null = no logger else it's ILoggerLike */
logger?: ILoggerLike | null;
/** optional namespace added to logs */
namespace?: string;
/** undefined = global logger, null = no logger else it's ILoggerLike */logger?: ILoggerLike | null; /** optional namespace added to logs */
namespace?: string;
};
type SolvedConfigOptions = {
/** optional logger instance */
logger: ILoggerLike | undefined;
/** optional namespace added to logs */
namespace: string | undefined;
/** optional logger instance */logger: ILoggerLike | undefined; /** optional namespace added to logs */
namespace: string | undefined;
};

@@ -454,4 +440,8 @@ /**

*/
declare function buildOptions({ logger, namespace }?: Partial<ConfigOptions>): SolvedConfigOptions;
declare function buildOptions({
logger,
namespace
}?: Partial<ConfigOptions>): SolvedConfigOptions;
//#endregion
//#region src/getConfigVariable.d.ts
/**

@@ -479,3 +469,4 @@ * get config variable from loaders

declare function getConfigVariable<Output>(rootKey: string, loaders: IConfigLoader[], parser: IConfigParser<unknown, Output>, defaultValueLoadable?: Loadable<Output>, params?: FormatParameters, options?: ConfigOptions, encodeOptions?: EncodeOptions): Promise<Output | undefined>;
//#endregion
//#region src/getConfigVariableResult.d.ts
/**

@@ -501,3 +492,4 @@ * get config variable from loaders

declare function getConfigVariableResult<Output>(rootKey: string, loaders: IConfigLoader[], parser: IConfigParser<unknown, Output>, defaultValueLoadable?: Loadable<Output>, params?: FormatParameters, options?: ConfigOptions): Promise<IResult<Output | undefined>>;
//#endregion
//#region src/getConfigObject.d.ts
/**

@@ -529,3 +521,4 @@ * Clear the seen map for default values (for unit testing purposes)

declare function getConfigObject<Output>(rootKey: string, loaders: IConfigLoader[], parser: IConfigParser<unknown, Output>, defaultValueLoadable?: Loadable<Output>, params?: FormatParameters, options?: ConfigOptions, encodeOptions?: EncodeOptions): Promise<LoaderTypeValue<Output>>;
//#endregion
//#region src/getConfigObjectResult.d.ts
/**

@@ -553,10 +546,11 @@ * Wrapper around getConfigObject that returns a Result

declare function getConfigObjectResult<Output>(rootKey: string, loaders: IConfigLoader[], parser: IConfigParser<unknown, Output>, defaultValueLoadable: Loadable<Output>, params?: FormatParameters, options?: ConfigOptions): Promise<IResult<{
type: string | undefined;
value: Output;
type: string | undefined;
value: Output;
}>>;
declare function getConfigObjectResult<Output>(rootKey: string, loaders: IConfigLoader[], parser: IConfigParser<unknown, Output>, defaultValueLoadable?: Loadable<Output>, params?: FormatParameters, options?: ConfigOptions): Promise<IResult<{
type: string | undefined;
value: Output | undefined;
type: string | undefined;
value: Output | undefined;
}>>;
//#endregion
//#region src/loaders/ConfigLoader.d.ts
/**

@@ -568,4 +562,3 @@ * ConfigLoaderEventMap is the event map for the ConfigLoader

type ConfigLoaderEventMap = {
/** notify when loader data is updated */
updated: [];
/** notify when loader data is updated */updated: [];
};

@@ -578,7 +571,7 @@ /**

interface IConfigLoaderProps {
disabled?: Loadable<boolean>;
disabled?: Loadable<boolean>;
}
type LoaderValue = {
value: string | undefined;
path: string;
value: string | undefined;
path: string;
};

@@ -594,36 +587,37 @@ /**

declare abstract class ConfigLoader<Props extends IConfigLoaderProps, OverrideMap extends OverrideKeyMap = OverrideKeyMap> extends EventEmitter<ConfigLoaderEventMap> implements IConfigLoader {
abstract loaderType: Lowercase<string>;
protected abstract defaultOptions: Props;
protected options: Loadable<Partial<Props>>;
protected overrideKeys: Partial<OverrideMap>;
protected valueSeen: Map<string, string>;
constructor(props?: Loadable<Partial<Props>>, overrideKeys?: Partial<OverrideMap>);
getLoaderResult(lookupKey: string): Promise<{
value: string | undefined;
path: string;
seen: boolean;
} | undefined>;
isLoaderDisabled(): Promise<boolean | undefined>;
setDisabled(disabled: Loadable<boolean>): Promise<void>;
/**
* Get options from loader and merge with default options
* @returns {Promise<DefaultProps>} - Promise of DefaultProps & Props
*/
protected getOptions(): Promise<Props>;
protected setOption<Key extends keyof Props>(key: Key, value: Props[Key]): Promise<void>;
/**
* Build error string `ConfigVariables[<type>]: <message>`
* @param {string} message - error message
* @returns {string} - error string
*/
protected buildErrorStr(message: string): string;
/**
* implementation of config loader function
* @param lookupKey - key to lookup in config
* @param params - optional passing params for handleLoader (i.e. lookup key override, settings etc.)
* @returns {LoaderValue | Promise<LoaderValue>} - Promise of LoaderValue
*/
protected abstract handleLoaderValue(lookupKey: string): undefined | LoaderValue | Promise<undefined | LoaderValue>;
abstract loaderType: Lowercase<string>;
protected abstract defaultOptions: Props;
protected options: Loadable<Partial<Props>>;
protected overrideKeys: Partial<OverrideMap>;
protected valueSeen: Map<string, string>;
constructor(props?: Loadable<Partial<Props>>, overrideKeys?: Partial<OverrideMap>);
getLoaderResult(lookupKey: string): Promise<{
value: string | undefined;
path: string;
seen: boolean;
} | undefined>;
isLoaderDisabled(): Promise<boolean | undefined>;
setDisabled(disabled: Loadable<boolean>): Promise<void>;
/**
* Get options from loader and merge with default options
* @returns {Promise<DefaultProps>} - Promise of DefaultProps & Props
*/
protected getOptions(): Promise<Props>;
protected setOption<Key extends keyof Props>(key: Key, value: Props[Key]): Promise<void>;
/**
* Build error string `ConfigVariables[<type>]: <message>`
* @param {string} message - error message
* @returns {string} - error string
*/
protected buildErrorStr(message: string): string;
/**
* implementation of config loader function
* @param lookupKey - key to lookup in config
* @param params - optional passing params for handleLoader (i.e. lookup key override, settings etc.)
* @returns {LoaderValue | Promise<LoaderValue>} - Promise of LoaderValue
*/
protected abstract handleLoaderValue(lookupKey: string): undefined | LoaderValue | Promise<undefined | LoaderValue>;
}
//#endregion
//#region src/loaders/RecordConfigLoader.d.ts
/**

@@ -636,23 +630,24 @@ * RecordConfigLoader is a class that extends ConfigLoader and adds the ability to reload the data.

declare abstract class RecordConfigLoader<Props extends IConfigLoaderProps, OverrideMap extends OverrideKeyMap = OverrideKeyMap> extends ConfigLoader<Props, OverrideMap> {
protected abstract defaultOptions: Props;
protected _isLoaded: boolean;
protected dataPromise: Promise<Record<string, string>> | undefined;
/**
* reloads the data
*/
reload(): Promise<void>;
/**
* is the data loaded
* @returns {boolean} - true if the data is loaded, false otherwise
*/
isLoaded(): boolean;
/**
* load data from the loader. If the data is loaded successfully
* an "updated" event will be emitted.
* @returns {Promise<void>}
*/
protected loadData(): Promise<void>;
protected abstract handleData(): Promise<Record<string, string>>;
protected abstract defaultOptions: Props;
protected _isLoaded: boolean;
protected dataPromise: Promise<Record<string, string>> | undefined;
/**
* reloads the data
*/
reload(): Promise<void>;
/**
* is the data loaded
* @returns {boolean} - true if the data is loaded, false otherwise
*/
isLoaded(): boolean;
/**
* load data from the loader. If the data is loaded successfully
* an "updated" event will be emitted.
* @returns {Promise<void>}
*/
protected loadData(): Promise<void>;
protected abstract handleData(): Promise<Record<string, string>>;
}
//#endregion
//#region src/loaders/EnvConfigLoader.d.ts
/**

@@ -667,10 +662,11 @@ * env loader class is used to load env variables from process.env

declare class EnvConfigLoader<OverrideMap extends OverrideKeyMap = OverrideKeyMap> extends ConfigLoader<IConfigLoaderProps, OverrideMap> {
readonly loaderType = "env";
defaultOptions: IConfigLoaderProps;
protected handleLoaderValue(lookupKey: string): {
value: string | undefined;
path: string;
};
readonly loaderType = "env";
defaultOptions: IConfigLoaderProps;
protected handleLoaderValue(lookupKey: string): {
value: string | undefined;
path: string;
};
}
//#endregion
//#region src/loaders/ReactEnvConfigLoader.d.ts
/**

@@ -685,10 +681,11 @@ * React env loader class is used to load env variables from process.env.REACT_APP_*

declare class ReactEnvConfigLoader<OverrideMap extends OverrideKeyMap = OverrideKeyMap> extends ConfigLoader<IConfigLoaderProps, OverrideMap> {
readonly loaderType = "react-env";
defaultOptions: IConfigLoaderProps;
protected handleLoaderValue(lookupKey: string): {
value: string | undefined;
path: string;
};
readonly loaderType = "react-env";
defaultOptions: IConfigLoaderProps;
protected handleLoaderValue(lookupKey: string): {
value: string | undefined;
path: string;
};
}
//#endregion
//#region src/loaders/MapConfigLoader.d.ts
/**

@@ -702,23 +699,24 @@ * MapConfigLoader is a class that extends ConfigLoader and adds the ability to reload the data.

declare abstract class MapConfigLoader<Props extends IConfigLoaderProps, OverrideMap extends OverrideKeyMap = OverrideKeyMap> extends ConfigLoader<Props, OverrideMap> {
protected abstract defaultOptions: Props;
protected _isLoaded: boolean;
protected data: Map<string, string>;
/**
* clear maps and reloads the data
*/
reload(): Promise<void>;
/**
* is the data loaded
* @returns {boolean} Whether the data is loaded
*/
isLoaded(): boolean;
/**
* load data from the loader. If the data is loaded successfully
* an "updated" event will be emitted.
* @returns {Promise<void>}
*/
protected loadData(): Promise<void>;
protected abstract handleLoadData(): Promise<boolean>;
protected abstract defaultOptions: Props;
protected _isLoaded: boolean;
protected data: Map<string, string>;
/**
* clear maps and reloads the data
*/
reload(): Promise<void>;
/**
* is the data loaded
* @returns {boolean} Whether the data is loaded
*/
isLoaded(): boolean;
/**
* load data from the loader. If the data is loaded successfully
* an "updated" event will be emitted.
* @returns {Promise<void>}
*/
protected loadData(): Promise<void>;
protected abstract handleLoadData(): Promise<boolean>;
}
//#endregion
//#region src/loaders/FetchConfigLoader.d.ts
/**

@@ -729,11 +727,11 @@ * Options for the FetchConfigLoader

interface FetchConfigLoaderOptions extends IConfigLoaderProps {
fetchClient: typeof fetch;
/** this prevents Error to be thrown if have http error */
isSilent: boolean;
payload: 'json';
validate: ValidateCallback<Record<string, string | undefined>, Record<string, string | undefined>> | undefined;
logger: ILoggerLike | undefined;
cache: IRequestCache | undefined;
/** if we get a cache hit code (defaults 304), we use the cached response instead */
cacheHitHttpCode: number;
fetchClient: typeof fetch;
/** this prevents Error to be thrown if have http error */
isSilent: boolean;
payload: 'json';
validate: ValidateCallback<Record<string, string | undefined>, Record<string, string | undefined>> | undefined;
logger: ILoggerLike | undefined;
cache: IRequestCache | undefined;
/** if we get a cache hit code (defaults 304), we use the cached response instead */
cacheHitHttpCode: number;
}

@@ -748,45 +746,46 @@ type ConfigRequest = Request | RequestNotReady;

declare class FetchConfigLoader<OverrideMap extends OverrideKeyMap = OverrideKeyMap> extends MapConfigLoader<FetchConfigLoaderOptions, OverrideMap> {
readonly loaderType: Lowercase<string>;
private request;
private path;
protected defaultOptions: FetchConfigLoaderOptions;
/**
* Constructor for FetchConfigLoader
* @param {Loadable<ConfigRequest>} request - callback that returns a fetch request or a message object that the request is not ready
* @param {Loadable<Partial<FetchConfigLoaderOptions>>} options - optional options for FetchConfigLoader
* @param {Partial<OverrideMap>} overrideKeys - optional override keys for FetchConfigLoader
* @param {Lowercase<string>} type - optional name type for FetchConfigLoader (default: 'fetch')
*/
constructor(request: Loadable<ConfigRequest>, options?: Loadable<Partial<FetchConfigLoaderOptions>>, overrideKeys?: Partial<OverrideMap>, type?: Lowercase<string>);
protected handleLoaderValue(lookupKey: string): Promise<{
value: string | undefined;
path: string;
}>;
protected handleLoadData(): Promise<boolean>;
/**
* if client is offline, we will try return the cached response else add cache validation (ETag) and try get the response from the fetch request
* @param {Request} req - request to fetch
* @returns {Promise<Response | undefined>} - response or undefined
*/
private fetchRequestOrCacheResponse;
/**
* on error, check if we have a valid cached response
* @param {Request} req - request to fetch
* @param {Response} res - response from fetch
* @returns {Promise<Response>} - response
*/
private checkIfValidCacheResponse;
/**
* if we get a 304, get the cached response
* @param {Request} req - request to fetch
* @param {Response} res - response from fetch
* @returns {Promise<Response>} - response
*/
private handleNotModifiedCache;
private handleJson;
private getRequest;
readonly loaderType: Lowercase<string>;
private request;
private path;
protected defaultOptions: FetchConfigLoaderOptions;
/**
* Constructor for FetchConfigLoader
* @param {Loadable<ConfigRequest>} request - callback that returns a fetch request or a message object that the request is not ready
* @param {Loadable<Partial<FetchConfigLoaderOptions>>} options - optional options for FetchConfigLoader
* @param {Partial<OverrideMap>} overrideKeys - optional override keys for FetchConfigLoader
* @param {Lowercase<string>} type - optional name type for FetchConfigLoader (default: 'fetch')
*/
constructor(request: Loadable<ConfigRequest>, options?: Loadable<Partial<FetchConfigLoaderOptions>>, overrideKeys?: Partial<OverrideMap>, type?: Lowercase<string>);
protected handleLoaderValue(lookupKey: string): Promise<{
value: string | undefined;
path: string;
}>;
protected handleLoadData(): Promise<boolean>;
/**
* if client is offline, we will try return the cached response else add cache validation (ETag) and try get the response from the fetch request
* @param {Request} req - request to fetch
* @returns {Promise<Response | undefined>} - response or undefined
*/
private fetchRequestOrCacheResponse;
/**
* on error, check if we have a valid cached response
* @param {Request} req - request to fetch
* @param {Response} res - response from fetch
* @returns {Promise<Response>} - response
*/
private checkIfValidCacheResponse;
/**
* if we get a 304, get the cached response
* @param {Request} req - request to fetch
* @param {Response} res - response from fetch
* @returns {Promise<Response>} - response
*/
private handleNotModifiedCache;
private handleJson;
private getRequest;
}
//#endregion
//#region src/loaders/MemoryConfigLoader.d.ts
interface IMemoryConfigLoaderProps extends IConfigLoaderProps {
logger: ILoggerLike | undefined;
logger: ILoggerLike | undefined;
}

@@ -802,16 +801,17 @@ /**

declare class MemoryConfigLoader<MemoryMap extends Record<string, string | undefined>, OverrideMap extends OverrideKeyMap = OverrideKeyMap> extends ConfigLoader<IMemoryConfigLoaderProps, OverrideMap> {
readonly loaderType: Lowercase<string>;
private data;
protected defaultOptions: IMemoryConfigLoaderProps;
constructor(initialData: MemoryMap, options?: Loadable<Partial<IMemoryConfigLoaderProps>>, overrideKeys?: Partial<OverrideMap>, type?: Lowercase<string>);
set(key: keyof MemoryMap, value: string | undefined): Promise<void>;
get(key: keyof MemoryMap): Promise<string | undefined>;
protected handleLoaderValue(lookupKey: string): {
value: string | undefined;
path: string;
};
readonly loaderType: Lowercase<string>;
private data;
protected defaultOptions: IMemoryConfigLoaderProps;
constructor(initialData: MemoryMap, options?: Loadable<Partial<IMemoryConfigLoaderProps>>, overrideKeys?: Partial<OverrideMap>, type?: Lowercase<string>);
set(key: keyof MemoryMap, value: string | undefined): Promise<void>;
get(key: keyof MemoryMap): Promise<string | undefined>;
protected handleLoaderValue(lookupKey: string): {
value: string | undefined;
path: string;
};
}
//#endregion
//#region src/loaders/SwitchLoader.d.ts
interface ISwitchLoaderProps extends IConfigLoaderProps {
logger?: ILoggerLike;
logger?: ILoggerLike;
}

@@ -847,20 +847,19 @@ /**

declare class SwitchLoader<Config extends Record<string, unknown>, Key extends string> extends ConfigLoader<ISwitchLoaderProps> {
readonly loaderType: Lowercase<string>;
private readonly config;
private readonly keys;
protected defaultOptions: ISwitchLoaderProps;
constructor(configs: SwitchConfigMap<Config, Key>, props?: Loadable<Partial<ISwitchLoaderProps>>, type?: Lowercase<string>);
activateSwitch(key: Key): Promise<void>;
deactivateSwitch(key: Key): Promise<void>;
getCurrentKeys(): Readonly<Set<Key>>;
protected handleLoaderValue(lookupKey: string): LoaderValue | undefined;
protected getConfigKeys(key: Key): string[];
readonly loaderType: Lowercase<string>;
private readonly config;
private readonly keys;
protected defaultOptions: ISwitchLoaderProps;
constructor(configs: SwitchConfigMap<Config, Key>, props?: Loadable<Partial<ISwitchLoaderProps>>, type?: Lowercase<string>);
activateSwitch(key: Key): Promise<void>;
deactivateSwitch(key: Key): Promise<void>;
getCurrentKeys(): Readonly<Set<Key>>;
protected handleLoaderValue(lookupKey: string): LoaderValue | undefined;
protected getConfigKeys(key: Key): string[];
}
//#endregion
//#region src/parsers/JsonConfigParser.d.ts
type JsonConfigParserOptions<Out extends Record<string, unknown>> = {
validate?: ValidateCallback<object, Out>;
/** keys to hide or partially hide */
protectedKeys?: Iterable<keyof Out>;
/** if and how to show protected keys */
showProtectedKeys?: ShowValueType;
validate?: ValidateCallback<object, Out>; /** keys to hide or partially hide */
protectedKeys?: Iterable<keyof Out>; /** if and how to show protected keys */
showProtectedKeys?: ShowValueType;
};

@@ -887,19 +886,28 @@ /**

declare class JsonConfigParser<Out extends Record<string, unknown>> implements IConfigParser<object, Out> {
name: string;
private validate;
private showProtectedKeys?;
private protectedKeys;
constructor({ protectedKeys, validate, showProtectedKeys }?: JsonConfigParserOptions<Out>);
parse({ value }: ParserProps): object;
postValidate({ value }: PostValidateProps<Record<string, unknown>>): Promise<Out | undefined>;
toString(config: Out): string;
toLogString(config: Out): string;
/**
* Build a string record from the given data
* @param {unknown} data - to be validated as object
* @returns {object} A record with string values
*/
private buildBaseRecord;
name: string;
private validate;
private showProtectedKeys?;
private protectedKeys;
constructor({
protectedKeys,
validate,
showProtectedKeys
}?: JsonConfigParserOptions<Out>);
parse({
value
}: ParserProps): object;
postValidate({
value
}: PostValidateProps<Record<string, unknown>>): Promise<Out | undefined>;
toString(config: Out): string;
toLogString(config: Out): string;
/**
* Build a string record from the given data
* @param {unknown} data - to be validated as object
* @returns {object} A record with string values
*/
private buildBaseRecord;
}
//#endregion
//#region src/parsers/UrlParser.d.ts
/**

@@ -910,3 +918,3 @@ * Properties for the UrlParser

interface UrlParserProps {
urlSanitize?: boolean;
urlSanitize?: boolean;
}

@@ -921,23 +929,28 @@ /**

declare class UrlParser implements IConfigParser<URL, URL> {
name: string;
/**
* Should the username and password be sanitized
*/
private urlSanitize;
/**
* Create a new UrlParser
* @param {UrlParserProps} properties - Properties for the UrlParser
*/
constructor({ urlSanitize }?: UrlParserProps);
parse({ value }: ParserProps): URL;
toString(value: URL): string;
toLogString(value: URL): string;
/**
* Build a URL object from a string and sanitize the username and password
* @param {string} value string to parse
* @returns {URL} URL object with sanitized username and password
*/
private handleUrlSanitize;
name: string;
/**
* Should the username and password be sanitized
*/
private urlSanitize;
/**
* Create a new UrlParser
* @param {UrlParserProps} properties - Properties for the UrlParser
*/
constructor({
urlSanitize
}?: UrlParserProps);
parse({
value
}: ParserProps): URL;
toString(value: URL): string;
toLogString(value: URL): string;
/**
* Build a URL object from a string and sanitize the username and password
* @param {string} value string to parse
* @returns {URL} URL object with sanitized username and password
*/
private handleUrlSanitize;
}
//#endregion
//#region src/parsers/stringParser.d.ts
/**

@@ -952,3 +965,4 @@ * Build parser and have optional post validation (as example for literal values)

declare function stringParser<Output extends string = string>(validate?: TypeGuardValidate<Output>): IConfigParser<string, Output>;
//#endregion
//#region src/lib/semicolonUtils.d.ts
/**

@@ -989,3 +1003,4 @@ * Parse a semicolon separated string into a config object

declare function logStringifySemicolonConfig<Out extends Record<string, unknown>>(config: Out, showProtectedKeys: ShowValueType | undefined, protectedKeys: Set<keyof Out>): string;
//#endregion
//#region src/lib/objectUtils.d.ts
/**

@@ -1020,3 +1035,4 @@ * validate if a value is a valid object

declare function applyStringMap(obj: Record<string, string | undefined>, target: Map<string, string>): Map<string, string>;
//#endregion
//#region src/lib/seenUtils.d.ts
/**

@@ -1031,11 +1047,12 @@ * Function to check if we have seen a value before and update the seenMap

declare function handleSeen(seenMap: Map<string, string>, key: string, value: string | undefined): boolean;
//#endregion
//#region src/parsers/SemicolonConfigParser.d.ts
interface SemicolonConfigParserOptions<OutType extends Record<string, unknown> = Record<string, unknown>> {
validate?: ValidateCallback<Record<string, string>, OutType>;
/** keys to hide or partially hide */
protectedKeys?: Iterable<keyof OutType>;
/** if and how to show protected keys */
showProtectedKeys?: ShowValueType;
/** keep case of keys, if set as false then will convert keys first letters to lower case (Js Style) */
keepCase?: boolean;
validate?: ValidateCallback<Record<string, string>, OutType>;
/** keys to hide or partially hide */
protectedKeys?: Iterable<keyof OutType>;
/** if and how to show protected keys */
showProtectedKeys?: ShowValueType;
/** keep case of keys, if set as false then will convert keys first letters to lower case (Js Style) */
keepCase?: boolean;
}

@@ -1062,14 +1079,24 @@ /**

declare class SemicolonConfigParser<Out extends Record<string, unknown> = Record<string, unknown>> implements IConfigParser<Record<string, string>, Out> {
name: string;
private validate;
private keepCase;
private showProtectedKeys;
private protectedKeys;
constructor({ keepCase, protectedKeys, validate, showProtectedKeys }?: SemicolonConfigParserOptions<Out>);
parse({ value }: ParserProps): Promise<Record<string, string>>;
postValidate({ value }: PostValidateProps<Record<string, string>>): Promise<Out | undefined>;
toString(config: Out, options?: EncodeOptions): string;
toLogString(config: Out): string;
name: string;
private validate;
private keepCase;
private showProtectedKeys;
private protectedKeys;
constructor({
keepCase,
protectedKeys,
validate,
showProtectedKeys
}?: SemicolonConfigParserOptions<Out>);
parse({
value
}: ParserProps): Promise<Record<string, string>>;
postValidate({
value
}: PostValidateProps<Record<string, string>>): Promise<Out | undefined>;
toString(config: Out, options?: EncodeOptions): string;
toLogString(config: Out): string;
}
//#endregion
//#region src/parsers/booleanParser.d.ts
/**

@@ -1092,3 +1119,4 @@ * Build parser and have optional post validation (as example for literal values)

declare function booleanParser<Output extends boolean = boolean>(validate?: TypeGuardValidate<Output>): IConfigParser<boolean, Output>;
//#endregion
//#region src/parsers/floatParser.d.ts
/**

@@ -1103,3 +1131,4 @@ * Build parser and have optional post validation (as example for literal values)

declare function floatParser<Output extends number = number>(validate?: TypeGuardValidate<Output>): IConfigParser<number, Output>;
//#endregion
//#region src/parsers/integerParser.d.ts
/**

@@ -1114,3 +1143,4 @@ * Build parser and have optional post validation (as example for literal values)

declare function integerParser<Output extends number = number>(validate?: TypeGuardValidate<Output>): IConfigParser<number, Output>;
//#endregion
//#region src/parsers/arrayParser.d.ts
/**

@@ -1128,3 +1158,4 @@ * Build parser for array of values

declare function arrayParser<Input, Output>(parse: IConfigParser<Input, Output>, separator?: string, validate?: TypeGuardValidate<Output>): IConfigParser<Input[], Output[]>;
//#endregion
//#region src/parsers/bigIntParser.d.ts
/**

@@ -1139,3 +1170,4 @@ * Build parser and have optional post validation (as example for literal values)

declare function bigIntParser<Output extends bigint = bigint>(validate?: TypeGuardValidate<Output>): IConfigParser<bigint, Output>;
//#endregion
//#region src/VariableError.d.ts
/**

@@ -1152,10 +1184,11 @@ * Custom error class for variable errors.

declare class VariableError extends Error {
/**
* Create a new VariableError
* @param {string} message - The error message
* @param {ErrorOptions} [options] - The error options
*/
constructor(message: string, options?: ErrorOptions);
/**
* Create a new VariableError
* @param {string} message - The error message
* @param {ErrorOptions} [options] - The error options
*/
constructor(message: string, options?: ErrorOptions);
}
//#endregion
//#region src/VariableLookupError.d.ts
/**

@@ -1169,12 +1202,13 @@ * Custom error class for variable lookup errors.

declare class VariableLookupError extends VariableError {
readonly variableKey: string;
/**
* Create a new VariableLookupError
* @param {string} variableKey - The variable key.
* @param {string} message - The error message.
* @param {ErrorOptions} [options] - The error options
*/
constructor(variableKey: string, message: string, options?: ErrorOptions);
readonly variableKey: string;
/**
* Create a new VariableLookupError
* @param {string} variableKey - The variable key.
* @param {string} message - The error message.
* @param {ErrorOptions} [options] - The error options
*/
constructor(variableKey: string, message: string, options?: ErrorOptions);
}
//#endregion
//#region src/ConfigMap.d.ts
/**

@@ -1206,120 +1240,121 @@ * TypeValueRecords

declare class ConfigMap<Data extends Record<string, unknown>> implements ISetOptionalLogger {
private schema;
private options;
private loaders;
/**
* ConfigMap constructor
* @param {EnvMapSchema<Data>} schema - schema of config map
* @param {Iterable<IConfigLoader>} loaders - iterable of config loaders
* @param {ConfigOptions} options - optional config options (logger, namespace)
*/
constructor(schema: EnvMapSchema<Data>, loaders: Loadable<Iterable<IConfigLoader>>, options?: ConfigOptions);
/**
* Set logger value
* @param {ILoggerLike | undefined} logger - logger like object
*/
setLogger(logger: ILoggerLike | undefined): void;
/**
* get env object from config map
* @returns {Promise<TypeValueRecords<Data>>} Promise of env object or undefined
* @example
* const valueObject: LoaderTypeValue<number> = await config.getObject('PORT');
* console.log(valueObject.type, valueObject.value); // 'env', 3000
* @param {string} key - key of config map
* @param {EncodeOptions} [encodeOptions] - optional encode options
*/
getObject<Key extends keyof Data = keyof Data>(key: Key, encodeOptions?: EncodeOptions): Promise<LoaderTypeValueStrict<Data[Key]>>;
/**
* get env object from config map as Result
* @returns {Promise<Result<TypeValueRecords<Data>>>} Result Promise of env object or undefined
* @example
* const valueObject: Result<LoaderTypeValue<number>> = await config.getObjectResult('PORT');
* if (valueObject.isOk()) {
* const {type, value} = valueObject.ok();
* console.log(type, value); // 'env', 3000
* }
* @param {string} key - key of config map
* @param {EncodeOptions} [encodeOptions] - optional encode options
*/
getObjectResult<Key extends keyof Data = keyof Data>(key: Key, encodeOptions?: EncodeOptions): Promise<IResult<LoaderTypeValueStrict<Data[Key]>>>;
/**
* get env value from config map
* @returns {Promise<Data[Key]>} Promise of value or undefined
* @example
* const port: number = await config.get('PORT');
* @param {string} key - key of config map
* @param {EncodeOptions} [encodeOptions] - optional encode options
*/
get<Key extends keyof Data = keyof Data>(key: Key, encodeOptions?: EncodeOptions): Promise<Data[Key]>;
/**
* get env value as string from config map
* @returns {Promise<string | undefined>} Promise of string value or undefined
* @example
* const port: string = await config.getString('PORT');
* @param {string} key - key of config map
* @param {EncodeOptions} [encodeOptions] - optional encode options
*/
getString<Key extends keyof Data = keyof Data>(key: Key, encodeOptions?: EncodeOptions): Promise<undefined extends Data[Key] ? string | undefined : string>;
/**
* get env value from config map as Result
* @returns {Promise<Result<Data[Key]>>} Result Promise of value or undefined
* @example
* const port: Result<number> = await config.getResult('PORT');
* if (port.isOk()) {
* console.log('port', port.ok());
* }
* @param {string} key - key of config map
* @param {EncodeOptions} [encodeOptions] - optional encode options
*/
getResult<Key extends keyof Data = keyof Data>(key: Key, encodeOptions?: EncodeOptions): Promise<IResult<Data[Key]>>;
/**
* get env value as string from config map as Result
* @returns {Promise<Result<string | undefined>>} Result Promise of string value or undefined
* @example
* const port: Result<string> = await config.getStringResult('PORT');
* if (port.isOk()) {
* console.log('port', port.ok());
* }
* @param {string} key - key of config map
* @param {EncodeOptions} [encodeOptions] - optional encode options
*/
getStringResult<Key extends keyof Data = keyof Data>(key: Key, encodeOptions?: EncodeOptions): Promise<IResult<undefined extends Data[Key] ? string | undefined : string>>;
/**
* get all env value objects from config map
* @returns {Promise<TypeValueRecords<Data>>} Promise of all values
* @example
* const values: TypeValueRecords<Data> = await config.getAll();
* console.log(values.PORT.type, values.PORT.value); // 'env', 3000
*/
getAllObjects(): Promise<TypeValueRecords<Data>>;
/**
* get all env values from config map
* @returns {Promise<Data>} Promise of all values
* @example
* const values: Data = await config.getAllValues();
* console.log('PORT', values.PORT); // 3000 (number)
*/
getAllValues(): Promise<Data>;
/**
* get all env values from config map as string
* @returns {Promise<Record<keyof Data, string>>} Promise of all values as string
* @example
* const values: Record<keyof Data, string> = await config.getAllStringValues();
* console.log('PORT', values.PORT); // '3000' (string)
* @param {EncodeOptions} [encodeOptions] - optional encode options
*/
getAllStringValues(encodeOptions?: EncodeOptions): Promise<Record<keyof Data, string>>;
/**
* Validate all env values from config map, expect to throw error if error exists
* @param {(data: Data) => void} callback callback function
*/
validateAll(callback: (data: Data) => void): Promise<void>;
/**
* run lookup to all keys and return all promises
* @param {EncodeOptions} [encodeOptions] - optional encode options
* @returns {Promise<[keyof Data, LoaderTypeValueStrict<Data[keyof Data]>][]>} Promise of all values
*/
private getAllPromises;
private schema;
private options;
private loaders;
/**
* ConfigMap constructor
* @param {EnvMapSchema<Data>} schema - schema of config map
* @param {Iterable<IConfigLoader>} loaders - iterable of config loaders
* @param {ConfigOptions} options - optional config options (logger, namespace)
*/
constructor(schema: EnvMapSchema<Data>, loaders: Loadable<Iterable<IConfigLoader>>, options?: ConfigOptions);
/**
* Set logger value
* @param {ILoggerLike | undefined} logger - logger like object
*/
setLogger(logger: ILoggerLike | undefined): void;
/**
* get env object from config map
* @returns {Promise<TypeValueRecords<Data>>} Promise of env object or undefined
* @example
* const valueObject: LoaderTypeValue<number> = await config.getObject('PORT');
* console.log(valueObject.type, valueObject.value); // 'env', 3000
* @param {string} key - key of config map
* @param {EncodeOptions} [encodeOptions] - optional encode options
*/
getObject<Key extends keyof Data = keyof Data>(key: Key, encodeOptions?: EncodeOptions): Promise<LoaderTypeValueStrict<Data[Key]>>;
/**
* get env object from config map as Result
* @returns {Promise<Result<TypeValueRecords<Data>>>} Result Promise of env object or undefined
* @example
* const valueObject: Result<LoaderTypeValue<number>> = await config.getObjectResult('PORT');
* if (valueObject.isOk()) {
* const {type, value} = valueObject.ok();
* console.log(type, value); // 'env', 3000
* }
* @param {string} key - key of config map
* @param {EncodeOptions} [encodeOptions] - optional encode options
*/
getObjectResult<Key extends keyof Data = keyof Data>(key: Key, encodeOptions?: EncodeOptions): Promise<IResult<LoaderTypeValueStrict<Data[Key]>>>;
/**
* get env value from config map
* @returns {Promise<Data[Key]>} Promise of value or undefined
* @example
* const port: number = await config.get('PORT');
* @param {string} key - key of config map
* @param {EncodeOptions} [encodeOptions] - optional encode options
*/
get<Key extends keyof Data = keyof Data>(key: Key, encodeOptions?: EncodeOptions): Promise<Data[Key]>;
/**
* get env value as string from config map
* @returns {Promise<string | undefined>} Promise of string value or undefined
* @example
* const port: string = await config.getString('PORT');
* @param {string} key - key of config map
* @param {EncodeOptions} [encodeOptions] - optional encode options
*/
getString<Key extends keyof Data = keyof Data>(key: Key, encodeOptions?: EncodeOptions): Promise<undefined extends Data[Key] ? string | undefined : string>;
/**
* get env value from config map as Result
* @returns {Promise<Result<Data[Key]>>} Result Promise of value or undefined
* @example
* const port: Result<number> = await config.getResult('PORT');
* if (port.isOk()) {
* console.log('port', port.ok());
* }
* @param {string} key - key of config map
* @param {EncodeOptions} [encodeOptions] - optional encode options
*/
getResult<Key extends keyof Data = keyof Data>(key: Key, encodeOptions?: EncodeOptions): Promise<IResult<Data[Key]>>;
/**
* get env value as string from config map as Result
* @returns {Promise<Result<string | undefined>>} Result Promise of string value or undefined
* @example
* const port: Result<string> = await config.getStringResult('PORT');
* if (port.isOk()) {
* console.log('port', port.ok());
* }
* @param {string} key - key of config map
* @param {EncodeOptions} [encodeOptions] - optional encode options
*/
getStringResult<Key extends keyof Data = keyof Data>(key: Key, encodeOptions?: EncodeOptions): Promise<IResult<undefined extends Data[Key] ? string | undefined : string>>;
/**
* get all env value objects from config map
* @returns {Promise<TypeValueRecords<Data>>} Promise of all values
* @example
* const values: TypeValueRecords<Data> = await config.getAll();
* console.log(values.PORT.type, values.PORT.value); // 'env', 3000
*/
getAllObjects(): Promise<TypeValueRecords<Data>>;
/**
* get all env values from config map
* @returns {Promise<Data>} Promise of all values
* @example
* const values: Data = await config.getAllValues();
* console.log('PORT', values.PORT); // 3000 (number)
*/
getAllValues(): Promise<Data>;
/**
* get all env values from config map as string
* @returns {Promise<Record<keyof Data, string>>} Promise of all values as string
* @example
* const values: Record<keyof Data, string> = await config.getAllStringValues();
* console.log('PORT', values.PORT); // '3000' (string)
* @param {EncodeOptions} [encodeOptions] - optional encode options
*/
getAllStringValues(encodeOptions?: EncodeOptions): Promise<Record<keyof Data, string>>;
/**
* Validate all env values from config map, expect to throw error if error exists
* @param {(data: Data) => void} callback callback function
*/
validateAll(callback: (data: Data) => void): Promise<void>;
/**
* run lookup to all keys and return all promises
* @param {EncodeOptions} [encodeOptions] - optional encode options
* @returns {Promise<[keyof Data, LoaderTypeValueStrict<Data[keyof Data]>][]>} Promise of all values
*/
private getAllPromises;
}
//#endregion
//#region src/logger.d.ts
/**

@@ -1347,3 +1382,4 @@ * Set the logger to be used by the module

declare function resolveLogger(logger: undefined | null | ILoggerLike): ILoggerLike | undefined;
export { ConfigLoader, type ConfigLoaderEventMap, ConfigMap, type ConfigOptions, type ConfigRequest, type EncodeOptions, EnvConfigLoader, type EnvMapSchema, FetchConfigLoader, type FetchConfigLoaderOptions, type FormatParameters, type IConfigLoader, type IConfigLoaderProps, type IConfigParser, type IMemoryConfigLoaderProps, type IRequestCache, type ISwitchLoaderProps, type InferOverrideKeyMap, JsonConfigParser, type JsonConfigParserOptions, type LoaderTypeValue, type LoaderTypeValueStrict, type LoaderValue, type LoaderValueResult, MapConfigLoader, MemoryConfigLoader, type OptionalEnvEntry, type OverrideKeyMap, type ParserProps, type PartialHiddenValueStringType, type PostValidateProps, type PreValidateProps, ReactEnvConfigLoader, RecordConfigLoader, type RequestNotReady, type RequiredEnvEntry, type RequiredUndefinedThrowEntry, SemicolonConfigParser, type SemicolonConfigParserOptions, type ShowValueType, type SolvedConfigOptions, type SwitchConfigMap, SwitchLoader, type TypeGuardValidate, type TypeValueRecords, UrlParser, type UrlParserProps, type ValidateCallback, VariableError, VariableLookupError, applyStringMap, arrayParser, bigIntParser, booleanParser, buildHiddenAsterisksValueString, buildHiddenValue, buildOptions, buildPartialHiddenValueString, buildStringMap, buildStringObject, clearDefaultValueSeenMap, createRequestNotReady, floatParser, getConfigObject, getConfigObjectResult, getConfigVariable, getConfigVariableResult, getLogger, handleSeen, integerParser, isRequestNotReadMessage, isValidObject, logStringifySemicolonConfig, parseSemicolonConfig, printValue, resolveLogger, setLogger, stringParser, stringifySemicolonConfig, urlSanitize };
//#endregion
export { ConfigLoader, ConfigLoaderEventMap, ConfigMap, ConfigOptions, ConfigRequest, EncodeOptions, EnvConfigLoader, EnvMapSchema, FetchConfigLoader, FetchConfigLoaderOptions, FormatParameters, IConfigLoader, IConfigLoaderProps, IConfigParser, IMemoryConfigLoaderProps, IRequestCache, ISwitchLoaderProps, InferOverrideKeyMap, JsonConfigParser, JsonConfigParserOptions, LoaderTypeValue, LoaderTypeValueStrict, LoaderValue, LoaderValueResult, MapConfigLoader, MemoryConfigLoader, OptionalEnvEntry, OverrideKeyMap, ParserProps, PartialHiddenValueStringType, PostValidateProps, PreValidateProps, ReactEnvConfigLoader, RecordConfigLoader, RequestNotReady, RequiredEnvEntry, RequiredUndefinedThrowEntry, SemicolonConfigParser, SemicolonConfigParserOptions, ShowValueType, SolvedConfigOptions, SwitchConfigMap, SwitchLoader, TypeGuardValidate, TypeValueRecords, UrlParser, UrlParserProps, ValidateCallback, VariableError, VariableLookupError, applyStringMap, arrayParser, bigIntParser, booleanParser, buildHiddenAsterisksValueString, buildHiddenValue, buildOptions, buildPartialHiddenValueString, buildStringMap, buildStringObject, clearDefaultValueSeenMap, createRequestNotReady, floatParser, getConfigObject, getConfigObjectResult, getConfigVariable, getConfigVariableResult, getLogger, handleSeen, integerParser, isRequestNotReadMessage, isValidObject, logStringifySemicolonConfig, parseSemicolonConfig, printValue, resolveLogger, setLogger, stringParser, stringifySemicolonConfig, urlSanitize$1 as urlSanitize };
//# sourceMappingURL=index.d.mts.map
+1517
-1253

@@ -1,1396 +0,1660 @@

var __defProp = Object.defineProperty;
var __defProps = Object.defineProperties;
var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __propIsEnum = Object.prototype.propertyIsEnumerable;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __spreadValues = (a, b) => {
for (var prop in b || (b = {}))
if (__hasOwnProp.call(b, prop))
__defNormalProp(a, prop, b[prop]);
if (__getOwnPropSymbols)
for (var prop of __getOwnPropSymbols(b)) {
if (__propIsEnum.call(b, prop))
__defNormalProp(a, prop, b[prop]);
}
return a;
};
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
var __async = (__this, __arguments, generator) => {
return new Promise((resolve, reject) => {
var fulfilled = (value) => {
try {
step(generator.next(value));
} catch (e) {
reject(e);
}
};
var rejected = (value) => {
try {
step(generator.throw(value));
} catch (e) {
reject(e);
}
};
var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
step((generator = generator.apply(__this, __arguments)).next());
});
};
import { ErrorCore, LoadableCore } from "@luolapeikko/ts-common";
import { Err, Ok } from "@luolapeikko/result-option";
import { EventEmitter } from "events";
// src/types/RequestNotReady.ts
//#region src/types/RequestNotReady.ts
/**
* Type guard for RequestNotReady
* @param {unknown} obj - object to check if it is a RequestNotReady payload
* @returns {boolean} boolean indicating if the object is a RequestNotReady payload
* @category Utils
* @since v0.2.8
*/
function isRequestNotReadMessage(obj) {
return typeof obj === "object" && obj !== null && "_type" in obj && obj._type === "RequestNotReady" && "message" in obj && typeof obj.message === "string";
return typeof obj === "object" && obj !== null && "_type" in obj && obj._type === "RequestNotReady" && "message" in obj && typeof obj.message === "string";
}
/**
* function to create a RequestNotReady payload to indicate that the request is not ready to be loaded yet. (e.g. the user is not logged in)
* @param {string} message - reason why the request is not ready yet
* @returns {RequestNotReady} RequestNotReady payload
* @category Utils
* @since v0.2.8
*/
function createRequestNotReady(message) {
return { message, _type: "RequestNotReady" };
return {
message,
_type: "RequestNotReady"
};
}
// src/getConfigObject.ts
import { LoadableCore } from "@luolapeikko/ts-common";
// src/logger.ts
var logger;
//#endregion
//#region src/logger.ts
let logger;
/**
* Set the logger to be used by the module
* @category Utils
* @param {ILoggerLike} newLogger - logger to be used
* @since v0.2.18
*/
function setLogger(newLogger) {
logger = newLogger;
logger = newLogger;
}
/**
* Get current logger instance
* @category Utils
* @returns {ILoggerLike | undefined} - current logger
* @since v0.5.9
*/
function getLogger() {
return logger;
return logger;
}
function resolveLogger(logger2) {
if (logger2 === void 0) {
return getLogger();
}
return logger2 != null ? logger2 : void 0;
/**
* resolve logger: undefined = global logger, null = no logger else it's ILoggerLike
* @param {ILoggerLike | undefined | null} logger - logger to resolve
* @category Utils
* @returns {ILoggerLike | undefined} - resolved logger
* @since v0.5.9
*/
function resolveLogger(logger) {
if (logger === void 0) return getLogger();
return logger ?? void 0;
}
// src/ConfigOptions.ts
function buildOptions({ logger: logger2, namespace } = {}) {
return {
logger: resolveLogger(logger2),
namespace
};
//#endregion
//#region src/ConfigOptions.ts
/**
* Build options from partial options
* @param {Partial<ConfigOptions>} options - partial config options
* @returns {SolvedConfigOptions} - solved config options
* @category Config
* @since v0.6.0
*/
function buildOptions({ logger, namespace } = {}) {
return {
logger: resolveLogger(logger),
namespace
};
}
// src/lib/seenUtils.ts
//#endregion
//#region src/lib/seenUtils.ts
/**
* Function to check if we have seen a value before and update the seenMap
* @param {Map<string, string>} seenMap - Map of seen values
* @param {string} key - key to check
* @param {string | undefined} value - value to check
* @returns {boolean} - true if already seen value, false if not
* @since v1.0.0
*/
function handleSeen(seenMap, key, value) {
if (value === void 0) {
return false;
}
const lastValue = seenMap.get(key);
const seen = value === lastValue;
if (!seen) {
seenMap.set(key, value);
}
return seen;
if (value === void 0) return false;
const seen = value === seenMap.get(key);
if (!seen) seenMap.set(key, value);
return seen;
}
// src/lib/formatUtils.ts
function urlSanitize(value, logger2) {
try {
const url = new URL(value);
url.password = "*".repeat(url.password.length);
url.username = "*".repeat(url.username.length);
return url.href;
} catch (err) {
logger2 == null ? void 0 : logger2.warn("variables:", err);
return value;
}
//#endregion
//#region src/lib/formatUtils.ts
/**
* Sanitizes a URL by replacing the username and password with asterisks.
* @param {string} value - The URL to sanitize.
* @param {ILoggerLike} [logger] - An optional logger to use for logging warnings.
* @returns {string} The sanitized URL.
* @category Utils
* @since v0.2.5
*/
function urlSanitize(value, logger) {
try {
const url = new URL(value);
url.password = "*".repeat(url.password.length);
url.username = "*".repeat(url.username.length);
return url.href;
} catch (err) {
logger?.warn("variables:", err);
return value;
}
}
/**
* Returns a formatted string representation of a value, enclosed in square brackets.
* @param {string | undefined} value - The value to format.
* @param {FormatParameters | undefined} config - An optional configuration object.
* @returns {string} The formatted string representation of the value.
* @category Utils
* @since v0.2.5
*/
function printValue(value, config) {
if (!value || !config) {
return "";
}
return ` [${buildHiddenValue(value, config.showValue)}]`;
if (!value || !config) return "";
return ` [${buildHiddenValue(value, config.showValue)}]`;
}
/**
* Constructs a hidden value string based on the specified visibility option.
* @param {string} value - The original string value to be hidden.
* @param {ShowValueType | undefined} show - Determines how the value should be displayed:
* - `true`: shows the full value
* - `undefined` or `false`: hides the value completely with asterisks
* - `PartialHiddenValueStringType`: shows part of the value (prefix, suffix, or both)
* @returns {string} The resulting string with the specified visibility.
* @since v0.2.5
*/
function buildHiddenValue(value, show) {
if (show === true) {
return value;
}
if (!show) {
return buildHiddenAsterisksValueString(value);
}
return buildPartialHiddenValueString(value, show);
if (show === true) return value;
if (!show) return buildHiddenAsterisksValueString(value);
return buildPartialHiddenValueString(value, show);
}
/**
* Builds a hidden value string, replacing each character with an asterisk.
* @param {string} value - The value to be hidden.
* @returns {string} The hidden value string with asterisks.
* @since v0.2.5
*/
function buildHiddenAsterisksValueString(value) {
return "*".repeat(value.length);
return "*".repeat(value.length);
}
/**
* Show only 1-3 characters of the secret value based on length of the value.
* @param {string} value - The value to partially hide.
* @param {PartialHiddenValueStringType} type - The type of partial hiding to apply ('prefix', 'suffix', or 'prefix-suffix').
* @returns {string} The partially hidden value string.
* @since v0.2.5
*/
function buildPartialHiddenValueString(value, type) {
const visibleCharacters = Math.min(3, Math.max(1, Math.floor(value.length * 0.2)));
switch (type) {
case "prefix":
return `${value.slice(0, visibleCharacters)}${"*".repeat(value.length - visibleCharacters)}`;
case "suffix":
return `${"*".repeat(value.length - visibleCharacters)}${value.slice(-visibleCharacters)}`;
case "prefix-suffix": {
const halfOfVisibleCharacters = Math.max(1, Math.ceil(visibleCharacters / 2));
return `${value.slice(0, halfOfVisibleCharacters)}${"*".repeat(value.length - halfOfVisibleCharacters * 2)}${value.slice(-halfOfVisibleCharacters)}`;
}
}
const visibleCharacters = Math.min(3, Math.max(1, Math.floor(value.length * .2)));
switch (type) {
case "prefix": return `${value.slice(0, visibleCharacters)}${"*".repeat(value.length - visibleCharacters)}`;
case "suffix": return `${"*".repeat(value.length - visibleCharacters)}${value.slice(-visibleCharacters)}`;
case "prefix-suffix": {
const halfOfVisibleCharacters = Math.max(1, Math.ceil(visibleCharacters / 2));
return `${value.slice(0, halfOfVisibleCharacters)}${"*".repeat(value.length - halfOfVisibleCharacters * 2)}${value.slice(-halfOfVisibleCharacters)}`;
}
}
}
// src/VariableError.ts
//#endregion
//#region src/VariableError.ts
/**
* Custom error class for variable errors.
* @class VariableError
* @augments Error
* @param {string} message - The error message.
* @category Errors
* @since v0.2.2
* @example
* throw new VariableError('Variable not found');
*/
var VariableError = class extends Error {
/**
* Create a new VariableError
* @param {string} message - The error message
* @param {ErrorOptions} [options] - The error options
*/
constructor(message, options) {
super(message, options);
this.name = "VariableError";
}
/**
* Create a new VariableError
* @param {string} message - The error message
* @param {ErrorOptions} [options] - The error options
*/
constructor(message, options) {
super(message, options);
this.name = "VariableError";
}
};
// src/loaderUtils.ts
function printLog({ logger: logger2, namespace }, type, key, value, path, params) {
const namespaceString = namespace ? `:${namespace}` : "";
logger2 == null ? void 0 : logger2.info(`ConfigVariables${namespaceString}[${type}]: ${key}${printValue(value, params)} from ${path}`);
//#endregion
//#region src/loaderUtils.ts
/**
* function to log variable output
* @param {SolvedConfigOptions} options - options to use
* @param {string} type - type of variable
* @param {string} key - key of variable
* @param {string} value - value of variable
* @param {string} path - path of variable
* @param {FormatParameters} [params] - optional format parameters
* @returns {void}
* @category Utils
* @since v1.0.0
*/
function printLog({ logger, namespace }, type, key, value, path, params) {
const namespaceString = namespace ? `:${namespace}` : "";
logger?.info(`ConfigVariables${namespaceString}[${type}]: ${key}${printValue(value, params)} from ${path}`);
}
/**
* Rebuild Error as raw VariableError
* @param {unknown} err - error to rebuild
* @returns {VariableError} - rebuilt error
* @category Utils
* @since v0.12.0
*/
function handleAsVariableError(err) {
if (err instanceof VariableError) {
return err;
}
if (err instanceof Error) {
const varError = new VariableError(err.message);
varError.stack = err.stack;
return varError;
}
return new VariableError(`Unknown error ${JSON.stringify(err)}`);
if (err instanceof VariableError) return err;
if (err instanceof Error) {
const varError = new VariableError(err.message);
varError.stack = err.stack;
return varError;
}
return new VariableError(`Unknown error ${JSON.stringify(err)}`);
}
/**
* Rebuild Error as VariableError
* @param {string} value - value of variable
* @param {Error} error - error to rebuild
* @param {IConfigLoader} loader - loader to use
* @param {IConfigParser<unknown, unknown>} parser - parser used
* @param {FormatParameters} [params] - optional format parameters
* @returns {VariableError} - rebuilt error
* @category Errors
* @since v1.0.0
*/
function rebuildAsVariableError(value, error, loader, parser, params) {
if (error instanceof VariableError) {
return error;
} else {
const varError = new VariableError(`variables[${loader.loaderType}](${parser.name}):${printValue(value, params)} ${error.message}`);
varError.stack = error.stack;
return varError;
}
if (error instanceof VariableError) return error;
else {
const varError = new VariableError(`variables[${loader.loaderType}](${parser.name}):${printValue(value, params)} ${error.message}`);
varError.stack = error.stack;
return varError;
}
}
/**
* build error message for preValidate step
* @param {string} value - value of variable
* @param {unknown} err - error to rebuild
* @param {IConfigLoader} loader - loader to use
* @param {IConfigParser<unknown, unknown>} parser - parser used
* @param {FormatParameters} [params] - optional format parameters
* @returns {VariableError} - error message
* @category Errors
* @since v1.0.0
*/
function buildPreValidateErrorMessage(value, err, loader, parser, params) {
if (err instanceof Error) {
return rebuildAsVariableError(value, err, loader, parser, params);
} else {
return new VariableError(`variables[${loader.loaderType}](${parser.name}):${printValue(value, params)} unknown preValidate error`);
}
if (err instanceof Error) return rebuildAsVariableError(value, err, loader, parser, params);
else return new VariableError(`variables[${loader.loaderType}](${parser.name}):${printValue(value, params)} unknown preValidate error`);
}
/**
* build error message for parser step
* @param {string} value - value of variable
* @param {unknown} err - error to rebuild
* @param {IConfigLoader} loader - loader to use
* @param {IConfigParser<unknown, unknown>} parser - parser used
* @param {FormatParameters} [params] - optional format parameters
* @returns {VariableError} - error message
* @category Errors
* @since v1.0.0
*/
function buildParserErrorMessage(value, err, loader, parser, params) {
if (err instanceof Error) {
return rebuildAsVariableError(value, err, loader, parser, params);
} else {
return new VariableError(`variables[${loader.loaderType}](${parser.name}):${printValue(value, params)} unknown parse error`);
}
if (err instanceof Error) return rebuildAsVariableError(value, err, loader, parser, params);
else return new VariableError(`variables[${loader.loaderType}](${parser.name}):${printValue(value, params)} unknown parse error`);
}
/**
* build error message for postValidate step
* @param {string} value - value of variable
* @param {unknown} err - error to rebuild
* @param {IConfigLoader} loader - loader to use
* @param {IConfigParser<unknown, unknown>} parser - parser used
* @param {FormatParameters} [params] - optional format parameters
* @returns {VariableError} - error message
* @category Errors
* @since v1.0.0
*/
function buildPostValidateErrorMessage(value, err, loader, parser, params) {
if (err instanceof Error) {
return rebuildAsVariableError(value, err, loader, parser, params);
} else {
return new VariableError(`variables[${loader.loaderType}](${parser.name}):${printValue(value, params)} unknown postValidate error`);
}
if (err instanceof Error) return rebuildAsVariableError(value, err, loader, parser, params);
else return new VariableError(`variables[${loader.loaderType}](${parser.name}):${printValue(value, params)} unknown postValidate error`);
}
function handleLoader(rootKey, loader, parser, params, options, encodeOptions) {
return __async(this, null, function* () {
var _a, _b, _c, _d, _e;
try {
if (yield loader.isLoaderDisabled()) {
return void 0;
}
const result = yield loader.getLoaderResult(rootKey);
if (!result) {
return void 0;
}
const { value, path, seen } = result;
if (value) {
try {
yield (_a = parser.preValidate) == null ? void 0 : _a.call(parser, { key: rootKey, value, loader });
} catch (err) {
throw buildPreValidateErrorMessage(value, err, loader, parser, params);
}
let rawOutput;
try {
rawOutput = yield parser.parse({ key: rootKey, value, loader });
} catch (err) {
throw buildParserErrorMessage(value, err, loader, parser, params);
}
let output = rawOutput;
try {
const validateData = yield (_b = parser.postValidate) == null ? void 0 : _b.call(parser, { key: rootKey, value: rawOutput, loader });
if (validateData) {
output = validateData;
}
} catch (err) {
throw buildPostValidateErrorMessage(value, err, loader, parser, params);
}
const stringValue = parser.toString(output, encodeOptions);
if (!seen && !(encodeOptions == null ? void 0 : encodeOptions.silent)) {
const logValue = (_d = (_c = parser.toLogString) == null ? void 0 : _c.call(parser, output)) != null ? _d : stringValue;
printLog(options, loader.loaderType, rootKey, logValue, path, params);
}
return { namespace: options.namespace, stringValue, type: loader.loaderType, value: output };
}
} catch (err) {
(_e = options.logger) == null ? void 0 : _e.error(err);
}
return void 0;
});
/**
* handle function for loader
* @template Input - Type of input value
* @template Output - Type of output value
* @param {string} rootKey - key of variable
* @param {IConfigLoader} loader - loader to use
* @param {IConfigParser<Output, RawOutput>} parser - parser to use
* @param {FormatParameters} [params] - optional format parameters
* @param {SolvedConfigOptions} options - options to use
* @param {EncodeOptions} [encodeOptions] - optional encode options
* @returns {Promise<{type: string; value: Output | undefined} | undefined>} - parsed value
* @since v1.0.0
*/
async function handleLoader(rootKey, loader, parser, params, options, encodeOptions) {
try {
if (await loader.isLoaderDisabled()) return;
const result = await loader.getLoaderResult(rootKey);
if (!result) return;
const { value, path, seen } = result;
if (value) {
/**
* parser pre-validate
*/
try {
await parser.preValidate?.({
key: rootKey,
value,
loader
});
} catch (err) {
throw buildPreValidateErrorMessage(value, err, loader, parser, params);
}
/**
* parse value
*/
let rawOutput;
try {
rawOutput = await parser.parse({
key: rootKey,
value,
loader
});
} catch (err) {
throw buildParserErrorMessage(value, err, loader, parser, params);
}
/**
* parser post-validate
*/
let output = rawOutput;
try {
const validateData = await parser.postValidate?.({
key: rootKey,
value: rawOutput,
loader
});
if (validateData) output = validateData;
} catch (err) {
throw buildPostValidateErrorMessage(value, err, loader, parser, params);
}
/**
* print log
*/
const stringValue = parser.toString(output, encodeOptions);
if (!seen && !encodeOptions?.silent) {
const logValue = parser.toLogString?.(output) ?? stringValue;
printLog(options, loader.loaderType, rootKey, logValue, path, params);
}
return {
namespace: options.namespace,
stringValue,
type: loader.loaderType,
value: output
};
}
} catch (err) {
options.logger?.error(err);
}
}
// src/getConfigObject.ts
var defaultValueSeenMap = /* @__PURE__ */ new Map();
//#endregion
//#region src/getConfigObject.ts
/**
* Map of seen default values
*/
const defaultValueSeenMap = /* @__PURE__ */ new Map();
/**
* Clear the seen map for default values (for unit testing purposes)
* @since v0.2.5
*/
function clearDefaultValueSeenMap() {
defaultValueSeenMap.clear();
defaultValueSeenMap.clear();
}
function getConfigObject(rootKey, loaders, parser, defaultValueLoadable, params, options, encodeOptions) {
return __async(this, null, function* () {
var _a, _b;
const currentOptions = buildOptions(options);
let defaultValue;
let type;
if (defaultValueLoadable !== void 0) {
try {
defaultValue = yield LoadableCore.resolve(defaultValueLoadable);
type = "default";
} catch (err) {
(_a = currentOptions.logger) == null ? void 0 : _a.error(err);
throw handleAsVariableError(err);
}
}
for (const loader of loaders) {
let output;
try {
output = yield handleLoader(rootKey, loader, parser, params, currentOptions, encodeOptions);
} catch (err) {
(_b = currentOptions.logger) == null ? void 0 : _b.error(err);
}
if (output !== void 0) {
return output;
}
}
let stringValue;
if (defaultValue !== void 0) {
stringValue = parser.toString(defaultValue, encodeOptions);
if (!handleSeen(defaultValueSeenMap, rootKey, stringValue) && !(encodeOptions == null ? void 0 : encodeOptions.silent)) {
printLog(currentOptions, "default", rootKey, stringValue, "default", params);
}
return { namespace: currentOptions.namespace, stringValue, type: "default", value: defaultValue };
}
return { namespace: currentOptions.namespace, stringValue, type, value: defaultValue };
});
async function getConfigObject(rootKey, loaders, parser, defaultValueLoadable, params, options, encodeOptions) {
const currentOptions = buildOptions(options);
let defaultValue;
let type;
/**
* get default value before loaders (to throw error before loaders)
*/
if (defaultValueLoadable !== void 0) try {
defaultValue = await LoadableCore.resolve(defaultValueLoadable);
type = "default";
} catch (err) {
currentOptions.logger?.error(err);
throw handleAsVariableError(err);
}
for (const loader of loaders) {
let output;
try {
output = await handleLoader(rootKey, loader, parser, params, currentOptions, encodeOptions);
} catch (err) {
currentOptions.logger?.error(err);
}
if (output !== void 0) return output;
}
let stringValue;
if (defaultValue !== void 0) {
stringValue = parser.toString(defaultValue, encodeOptions);
if (!handleSeen(defaultValueSeenMap, rootKey, stringValue) && !encodeOptions?.silent) printLog(currentOptions, "default", rootKey, stringValue, "default", params);
return {
namespace: currentOptions.namespace,
stringValue,
type: "default",
value: defaultValue
};
}
return {
namespace: currentOptions.namespace,
stringValue,
type,
value: defaultValue
};
}
// src/getConfigVariable.ts
function getConfigVariable(rootKey, loaders, parser, defaultValueLoadable, params, options, encodeOptions) {
return __async(this, null, function* () {
return (yield getConfigObject(rootKey, loaders, parser, defaultValueLoadable, params, options, encodeOptions)).value;
});
//#endregion
//#region src/getConfigVariable.ts
async function getConfigVariable(rootKey, loaders, parser, defaultValueLoadable, params, options, encodeOptions) {
return (await getConfigObject(rootKey, loaders, parser, defaultValueLoadable, params, options, encodeOptions)).value;
}
// src/getConfigVariableResult.ts
import { Err, Ok } from "@luolapeikko/result-option";
function getConfigVariableResult(rootKey, loaders, parser, defaultValueLoadable, params, options) {
return __async(this, null, function* () {
try {
return Ok(yield getConfigVariable(rootKey, loaders, parser, defaultValueLoadable, params, options));
} catch (err) {
return Err(err);
}
});
//#endregion
//#region src/getConfigVariableResult.ts
async function getConfigVariableResult(rootKey, loaders, parser, defaultValueLoadable, params, options) {
try {
return Ok(await getConfigVariable(rootKey, loaders, parser, defaultValueLoadable, params, options));
} catch (err) {
return Err(err);
}
}
// src/getConfigObjectResult.ts
import { Err as Err2, Ok as Ok2 } from "@luolapeikko/result-option";
function getConfigObjectResult(rootKey, loaders, parser, defaultValueLoadable, params, options) {
return __async(this, null, function* () {
try {
return Ok2(yield getConfigObject(rootKey, loaders, parser, defaultValueLoadable, params, options));
} catch (err) {
return Err2(err);
}
});
//#endregion
//#region src/getConfigObjectResult.ts
async function getConfigObjectResult(rootKey, loaders, parser, defaultValueLoadable, params, options) {
try {
return Ok(await getConfigObject(rootKey, loaders, parser, defaultValueLoadable, params, options));
} catch (err) {
return Err(err);
}
}
// src/loaders/ConfigLoader.ts
import { EventEmitter } from "events";
import { LoadableCore as LoadableCore2 } from "@luolapeikko/ts-common";
// src/lib/semicolonUtils.ts
//#endregion
//#region src/lib/semicolonUtils.ts
/**
* Make the first character of a string lowercase
* @param {string} data String to make lowercase
* @returns {string} String with first character lowercase
* @category Utils
* @example
* lcFirst('Hello') // 'hello'
*/
function lcFirst(data) {
return data.length > 0 ? data.charAt(0).toLowerCase() + data.slice(1) : data;
return data.length > 0 ? data.charAt(0).toLowerCase() + data.slice(1) : data;
}
/**
* Parse a semicolon separated string into a config object
* @param {string} config Semicolon separated string
* @param {boolean} [keepCase] Keep the case of the keys, default true
* @returns {Record<string, string>} Config object
* @category Utils
* @since v1.0.0
* @example
* parseSemicolonConfig('a=b;c=d') // {a: 'b', c: 'd'}
*/
function parseSemicolonConfig(config, keepCase = true) {
return config.split(";").reduce((last, c) => {
const [k, v] = c.split("=", 2);
if (k && v) {
const key = keepCase ? k.trim() : lcFirst(k.trim());
if (key) {
last[key] = decodeURIComponent(v.trim());
}
}
return last;
}, {});
return config.split(";").reduce((last, c) => {
const [k, v] = c.split("=", 2);
if (k && v) {
const key = keepCase ? k.trim() : lcFirst(k.trim());
if (key) last[key] = decodeURIComponent(v.trim());
}
return last;
}, {});
}
/**
* Stringify a config object to a semicolon separated string
* @param {Record<string, string>} config Object to stringify
* @param {boolean} [uriEncode] Use URI encoding for string outputs
* @returns {string} Stringified config
* @category Utils
* @since v1.0.0
* @example
* stringifySemicolonConfig({a: 'b', c: 'd'}) // 'a=b;c=d'
*/
function stringifySemicolonConfig(config, uriEncode = true) {
return Object.entries(config).reduce((last, [key, value]) => {
if (value !== void 0) {
const encodedValue = uriEncode ? encodeURIComponent(String(value)) : String(value);
last.push(`${key}=${encodedValue}`);
}
return last;
}, []).join(";");
return Object.entries(config).reduce((last, [key, value]) => {
if (value !== void 0) {
const encodedValue = uriEncode ? encodeURIComponent(String(value)) : String(value);
last.push(`${key}=${encodedValue}`);
}
return last;
}, []).join(";");
}
/**
* Stringify a config object to a semicolon separated string for logging
* @template Out - Type of output
* @param {Record<string, string>} config Object to stringify
* @param {ShowValueType} showProtectedKeys How to show protected keys
* @param {string[]} protectedKeys list of protected keys
* @returns {string} Stringified config
* @category Utils
* @since v1.0.0
* @example
* logStringifySemicolonConfig({a: 'b', c: 'd'}) // 'a=b;c=d'
*/
function logStringifySemicolonConfig(config, showProtectedKeys, protectedKeys) {
return Object.entries(config).reduce((last, [key, value]) => {
if (value !== void 0) {
if (protectedKeys.has(key)) {
const hiddenValue = buildHiddenValue(String(value), showProtectedKeys);
last.push(`${key}=${hiddenValue}`);
} else {
last.push(`${key}=${String(value)}`);
}
}
return last;
}, []).join(";");
return Object.entries(config).reduce((last, [key, value]) => {
if (value !== void 0) if (protectedKeys.has(key)) {
const hiddenValue = buildHiddenValue(String(value), showProtectedKeys);
last.push(`${key}=${hiddenValue}`);
} else last.push(`${key}=${String(value)}`);
return last;
}, []).join(";");
}
// src/lib/objectUtils.ts
//#endregion
//#region src/lib/objectUtils.ts
/**
* validate if a value is a valid object
* @param {unknown} value - value to validate
* @returns {value is Record<string, unknown>} - true if value is a valid object
* @since v0.2.5
*/
function isValidObject(value) {
return typeof value === "object" && value !== null && !Array.isArray(value);
return typeof value === "object" && value !== null && !Array.isArray(value);
}
/**
* Check if a value has a toJSON method
* @param {unknown} value - value to check
* @returns {value is {toJSON: () => string}} - true if value has a toJSON method
*/
function haveToJSON(value) {
return typeof value === "object" && value !== null && "toJSON" in value;
return typeof value === "object" && value !== null && "toJSON" in value;
}
/**
* Stringify a value to a string
* @param {unknown} value - value to stringify
* @returns {string} - stringified value
*/
function stringifyValue(value) {
if (isValidObject(value)) {
return haveToJSON(value) ? value.toJSON() : JSON.stringify(value);
}
return String(value);
if (isValidObject(value)) return haveToJSON(value) ? value.toJSON() : JSON.stringify(value);
return String(value);
}
/**
* Convert an object to a string value object
* @param {Record<string, unknown>} obj - object to convert
* @returns {Record<string, string | undefined>} - object with string values
* @since v0.2.5
*/
function buildStringObject(obj) {
return Object.entries(obj).reduce((last, [key, value]) => {
if (value) {
last[key] = stringifyValue(value);
}
return last;
}, {});
return Object.entries(obj).reduce((last, [key, value]) => {
if (value) last[key] = stringifyValue(value);
return last;
}, {});
}
/**
* Convert an object to a Map<string, string>
* @param {Record<string, unknown>} obj - object to convert
* @returns {Map<string, string>} - Map with string values
* @since v0.2.5
*/
function buildStringMap(obj) {
return Object.entries(obj).reduce((acc, [key, value]) => {
if (value) {
acc.set(key, stringifyValue(value));
}
return acc;
}, /* @__PURE__ */ new Map());
return Object.entries(obj).reduce((acc, [key, value]) => {
if (value) acc.set(key, stringifyValue(value));
return acc;
}, /* @__PURE__ */ new Map());
}
/**
* Apply a Record<string, string> to a Map<string, string>
* @param {Record<string, string | undefined>} obj - object to apply
* @param {Map<string, string>} target - Map to apply to
* @returns {Map<string, string>} - target with updates
* @since v0.2.5
*/
function applyStringMap(obj, target) {
return Object.entries(obj).reduce((acc, [key, value]) => {
if (value) {
acc.set(key, value);
}
return acc;
}, target);
return Object.entries(obj).reduce((acc, [key, value]) => {
if (value) acc.set(key, value);
return acc;
}, target);
}
// src/loaders/ConfigLoader.ts
//#endregion
//#region src/loaders/ConfigLoader.ts
/**
* Abstract base class for config loaders
* @template Props - the type of the props
* @template OverrideMap - the type of the override key map
* @category Loaders
* @abstract
* @since v1.0.0
*/
var ConfigLoader = class extends EventEmitter {
constructor(props = {}, overrideKeys = {}) {
super();
this.valueSeen = /* @__PURE__ */ new Map();
this.options = props;
this.overrideKeys = overrideKeys;
}
getLoaderResult(lookupKey) {
return __async(this, null, function* () {
var _a;
const loaderValue = yield this.handleLoaderValue((_a = this.overrideKeys[lookupKey]) != null ? _a : lookupKey);
if (!loaderValue) {
return void 0;
}
return {
value: loaderValue.value,
path: loaderValue.path,
seen: handleSeen(this.valueSeen, lookupKey, loaderValue.value)
};
});
}
isLoaderDisabled() {
return __async(this, null, function* () {
const loadableDisabled = (yield this.getOptions()).disabled;
return LoadableCore2.resolve(loadableDisabled);
});
}
setDisabled(disabled) {
return this.setOption("disabled", disabled);
}
/**
* Get options from loader and merge with default options
* @returns {Promise<DefaultProps>} - Promise of DefaultProps & Props
*/
getOptions() {
return __async(this, null, function* () {
const resolvedOptions = yield typeof this.options === "function" ? this.options() : this.options;
return Object.assign({}, this.defaultOptions, resolvedOptions);
});
}
setOption(key, value) {
return __async(this, null, function* () {
this.options = Object.assign({}, yield this.getOptions(), {
[key]: value
});
});
}
/**
* Build error string `ConfigVariables[<type>]: <message>`
* @param {string} message - error message
* @returns {string} - error string
*/
buildErrorStr(message) {
return `ConfigLoader[${this.loaderType}]: ${message}`;
}
constructor(props = {}, overrideKeys = {}) {
super();
this.valueSeen = /* @__PURE__ */ new Map();
this.options = props;
this.overrideKeys = overrideKeys;
}
async getLoaderResult(lookupKey) {
const loaderValue = await this.handleLoaderValue(this.overrideKeys[lookupKey] ?? lookupKey);
if (!loaderValue) return;
return {
value: loaderValue.value,
path: loaderValue.path,
seen: handleSeen(this.valueSeen, lookupKey, loaderValue.value)
};
}
async isLoaderDisabled() {
const loadableDisabled = (await this.getOptions()).disabled;
return LoadableCore.resolve(loadableDisabled);
}
setDisabled(disabled) {
return this.setOption("disabled", disabled);
}
/**
* Get options from loader and merge with default options
* @returns {Promise<DefaultProps>} - Promise of DefaultProps & Props
*/
async getOptions() {
const resolvedOptions = await (typeof this.options === "function" ? this.options() : this.options);
return Object.assign({}, this.defaultOptions, resolvedOptions);
}
async setOption(key, value) {
this.options = Object.assign({}, await this.getOptions(), { [key]: value });
}
/**
* Build error string `ConfigVariables[<type>]: <message>`
* @param {string} message - error message
* @returns {string} - error string
*/
buildErrorStr(message) {
return `ConfigLoader[${this.loaderType}]: ${message}`;
}
};
// src/loaders/RecordConfigLoader.ts
//#endregion
//#region src/loaders/RecordConfigLoader.ts
/**
* RecordConfigLoader is a class that extends ConfigLoader and adds the ability to reload the data.
* @template Props - the type of the options for the loader
* @template OverrideMap - the type of the override key map
* @since v1.0.0
*/
var RecordConfigLoader = class extends ConfigLoader {
constructor() {
super(...arguments);
this._isLoaded = false;
}
/**
* reloads the data
*/
reload() {
return __async(this, null, function* () {
this.valueSeen.clear();
yield this.loadData();
});
}
/**
* is the data loaded
* @returns {boolean} - true if the data is loaded, false otherwise
*/
isLoaded() {
return this._isLoaded;
}
/**
* load data from the loader. If the data is loaded successfully
* an "updated" event will be emitted.
* @returns {Promise<void>}
*/
loadData() {
return __async(this, null, function* () {
this.dataPromise = this.handleData();
yield this.dataPromise;
this.emit("updated");
});
}
constructor(..._args) {
super(..._args);
this._isLoaded = false;
}
/**
* reloads the data
*/
async reload() {
this.valueSeen.clear();
await this.loadData();
}
/**
* is the data loaded
* @returns {boolean} - true if the data is loaded, false otherwise
*/
isLoaded() {
return this._isLoaded;
}
/**
* load data from the loader. If the data is loaded successfully
* an "updated" event will be emitted.
* @returns {Promise<void>}
*/
async loadData() {
this.dataPromise = this.handleData();
await this.dataPromise;
this.emit("updated");
}
};
// src/loaders/EnvConfigLoader.ts
//#endregion
//#region src/loaders/EnvConfigLoader.ts
/**
* env loader class is used to load env variables from process.env
* @template OverrideMap - the type of the override key map
* @param {string} [overrideKey] - optional override key for lookup
* @returns {IConfigLoader} - IConfigLoader object
* @category Loaders
* @since v1.0.0
*/
var EnvConfigLoader = class extends ConfigLoader {
constructor() {
super(...arguments);
this.loaderType = "env";
this.defaultOptions = {
disabled: false
};
}
handleLoaderValue(lookupKey) {
return { value: process.env[lookupKey], path: `process.env.${lookupKey}` };
}
constructor(..._args) {
super(..._args);
this.loaderType = "env";
this.defaultOptions = { disabled: false };
}
handleLoaderValue(lookupKey) {
return {
value: process.env[lookupKey],
path: `process.env.${lookupKey}`
};
}
};
// src/loaders/ReactEnvConfigLoader.ts
//#endregion
//#region src/loaders/ReactEnvConfigLoader.ts
/**
* React env loader class is used to load env variables from process.env.REACT_APP_*
* @template OverrideMap - the type of the override key map
* @param {Partial<OverrideMap>} [override] - optional override key for lookup
* @returns {IConfigLoader} - IConfigLoader object
* @category Loaders
* @since v1.0.0
*/
var ReactEnvConfigLoader = class extends ConfigLoader {
constructor() {
super(...arguments);
this.loaderType = "react-env";
this.defaultOptions = {
disabled: false
};
}
handleLoaderValue(lookupKey) {
const targetKey = `REACT_APP_${lookupKey}`;
return { value: process.env[targetKey], path: `process.env.${targetKey}` };
}
constructor(..._args) {
super(..._args);
this.loaderType = "react-env";
this.defaultOptions = { disabled: false };
}
handleLoaderValue(lookupKey) {
const targetKey = `REACT_APP_${lookupKey}`;
return {
value: process.env[targetKey],
path: `process.env.${targetKey}`
};
}
};
// src/loaders/MapConfigLoader.ts
//#endregion
//#region src/loaders/MapConfigLoader.ts
/**
* MapConfigLoader is a class that extends ConfigLoader and adds the ability to reload the data.
* @template Props - the type of the options for the loader
* @template OverrideMap - the type of the override key map
* @since v1.0.0
* @category Loaders
*/
var MapConfigLoader = class extends ConfigLoader {
constructor() {
super(...arguments);
this._isLoaded = false;
this.data = /* @__PURE__ */ new Map();
}
/**
* clear maps and reloads the data
*/
reload() {
return __async(this, null, function* () {
this.data.clear();
this.valueSeen.clear();
yield this.loadData();
});
}
/**
* is the data loaded
* @returns {boolean} Whether the data is loaded
*/
isLoaded() {
return this._isLoaded;
}
/**
* load data from the loader. If the data is loaded successfully
* an "updated" event will be emitted.
* @returns {Promise<void>}
*/
loadData() {
return __async(this, null, function* () {
if (yield this.handleLoadData()) {
this.emit("updated");
}
});
}
constructor(..._args) {
super(..._args);
this._isLoaded = false;
this.data = /* @__PURE__ */ new Map();
}
/**
* clear maps and reloads the data
*/
async reload() {
this.data.clear();
this.valueSeen.clear();
await this.loadData();
}
/**
* is the data loaded
* @returns {boolean} Whether the data is loaded
*/
isLoaded() {
return this._isLoaded;
}
/**
* load data from the loader. If the data is loaded successfully
* an "updated" event will be emitted.
* @returns {Promise<void>}
*/
async loadData() {
if (await this.handleLoadData()) this.emit("updated");
}
};
// src/loaders/FetchConfigLoader.ts
//#endregion
//#region src/loaders/FetchConfigLoader.ts
/**
* FetchConfigLoader is used to load config from a fetch request
* @template OverrideMap - the type of the override key map
* @category Loaders
* @since v1.0.0
*/
var FetchConfigLoader = class extends MapConfigLoader {
/**
* Constructor for FetchConfigLoader
* @param {Loadable<ConfigRequest>} request - callback that returns a fetch request or a message object that the request is not ready
* @param {Loadable<Partial<FetchConfigLoaderOptions>>} options - optional options for FetchConfigLoader
* @param {Partial<OverrideMap>} overrideKeys - optional override keys for FetchConfigLoader
* @param {Lowercase<string>} type - optional name type for FetchConfigLoader (default: 'fetch')
*/
constructor(request, options = {}, overrideKeys = {}, type = "fetch") {
super(options, overrideKeys);
this.path = "undefined";
this.defaultOptions = {
cache: void 0,
cacheHitHttpCode: 304,
disabled: false,
fetchClient: typeof window === "object" ? fetch.bind(window) : fetch,
isSilent: false,
logger: void 0,
payload: "json",
validate: void 0
};
this.request = request;
this.loaderType = type;
}
handleLoaderValue(lookupKey) {
return __async(this, null, function* () {
if (!this._isLoaded) {
yield this.loadData();
this._isLoaded = true;
}
return { value: this.data.get(lookupKey), path: this.path };
});
}
handleLoadData() {
return __async(this, null, function* () {
const { logger: logger2, cache, isSilent, cacheHitHttpCode, payload } = yield this.getOptions();
const req = yield this.getRequest();
if (isRequestNotReadMessage(req)) {
logger2 == null ? void 0 : logger2.debug(`FetchEnvConfig: ${req.message}`);
return false;
}
this.path = urlSanitize(req.url);
logger2 == null ? void 0 : logger2.debug(`fetching config from ${this.path}`);
let res = yield this.fetchRequestOrCacheResponse(req);
if (!res) {
logger2 == null ? void 0 : logger2.info(`client is offline and not have cached response for FetchEnvConfig`);
return false;
}
if (res.ok && cache) {
logger2 == null ? void 0 : logger2.debug(`storing response in cache for FetchEnvConfig`);
yield cache.storeRequest(req, res);
}
if (res.status >= 400) {
res = yield this.checkIfValidCacheResponse(req, res);
if (res.status >= 400) {
if (isSilent) {
logger2 == null ? void 0 : logger2.info(`http error ${res.status.toString()} from FetchEnvConfig`);
return false;
}
throw new VariableError(`http error ${res.status.toString()} from FetchEnvConfig`);
}
}
if (res.status === cacheHitHttpCode) {
res = yield this.handleNotModifiedCache(req, res);
}
const contentType = res.headers.get("content-type");
if ((contentType == null ? void 0 : contentType.startsWith("application/json")) && payload === "json") {
applyStringMap(yield this.handleJson(res), this.data);
logger2 == null ? void 0 : logger2.debug("successfully loaded config from FetchEnvConfig");
return true;
}
if (isSilent) {
logger2 == null ? void 0 : logger2.info(`unsupported content-type ${String(contentType)} from FetchEnvConfig`);
return false;
}
throw new VariableError(`unsupported content-type ${String(contentType)} from FetchEnvConfig`);
});
}
/**
* if client is offline, we will try return the cached response else add cache validation (ETag) and try get the response from the fetch request
* @param {Request} req - request to fetch
* @returns {Promise<Response | undefined>} - response or undefined
*/
fetchRequestOrCacheResponse(req) {
return __async(this, null, function* () {
const { logger: logger2, cache, fetchClient } = yield this.getOptions();
if (cache) {
const cacheRes = yield cache.fetchRequest(req);
if (!cache.isOnline()) {
if (cacheRes) {
logger2 == null ? void 0 : logger2.debug(`client is offline, returned cached response for FetchEnvConfig`);
return cacheRes;
}
return void 0;
}
if (cacheRes) {
const etag = cacheRes.headers.get("etag");
if (etag) {
req.headers.set("If-None-Match", etag);
}
}
}
return fetchClient(req);
});
}
/**
* on error, check if we have a valid cached response
* @param {Request} req - request to fetch
* @param {Response} res - response from fetch
* @returns {Promise<Response>} - response
*/
checkIfValidCacheResponse(req, res) {
return __async(this, null, function* () {
const { cache } = yield this.getOptions();
if (cache) {
const cacheRes = yield cache.fetchRequest(req);
if (cacheRes) {
return cacheRes;
}
}
return res;
});
}
/**
* if we get a 304, get the cached response
* @param {Request} req - request to fetch
* @param {Response} res - response from fetch
* @returns {Promise<Response>} - response
*/
handleNotModifiedCache(req, res) {
return __async(this, null, function* () {
const { cache, logger: logger2 } = yield this.getOptions();
if (cache) {
const cacheRes = yield cache.fetchRequest(req);
if (cacheRes) {
logger2 == null ? void 0 : logger2.debug(`returned cached response for FetchEnvConfig`);
return cacheRes;
}
throw new VariableError(`http error ${res.status.toString()} from FetchEnvConfig (using cached version)`);
}
return res;
});
}
handleJson(res) {
return __async(this, null, function* () {
const { validate, isSilent, logger: logger2 } = yield this.getOptions();
try {
const contentType = res.headers.get("content-type");
if (!(contentType == null ? void 0 : contentType.startsWith("application/json"))) {
throw new Error(`unsupported content-type ${String(contentType)}`);
}
const rawData = yield res.json();
if (!isValidObject(rawData)) {
throw new Error(`is not valid JSON object`);
}
const data = buildStringObject(rawData);
if (validate) {
return yield validate(data);
}
return data;
} catch (error) {
const err = error instanceof Error ? error : new Error("unknown error");
if (isSilent) {
logger2 == null ? void 0 : logger2.info(`FetchEnvConfig JSON error: ${err.message}`);
return Promise.resolve({});
}
throw new VariableError(`FetchEnvConfig JSON error: ${err.message}`);
}
});
}
getRequest() {
return __async(this, null, function* () {
return typeof this.request === "function" ? this.request() : this.request;
});
}
/**
* Constructor for FetchConfigLoader
* @param {Loadable<ConfigRequest>} request - callback that returns a fetch request or a message object that the request is not ready
* @param {Loadable<Partial<FetchConfigLoaderOptions>>} options - optional options for FetchConfigLoader
* @param {Partial<OverrideMap>} overrideKeys - optional override keys for FetchConfigLoader
* @param {Lowercase<string>} type - optional name type for FetchConfigLoader (default: 'fetch')
*/
constructor(request, options = {}, overrideKeys = {}, type = "fetch") {
super(options, overrideKeys);
this.path = "undefined";
this.defaultOptions = {
cache: void 0,
cacheHitHttpCode: 304,
disabled: false,
fetchClient: typeof window === "object" ? fetch.bind(window) : fetch,
isSilent: false,
logger: void 0,
payload: "json",
validate: void 0
};
this.request = request;
this.loaderType = type;
}
async handleLoaderValue(lookupKey) {
if (!this._isLoaded) {
await this.loadData();
this._isLoaded = true;
}
return {
value: this.data.get(lookupKey),
path: this.path
};
}
async handleLoadData() {
const { logger, cache, isSilent, cacheHitHttpCode, payload } = await this.getOptions();
const req = await this.getRequest();
if (isRequestNotReadMessage(req)) {
logger?.debug(`FetchEnvConfig: ${req.message}`);
return false;
}
this.path = urlSanitize(req.url);
logger?.debug(`fetching config from ${this.path}`);
let res = await this.fetchRequestOrCacheResponse(req);
if (!res) {
logger?.info(`client is offline and not have cached response for FetchEnvConfig`);
return false;
}
if (res.ok && cache) {
logger?.debug(`storing response in cache for FetchEnvConfig`);
await cache.storeRequest(req, res);
}
if (res.status >= 400) {
res = await this.checkIfValidCacheResponse(req, res);
if (res.status >= 400) {
if (isSilent) {
logger?.info(`http error ${res.status.toString()} from FetchEnvConfig`);
return false;
}
throw new VariableError(`http error ${res.status.toString()} from FetchEnvConfig`);
}
}
if (res.status === cacheHitHttpCode) res = await this.handleNotModifiedCache(req, res);
const contentType = res.headers.get("content-type");
if (contentType?.startsWith("application/json") && payload === "json") {
applyStringMap(await this.handleJson(res), this.data);
logger?.debug("successfully loaded config from FetchEnvConfig");
return true;
}
if (isSilent) {
logger?.info(`unsupported content-type ${String(contentType)} from FetchEnvConfig`);
return false;
}
throw new VariableError(`unsupported content-type ${String(contentType)} from FetchEnvConfig`);
}
/**
* if client is offline, we will try return the cached response else add cache validation (ETag) and try get the response from the fetch request
* @param {Request} req - request to fetch
* @returns {Promise<Response | undefined>} - response or undefined
*/
async fetchRequestOrCacheResponse(req) {
const { logger, cache, fetchClient } = await this.getOptions();
if (cache) {
const cacheRes = await cache.fetchRequest(req);
if (!cache.isOnline()) {
if (cacheRes) {
logger?.debug(`client is offline, returned cached response for FetchEnvConfig`);
return cacheRes;
}
return;
}
if (cacheRes) {
const etag = cacheRes.headers.get("etag");
if (etag) req.headers.set("If-None-Match", etag);
}
}
return fetchClient(req);
}
/**
* on error, check if we have a valid cached response
* @param {Request} req - request to fetch
* @param {Response} res - response from fetch
* @returns {Promise<Response>} - response
*/
async checkIfValidCacheResponse(req, res) {
const { cache } = await this.getOptions();
if (cache) {
const cacheRes = await cache.fetchRequest(req);
if (cacheRes) return cacheRes;
}
return res;
}
/**
* if we get a 304, get the cached response
* @param {Request} req - request to fetch
* @param {Response} res - response from fetch
* @returns {Promise<Response>} - response
*/
async handleNotModifiedCache(req, res) {
const { cache, logger } = await this.getOptions();
if (cache) {
const cacheRes = await cache.fetchRequest(req);
if (cacheRes) {
logger?.debug(`returned cached response for FetchEnvConfig`);
return cacheRes;
}
throw new VariableError(`http error ${res.status.toString()} from FetchEnvConfig (using cached version)`);
}
return res;
}
async handleJson(res) {
const { validate, isSilent, logger } = await this.getOptions();
try {
const contentType = res.headers.get("content-type");
if (!contentType?.startsWith("application/json")) throw new Error(`unsupported content-type ${String(contentType)}`);
const rawData = await res.json();
if (!isValidObject(rawData)) throw new Error(`is not valid JSON object`);
const data = buildStringObject(rawData);
if (validate) return await validate(data);
return data;
} catch (error) {
const err = error instanceof Error ? error : /* @__PURE__ */ new Error("unknown error");
if (isSilent) {
logger?.info(`FetchEnvConfig JSON error: ${err.message}`);
return Promise.resolve({});
}
throw new VariableError(`FetchEnvConfig JSON error: ${err.message}`);
}
}
async getRequest() {
return typeof this.request === "function" ? this.request() : this.request;
}
};
// src/loaders/MemoryConfigLoader.ts
//#endregion
//#region src/loaders/MemoryConfigLoader.ts
/**
* Config loader with in-memory data which can be set and retrieved variables on the fly.
* - Useful for temporary controlled overrides or testing
* @template MemoryMap - the type of the memory map
* @template OverrideMap - the type of the override key map
* @since v1.0.0
* @category Loaders
*/
var MemoryConfigLoader = class extends ConfigLoader {
constructor(initialData, options = {}, overrideKeys = {}, type = "memory") {
super(options, overrideKeys);
this.defaultOptions = {
disabled: false,
logger: void 0
};
this.data = new Map(Object.entries(initialData));
this.loaderType = type;
}
set(key, value) {
return __async(this, null, function* () {
var _a;
const options = yield this.getOptions();
(_a = options.logger) == null ? void 0 : _a.debug(this.buildErrorStr(`setting key ${String(key)} to '${String(value)}'`));
if (this.data.get(key) !== value) {
this.valueSeen.delete(String(key));
}
this.data.set(key, value);
this.emit("updated");
});
}
get(key) {
return __async(this, null, function* () {
var _a;
const options = yield this.getOptions();
(_a = options.logger) == null ? void 0 : _a.debug(this.buildErrorStr(`getting key ${String(key)}`));
return this.data.get(key);
});
}
handleLoaderValue(lookupKey) {
return { value: this.data.get(lookupKey), path: `key:${lookupKey}` };
}
constructor(initialData, options = {}, overrideKeys = {}, type = "memory") {
super(options, overrideKeys);
this.defaultOptions = {
disabled: false,
logger: void 0
};
this.data = new Map(Object.entries(initialData));
this.loaderType = type;
}
async set(key, value) {
(await this.getOptions()).logger?.debug(this.buildErrorStr(`setting key ${String(key)} to '${String(value)}'`));
if (this.data.get(key) !== value) this.valueSeen.delete(String(key));
this.data.set(key, value);
this.emit("updated");
}
async get(key) {
(await this.getOptions()).logger?.debug(this.buildErrorStr(`getting key ${String(key)}`));
return this.data.get(key);
}
handleLoaderValue(lookupKey) {
return {
value: this.data.get(lookupKey),
path: `key:${lookupKey}`
};
}
};
// src/loaders/SwitchLoader.ts
//#endregion
//#region src/loaders/SwitchLoader.ts
/**
* SwitchLoader, this loader will switch between different configurations based on the active key.
* @example
* type TestConfigMapEnv = { DEMO: string; ANOTHER: string; };
* const switchLoader = new SwitchLoader<TestEnv, 'switch1' | 'switch2'>({
* switch1: { DEMO: 'value' },
* switch2: { DEMO: 'value2' },
* });
* const switcher = switchLoader.getLoader; // use this on the config map loaders: [switcher(), dotenv(), env(), ...]
* await switchLoader.activateSwitch('switch1'); // when you want enable switch1 values
* @template Config - The configuration map
* @template Key - The key to switch between
* @since v1.0.0
* @category Loaders
*/
var SwitchLoader = class extends ConfigLoader {
constructor(configs, props = {}, type = "switch") {
super(props);
this.keys = /* @__PURE__ */ new Set();
this.defaultOptions = {
disabled: false,
logger: void 0
};
this.config = configs;
this.loaderType = type;
}
activateSwitch(key) {
return __async(this, null, function* () {
var _a;
const options = yield this.getOptions();
(_a = options.logger) == null ? void 0 : _a.debug(this.buildErrorStr(`activating key '${String(key)}' => [${this.getConfigKeys(key).join(", ")}]`));
this.keys.add(key);
this.emit("updated");
});
}
deactivateSwitch(key) {
return __async(this, null, function* () {
var _a;
const options = yield this.getOptions();
(_a = options.logger) == null ? void 0 : _a.debug(this.buildErrorStr(`deactivating key '${String(key)}' => [${this.getConfigKeys(key).join(", ")}]`));
this.keys.delete(key);
this.emit("updated");
});
}
getCurrentKeys() {
return this.keys;
}
handleLoaderValue(lookupKey) {
let output;
for (const key of Array.from(this.keys)) {
const currentValue = this.config[key][lookupKey];
if (currentValue) {
output = { value: currentValue, path: `switch:${String(key)}:${lookupKey}` };
}
}
return output;
}
getConfigKeys(key) {
return Object.keys(this.config[key]);
}
constructor(configs, props = {}, type = "switch") {
super(props);
this.keys = /* @__PURE__ */ new Set();
this.defaultOptions = {
disabled: false,
logger: void 0
};
this.config = configs;
this.loaderType = type;
}
async activateSwitch(key) {
(await this.getOptions()).logger?.debug(this.buildErrorStr(`activating key '${String(key)}' => [${this.getConfigKeys(key).join(", ")}]`));
this.keys.add(key);
this.emit("updated");
}
async deactivateSwitch(key) {
(await this.getOptions()).logger?.debug(this.buildErrorStr(`deactivating key '${String(key)}' => [${this.getConfigKeys(key).join(", ")}]`));
this.keys.delete(key);
this.emit("updated");
}
getCurrentKeys() {
return this.keys;
}
handleLoaderValue(lookupKey) {
let output;
for (const key of Array.from(this.keys)) {
const currentValue = this.config[key][lookupKey];
if (currentValue) output = {
value: currentValue,
path: `switch:${String(key)}:${lookupKey}`
};
}
return output;
}
getConfigKeys(key) {
return Object.keys(this.config[key]);
}
};
// src/parsers/JsonConfigParser.ts
//#endregion
//#region src/parsers/JsonConfigParser.ts
/**
* Config parser to parse JSON string as object
* @example
* const objectSchema = z.object({
* foo: z.string(),
* baz: z.string(),
* secret: z.string(),
* });
* // parses '{"foo": "bar", "baz": "qux", "secret": "secret"}' string to {foo: "bar", baz: "qux", secret: "secret"}
* const fooBarJsonParser = new JsonConfigParser({
* validate: (value) => objectSchema.parse(value),
* protectedKeys: ['secret'],
* showProtectedKeys: 'prefix-suffix', // shows secret value with few characters from start and end on logging
* });
* @template Out - the type of the output object
* @implements {IConfigParser<Out, object>}
* @category Parsers
* @since v1.0.0
*/
var JsonConfigParser = class {
constructor({ protectedKeys, validate, showProtectedKeys } = {}) {
this.name = "jsonConfigParser";
this.protectedKeys = new Set(protectedKeys);
this.validate = validate;
this.showProtectedKeys = showProtectedKeys;
}
parse({ value }) {
return this.buildBaseRecord(JSON.parse(value));
}
postValidate(_0) {
return __async(this, arguments, function* ({ value }) {
var _a;
return yield (_a = this.validate) == null ? void 0 : _a.call(this, value);
});
}
toString(config) {
return JSON.stringify(
Object.entries(config).reduce((last, [key, value]) => {
if (value) {
last[key] = value;
}
return last;
}, {})
);
}
toLogString(config) {
return JSON.stringify(
Object.entries(config).reduce((last, [key, value]) => {
if (value) {
if (this.protectedKeys.has(key)) {
last[key] = buildHiddenValue(String(value), this.showProtectedKeys);
} else {
last[key] = value;
}
}
return last;
}, {})
);
}
/**
* Build a string record from the given data
* @param {unknown} data - to be validated as object
* @returns {object} A record with string values
*/
buildBaseRecord(data) {
if (typeof data !== "object" || data === null || Array.isArray(data)) {
return {};
}
return data;
}
constructor({ protectedKeys, validate, showProtectedKeys } = {}) {
this.name = "jsonConfigParser";
this.protectedKeys = new Set(protectedKeys);
this.validate = validate;
this.showProtectedKeys = showProtectedKeys;
}
parse({ value }) {
return this.buildBaseRecord(JSON.parse(value));
}
async postValidate({ value }) {
return await this.validate?.(value);
}
toString(config) {
return JSON.stringify(Object.entries(config).reduce((last, [key, value]) => {
if (value) last[key] = value;
return last;
}, {}));
}
toLogString(config) {
return JSON.stringify(Object.entries(config).reduce((last, [key, value]) => {
if (value) if (this.protectedKeys.has(key)) last[key] = buildHiddenValue(String(value), this.showProtectedKeys);
else last[key] = value;
return last;
}, {}));
}
/**
* Build a string record from the given data
* @param {unknown} data - to be validated as object
* @returns {object} A record with string values
*/
buildBaseRecord(data) {
if (typeof data !== "object" || data === null || Array.isArray(data)) return {};
return data;
}
};
// src/parsers/UrlParser.ts
import { ErrorCore } from "@luolapeikko/ts-common";
//#endregion
//#region src/parsers/UrlParser.ts
/**
* UrlParser class is used to parse and validate env variables of type URL.
* @class UrlParser
* @implements {IConfigParser<URL, URL>}
* @category Parsers
* @since v0.2.5
*/
var UrlParser = class {
/**
* Create a new UrlParser
* @param {UrlParserProps} properties - Properties for the UrlParser
*/
constructor({ urlSanitize: urlSanitize2 } = {}) {
this.name = "urlParser";
this.urlSanitize = urlSanitize2 != null ? urlSanitize2 : false;
}
parse({ value }) {
try {
return new URL(value);
} catch (err) {
throw ErrorCore.from(err);
}
}
toString(value) {
return value.href;
}
toLogString(value) {
if (this.urlSanitize) {
return this.handleUrlSanitize(value.href);
}
return value.toString();
}
/**
* Build a URL object from a string and sanitize the username and password
* @param {string} value string to parse
* @returns {URL} URL object with sanitized username and password
*/
handleUrlSanitize(value) {
const url = new URL(value);
url.password = "*".repeat(url.password.length);
url.username = "*".repeat(url.username.length);
return url.href;
}
/**
* Create a new UrlParser
* @param {UrlParserProps} properties - Properties for the UrlParser
*/
constructor({ urlSanitize } = {}) {
this.name = "urlParser";
this.urlSanitize = urlSanitize ?? false;
}
parse({ value }) {
try {
return new URL(value);
} catch (err) {
throw ErrorCore.from(err);
}
}
toString(value) {
return value.href;
}
toLogString(value) {
if (this.urlSanitize) return this.handleUrlSanitize(value.href);
return value.toString();
}
/**
* Build a URL object from a string and sanitize the username and password
* @param {string} value string to parse
* @returns {URL} URL object with sanitized username and password
*/
handleUrlSanitize(value) {
const url = new URL(value);
url.password = "*".repeat(url.password.length);
url.username = "*".repeat(url.username.length);
return url.href;
}
};
// src/lib/primitiveUtils.ts
import { Err as Err3, Ok as Ok3 } from "@luolapeikko/result-option";
//#endregion
//#region src/lib/primitiveUtils.ts
/**
* Get an integer from a string
* @param {string} value - string to parse
* @returns {IResult<number, TypeError>} - Ok if value is an integer string, Err if not
* @since v0.2.5
*/
function getInteger(value) {
const parsed = parseInt(value, 10);
if (isNaN(parsed)) {
return Err3(new TypeError(`${value} is not an integer string`));
}
return Ok3(parsed);
const parsed = parseInt(value, 10);
if (isNaN(parsed)) return Err(/* @__PURE__ */ new TypeError(`${value} is not an integer string`));
return Ok(parsed);
}
var booleanTrueStringValues = ["true", "1", "yes", "y", "on"];
var booleanFalseStringValues = ["false", "0", "no", "n", "off"];
var allBooleanStringValues = [...booleanFalseStringValues, ...booleanTrueStringValues];
const booleanTrueStringValues = [
"true",
"1",
"yes",
"y",
"on"
];
const allBooleanStringValues = [...[
"false",
"0",
"no",
"n",
"off"
], ...booleanTrueStringValues];
/**
* Parse a string or boolean into a boolean value.
* @param {string | boolean} value - The value to parse.
* @returns {IResult<boolean, TypeError>} - Ok with boolean if successful, Err with TypeError if not.
* @since v0.2.5
*/
function getBoolean(value) {
if (typeof value === "boolean") {
return Ok3(value);
}
const output = value.toLowerCase();
if (!allBooleanStringValues.includes(output)) {
return Err3(new TypeError(`${value} is not a boolean string`));
}
return Ok3(booleanTrueStringValues.includes(output));
if (typeof value === "boolean") return Ok(value);
const output = value.toLowerCase();
if (!allBooleanStringValues.includes(output)) return Err(/* @__PURE__ */ new TypeError(`${value} is not a boolean string`));
return Ok(booleanTrueStringValues.includes(output));
}
/**
* Simply returns the input string as a successful result.
* @param {string} value - input string
* @returns {IResult<string, TypeError>} - Ok with input string, never Err
* @since v0.2.5
*/
function getString(value) {
return Ok3(value);
return Ok(value);
}
/**
* Parse a string into a float value.
* @param {string} value - The value to parse.
* @returns {IResult<number, TypeError>} - Ok with float if successful, Err with TypeError if not.
* @since v0.2.5
*/
function getFloat(value) {
const parsed = parseFloat(value);
if (isNaN(parsed)) {
return Err3(new TypeError(`${value} is not a float string`));
}
return Ok3(parsed);
const parsed = parseFloat(value);
if (isNaN(parsed)) return Err(/* @__PURE__ */ new TypeError(`${value} is not a float string`));
return Ok(parsed);
}
/**
* Parse a string into a bigint value.
* @param {string} value - The value to parse.
* @returns {IResult<bigint, TypeError>} - Ok with bigint if successful, Err with TypeError if not.
* @since v0.2.5
*/
function getBigInt(value) {
try {
return Ok3(BigInt(value));
} catch (_err) {
return Err3(new TypeError(`${value} is not a bigint string`));
}
try {
return Ok(BigInt(value));
} catch (_err) {
return Err(/* @__PURE__ */ new TypeError(`${value} is not a bigint string`));
}
}
// src/parsers/stringParser.ts
//#endregion
//#region src/parsers/stringParser.ts
/**
* Build parser and have optional post validation (as example for literal values)
* @template Output - Type of output, defaults to string
* @param {TypeGuardValidate<Output>} [validate] - optional post validation
* @returns {IConfigParser<string, Output>} - parser
* @category Parsers
* @since v1.0.0
*/
function stringParser(validate) {
return {
name: "stringParser",
parse: ({ value, key }) => {
return getString(value).mapErr((cause) => new TypeError(`value for key ${key} is not a string`, { cause })).unwrap();
},
postValidate: (props) => __async(null, null, function* () {
var _a;
if (!((_a = yield validate) == null ? void 0 : _a(props.value))) {
return void 0;
}
return props.value;
}),
preValidate: ({ key, value }) => {
if (typeof value !== "string") {
throw new TypeError(`value for key ${key} is not a string`);
}
},
toString: (value) => {
return value;
}
};
return {
name: "stringParser",
parse: ({ value, key }) => {
return getString(value).mapErr((cause) => new TypeError(`value for key ${key} is not a string`, { cause })).unwrap();
},
postValidate: async (props) => {
if (!(await validate)?.(props.value)) return;
return props.value;
},
preValidate: ({ key, value }) => {
if (typeof value !== "string") throw new TypeError(`value for key ${key} is not a string`);
},
toString: (value) => {
return value;
}
};
}
// src/parsers/SemicolonConfigParser.ts
//#endregion
//#region src/parsers/SemicolonConfigParser.ts
/**
* Config parser to parse semicolon separated string key=value pairs as object
* @example
* const objectSchema = z.object({
* foo: z.string(),
* baz: z.string(),
* secret: z.string(),
* });
* // parses 'foo=bar;baz=qux;secret=secret' string to {foo: "bar", baz: "qux", secret: "secret"}
* const fooBarJsonParser = new SemicolonConfigParser({
* validate: (value) => objectSchema.parse(value),
* protectedKeys: ['secret'],
* showProtectedKeys: 'prefix-suffix', // shows secret value with few characters from start and end on logging
* });
* @template Out - the type of the output object
* @implements {IConfigParser<Out, Record<string, string>>}
* @category Parsers
* @since v1.0.0
*/
var SemicolonConfigParser = class {
constructor({ keepCase, protectedKeys, validate, showProtectedKeys } = {}) {
this.name = "semicolonConfigParser";
this.protectedKeys = new Set(protectedKeys);
this.validate = validate;
this.keepCase = keepCase != null ? keepCase : true;
this.showProtectedKeys = showProtectedKeys;
}
parse({ value }) {
return Promise.resolve(parseSemicolonConfig(value, this.keepCase));
}
postValidate(_0) {
return __async(this, arguments, function* ({ value }) {
var _a;
return yield (_a = this.validate) == null ? void 0 : _a.call(this, value);
});
}
toString(config, options) {
return stringifySemicolonConfig(config, options == null ? void 0 : options.uriEncode);
}
toLogString(config) {
return logStringifySemicolonConfig(config, this.showProtectedKeys, this.protectedKeys);
}
constructor({ keepCase, protectedKeys, validate, showProtectedKeys } = {}) {
this.name = "semicolonConfigParser";
this.protectedKeys = new Set(protectedKeys);
this.validate = validate;
this.keepCase = keepCase ?? true;
this.showProtectedKeys = showProtectedKeys;
}
parse({ value }) {
return Promise.resolve(parseSemicolonConfig(value, this.keepCase));
}
async postValidate({ value }) {
return await this.validate?.(value);
}
toString(config, options) {
return stringifySemicolonConfig(config, options?.uriEncode);
}
toLogString(config) {
return logStringifySemicolonConfig(config, this.showProtectedKeys, this.protectedKeys);
}
};
// src/parsers/booleanParser.ts
//#endregion
//#region src/parsers/booleanParser.ts
/**
* Build parser and have optional post validation (as example for literal values)
*
* supports the following string ___true___ values:
*
* ```['true', '1', 'yes', 'y', 'on']```
*
* supports the following string ___false___ values:
*
* ```['false', '0', 'no', 'n', 'off']```
* @template Output - Type of output, defaults to number
* @param {TypeGuardValidate<Output>} [validate] - optional post validation
* @returns {IConfigParser<boolean, Output>} - parser
* @category Parsers
* @since v1.0.0
*/
function booleanParser(validate) {
return {
name: "booleanParser",
parse: ({ key, value }) => {
return getBoolean(value).mapErr((cause) => new TypeError(`value for key ${key} is not a boolean string`, { cause })).unwrap();
},
postValidate: (props) => __async(null, null, function* () {
var _a;
if (!((_a = yield validate) == null ? void 0 : _a(props.value))) {
return void 0;
}
return props.value;
}),
preValidate: ({ key, value }) => {
if (typeof value === "boolean") {
return;
}
if (typeof value !== "string") {
throw new TypeError(`value for key ${key} is not a string`);
}
},
toString: (value) => {
return value.toString();
}
};
return {
name: "booleanParser",
parse: ({ key, value }) => {
return getBoolean(value).mapErr((cause) => new TypeError(`value for key ${key} is not a boolean string`, { cause })).unwrap();
},
postValidate: async (props) => {
if (!(await validate)?.(props.value)) return;
return props.value;
},
preValidate: ({ key, value }) => {
if (typeof value === "boolean") return;
if (typeof value !== "string") throw new TypeError(`value for key ${key} is not a string`);
},
toString: (value) => {
return value.toString();
}
};
}
// src/parsers/floatParser.ts
//#endregion
//#region src/parsers/floatParser.ts
/**
* Build parser and have optional post validation (as example for literal values)
* @template Output - Type of output, defaults to number
* @param {TypeGuardValidate<Output>} [validate] - optional post validation
* @returns {IConfigParser<number, Output>} - parser
* @category Parsers
* @since v1.0.0
*/
function floatParser(validate) {
return {
name: "floatParser",
parse: ({ key, value }) => {
return getFloat(value).mapErr((cause) => new TypeError(`value for key ${key} is not a float string`, { cause })).unwrap();
},
postValidate: (props) => __async(null, null, function* () {
var _a;
if (!((_a = yield validate) == null ? void 0 : _a(props.value))) {
return void 0;
}
return props.value;
}),
preValidate: ({ key, value }) => {
if (typeof value !== "string") {
throw new TypeError(`value for key ${key} is not a string`);
}
},
toString: (value) => {
return value.toString();
}
};
return {
name: "floatParser",
parse: ({ key, value }) => {
return getFloat(value).mapErr((cause) => new TypeError(`value for key ${key} is not a float string`, { cause })).unwrap();
},
postValidate: async (props) => {
if (!(await validate)?.(props.value)) return;
return props.value;
},
preValidate: ({ key, value }) => {
if (typeof value !== "string") throw new TypeError(`value for key ${key} is not a string`);
},
toString: (value) => {
return value.toString();
}
};
}
// src/parsers/integerParser.ts
//#endregion
//#region src/parsers/integerParser.ts
/**
* Build parser and have optional post validation (as example for literal values)
* @template Output - Type of output, defaults to number
* @param {TypeGuardValidate<Output>} [validate] - optional post validation
* @returns {IConfigParser<number, Output>} - parser
* @category Parsers
* @since v1.0.0
*/
function integerParser(validate) {
return {
name: "integerParser",
parse: ({ key, value }) => {
return getInteger(value).mapErr((cause) => new TypeError(`value for key ${key} is not an integer string`, { cause })).unwrap();
},
postValidate: (props) => __async(null, null, function* () {
var _a;
if (!((_a = yield validate) == null ? void 0 : _a(props.value))) {
return void 0;
}
return props.value;
}),
toString: (value) => {
return value.toString();
}
};
return {
name: "integerParser",
parse: ({ key, value }) => {
return getInteger(value).mapErr((cause) => new TypeError(`value for key ${key} is not an integer string`, { cause })).unwrap();
},
postValidate: async (props) => {
if (!(await validate)?.(props.value)) return;
return props.value;
},
toString: (value) => {
return value.toString();
}
};
}
// src/parsers/arrayParser.ts
//#endregion
//#region src/parsers/arrayParser.ts
/**
* Build parser for array of values
* @template Input - Type of input
* @template Output - Type of output
* @param {IConfigParser<Input, Output>} parse parser for the array values
* @param {string} separator separator for the array values, defaults to ';'
* @param {TypeGuardValidate<Output> | undefined} validate optional post validation
* @returns {IConfigParser<Output[], Input[]>} Parser for array of values
* @category Parsers
* @since v1.0.0
*/
function arrayParser(parse, separator = ";", validate) {
return {
name: "arraySeparatorParser",
parse: (props) => {
return Promise.all(props.value.split(separator).map((v) => parse.parse(__spreadProps(__spreadValues({}, props), { value: v }))));
},
postValidate: (props) => __async(null, null, function* () {
if (!validate) {
return void 0;
}
const valueList = yield Promise.all(
props.value.map((v) => __async(null, null, function* () {
var _a;
if (!((_a = yield validate) == null ? void 0 : _a(v))) {
return void 0;
}
return v;
}))
);
return valueList.filter((v) => v !== void 0);
}),
toString: (value) => {
return value.map((v) => parse.toString(v)).join(separator);
}
};
return {
name: "arraySeparatorParser",
parse: (props) => {
return Promise.all(props.value.split(separator).map((v) => parse.parse({
...props,
value: v
})));
},
postValidate: async (props) => {
if (!validate) return;
return (await Promise.all(props.value.map(async (v) => {
if (!(await validate)?.(v)) return;
return v;
}))).filter((v) => v !== void 0);
},
toString: (value) => {
return value.map((v) => parse.toString(v)).join(separator);
}
};
}
// src/parsers/bigIntParser.ts
//#endregion
//#region src/parsers/bigIntParser.ts
/**
* Build parser and have optional post validation (as example for literal values)
* @template Output - Type of output, defaults to bigint
* @param {TypeGuardValidate<Output>} [validate] - optional post validation
* @returns {IConfigParser<bigint, Output>} - parser
* @category Parsers
* @since v1.0.0
*/
function bigIntParser(validate) {
return {
name: "bigIntParser",
parse: ({ key, value }) => {
return getBigInt(value).mapErr((cause) => new TypeError(`value for key ${key} is not an integer string`, { cause })).unwrap();
},
postValidate: (props) => __async(null, null, function* () {
var _a;
if (!((_a = yield validate) == null ? void 0 : _a(props.value))) {
return void 0;
}
return props.value;
}),
toString: (value) => {
return value.toString();
}
};
return {
name: "bigIntParser",
parse: ({ key, value }) => {
return getBigInt(value).mapErr((cause) => new TypeError(`value for key ${key} is not an integer string`, { cause })).unwrap();
},
postValidate: async (props) => {
if (!(await validate)?.(props.value)) return;
return props.value;
},
toString: (value) => {
return value.toString();
}
};
}
// src/VariableLookupError.ts
//#endregion
//#region src/VariableLookupError.ts
/**
* Custom error class for variable lookup errors.
* @class VariableLookupError
* @augments VariableError
* @category Errors
* @since v0.2.2
*/
var VariableLookupError = class extends VariableError {
/**
* Create a new VariableLookupError
* @param {string} variableKey - The variable key.
* @param {string} message - The error message.
* @param {ErrorOptions} [options] - The error options
*/
constructor(variableKey, message, options) {
super(message, options);
this.variableKey = variableKey;
this.name = "VariableLookupError";
}
/**
* Create a new VariableLookupError
* @param {string} variableKey - The variable key.
* @param {string} message - The error message.
* @param {ErrorOptions} [options] - The error options
*/
constructor(variableKey, message, options) {
super(message, options);
this.variableKey = variableKey;
this.name = "VariableLookupError";
}
};
// src/ConfigMap.ts
import { Err as Err4, Ok as Ok4 } from "@luolapeikko/result-option";
import { LoadableCore as LoadableCore3 } from "@luolapeikko/ts-common";
//#endregion
//#region src/ConfigMap.ts
/**
* ConfigMap
* @example
* type ConfigEnv = {
* PORT: number;
* HOST: string;
* DEBUG: boolean;
* URL: URL;
* };
* const config = new ConfigMap<ConfigEnv>({
* DEBUG: {loaders: [env()], parser: booleanParser, defaultValue: false},
* HOST: {loaders: [env()], parser: stringParser, defaultValue: 'localhost'},
* PORT: {loaders: [env()], parser: integerParser, defaultValue: 3000},
* URL: {loaders: [env()], parser: new UrlParser({urlSanitize: true}), defaultValue: new URL('http://localhost:3000')},
* });
* console.log('port', await config.get('PORT'));
* @template Data - type of config map
* @since v1.1.0
*/
var ConfigMap = class {
/**
* ConfigMap constructor
* @param {EnvMapSchema<Data>} schema - schema of config map
* @param {Iterable<IConfigLoader>} loaders - iterable of config loaders
* @param {ConfigOptions} options - optional config options (logger, namespace)
*/
constructor(schema, loaders, options = { logger: void 0, namespace: void 0 }) {
this.schema = schema;
this.options = options;
this.loaders = loaders;
}
/**
* Set logger value
* @param {ILoggerLike | undefined} logger - logger like object
*/
setLogger(logger2) {
this.options.logger = logger2;
}
/**
* get env object from config map
* @returns {Promise<TypeValueRecords<Data>>} Promise of env object or undefined
* @example
* const valueObject: LoaderTypeValue<number> = await config.getObject('PORT');
* console.log(valueObject.type, valueObject.value); // 'env', 3000
* @param {string} key - key of config map
* @param {EncodeOptions} [encodeOptions] - optional encode options
*/
getObject(key, encodeOptions) {
return __async(this, null, function* () {
var _a;
if (typeof key !== "string") {
throw new VariableError(`ConfigMap key ${String(key)} is not a string`);
}
const entry = this.schema[key];
if (!entry) {
throw new VariableLookupError(key, `ConfigMap key ${String(key)} not found in config map`);
}
const { parser, defaultValue, params, undefinedThrowsError, undefinedErrorMessage } = entry;
const loaders = Array.from(yield LoadableCore3.resolve(this.loaders));
const configObject = yield getConfigObject(key, loaders, parser, defaultValue, params, this.options, encodeOptions);
if (undefinedThrowsError && configObject.value === void 0) {
(_a = buildOptions(this.options).logger) == null ? void 0 : _a.info(`ConfigMap key ${String(key)} is undefined (expect to throw error)`);
throw new VariableError(undefinedErrorMessage != null ? undefinedErrorMessage : `ConfigMap key ${String(key)} is undefined`);
}
return configObject;
});
}
/**
* get env object from config map as Result
* @returns {Promise<Result<TypeValueRecords<Data>>>} Result Promise of env object or undefined
* @example
* const valueObject: Result<LoaderTypeValue<number>> = await config.getObjectResult('PORT');
* if (valueObject.isOk()) {
* const {type, value} = valueObject.ok();
* console.log(type, value); // 'env', 3000
* }
* @param {string} key - key of config map
* @param {EncodeOptions} [encodeOptions] - optional encode options
*/
getObjectResult(key, encodeOptions) {
return __async(this, null, function* () {
try {
return Ok4(yield this.getObject(key, encodeOptions));
} catch (err) {
return Err4(err);
}
});
}
/**
* get env value from config map
* @returns {Promise<Data[Key]>} Promise of value or undefined
* @example
* const port: number = await config.get('PORT');
* @param {string} key - key of config map
* @param {EncodeOptions} [encodeOptions] - optional encode options
*/
get(key, encodeOptions) {
return __async(this, null, function* () {
return (yield this.getObject(key, encodeOptions)).value;
});
}
/**
* get env value as string from config map
* @returns {Promise<string | undefined>} Promise of string value or undefined
* @example
* const port: string = await config.getString('PORT');
* @param {string} key - key of config map
* @param {EncodeOptions} [encodeOptions] - optional encode options
*/
getString(key, encodeOptions) {
return __async(this, null, function* () {
return (yield this.getObject(key, encodeOptions)).stringValue;
});
}
/**
* get env value from config map as Result
* @returns {Promise<Result<Data[Key]>>} Result Promise of value or undefined
* @example
* const port: Result<number> = await config.getResult('PORT');
* if (port.isOk()) {
* console.log('port', port.ok());
* }
* @param {string} key - key of config map
* @param {EncodeOptions} [encodeOptions] - optional encode options
*/
getResult(key, encodeOptions) {
return __async(this, null, function* () {
try {
return Ok4(yield this.get(key, encodeOptions));
} catch (err) {
return Err4(err);
}
});
}
/**
* get env value as string from config map as Result
* @returns {Promise<Result<string | undefined>>} Result Promise of string value or undefined
* @example
* const port: Result<string> = await config.getStringResult('PORT');
* if (port.isOk()) {
* console.log('port', port.ok());
* }
* @param {string} key - key of config map
* @param {EncodeOptions} [encodeOptions] - optional encode options
*/
getStringResult(key, encodeOptions) {
return __async(this, null, function* () {
try {
return Ok4(yield this.getString(key, encodeOptions));
} catch (err) {
return Err4(err);
}
});
}
/**
* get all env value objects from config map
* @returns {Promise<TypeValueRecords<Data>>} Promise of all values
* @example
* const values: TypeValueRecords<Data> = await config.getAll();
* console.log(values.PORT.type, values.PORT.value); // 'env', 3000
*/
getAllObjects() {
return __async(this, null, function* () {
const values = yield this.getAllPromises();
return values.reduce((result, [key, value]) => {
result[key] = value;
return result;
}, {});
});
}
/**
* get all env values from config map
* @returns {Promise<Data>} Promise of all values
* @example
* const values: Data = await config.getAllValues();
* console.log('PORT', values.PORT); // 3000 (number)
*/
getAllValues() {
return __async(this, null, function* () {
const values = yield this.getAllPromises();
return values.reduce((result, [key, value]) => {
result[key] = value.value;
return result;
}, {});
});
}
/**
* get all env values from config map as string
* @returns {Promise<Record<keyof Data, string>>} Promise of all values as string
* @example
* const values: Record<keyof Data, string> = await config.getAllStringValues();
* console.log('PORT', values.PORT); // '3000' (string)
* @param {EncodeOptions} [encodeOptions] - optional encode options
*/
getAllStringValues(encodeOptions) {
return __async(this, null, function* () {
const values = yield this.getAllPromises(encodeOptions);
return values.reduce(
(result, [key, value]) => {
result[key] = value.stringValue;
return result;
},
{}
);
});
}
/**
* Validate all env values from config map, expect to throw error if error exists
* @param {(data: Data) => void} callback callback function
*/
validateAll(callback) {
return __async(this, null, function* () {
const data = yield this.getAllValues();
callback(data);
});
}
/**
* run lookup to all keys and return all promises
* @param {EncodeOptions} [encodeOptions] - optional encode options
* @returns {Promise<[keyof Data, LoaderTypeValueStrict<Data[keyof Data]>][]>} Promise of all values
*/
getAllPromises(encodeOptions) {
return Promise.all(
Object.keys(this.schema).map((key) => __async(this, null, function* () {
return [key, yield this.getObject(key, encodeOptions)];
}))
);
}
/**
* ConfigMap constructor
* @param {EnvMapSchema<Data>} schema - schema of config map
* @param {Iterable<IConfigLoader>} loaders - iterable of config loaders
* @param {ConfigOptions} options - optional config options (logger, namespace)
*/
constructor(schema, loaders, options = {
logger: void 0,
namespace: void 0
}) {
this.schema = schema;
this.options = options;
this.loaders = loaders;
}
/**
* Set logger value
* @param {ILoggerLike | undefined} logger - logger like object
*/
setLogger(logger) {
this.options.logger = logger;
}
/**
* get env object from config map
* @returns {Promise<TypeValueRecords<Data>>} Promise of env object or undefined
* @example
* const valueObject: LoaderTypeValue<number> = await config.getObject('PORT');
* console.log(valueObject.type, valueObject.value); // 'env', 3000
* @param {string} key - key of config map
* @param {EncodeOptions} [encodeOptions] - optional encode options
*/
async getObject(key, encodeOptions) {
if (typeof key !== "string") throw new VariableError(`ConfigMap key ${String(key)} is not a string`);
const entry = this.schema[key];
if (!entry) throw new VariableLookupError(key, `ConfigMap key ${String(key)} not found in config map`);
const { parser, defaultValue, params, undefinedThrowsError, undefinedErrorMessage } = entry;
const configObject = await getConfigObject(key, Array.from(await LoadableCore.resolve(this.loaders)), parser, defaultValue, params, this.options, encodeOptions);
if (undefinedThrowsError && configObject.value === void 0) {
buildOptions(this.options).logger?.info(`ConfigMap key ${String(key)} is undefined (expect to throw error)`);
throw new VariableError(undefinedErrorMessage ?? `ConfigMap key ${String(key)} is undefined`);
}
return configObject;
}
/**
* get env object from config map as Result
* @returns {Promise<Result<TypeValueRecords<Data>>>} Result Promise of env object or undefined
* @example
* const valueObject: Result<LoaderTypeValue<number>> = await config.getObjectResult('PORT');
* if (valueObject.isOk()) {
* const {type, value} = valueObject.ok();
* console.log(type, value); // 'env', 3000
* }
* @param {string} key - key of config map
* @param {EncodeOptions} [encodeOptions] - optional encode options
*/
async getObjectResult(key, encodeOptions) {
try {
return Ok(await this.getObject(key, encodeOptions));
} catch (err) {
return Err(err);
}
}
/**
* get env value from config map
* @returns {Promise<Data[Key]>} Promise of value or undefined
* @example
* const port: number = await config.get('PORT');
* @param {string} key - key of config map
* @param {EncodeOptions} [encodeOptions] - optional encode options
*/
async get(key, encodeOptions) {
return (await this.getObject(key, encodeOptions)).value;
}
/**
* get env value as string from config map
* @returns {Promise<string | undefined>} Promise of string value or undefined
* @example
* const port: string = await config.getString('PORT');
* @param {string} key - key of config map
* @param {EncodeOptions} [encodeOptions] - optional encode options
*/
async getString(key, encodeOptions) {
return (await this.getObject(key, encodeOptions)).stringValue;
}
/**
* get env value from config map as Result
* @returns {Promise<Result<Data[Key]>>} Result Promise of value or undefined
* @example
* const port: Result<number> = await config.getResult('PORT');
* if (port.isOk()) {
* console.log('port', port.ok());
* }
* @param {string} key - key of config map
* @param {EncodeOptions} [encodeOptions] - optional encode options
*/
async getResult(key, encodeOptions) {
try {
return Ok(await this.get(key, encodeOptions));
} catch (err) {
return Err(err);
}
}
/**
* get env value as string from config map as Result
* @returns {Promise<Result<string | undefined>>} Result Promise of string value or undefined
* @example
* const port: Result<string> = await config.getStringResult('PORT');
* if (port.isOk()) {
* console.log('port', port.ok());
* }
* @param {string} key - key of config map
* @param {EncodeOptions} [encodeOptions] - optional encode options
*/
async getStringResult(key, encodeOptions) {
try {
return Ok(await this.getString(key, encodeOptions));
} catch (err) {
return Err(err);
}
}
/**
* get all env value objects from config map
* @returns {Promise<TypeValueRecords<Data>>} Promise of all values
* @example
* const values: TypeValueRecords<Data> = await config.getAll();
* console.log(values.PORT.type, values.PORT.value); // 'env', 3000
*/
async getAllObjects() {
return (await this.getAllPromises()).reduce((result, [key, value]) => {
result[key] = value;
return result;
}, {});
}
/**
* get all env values from config map
* @returns {Promise<Data>} Promise of all values
* @example
* const values: Data = await config.getAllValues();
* console.log('PORT', values.PORT); // 3000 (number)
*/
async getAllValues() {
return (await this.getAllPromises()).reduce((result, [key, value]) => {
result[key] = value.value;
return result;
}, {});
}
/**
* get all env values from config map as string
* @returns {Promise<Record<keyof Data, string>>} Promise of all values as string
* @example
* const values: Record<keyof Data, string> = await config.getAllStringValues();
* console.log('PORT', values.PORT); // '3000' (string)
* @param {EncodeOptions} [encodeOptions] - optional encode options
*/
async getAllStringValues(encodeOptions) {
return (await this.getAllPromises(encodeOptions)).reduce((result, [key, value]) => {
result[key] = value.stringValue;
return result;
}, {});
}
/**
* Validate all env values from config map, expect to throw error if error exists
* @param {(data: Data) => void} callback callback function
*/
async validateAll(callback) {
callback(await this.getAllValues());
}
/**
* run lookup to all keys and return all promises
* @param {EncodeOptions} [encodeOptions] - optional encode options
* @returns {Promise<[keyof Data, LoaderTypeValueStrict<Data[keyof Data]>][]>} Promise of all values
*/
getAllPromises(encodeOptions) {
return Promise.all(Object.keys(this.schema).map(async (key) => {
return [key, await this.getObject(key, encodeOptions)];
}));
}
};
export {
ConfigLoader,
ConfigMap,
EnvConfigLoader,
FetchConfigLoader,
JsonConfigParser,
MapConfigLoader,
MemoryConfigLoader,
ReactEnvConfigLoader,
RecordConfigLoader,
SemicolonConfigParser,
SwitchLoader,
UrlParser,
VariableError,
VariableLookupError,
applyStringMap,
arrayParser,
bigIntParser,
booleanParser,
buildHiddenAsterisksValueString,
buildHiddenValue,
buildOptions,
buildPartialHiddenValueString,
buildStringMap,
buildStringObject,
clearDefaultValueSeenMap,
createRequestNotReady,
floatParser,
getConfigObject,
getConfigObjectResult,
getConfigVariable,
getConfigVariableResult,
getLogger,
handleSeen,
integerParser,
isRequestNotReadMessage,
isValidObject,
logStringifySemicolonConfig,
parseSemicolonConfig,
printValue,
resolveLogger,
setLogger,
stringParser,
stringifySemicolonConfig,
urlSanitize
};
//#endregion
export { ConfigLoader, ConfigMap, EnvConfigLoader, FetchConfigLoader, JsonConfigParser, MapConfigLoader, MemoryConfigLoader, ReactEnvConfigLoader, RecordConfigLoader, SemicolonConfigParser, SwitchLoader, UrlParser, VariableError, VariableLookupError, applyStringMap, arrayParser, bigIntParser, booleanParser, buildHiddenAsterisksValueString, buildHiddenValue, buildOptions, buildPartialHiddenValueString, buildStringMap, buildStringObject, clearDefaultValueSeenMap, createRequestNotReady, floatParser, getConfigObject, getConfigObjectResult, getConfigVariable, getConfigVariableResult, getLogger, handleSeen, integerParser, isRequestNotReadMessage, isValidObject, logStringifySemicolonConfig, parseSemicolonConfig, printValue, resolveLogger, setLogger, stringParser, stringifySemicolonConfig, urlSanitize };
//# sourceMappingURL=index.mjs.map
{
"name": "@avanio/variable-util",
"version": "1.2.1",
"description": "Utility to get variables from multiple resources",
"main": "./dist/index.js",
"module": "./dist/index.mjs",
"types": "./dist/index.d.ts",
"exports": {
".": {
"types": "./dist/index.d.ts",
"require": "./dist/index.js",
"import": "./dist/index.mjs"
}
},
"private": false,
"files": [
"dist"
],
"repository": {
"type": "git",
"url": "git+https://github.com/mharj/variable-util.git"
},
"keywords": [
"env",
"variables"
],
"author": "mharj",
"license": "MIT",
"bugs": {
"url": "https://github.com/mharj/variable-util/issues"
},
"homepage": "https://github.com/mharj/variable-util#readme",
"devDependencies": {
"@avanio/logger-like": "^0.2.12",
"@cspell/eslint-plugin": "^9.2.1",
"@eslint/js": "^9.35.0",
"@luolapeikko/result-option": "^2.0.1",
"@luolapeikko/ts-common": "^1.1.1",
"@stylistic/eslint-plugin": "^5.3.1",
"@types/etag": "^1.8.4",
"@types/node": "^22.18.3",
"@types/sinon": "^17.0.4",
"@typescript-eslint/eslint-plugin": "^8.43.0",
"@typescript-eslint/parser": "^8.43.0",
"@vitest/coverage-v8": "^3.2.4",
"c8": "^10.1.3",
"dotenv": "^17.2.2",
"eslint": "^9.35.0",
"eslint-config-prettier": "^10.1.8",
"eslint-import-resolver-typescript": "^4.4.4",
"eslint-plugin-import": "^2.32.0",
"eslint-plugin-jsdoc": "^56.1.2",
"eslint-plugin-prettier": "^5.5.4",
"eslint-plugin-sonarjs": "^3.0.5",
"etag": "^1.8.1",
"prettier": "^3.6.2",
"sinon": "^21.0.0",
"tsup": "^8.5.0",
"typedoc": "^0.28.12",
"typescript": "^5.9.2",
"typescript-eslint": "^8.43.0",
"vite": "^7.1.5",
"vitest": "^3.2.4",
"zod": "^3.25.76"
},
"peerDependencies": {
"@avanio/logger-like": ">= 0.1.0",
"@luolapeikko/result-option": ">= 2.0.0",
"@luolapeikko/ts-common": ">= 1.0.0"
},
"scripts": {
"doc": "typedoc",
"build": "tsup src/index.ts --sourcemap --format cjs,esm --dts --clean",
"test": "vitest test --run --no-isolate --coverage",
"coverage": "vitest test --run --no-isolate --reporter=dot --coverage --coverage.reporter=lcov",
"lint": "eslint . --ext .ts",
"validate": "tsc --noEmit --project tsconfig.test.json"
}
}
"name": "@avanio/variable-util",
"version": "1.2.2",
"description": "Utility to get variables from multiple resources",
"main": "./dist/index.cjs",
"module": "./dist/index.mjs",
"types": "./dist/index.d.mts",
"exports": {
"types": {
"import": "./dist/index.d.mts",
"require": "./dist/index.d.cts"
},
"import": "./dist/index.mjs",
"require": "./dist/index.cjs",
"default": "./dist/index.mjs"
},
"private": false,
"scripts": {
"doc": "typedoc",
"build": "tsdown src/index.ts --sourcemap --format cjs,esm --dts --clean",
"prepublishOnly": "npm run build",
"test": "vitest test --run --no-isolate --coverage",
"coverage": "vitest test --run --no-isolate --reporter=dot --coverage --coverage.reporter=lcov",
"lint": "eslint . --ext .ts",
"validate": "tsc --noEmit --project tsconfig.test.json"
},
"files": [
"dist"
],
"repository": {
"type": "git",
"url": "github:mharj/variable-util"
},
"keywords": [
"env",
"variables"
],
"author": "mharj",
"license": "MIT",
"bugs": {
"url": "https://github.com/mharj/variable-util/issues"
},
"homepage": "https://github.com/mharj/variable-util#readme",
"devDependencies": {
"@avanio/logger-like": "^0.2.12",
"@cspell/eslint-plugin": "^9.2.1",
"@eslint/js": "^9.35.0",
"@luolapeikko/result-option": "^2.0.1",
"@luolapeikko/ts-common": "^1.1.1",
"@stylistic/eslint-plugin": "^5.3.1",
"@types/etag": "^1.8.4",
"@types/node": "^22.18.3",
"@types/sinon": "^17.0.4",
"@typescript-eslint/eslint-plugin": "^8.43.0",
"@typescript-eslint/parser": "^8.43.0",
"@vitest/coverage-v8": "^3.2.4",
"c8": "^10.1.3",
"dotenv": "^17.2.2",
"eslint": "^9.35.0",
"eslint-config-prettier": "^10.1.8",
"eslint-import-resolver-typescript": "^4.4.4",
"eslint-plugin-import": "^2.32.0",
"eslint-plugin-jsdoc": "^56.1.2",
"eslint-plugin-prettier": "^5.5.4",
"eslint-plugin-sonarjs": "^3.0.5",
"etag": "^1.8.1",
"prettier": "^3.6.2",
"sinon": "^21.0.0",
"tsdown": "^0.20.3",
"typedoc": "^0.28.12",
"typescript": "^5.9.2",
"typescript-eslint": "^8.43.0",
"vite": "^7.1.5",
"vitest": "^3.2.4",
"zod": "^3.25.76"
},
"peerDependencies": {
"@avanio/logger-like": ">= 0.1.0",
"@luolapeikko/result-option": ">= 1.0.0",
"@luolapeikko/ts-common": ">= 1.0.0"
},
"packageManager": "pnpm@10.15.1+sha512.34e538c329b5553014ca8e8f4535997f96180a1d0f614339357449935350d924e22f8614682191264ec33d1462ac21561aff97f6bb18065351c162c7e8f6de67"
}
import { Loadable } from '@luolapeikko/ts-common';
import { ILoggerLike, ISetOptionalLogger } from '@avanio/logger-like';
import { IResult } from '@luolapeikko/result-option';
import { EventEmitter } from 'events';
/**
* Represents the result of loading a configuration value.
* @property value - The value associated with the configuration key, or `undefined` if not found.
* @property path - The source path from which the value was loaded.
* @property seen - Indicates whether the key and value have already been encountered (used for logging purposes).
* @since v0.8.0
* @category Loaders
*/
type LoaderValueResult = {
/** this is shown on logs `ConfigVariables[type]: KEY [___VALUE___] from {path}` if showValue is true */
value: string | undefined;
/** this is shown on logs `ConfigVariables[type]: KEY [VALUE] from {___path___}` */
path: string;
/** is key and value already seen (for logging) */
seen: boolean;
};
/**
* Interface for config loaders
* @since v1.0.0
* @category Loaders
*/
interface IConfigLoader {
/** this is shown on logs `ConfigVariables[___type___]: KEY [VALUE] from {path}` */
readonly loaderType: Lowercase<string>;
/**
* get loader result for lookupKey
* @param {string} lookupKey - key to lookup
* @returns {undefined | LoaderValueResult | Promise<undefined | LoaderValueResult>} - Promise of LoaderValueResult or undefined
*/
getLoaderResult(lookupKey: string): undefined | LoaderValueResult | Promise<undefined | LoaderValueResult>;
/**
* Check if loader is disabled
* @returns {boolean | undefined | Promise<boolean | undefined>} - Promise of boolean or undefined
*/
isLoaderDisabled(): boolean | undefined | Promise<boolean | undefined>;
}
/**
* Helper type to write override keys to config loaders
* @since v1.0.0
* @category Loaders
* @example
* type OverrideKeyMap = InferOverrideKeyMap<MainEnv & TestEnv>;
* // Example usage of OverrideKeyMap, where the keys are the original config keys and the values are the override keys
* const env = new EnvConfigLoader<OverrideKeyMap>(undefined, {PORT: 'HTTP_PORT'}); // get PORT value from process.env.HTTP_PORT
*/
type OverrideKeyMap = Record<string, string>;
/**
* Helper infer type to write override keys to config loaders
* @template T - The type of the config object.
* @since v1.0.0
* @category Loaders
* @example
* type OverrideKeyMap = InferOverrideKeyMap<MainEnv & TestEnv>;
* // Example usage of OverrideKeyMap, where the keys are the original config keys and the values are the override keys
* const env = new EnvConfigLoader<OverrideKeyMap>(undefined, {PORT: 'HTTP_PORT'}); // get PORT value from process.env.HTTP_PORT
*/
type InferOverrideKeyMap<T> = Record<keyof T, string>;
/**
* Parser method props
* @since v0.11.0
*/
type ParserProps = {
loader: IConfigLoader;
key: string;
value: string;
};
/**
* PreValidate method props
* @since v0.11.0
*/
type PreValidateProps = {
loader: IConfigLoader;
key: string;
value: unknown;
};
/**
* PostValidate method props
* @template T - Type of value
* @since v0.11.0
*/
type PostValidateProps<T> = {
loader: IConfigLoader;
key: string;
value: T;
};
/**
* PreValidate function
* @template Output - Type of output value
* @since v1.0.0
*/
type TypeGuardValidate<Output> = ((value: unknown) => value is Output) | Promise<(value: unknown) => value is Output>;
/**
* String encoder options for parsers
* @since v0.11.0
*/
type EncodeOptions = {
/**
* use URI encoding for string outputs (used by semicolon parser)
*/
uriEncode?: boolean;
/**
* not logging the value
*/
silent?: boolean;
};
/**
* Interface for config parsers
* @template Input - Type of raw input value
* @template Output - Type of output value
* @since v1.0.0
*/
interface IConfigParser<Input, Output> {
/**
* name of the parser
*/
name: string;
/**
* Config parser function
* @throws Error if parsing fails
*/
parse(parserProps: ParserProps): Input | Promise<Input>;
/**
* Optional raw string value validation before parsing.
* @throws Error if validation fails
*/
preValidate?(preValidateProps: PreValidateProps): void | Promise<void>;
/**
* Optional value validation after parsing
* @throws Error if validation fails
*/
postValidate?(postValidateProps: PostValidateProps<Input>): Output | undefined | Promise<Output | undefined>;
/**
* Build readable string from value
*/
toString(config: Output, options?: EncodeOptions): string;
/**
* Optional build readable string from value for log (can hide sensitive part from logs) else toString is used
* @param config - value to log
*/
toLogString?(config: Output): string;
}
/**
* Interface for validation function
* @template Input - Type of raw input
* @template Output - Type of output
* @since v1.0.0
*/
type ValidateCallback<Input, Output> = (data: Input) => Output | Promise<Output>;
/**
* interface for a request cache
* @category Utils
* @example
* const exampleCache: IRequestCache = {
* isOnline() {
* return (typeof window !== 'undefined' && window.navigator && window.navigator.onLine) || true;
* },
* async fetchRequest(req: Request) {
* if (typeof window !== 'undefined' && window.caches) {
* const cache = await window.caches.open('fetch');
* return cache.match(req);
* }
* return undefined;
* },
* async storeRequest(req: Request, res: Response) {
* if (typeof window !== 'undefined' && window.caches && res.ok) {
* const cache = await window.caches.open('fetch');
* req.headers.delete('Authorization');
* await cache.put(req, res.clone());
* }
* },
* };
* @since v0.2.8
*/
interface IRequestCache {
/**
* check if the client is connected to the internet
*/
isOnline(): boolean;
/**
* get the cached response for a request
*/
fetchRequest(req: Request): Promise<Response | undefined>;
/**
* store the response for a request
*/
storeRequest(req: Request, res: Response): Promise<void>;
}
/**
* RequestNotReady indicates that the request is not ready to be loaded yet. (e.g. the user is not logged in)
* @category Utils
* @since v0.2.8
*/
interface RequestNotReady {
_type: 'RequestNotReady';
message: string;
}
/**
* Type guard for RequestNotReady
* @param {unknown} obj - object to check if it is a RequestNotReady payload
* @returns {boolean} boolean indicating if the object is a RequestNotReady payload
* @category Utils
* @since v0.2.8
*/
declare function isRequestNotReadMessage(obj: unknown): obj is RequestNotReady;
/**
* function to create a RequestNotReady payload to indicate that the request is not ready to be loaded yet. (e.g. the user is not logged in)
* @param {string} message - reason why the request is not ready yet
* @returns {RequestNotReady} RequestNotReady payload
* @category Utils
* @since v0.2.8
*/
declare function createRequestNotReady(message: string): RequestNotReady;
type PartialHiddenValueStringType =
/** show only prefix of secret "j43****" */
'prefix'
/** show only suffix of secret "****7hd" */
| 'suffix'
/** show both prefix and suffix of secret "j43****7hd" */
| 'prefix-suffix';
type ShowValueType = boolean | PartialHiddenValueStringType;
/**
* Format parameters for the variables
* @since v0.2.5
*/
interface FormatParameters {
/**
* Whether to show the value in the output, defaults to undefined
* - undefined: hide the value
* - true: show the actual value
* - false: show the value with asterisks
* - 'prefix': show only prefix of value (1-3 characters based on length of the value)
* - 'suffix': show only suffix of value (1-3 characters based on length of the value)
* - 'both': show both prefix and suffix of value (1-2 characters on suffix and prefix based on length of the value)
* @default false
*/
showValue?: ShowValueType;
}
/**
* Sanitizes a URL by replacing the username and password with asterisks.
* @param {string} value - The URL to sanitize.
* @param {ILoggerLike} [logger] - An optional logger to use for logging warnings.
* @returns {string} The sanitized URL.
* @category Utils
* @since v0.2.5
*/
declare function urlSanitize(value: string, logger?: ILoggerLike): string;
/**
* Returns a formatted string representation of a value, enclosed in square brackets.
* @param {string | undefined} value - The value to format.
* @param {FormatParameters | undefined} config - An optional configuration object.
* @returns {string} The formatted string representation of the value.
* @category Utils
* @since v0.2.5
*/
declare function printValue(value: string | undefined, config: FormatParameters | undefined): string;
/**
* Constructs a hidden value string based on the specified visibility option.
* @param {string} value - The original string value to be hidden.
* @param {ShowValueType | undefined} show - Determines how the value should be displayed:
* - `true`: shows the full value
* - `undefined` or `false`: hides the value completely with asterisks
* - `PartialHiddenValueStringType`: shows part of the value (prefix, suffix, or both)
* @returns {string} The resulting string with the specified visibility.
* @since v0.2.5
*/
declare function buildHiddenValue(value: string, show: ShowValueType | undefined): string;
/**
* Builds a hidden value string, replacing each character with an asterisk.
* @param {string} value - The value to be hidden.
* @returns {string} The hidden value string with asterisks.
* @since v0.2.5
*/
declare function buildHiddenAsterisksValueString(value: string): string;
/**
* Show only 1-3 characters of the secret value based on length of the value.
* @param {string} value - The value to partially hide.
* @param {PartialHiddenValueStringType} type - The type of partial hiding to apply ('prefix', 'suffix', or 'prefix-suffix').
* @returns {string} The partially hidden value string.
* @since v0.2.5
*/
declare function buildPartialHiddenValueString(value: string, type: PartialHiddenValueStringType): string;
/**
* Optional environment entry
* @template Value - type of value
* @since v1.1.0
*/
type OptionalEnvEntry<Value> = {
/**
* The parser to use to parse the value
*/
parser: IConfigParser<unknown, Value>;
/**
* The default value to use if the variable is not defined
*/
defaultValue?: Loadable<Value>;
/**
* The format parameters to use to format the value
*/
params?: FormatParameters;
/**
* Whether to throw an error if the variable is undefined
*/
undefinedThrowsError?: boolean;
/**
* Replaces the default throw error message with this message
*/
undefinedErrorMessage?: string;
};
/**
* Required environment entry
* @template Value - type of value
* @since v1.1.0
*/
type RequiredEnvEntry<Value> = {
/**
* The parser to use to parse the value
*/
parser: IConfigParser<unknown, Value>;
/**
* The default value to use if the variable is not defined
*/
defaultValue: Loadable<Value>;
/**
* The format parameters to use to format the value
*/
params?: FormatParameters;
/**
* Whether to throw an error if the variable is undefined
*/
undefinedThrowsError?: boolean;
/**
* Replaces the default throw error message with this message
*/
undefinedErrorMessage?: string;
};
/**
* Required environment entry with undefinedThrowsError
* @template Value - type of value
* @since v1.1.0
*/
type RequiredUndefinedThrowEntry<Value> = {
/**
* The parser to use to parse the value
*/
parser: IConfigParser<unknown, Value>;
/**
* The default value to use if the variable is not defined
*/
defaultValue?: Loadable<Value>;
/**
* The format parameters to use to format the value
*/
params?: FormatParameters;
/**
* Whether to throw an error if the variable is undefined
*/
undefinedThrowsError: true;
/**
* Replaces the default throw error message with this message
*/
undefinedErrorMessage?: string;
};
/**
* Environment map schema
* @template Output - type of output value
* @since v0.2.15
*/
type EnvMapSchema<Output extends Record<string, unknown>> = {
[K in keyof Required<Output>]: undefined extends Output[K] ? OptionalEnvEntry<Output[K]> : RequiredEnvEntry<Output[K]> | RequiredUndefinedThrowEntry<Output[K]>;
};
/**
* Loader type value
* @template T - type of value
* @since 0.2.18
*/
type LoaderTypeValue<T> = {
/** type of loader output (default, env,...) */
type: string | undefined;
/** output value */
value: T | undefined;
/** string value of output */
stringValue: string | undefined;
/** variable namespace */
namespace: string | undefined;
};
/**
* Loader type strict value
* @template T - type of value
* @since 0.2.18
*/
type LoaderTypeValueStrict<T> = {
/** type of loader output (default, env,...) */
type: string | undefined;
/** output value */
value: T;
/** string value of output */
stringValue: string;
/** variable namespace */
namespace: string | undefined;
};
type ConfigOptions = {
/** undefined = global logger, null = no logger else it's ILoggerLike */
logger?: ILoggerLike | null;
/** optional namespace added to logs */
namespace?: string;
};
type SolvedConfigOptions = {
/** optional logger instance */
logger: ILoggerLike | undefined;
/** optional namespace added to logs */
namespace: string | undefined;
};
/**
* Build options from partial options
* @param {Partial<ConfigOptions>} options - partial config options
* @returns {SolvedConfigOptions} - solved config options
* @category Config
* @since v0.6.0
*/
declare function buildOptions({ logger, namespace }?: Partial<ConfigOptions>): SolvedConfigOptions;
/**
* get config variable from loaders
* @example
* // from "@avanio/variable-util-node"
* const fileEnv = new FileConfigLoader({fileName: './settings.json', type: 'json'}).getLoader;
*
* const port: Promise<string> = await getConfigVariable('PORT', [env(), fileEnv()], stringParser, '8080', {showValue: true});
* // with override key
* const port: Promise<string> = await getConfigVariable('PORT', [env('HTTP_PORT', fileEnv())], stringParser, '8080', {showValue: true});
* @template Output - Type of output
* @param {string} rootKey - root key of config variable
* @param {IConfigLoader[]} loaders - loaders to use
* @param {IConfigParser<unknown, Output>} parser - parser to use
* @param {Loadable<Output>} [defaultValueLoadable] - default value to use
* @param {FormatParameters} [params] - optional format parameters
* @param {ConfigOptions} [options] - optional config options
* @param {EncodeOptions} [encodeOptions] - optional encode options
* @returns {Promise<Output>} - config variable
* @since v0.2.5
*/
declare function getConfigVariable<Output>(rootKey: string, loaders: IConfigLoader[], parser: IConfigParser<unknown, Output>, defaultValueLoadable: Loadable<Output>, params?: FormatParameters, options?: ConfigOptions, encodeOptions?: EncodeOptions): Promise<Output>;
declare function getConfigVariable<Output>(rootKey: string, loaders: IConfigLoader[], parser: IConfigParser<unknown, Output>, defaultValueLoadable?: Loadable<Output>, params?: FormatParameters, options?: ConfigOptions, encodeOptions?: EncodeOptions): Promise<Output | undefined>;
/**
* get config variable from loaders
* @example
* // from "@avanio/variable-util-node"
* const portResult: Result<string> = await getConfigVariableResult('PORT', [env(), fileEnv()], stringParser, '8080', {showValue: true});
*
* const value: string = portResult.unwrap(); // get value or throw error
* const value: string | undefined = portResult.ok(); // get value or undefined
* @template Output - Type of output
* @param {string} rootKey - root key of config variable
* @param {IConfigLoader[]} loaders - loaders to use
* @param {IConfigParser<unknown, Output>} parser - parser to use
* @param {Loadable<Output>} [defaultValueLoadable] - default value to use
* @param {FormatParameters} [params] - optional format parameters
* @param {ConfigOptions} [options] - optional config options
* @returns {Promise<IResult<Output | undefined>>} - result with value or error
* @since v1.0.0
*/
declare function getConfigVariableResult<Output>(rootKey: string, loaders: IConfigLoader[], parser: IConfigParser<unknown, Output>, defaultValueLoadable: Loadable<Output>, params?: FormatParameters, options?: ConfigOptions): Promise<IResult<Output>>;
declare function getConfigVariableResult<Output>(rootKey: string, loaders: IConfigLoader[], parser: IConfigParser<unknown, Output>, defaultValueLoadable?: Loadable<Output>, params?: FormatParameters, options?: ConfigOptions): Promise<IResult<Output | undefined>>;
/**
* Clear the seen map for default values (for unit testing purposes)
* @since v0.2.5
*/
declare function clearDefaultValueSeenMap(): void;
/**
* get config object which contains value and type of loader
* @example
* // from "@avanio/variable-util-node"
* const fileEnv = new FileConfigLoader({fileName: './settings.json', type: 'json'}).getLoader;
* const portConfig: {type: string | undefined; value: string} = await getConfigObject('PORT', [env(), fileEnv()], stringParser, '8080', {showValue: true});
* const value: string = portConfig.value;
* const type: string | undefined = portConfig.type; // loader type name
* @template Output - Type of output
* @param {string} rootKey root key of config object
* @param {IConfigLoader[]} loaders array of loaders
* @param {IConfigParser<unknown, Output>} parser parser for value
* @param {Loadable<Output>} defaultValueLoadable optional default value
* @param {FormatParameters} params optional format parameters
* @param {ConfigOptions} options optional config options
* @param {EncodeOptions} [encodeOptions] optional encode options
* @returns {Promise<LoaderTypeValue<Output>>} config object
* @since v0.2.5
*/
declare function getConfigObject<Output>(rootKey: string, loaders: IConfigLoader[], parser: IConfigParser<unknown, Output>, defaultValueLoadable: Loadable<Output>, params?: FormatParameters, options?: ConfigOptions, encodeOptions?: EncodeOptions): Promise<LoaderTypeValueStrict<Output>>;
declare function getConfigObject<Output>(rootKey: string, loaders: IConfigLoader[], parser: IConfigParser<unknown, Output>, defaultValueLoadable?: Loadable<Output>, params?: FormatParameters, options?: ConfigOptions, encodeOptions?: EncodeOptions): Promise<LoaderTypeValue<Output>>;
/**
* Wrapper around getConfigObject that returns a Result
* @example
* // from "@avanio/variable-util-node"
* const fileEnv = new FileConfigLoader({fileName: './settings.json', type: 'json'}).getLoader;
* const portConfig: Result<{type: string | undefined; value: string}> = await getConfigObject('PORT', [env(), fileEnv()], stringParser, '8080', {showValue: true});
* if ( portConfig.isOk() ) {
* const {value, type} = portConfig.unwrap();
* } else {
* // handle error
* }
* @template Output - Type of output
* @param {string} rootKey root key of config object
* @param {IConfigLoader[]} loaders array of loaders
* @param {IConfigParser<unknown, Output>} parser parser for value
* @param {Loadable<Output>} defaultValueLoadable optional default value
* @param {FormatParameters} params optional format parameters
* @param {ConfigOptions} options optional config options
* @returns {Promise<IResult<{type: string | undefined; value: Output}>>} result
* @since v0.2.5
*/
declare function getConfigObjectResult<Output>(rootKey: string, loaders: IConfigLoader[], parser: IConfigParser<unknown, Output>, defaultValueLoadable: Loadable<Output>, params?: FormatParameters, options?: ConfigOptions): Promise<IResult<{
type: string | undefined;
value: Output;
}>>;
declare function getConfigObjectResult<Output>(rootKey: string, loaders: IConfigLoader[], parser: IConfigParser<unknown, Output>, defaultValueLoadable?: Loadable<Output>, params?: FormatParameters, options?: ConfigOptions): Promise<IResult<{
type: string | undefined;
value: Output | undefined;
}>>;
/**
* ConfigLoaderEventMap is the event map for the ConfigLoader
* @category Loaders
* @since v0.11.1
*/
type ConfigLoaderEventMap = {
/** notify when loader data is updated */
updated: [];
};
/**
* IConfigLoaderProps is the interface for ConfigLoader props
* @category Loaders
* @since v0.8.0
*/
interface IConfigLoaderProps {
disabled?: Loadable<boolean>;
}
type LoaderValue = {
value: string | undefined;
path: string;
};
/**
* Abstract base class for config loaders
* @template Props - the type of the props
* @template OverrideMap - the type of the override key map
* @category Loaders
* @abstract
* @since v1.0.0
*/
declare abstract class ConfigLoader<Props extends IConfigLoaderProps, OverrideMap extends OverrideKeyMap = OverrideKeyMap> extends EventEmitter<ConfigLoaderEventMap> implements IConfigLoader {
abstract loaderType: Lowercase<string>;
protected abstract defaultOptions: Props;
protected options: Loadable<Partial<Props>>;
protected overrideKeys: Partial<OverrideMap>;
protected valueSeen: Map<string, string>;
constructor(props?: Loadable<Partial<Props>>, overrideKeys?: Partial<OverrideMap>);
getLoaderResult(lookupKey: string): Promise<{
value: string | undefined;
path: string;
seen: boolean;
} | undefined>;
isLoaderDisabled(): Promise<boolean | undefined>;
setDisabled(disabled: Loadable<boolean>): Promise<void>;
/**
* Get options from loader and merge with default options
* @returns {Promise<DefaultProps>} - Promise of DefaultProps & Props
*/
protected getOptions(): Promise<Props>;
protected setOption<Key extends keyof Props>(key: Key, value: Props[Key]): Promise<void>;
/**
* Build error string `ConfigVariables[<type>]: <message>`
* @param {string} message - error message
* @returns {string} - error string
*/
protected buildErrorStr(message: string): string;
/**
* implementation of config loader function
* @param lookupKey - key to lookup in config
* @param params - optional passing params for handleLoader (i.e. lookup key override, settings etc.)
* @returns {LoaderValue | Promise<LoaderValue>} - Promise of LoaderValue
*/
protected abstract handleLoaderValue(lookupKey: string): undefined | LoaderValue | Promise<undefined | LoaderValue>;
}
/**
* RecordConfigLoader is a class that extends ConfigLoader and adds the ability to reload the data.
* @template Props - the type of the options for the loader
* @template OverrideMap - the type of the override key map
* @since v1.0.0
*/
declare abstract class RecordConfigLoader<Props extends IConfigLoaderProps, OverrideMap extends OverrideKeyMap = OverrideKeyMap> extends ConfigLoader<Props, OverrideMap> {
protected abstract defaultOptions: Props;
protected _isLoaded: boolean;
protected dataPromise: Promise<Record<string, string>> | undefined;
/**
* reloads the data
*/
reload(): Promise<void>;
/**
* is the data loaded
* @returns {boolean} - true if the data is loaded, false otherwise
*/
isLoaded(): boolean;
/**
* load data from the loader. If the data is loaded successfully
* an "updated" event will be emitted.
* @returns {Promise<void>}
*/
protected loadData(): Promise<void>;
protected abstract handleData(): Promise<Record<string, string>>;
}
/**
* env loader class is used to load env variables from process.env
* @template OverrideMap - the type of the override key map
* @param {string} [overrideKey] - optional override key for lookup
* @returns {IConfigLoader} - IConfigLoader object
* @category Loaders
* @since v1.0.0
*/
declare class EnvConfigLoader<OverrideMap extends OverrideKeyMap = OverrideKeyMap> extends ConfigLoader<IConfigLoaderProps, OverrideMap> {
readonly loaderType = "env";
defaultOptions: IConfigLoaderProps;
protected handleLoaderValue(lookupKey: string): {
value: string | undefined;
path: string;
};
}
/**
* React env loader class is used to load env variables from process.env.REACT_APP_*
* @template OverrideMap - the type of the override key map
* @param {Partial<OverrideMap>} [override] - optional override key for lookup
* @returns {IConfigLoader} - IConfigLoader object
* @category Loaders
* @since v1.0.0
*/
declare class ReactEnvConfigLoader<OverrideMap extends OverrideKeyMap = OverrideKeyMap> extends ConfigLoader<IConfigLoaderProps, OverrideMap> {
readonly loaderType = "react-env";
defaultOptions: IConfigLoaderProps;
protected handleLoaderValue(lookupKey: string): {
value: string | undefined;
path: string;
};
}
/**
* MapConfigLoader is a class that extends ConfigLoader and adds the ability to reload the data.
* @template Props - the type of the options for the loader
* @template OverrideMap - the type of the override key map
* @since v1.0.0
* @category Loaders
*/
declare abstract class MapConfigLoader<Props extends IConfigLoaderProps, OverrideMap extends OverrideKeyMap = OverrideKeyMap> extends ConfigLoader<Props, OverrideMap> {
protected abstract defaultOptions: Props;
protected _isLoaded: boolean;
protected data: Map<string, string>;
/**
* clear maps and reloads the data
*/
reload(): Promise<void>;
/**
* is the data loaded
* @returns {boolean} Whether the data is loaded
*/
isLoaded(): boolean;
/**
* load data from the loader. If the data is loaded successfully
* an "updated" event will be emitted.
* @returns {Promise<void>}
*/
protected loadData(): Promise<void>;
protected abstract handleLoadData(): Promise<boolean>;
}
/**
* Options for the FetchConfigLoader
* @since v1.0.0
*/
interface FetchConfigLoaderOptions extends IConfigLoaderProps {
fetchClient: typeof fetch;
/** this prevents Error to be thrown if have http error */
isSilent: boolean;
payload: 'json';
validate: ValidateCallback<Record<string, string | undefined>, Record<string, string | undefined>> | undefined;
logger: ILoggerLike | undefined;
cache: IRequestCache | undefined;
/** if we get a cache hit code (defaults 304), we use the cached response instead */
cacheHitHttpCode: number;
}
type ConfigRequest = Request | RequestNotReady;
/**
* FetchConfigLoader is used to load config from a fetch request
* @template OverrideMap - the type of the override key map
* @category Loaders
* @since v1.0.0
*/
declare class FetchConfigLoader<OverrideMap extends OverrideKeyMap = OverrideKeyMap> extends MapConfigLoader<FetchConfigLoaderOptions, OverrideMap> {
readonly loaderType: Lowercase<string>;
private request;
private path;
protected defaultOptions: FetchConfigLoaderOptions;
/**
* Constructor for FetchConfigLoader
* @param {Loadable<ConfigRequest>} request - callback that returns a fetch request or a message object that the request is not ready
* @param {Loadable<Partial<FetchConfigLoaderOptions>>} options - optional options for FetchConfigLoader
* @param {Partial<OverrideMap>} overrideKeys - optional override keys for FetchConfigLoader
* @param {Lowercase<string>} type - optional name type for FetchConfigLoader (default: 'fetch')
*/
constructor(request: Loadable<ConfigRequest>, options?: Loadable<Partial<FetchConfigLoaderOptions>>, overrideKeys?: Partial<OverrideMap>, type?: Lowercase<string>);
protected handleLoaderValue(lookupKey: string): Promise<{
value: string | undefined;
path: string;
}>;
protected handleLoadData(): Promise<boolean>;
/**
* if client is offline, we will try return the cached response else add cache validation (ETag) and try get the response from the fetch request
* @param {Request} req - request to fetch
* @returns {Promise<Response | undefined>} - response or undefined
*/
private fetchRequestOrCacheResponse;
/**
* on error, check if we have a valid cached response
* @param {Request} req - request to fetch
* @param {Response} res - response from fetch
* @returns {Promise<Response>} - response
*/
private checkIfValidCacheResponse;
/**
* if we get a 304, get the cached response
* @param {Request} req - request to fetch
* @param {Response} res - response from fetch
* @returns {Promise<Response>} - response
*/
private handleNotModifiedCache;
private handleJson;
private getRequest;
}
interface IMemoryConfigLoaderProps extends IConfigLoaderProps {
logger: ILoggerLike | undefined;
}
/**
* Config loader with in-memory data which can be set and retrieved variables on the fly.
* - Useful for temporary controlled overrides or testing
* @template MemoryMap - the type of the memory map
* @template OverrideMap - the type of the override key map
* @since v1.0.0
* @category Loaders
*/
declare class MemoryConfigLoader<MemoryMap extends Record<string, string | undefined>, OverrideMap extends OverrideKeyMap = OverrideKeyMap> extends ConfigLoader<IMemoryConfigLoaderProps, OverrideMap> {
readonly loaderType: Lowercase<string>;
private data;
protected defaultOptions: IMemoryConfigLoaderProps;
constructor(initialData: MemoryMap, options?: Loadable<Partial<IMemoryConfigLoaderProps>>, overrideKeys?: Partial<OverrideMap>, type?: Lowercase<string>);
set(key: keyof MemoryMap, value: string | undefined): Promise<void>;
get(key: keyof MemoryMap): Promise<string | undefined>;
protected handleLoaderValue(lookupKey: string): {
value: string | undefined;
path: string;
};
}
interface ISwitchLoaderProps extends IConfigLoaderProps {
logger?: ILoggerLike;
}
/**
* ConfigMap, this is a helper type to define the configuration map for the switch loader.
* @template Key - The key to switch between
* @template Map - The configuration map
* @since v0.10.1
* @example
* type TestConfigMapEnv = { DEMO: string; ANOTHER: string; };
* const switchConfigMap: SwitchConfigMap<TestConfigMapEnv, 'switch1' | 'switch2'> = {
* switch1: { DEMO: 'value' },
* switch2: { DEMO: 'value2' },
* };
*/
type SwitchConfigMap<Map extends Record<string, unknown>, Key extends string> = Record<Key, Partial<Record<keyof Map, string>>>;
/**
* SwitchLoader, this loader will switch between different configurations based on the active key.
* @example
* type TestConfigMapEnv = { DEMO: string; ANOTHER: string; };
* const switchLoader = new SwitchLoader<TestEnv, 'switch1' | 'switch2'>({
* switch1: { DEMO: 'value' },
* switch2: { DEMO: 'value2' },
* });
* const switcher = switchLoader.getLoader; // use this on the config map loaders: [switcher(), dotenv(), env(), ...]
* await switchLoader.activateSwitch('switch1'); // when you want enable switch1 values
* @template Config - The configuration map
* @template Key - The key to switch between
* @since v1.0.0
* @category Loaders
*/
declare class SwitchLoader<Config extends Record<string, unknown>, Key extends string> extends ConfigLoader<ISwitchLoaderProps> {
readonly loaderType: Lowercase<string>;
private readonly config;
private readonly keys;
protected defaultOptions: ISwitchLoaderProps;
constructor(configs: SwitchConfigMap<Config, Key>, props?: Loadable<Partial<ISwitchLoaderProps>>, type?: Lowercase<string>);
activateSwitch(key: Key): Promise<void>;
deactivateSwitch(key: Key): Promise<void>;
getCurrentKeys(): Readonly<Set<Key>>;
protected handleLoaderValue(lookupKey: string): LoaderValue | undefined;
protected getConfigKeys(key: Key): string[];
}
type JsonConfigParserOptions<Out extends Record<string, unknown>> = {
validate?: ValidateCallback<object, Out>;
/** keys to hide or partially hide */
protectedKeys?: Iterable<keyof Out>;
/** if and how to show protected keys */
showProtectedKeys?: ShowValueType;
};
/**
* Config parser to parse JSON string as object
* @example
* const objectSchema = z.object({
* foo: z.string(),
* baz: z.string(),
* secret: z.string(),
* });
* // parses '{"foo": "bar", "baz": "qux", "secret": "secret"}' string to {foo: "bar", baz: "qux", secret: "secret"}
* const fooBarJsonParser = new JsonConfigParser({
* validate: (value) => objectSchema.parse(value),
* protectedKeys: ['secret'],
* showProtectedKeys: 'prefix-suffix', // shows secret value with few characters from start and end on logging
* });
* @template Out - the type of the output object
* @implements {IConfigParser<Out, object>}
* @category Parsers
* @since v1.0.0
*/
declare class JsonConfigParser<Out extends Record<string, unknown>> implements IConfigParser<object, Out> {
name: string;
private validate;
private showProtectedKeys?;
private protectedKeys;
constructor({ protectedKeys, validate, showProtectedKeys }?: JsonConfigParserOptions<Out>);
parse({ value }: ParserProps): object;
postValidate({ value }: PostValidateProps<Record<string, unknown>>): Promise<Out | undefined>;
toString(config: Out): string;
toLogString(config: Out): string;
/**
* Build a string record from the given data
* @param {unknown} data - to be validated as object
* @returns {object} A record with string values
*/
private buildBaseRecord;
}
/**
* Properties for the UrlParser
* @since v0.2.5
*/
interface UrlParserProps {
urlSanitize?: boolean;
}
/**
* UrlParser class is used to parse and validate env variables of type URL.
* @class UrlParser
* @implements {IConfigParser<URL, URL>}
* @category Parsers
* @since v0.2.5
*/
declare class UrlParser implements IConfigParser<URL, URL> {
name: string;
/**
* Should the username and password be sanitized
*/
private urlSanitize;
/**
* Create a new UrlParser
* @param {UrlParserProps} properties - Properties for the UrlParser
*/
constructor({ urlSanitize }?: UrlParserProps);
parse({ value }: ParserProps): URL;
toString(value: URL): string;
toLogString(value: URL): string;
/**
* Build a URL object from a string and sanitize the username and password
* @param {string} value string to parse
* @returns {URL} URL object with sanitized username and password
*/
private handleUrlSanitize;
}
/**
* Build parser and have optional post validation (as example for literal values)
* @template Output - Type of output, defaults to string
* @param {TypeGuardValidate<Output>} [validate] - optional post validation
* @returns {IConfigParser<string, Output>} - parser
* @category Parsers
* @since v1.0.0
*/
declare function stringParser<Output extends string = string>(validate?: TypeGuardValidate<Output>): IConfigParser<string, Output>;
/**
* Parse a semicolon separated string into a config object
* @param {string} config Semicolon separated string
* @param {boolean} [keepCase] Keep the case of the keys, default true
* @returns {Record<string, string>} Config object
* @category Utils
* @since v1.0.0
* @example
* parseSemicolonConfig('a=b;c=d') // {a: 'b', c: 'd'}
*/
declare function parseSemicolonConfig(config: string, keepCase?: boolean): Record<string, string>;
/**
* Stringify a config object to a semicolon separated string
* @param {Record<string, string>} config Object to stringify
* @param {boolean} [uriEncode] Use URI encoding for string outputs
* @returns {string} Stringified config
* @category Utils
* @since v1.0.0
* @example
* stringifySemicolonConfig({a: 'b', c: 'd'}) // 'a=b;c=d'
*/
declare function stringifySemicolonConfig(config: Record<string, unknown>, uriEncode?: boolean): string;
/**
* Stringify a config object to a semicolon separated string for logging
* @template Out - Type of output
* @param {Record<string, string>} config Object to stringify
* @param {ShowValueType} showProtectedKeys How to show protected keys
* @param {string[]} protectedKeys list of protected keys
* @returns {string} Stringified config
* @category Utils
* @since v1.0.0
* @example
* logStringifySemicolonConfig({a: 'b', c: 'd'}) // 'a=b;c=d'
*/
declare function logStringifySemicolonConfig<Out extends Record<string, unknown>>(config: Out, showProtectedKeys: ShowValueType | undefined, protectedKeys: Set<keyof Out>): string;
/**
* validate if a value is a valid object
* @param {unknown} value - value to validate
* @returns {value is Record<string, unknown>} - true if value is a valid object
* @since v0.2.5
*/
declare function isValidObject(value: unknown): value is Record<string, unknown>;
/**
* Convert an object to a string value object
* @param {Record<string, unknown>} obj - object to convert
* @returns {Record<string, string | undefined>} - object with string values
* @since v0.2.5
*/
declare function buildStringObject(obj: Record<string, unknown>): Record<string, string>;
/**
* Convert an object to a Map<string, string>
* @param {Record<string, unknown>} obj - object to convert
* @returns {Map<string, string>} - Map with string values
* @since v0.2.5
*/
declare function buildStringMap(obj: Record<string, unknown>): Map<string, string>;
/**
* Apply a Record<string, string> to a Map<string, string>
* @param {Record<string, string | undefined>} obj - object to apply
* @param {Map<string, string>} target - Map to apply to
* @returns {Map<string, string>} - target with updates
* @since v0.2.5
*/
declare function applyStringMap(obj: Record<string, string | undefined>, target: Map<string, string>): Map<string, string>;
/**
* Function to check if we have seen a value before and update the seenMap
* @param {Map<string, string>} seenMap - Map of seen values
* @param {string} key - key to check
* @param {string | undefined} value - value to check
* @returns {boolean} - true if already seen value, false if not
* @since v1.0.0
*/
declare function handleSeen(seenMap: Map<string, string>, key: string, value: string | undefined): boolean;
interface SemicolonConfigParserOptions<OutType extends Record<string, unknown> = Record<string, unknown>> {
validate?: ValidateCallback<Record<string, string>, OutType>;
/** keys to hide or partially hide */
protectedKeys?: Iterable<keyof OutType>;
/** if and how to show protected keys */
showProtectedKeys?: ShowValueType;
/** keep case of keys, if set as false then will convert keys first letters to lower case (Js Style) */
keepCase?: boolean;
}
/**
* Config parser to parse semicolon separated string key=value pairs as object
* @example
* const objectSchema = z.object({
* foo: z.string(),
* baz: z.string(),
* secret: z.string(),
* });
* // parses 'foo=bar;baz=qux;secret=secret' string to {foo: "bar", baz: "qux", secret: "secret"}
* const fooBarJsonParser = new SemicolonConfigParser({
* validate: (value) => objectSchema.parse(value),
* protectedKeys: ['secret'],
* showProtectedKeys: 'prefix-suffix', // shows secret value with few characters from start and end on logging
* });
* @template Out - the type of the output object
* @implements {IConfigParser<Out, Record<string, string>>}
* @category Parsers
* @since v1.0.0
*/
declare class SemicolonConfigParser<Out extends Record<string, unknown> = Record<string, unknown>> implements IConfigParser<Record<string, string>, Out> {
name: string;
private validate;
private keepCase;
private showProtectedKeys;
private protectedKeys;
constructor({ keepCase, protectedKeys, validate, showProtectedKeys }?: SemicolonConfigParserOptions<Out>);
parse({ value }: ParserProps): Promise<Record<string, string>>;
postValidate({ value }: PostValidateProps<Record<string, string>>): Promise<Out | undefined>;
toString(config: Out, options?: EncodeOptions): string;
toLogString(config: Out): string;
}
/**
* Build parser and have optional post validation (as example for literal values)
*
* supports the following string ___true___ values:
*
* ```['true', '1', 'yes', 'y', 'on']```
*
* supports the following string ___false___ values:
*
* ```['false', '0', 'no', 'n', 'off']```
* @template Output - Type of output, defaults to number
* @param {TypeGuardValidate<Output>} [validate] - optional post validation
* @returns {IConfigParser<boolean, Output>} - parser
* @category Parsers
* @since v1.0.0
*/
declare function booleanParser<Output extends boolean = boolean>(validate?: TypeGuardValidate<Output>): IConfigParser<boolean, Output>;
/**
* Build parser and have optional post validation (as example for literal values)
* @template Output - Type of output, defaults to number
* @param {TypeGuardValidate<Output>} [validate] - optional post validation
* @returns {IConfigParser<number, Output>} - parser
* @category Parsers
* @since v1.0.0
*/
declare function floatParser<Output extends number = number>(validate?: TypeGuardValidate<Output>): IConfigParser<number, Output>;
/**
* Build parser and have optional post validation (as example for literal values)
* @template Output - Type of output, defaults to number
* @param {TypeGuardValidate<Output>} [validate] - optional post validation
* @returns {IConfigParser<number, Output>} - parser
* @category Parsers
* @since v1.0.0
*/
declare function integerParser<Output extends number = number>(validate?: TypeGuardValidate<Output>): IConfigParser<number, Output>;
/**
* Build parser for array of values
* @template Input - Type of input
* @template Output - Type of output
* @param {IConfigParser<Input, Output>} parse parser for the array values
* @param {string} separator separator for the array values, defaults to ';'
* @param {TypeGuardValidate<Output> | undefined} validate optional post validation
* @returns {IConfigParser<Output[], Input[]>} Parser for array of values
* @category Parsers
* @since v1.0.0
*/
declare function arrayParser<Input, Output>(parse: IConfigParser<Input, Output>, separator?: string, validate?: TypeGuardValidate<Output>): IConfigParser<Input[], Output[]>;
/**
* Build parser and have optional post validation (as example for literal values)
* @template Output - Type of output, defaults to bigint
* @param {TypeGuardValidate<Output>} [validate] - optional post validation
* @returns {IConfigParser<bigint, Output>} - parser
* @category Parsers
* @since v1.0.0
*/
declare function bigIntParser<Output extends bigint = bigint>(validate?: TypeGuardValidate<Output>): IConfigParser<bigint, Output>;
/**
* Custom error class for variable errors.
* @class VariableError
* @augments Error
* @param {string} message - The error message.
* @category Errors
* @since v0.2.2
* @example
* throw new VariableError('Variable not found');
*/
declare class VariableError extends Error {
/**
* Create a new VariableError
* @param {string} message - The error message
* @param {ErrorOptions} [options] - The error options
*/
constructor(message: string, options?: ErrorOptions);
}
/**
* Custom error class for variable lookup errors.
* @class VariableLookupError
* @augments VariableError
* @category Errors
* @since v0.2.2
*/
declare class VariableLookupError extends VariableError {
readonly variableKey: string;
/**
* Create a new VariableLookupError
* @param {string} variableKey - The variable key.
* @param {string} message - The error message.
* @param {ErrorOptions} [options] - The error options
*/
constructor(variableKey: string, message: string, options?: ErrorOptions);
}
/**
* TypeValueRecords
* @template T - type of config map
* @since v0.6.0
*/
type TypeValueRecords<T> = Record<keyof T, LoaderTypeValueStrict<T[keyof T]>>;
/**
* ConfigMap
* @example
* type ConfigEnv = {
* PORT: number;
* HOST: string;
* DEBUG: boolean;
* URL: URL;
* };
* const config = new ConfigMap<ConfigEnv>({
* DEBUG: {loaders: [env()], parser: booleanParser, defaultValue: false},
* HOST: {loaders: [env()], parser: stringParser, defaultValue: 'localhost'},
* PORT: {loaders: [env()], parser: integerParser, defaultValue: 3000},
* URL: {loaders: [env()], parser: new UrlParser({urlSanitize: true}), defaultValue: new URL('http://localhost:3000')},
* });
* console.log('port', await config.get('PORT'));
* @template Data - type of config map
* @since v1.1.0
*/
declare class ConfigMap<Data extends Record<string, unknown>> implements ISetOptionalLogger {
private schema;
private options;
private loaders;
/**
* ConfigMap constructor
* @param {EnvMapSchema<Data>} schema - schema of config map
* @param {Iterable<IConfigLoader>} loaders - iterable of config loaders
* @param {ConfigOptions} options - optional config options (logger, namespace)
*/
constructor(schema: EnvMapSchema<Data>, loaders: Loadable<Iterable<IConfigLoader>>, options?: ConfigOptions);
/**
* Set logger value
* @param {ILoggerLike | undefined} logger - logger like object
*/
setLogger(logger: ILoggerLike | undefined): void;
/**
* get env object from config map
* @returns {Promise<TypeValueRecords<Data>>} Promise of env object or undefined
* @example
* const valueObject: LoaderTypeValue<number> = await config.getObject('PORT');
* console.log(valueObject.type, valueObject.value); // 'env', 3000
* @param {string} key - key of config map
* @param {EncodeOptions} [encodeOptions] - optional encode options
*/
getObject<Key extends keyof Data = keyof Data>(key: Key, encodeOptions?: EncodeOptions): Promise<LoaderTypeValueStrict<Data[Key]>>;
/**
* get env object from config map as Result
* @returns {Promise<Result<TypeValueRecords<Data>>>} Result Promise of env object or undefined
* @example
* const valueObject: Result<LoaderTypeValue<number>> = await config.getObjectResult('PORT');
* if (valueObject.isOk()) {
* const {type, value} = valueObject.ok();
* console.log(type, value); // 'env', 3000
* }
* @param {string} key - key of config map
* @param {EncodeOptions} [encodeOptions] - optional encode options
*/
getObjectResult<Key extends keyof Data = keyof Data>(key: Key, encodeOptions?: EncodeOptions): Promise<IResult<LoaderTypeValueStrict<Data[Key]>>>;
/**
* get env value from config map
* @returns {Promise<Data[Key]>} Promise of value or undefined
* @example
* const port: number = await config.get('PORT');
* @param {string} key - key of config map
* @param {EncodeOptions} [encodeOptions] - optional encode options
*/
get<Key extends keyof Data = keyof Data>(key: Key, encodeOptions?: EncodeOptions): Promise<Data[Key]>;
/**
* get env value as string from config map
* @returns {Promise<string | undefined>} Promise of string value or undefined
* @example
* const port: string = await config.getString('PORT');
* @param {string} key - key of config map
* @param {EncodeOptions} [encodeOptions] - optional encode options
*/
getString<Key extends keyof Data = keyof Data>(key: Key, encodeOptions?: EncodeOptions): Promise<undefined extends Data[Key] ? string | undefined : string>;
/**
* get env value from config map as Result
* @returns {Promise<Result<Data[Key]>>} Result Promise of value or undefined
* @example
* const port: Result<number> = await config.getResult('PORT');
* if (port.isOk()) {
* console.log('port', port.ok());
* }
* @param {string} key - key of config map
* @param {EncodeOptions} [encodeOptions] - optional encode options
*/
getResult<Key extends keyof Data = keyof Data>(key: Key, encodeOptions?: EncodeOptions): Promise<IResult<Data[Key]>>;
/**
* get env value as string from config map as Result
* @returns {Promise<Result<string | undefined>>} Result Promise of string value or undefined
* @example
* const port: Result<string> = await config.getStringResult('PORT');
* if (port.isOk()) {
* console.log('port', port.ok());
* }
* @param {string} key - key of config map
* @param {EncodeOptions} [encodeOptions] - optional encode options
*/
getStringResult<Key extends keyof Data = keyof Data>(key: Key, encodeOptions?: EncodeOptions): Promise<IResult<undefined extends Data[Key] ? string | undefined : string>>;
/**
* get all env value objects from config map
* @returns {Promise<TypeValueRecords<Data>>} Promise of all values
* @example
* const values: TypeValueRecords<Data> = await config.getAll();
* console.log(values.PORT.type, values.PORT.value); // 'env', 3000
*/
getAllObjects(): Promise<TypeValueRecords<Data>>;
/**
* get all env values from config map
* @returns {Promise<Data>} Promise of all values
* @example
* const values: Data = await config.getAllValues();
* console.log('PORT', values.PORT); // 3000 (number)
*/
getAllValues(): Promise<Data>;
/**
* get all env values from config map as string
* @returns {Promise<Record<keyof Data, string>>} Promise of all values as string
* @example
* const values: Record<keyof Data, string> = await config.getAllStringValues();
* console.log('PORT', values.PORT); // '3000' (string)
* @param {EncodeOptions} [encodeOptions] - optional encode options
*/
getAllStringValues(encodeOptions?: EncodeOptions): Promise<Record<keyof Data, string>>;
/**
* Validate all env values from config map, expect to throw error if error exists
* @param {(data: Data) => void} callback callback function
*/
validateAll(callback: (data: Data) => void): Promise<void>;
/**
* run lookup to all keys and return all promises
* @param {EncodeOptions} [encodeOptions] - optional encode options
* @returns {Promise<[keyof Data, LoaderTypeValueStrict<Data[keyof Data]>][]>} Promise of all values
*/
private getAllPromises;
}
/**
* Set the logger to be used by the module
* @category Utils
* @param {ILoggerLike} newLogger - logger to be used
* @since v0.2.18
*/
declare function setLogger(newLogger: ILoggerLike): void;
/**
* Get current logger instance
* @category Utils
* @returns {ILoggerLike | undefined} - current logger
* @since v0.5.9
*/
declare function getLogger(): ILoggerLike | undefined;
/**
* resolve logger: undefined = global logger, null = no logger else it's ILoggerLike
* @param {ILoggerLike | undefined | null} logger - logger to resolve
* @category Utils
* @returns {ILoggerLike | undefined} - resolved logger
* @since v0.5.9
*/
declare function resolveLogger(logger: undefined | null | ILoggerLike): ILoggerLike | undefined;
export { ConfigLoader, type ConfigLoaderEventMap, ConfigMap, type ConfigOptions, type ConfigRequest, type EncodeOptions, EnvConfigLoader, type EnvMapSchema, FetchConfigLoader, type FetchConfigLoaderOptions, type FormatParameters, type IConfigLoader, type IConfigLoaderProps, type IConfigParser, type IMemoryConfigLoaderProps, type IRequestCache, type ISwitchLoaderProps, type InferOverrideKeyMap, JsonConfigParser, type JsonConfigParserOptions, type LoaderTypeValue, type LoaderTypeValueStrict, type LoaderValue, type LoaderValueResult, MapConfigLoader, MemoryConfigLoader, type OptionalEnvEntry, type OverrideKeyMap, type ParserProps, type PartialHiddenValueStringType, type PostValidateProps, type PreValidateProps, ReactEnvConfigLoader, RecordConfigLoader, type RequestNotReady, type RequiredEnvEntry, type RequiredUndefinedThrowEntry, SemicolonConfigParser, type SemicolonConfigParserOptions, type ShowValueType, type SolvedConfigOptions, type SwitchConfigMap, SwitchLoader, type TypeGuardValidate, type TypeValueRecords, UrlParser, type UrlParserProps, type ValidateCallback, VariableError, VariableLookupError, applyStringMap, arrayParser, bigIntParser, booleanParser, buildHiddenAsterisksValueString, buildHiddenValue, buildOptions, buildPartialHiddenValueString, buildStringMap, buildStringObject, clearDefaultValueSeenMap, createRequestNotReady, floatParser, getConfigObject, getConfigObjectResult, getConfigVariable, getConfigVariableResult, getLogger, handleSeen, integerParser, isRequestNotReadMessage, isValidObject, logStringifySemicolonConfig, parseSemicolonConfig, printValue, resolveLogger, setLogger, stringParser, stringifySemicolonConfig, urlSanitize };
"use strict";
var __defProp = Object.defineProperty;
var __defProps = Object.defineProperties;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __propIsEnum = Object.prototype.propertyIsEnumerable;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __spreadValues = (a, b) => {
for (var prop in b || (b = {}))
if (__hasOwnProp.call(b, prop))
__defNormalProp(a, prop, b[prop]);
if (__getOwnPropSymbols)
for (var prop of __getOwnPropSymbols(b)) {
if (__propIsEnum.call(b, prop))
__defNormalProp(a, prop, b[prop]);
}
return a;
};
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var __async = (__this, __arguments, generator) => {
return new Promise((resolve, reject) => {
var fulfilled = (value) => {
try {
step(generator.next(value));
} catch (e) {
reject(e);
}
};
var rejected = (value) => {
try {
step(generator.throw(value));
} catch (e) {
reject(e);
}
};
var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
step((generator = generator.apply(__this, __arguments)).next());
});
};
// src/index.ts
var index_exports = {};
__export(index_exports, {
ConfigLoader: () => ConfigLoader,
ConfigMap: () => ConfigMap,
EnvConfigLoader: () => EnvConfigLoader,
FetchConfigLoader: () => FetchConfigLoader,
JsonConfigParser: () => JsonConfigParser,
MapConfigLoader: () => MapConfigLoader,
MemoryConfigLoader: () => MemoryConfigLoader,
ReactEnvConfigLoader: () => ReactEnvConfigLoader,
RecordConfigLoader: () => RecordConfigLoader,
SemicolonConfigParser: () => SemicolonConfigParser,
SwitchLoader: () => SwitchLoader,
UrlParser: () => UrlParser,
VariableError: () => VariableError,
VariableLookupError: () => VariableLookupError,
applyStringMap: () => applyStringMap,
arrayParser: () => arrayParser,
bigIntParser: () => bigIntParser,
booleanParser: () => booleanParser,
buildHiddenAsterisksValueString: () => buildHiddenAsterisksValueString,
buildHiddenValue: () => buildHiddenValue,
buildOptions: () => buildOptions,
buildPartialHiddenValueString: () => buildPartialHiddenValueString,
buildStringMap: () => buildStringMap,
buildStringObject: () => buildStringObject,
clearDefaultValueSeenMap: () => clearDefaultValueSeenMap,
createRequestNotReady: () => createRequestNotReady,
floatParser: () => floatParser,
getConfigObject: () => getConfigObject,
getConfigObjectResult: () => getConfigObjectResult,
getConfigVariable: () => getConfigVariable,
getConfigVariableResult: () => getConfigVariableResult,
getLogger: () => getLogger,
handleSeen: () => handleSeen,
integerParser: () => integerParser,
isRequestNotReadMessage: () => isRequestNotReadMessage,
isValidObject: () => isValidObject,
logStringifySemicolonConfig: () => logStringifySemicolonConfig,
parseSemicolonConfig: () => parseSemicolonConfig,
printValue: () => printValue,
resolveLogger: () => resolveLogger,
setLogger: () => setLogger,
stringParser: () => stringParser,
stringifySemicolonConfig: () => stringifySemicolonConfig,
urlSanitize: () => urlSanitize
});
module.exports = __toCommonJS(index_exports);
// src/types/RequestNotReady.ts
function isRequestNotReadMessage(obj) {
return typeof obj === "object" && obj !== null && "_type" in obj && obj._type === "RequestNotReady" && "message" in obj && typeof obj.message === "string";
}
function createRequestNotReady(message) {
return { message, _type: "RequestNotReady" };
}
// src/getConfigObject.ts
var import_ts_common = require("@luolapeikko/ts-common");
// src/logger.ts
var logger;
function setLogger(newLogger) {
logger = newLogger;
}
function getLogger() {
return logger;
}
function resolveLogger(logger2) {
if (logger2 === void 0) {
return getLogger();
}
return logger2 != null ? logger2 : void 0;
}
// src/ConfigOptions.ts
function buildOptions({ logger: logger2, namespace } = {}) {
return {
logger: resolveLogger(logger2),
namespace
};
}
// src/lib/seenUtils.ts
function handleSeen(seenMap, key, value) {
if (value === void 0) {
return false;
}
const lastValue = seenMap.get(key);
const seen = value === lastValue;
if (!seen) {
seenMap.set(key, value);
}
return seen;
}
// src/lib/formatUtils.ts
function urlSanitize(value, logger2) {
try {
const url = new URL(value);
url.password = "*".repeat(url.password.length);
url.username = "*".repeat(url.username.length);
return url.href;
} catch (err) {
logger2 == null ? void 0 : logger2.warn("variables:", err);
return value;
}
}
function printValue(value, config) {
if (!value || !config) {
return "";
}
return ` [${buildHiddenValue(value, config.showValue)}]`;
}
function buildHiddenValue(value, show) {
if (show === true) {
return value;
}
if (!show) {
return buildHiddenAsterisksValueString(value);
}
return buildPartialHiddenValueString(value, show);
}
function buildHiddenAsterisksValueString(value) {
return "*".repeat(value.length);
}
function buildPartialHiddenValueString(value, type) {
const visibleCharacters = Math.min(3, Math.max(1, Math.floor(value.length * 0.2)));
switch (type) {
case "prefix":
return `${value.slice(0, visibleCharacters)}${"*".repeat(value.length - visibleCharacters)}`;
case "suffix":
return `${"*".repeat(value.length - visibleCharacters)}${value.slice(-visibleCharacters)}`;
case "prefix-suffix": {
const halfOfVisibleCharacters = Math.max(1, Math.ceil(visibleCharacters / 2));
return `${value.slice(0, halfOfVisibleCharacters)}${"*".repeat(value.length - halfOfVisibleCharacters * 2)}${value.slice(-halfOfVisibleCharacters)}`;
}
}
}
// src/VariableError.ts
var VariableError = class extends Error {
/**
* Create a new VariableError
* @param {string} message - The error message
* @param {ErrorOptions} [options] - The error options
*/
constructor(message, options) {
super(message, options);
this.name = "VariableError";
}
};
// src/loaderUtils.ts
function printLog({ logger: logger2, namespace }, type, key, value, path, params) {
const namespaceString = namespace ? `:${namespace}` : "";
logger2 == null ? void 0 : logger2.info(`ConfigVariables${namespaceString}[${type}]: ${key}${printValue(value, params)} from ${path}`);
}
function handleAsVariableError(err) {
if (err instanceof VariableError) {
return err;
}
if (err instanceof Error) {
const varError = new VariableError(err.message);
varError.stack = err.stack;
return varError;
}
return new VariableError(`Unknown error ${JSON.stringify(err)}`);
}
function rebuildAsVariableError(value, error, loader, parser, params) {
if (error instanceof VariableError) {
return error;
} else {
const varError = new VariableError(`variables[${loader.loaderType}](${parser.name}):${printValue(value, params)} ${error.message}`);
varError.stack = error.stack;
return varError;
}
}
function buildPreValidateErrorMessage(value, err, loader, parser, params) {
if (err instanceof Error) {
return rebuildAsVariableError(value, err, loader, parser, params);
} else {
return new VariableError(`variables[${loader.loaderType}](${parser.name}):${printValue(value, params)} unknown preValidate error`);
}
}
function buildParserErrorMessage(value, err, loader, parser, params) {
if (err instanceof Error) {
return rebuildAsVariableError(value, err, loader, parser, params);
} else {
return new VariableError(`variables[${loader.loaderType}](${parser.name}):${printValue(value, params)} unknown parse error`);
}
}
function buildPostValidateErrorMessage(value, err, loader, parser, params) {
if (err instanceof Error) {
return rebuildAsVariableError(value, err, loader, parser, params);
} else {
return new VariableError(`variables[${loader.loaderType}](${parser.name}):${printValue(value, params)} unknown postValidate error`);
}
}
function handleLoader(rootKey, loader, parser, params, options, encodeOptions) {
return __async(this, null, function* () {
var _a, _b, _c, _d, _e;
try {
if (yield loader.isLoaderDisabled()) {
return void 0;
}
const result = yield loader.getLoaderResult(rootKey);
if (!result) {
return void 0;
}
const { value, path, seen } = result;
if (value) {
try {
yield (_a = parser.preValidate) == null ? void 0 : _a.call(parser, { key: rootKey, value, loader });
} catch (err) {
throw buildPreValidateErrorMessage(value, err, loader, parser, params);
}
let rawOutput;
try {
rawOutput = yield parser.parse({ key: rootKey, value, loader });
} catch (err) {
throw buildParserErrorMessage(value, err, loader, parser, params);
}
let output = rawOutput;
try {
const validateData = yield (_b = parser.postValidate) == null ? void 0 : _b.call(parser, { key: rootKey, value: rawOutput, loader });
if (validateData) {
output = validateData;
}
} catch (err) {
throw buildPostValidateErrorMessage(value, err, loader, parser, params);
}
const stringValue = parser.toString(output, encodeOptions);
if (!seen && !(encodeOptions == null ? void 0 : encodeOptions.silent)) {
const logValue = (_d = (_c = parser.toLogString) == null ? void 0 : _c.call(parser, output)) != null ? _d : stringValue;
printLog(options, loader.loaderType, rootKey, logValue, path, params);
}
return { namespace: options.namespace, stringValue, type: loader.loaderType, value: output };
}
} catch (err) {
(_e = options.logger) == null ? void 0 : _e.error(err);
}
return void 0;
});
}
// src/getConfigObject.ts
var defaultValueSeenMap = /* @__PURE__ */ new Map();
function clearDefaultValueSeenMap() {
defaultValueSeenMap.clear();
}
function getConfigObject(rootKey, loaders, parser, defaultValueLoadable, params, options, encodeOptions) {
return __async(this, null, function* () {
var _a, _b;
const currentOptions = buildOptions(options);
let defaultValue;
let type;
if (defaultValueLoadable !== void 0) {
try {
defaultValue = yield import_ts_common.LoadableCore.resolve(defaultValueLoadable);
type = "default";
} catch (err) {
(_a = currentOptions.logger) == null ? void 0 : _a.error(err);
throw handleAsVariableError(err);
}
}
for (const loader of loaders) {
let output;
try {
output = yield handleLoader(rootKey, loader, parser, params, currentOptions, encodeOptions);
} catch (err) {
(_b = currentOptions.logger) == null ? void 0 : _b.error(err);
}
if (output !== void 0) {
return output;
}
}
let stringValue;
if (defaultValue !== void 0) {
stringValue = parser.toString(defaultValue, encodeOptions);
if (!handleSeen(defaultValueSeenMap, rootKey, stringValue) && !(encodeOptions == null ? void 0 : encodeOptions.silent)) {
printLog(currentOptions, "default", rootKey, stringValue, "default", params);
}
return { namespace: currentOptions.namespace, stringValue, type: "default", value: defaultValue };
}
return { namespace: currentOptions.namespace, stringValue, type, value: defaultValue };
});
}
// src/getConfigVariable.ts
function getConfigVariable(rootKey, loaders, parser, defaultValueLoadable, params, options, encodeOptions) {
return __async(this, null, function* () {
return (yield getConfigObject(rootKey, loaders, parser, defaultValueLoadable, params, options, encodeOptions)).value;
});
}
// src/getConfigVariableResult.ts
var import_result_option = require("@luolapeikko/result-option");
function getConfigVariableResult(rootKey, loaders, parser, defaultValueLoadable, params, options) {
return __async(this, null, function* () {
try {
return (0, import_result_option.Ok)(yield getConfigVariable(rootKey, loaders, parser, defaultValueLoadable, params, options));
} catch (err) {
return (0, import_result_option.Err)(err);
}
});
}
// src/getConfigObjectResult.ts
var import_result_option2 = require("@luolapeikko/result-option");
function getConfigObjectResult(rootKey, loaders, parser, defaultValueLoadable, params, options) {
return __async(this, null, function* () {
try {
return (0, import_result_option2.Ok)(yield getConfigObject(rootKey, loaders, parser, defaultValueLoadable, params, options));
} catch (err) {
return (0, import_result_option2.Err)(err);
}
});
}
// src/loaders/ConfigLoader.ts
var import_events = require("events");
var import_ts_common2 = require("@luolapeikko/ts-common");
// src/lib/semicolonUtils.ts
function lcFirst(data) {
return data.length > 0 ? data.charAt(0).toLowerCase() + data.slice(1) : data;
}
function parseSemicolonConfig(config, keepCase = true) {
return config.split(";").reduce((last, c) => {
const [k, v] = c.split("=", 2);
if (k && v) {
const key = keepCase ? k.trim() : lcFirst(k.trim());
if (key) {
last[key] = decodeURIComponent(v.trim());
}
}
return last;
}, {});
}
function stringifySemicolonConfig(config, uriEncode = true) {
return Object.entries(config).reduce((last, [key, value]) => {
if (value !== void 0) {
const encodedValue = uriEncode ? encodeURIComponent(String(value)) : String(value);
last.push(`${key}=${encodedValue}`);
}
return last;
}, []).join(";");
}
function logStringifySemicolonConfig(config, showProtectedKeys, protectedKeys) {
return Object.entries(config).reduce((last, [key, value]) => {
if (value !== void 0) {
if (protectedKeys.has(key)) {
const hiddenValue = buildHiddenValue(String(value), showProtectedKeys);
last.push(`${key}=${hiddenValue}`);
} else {
last.push(`${key}=${String(value)}`);
}
}
return last;
}, []).join(";");
}
// src/lib/objectUtils.ts
function isValidObject(value) {
return typeof value === "object" && value !== null && !Array.isArray(value);
}
function haveToJSON(value) {
return typeof value === "object" && value !== null && "toJSON" in value;
}
function stringifyValue(value) {
if (isValidObject(value)) {
return haveToJSON(value) ? value.toJSON() : JSON.stringify(value);
}
return String(value);
}
function buildStringObject(obj) {
return Object.entries(obj).reduce((last, [key, value]) => {
if (value) {
last[key] = stringifyValue(value);
}
return last;
}, {});
}
function buildStringMap(obj) {
return Object.entries(obj).reduce((acc, [key, value]) => {
if (value) {
acc.set(key, stringifyValue(value));
}
return acc;
}, /* @__PURE__ */ new Map());
}
function applyStringMap(obj, target) {
return Object.entries(obj).reduce((acc, [key, value]) => {
if (value) {
acc.set(key, value);
}
return acc;
}, target);
}
// src/loaders/ConfigLoader.ts
var ConfigLoader = class extends import_events.EventEmitter {
constructor(props = {}, overrideKeys = {}) {
super();
this.valueSeen = /* @__PURE__ */ new Map();
this.options = props;
this.overrideKeys = overrideKeys;
}
getLoaderResult(lookupKey) {
return __async(this, null, function* () {
var _a;
const loaderValue = yield this.handleLoaderValue((_a = this.overrideKeys[lookupKey]) != null ? _a : lookupKey);
if (!loaderValue) {
return void 0;
}
return {
value: loaderValue.value,
path: loaderValue.path,
seen: handleSeen(this.valueSeen, lookupKey, loaderValue.value)
};
});
}
isLoaderDisabled() {
return __async(this, null, function* () {
const loadableDisabled = (yield this.getOptions()).disabled;
return import_ts_common2.LoadableCore.resolve(loadableDisabled);
});
}
setDisabled(disabled) {
return this.setOption("disabled", disabled);
}
/**
* Get options from loader and merge with default options
* @returns {Promise<DefaultProps>} - Promise of DefaultProps & Props
*/
getOptions() {
return __async(this, null, function* () {
const resolvedOptions = yield typeof this.options === "function" ? this.options() : this.options;
return Object.assign({}, this.defaultOptions, resolvedOptions);
});
}
setOption(key, value) {
return __async(this, null, function* () {
this.options = Object.assign({}, yield this.getOptions(), {
[key]: value
});
});
}
/**
* Build error string `ConfigVariables[<type>]: <message>`
* @param {string} message - error message
* @returns {string} - error string
*/
buildErrorStr(message) {
return `ConfigLoader[${this.loaderType}]: ${message}`;
}
};
// src/loaders/RecordConfigLoader.ts
var RecordConfigLoader = class extends ConfigLoader {
constructor() {
super(...arguments);
this._isLoaded = false;
}
/**
* reloads the data
*/
reload() {
return __async(this, null, function* () {
this.valueSeen.clear();
yield this.loadData();
});
}
/**
* is the data loaded
* @returns {boolean} - true if the data is loaded, false otherwise
*/
isLoaded() {
return this._isLoaded;
}
/**
* load data from the loader. If the data is loaded successfully
* an "updated" event will be emitted.
* @returns {Promise<void>}
*/
loadData() {
return __async(this, null, function* () {
this.dataPromise = this.handleData();
yield this.dataPromise;
this.emit("updated");
});
}
};
// src/loaders/EnvConfigLoader.ts
var EnvConfigLoader = class extends ConfigLoader {
constructor() {
super(...arguments);
this.loaderType = "env";
this.defaultOptions = {
disabled: false
};
}
handleLoaderValue(lookupKey) {
return { value: process.env[lookupKey], path: `process.env.${lookupKey}` };
}
};
// src/loaders/ReactEnvConfigLoader.ts
var ReactEnvConfigLoader = class extends ConfigLoader {
constructor() {
super(...arguments);
this.loaderType = "react-env";
this.defaultOptions = {
disabled: false
};
}
handleLoaderValue(lookupKey) {
const targetKey = `REACT_APP_${lookupKey}`;
return { value: process.env[targetKey], path: `process.env.${targetKey}` };
}
};
// src/loaders/MapConfigLoader.ts
var MapConfigLoader = class extends ConfigLoader {
constructor() {
super(...arguments);
this._isLoaded = false;
this.data = /* @__PURE__ */ new Map();
}
/**
* clear maps and reloads the data
*/
reload() {
return __async(this, null, function* () {
this.data.clear();
this.valueSeen.clear();
yield this.loadData();
});
}
/**
* is the data loaded
* @returns {boolean} Whether the data is loaded
*/
isLoaded() {
return this._isLoaded;
}
/**
* load data from the loader. If the data is loaded successfully
* an "updated" event will be emitted.
* @returns {Promise<void>}
*/
loadData() {
return __async(this, null, function* () {
if (yield this.handleLoadData()) {
this.emit("updated");
}
});
}
};
// src/loaders/FetchConfigLoader.ts
var FetchConfigLoader = class extends MapConfigLoader {
/**
* Constructor for FetchConfigLoader
* @param {Loadable<ConfigRequest>} request - callback that returns a fetch request or a message object that the request is not ready
* @param {Loadable<Partial<FetchConfigLoaderOptions>>} options - optional options for FetchConfigLoader
* @param {Partial<OverrideMap>} overrideKeys - optional override keys for FetchConfigLoader
* @param {Lowercase<string>} type - optional name type for FetchConfigLoader (default: 'fetch')
*/
constructor(request, options = {}, overrideKeys = {}, type = "fetch") {
super(options, overrideKeys);
this.path = "undefined";
this.defaultOptions = {
cache: void 0,
cacheHitHttpCode: 304,
disabled: false,
fetchClient: typeof window === "object" ? fetch.bind(window) : fetch,
isSilent: false,
logger: void 0,
payload: "json",
validate: void 0
};
this.request = request;
this.loaderType = type;
}
handleLoaderValue(lookupKey) {
return __async(this, null, function* () {
if (!this._isLoaded) {
yield this.loadData();
this._isLoaded = true;
}
return { value: this.data.get(lookupKey), path: this.path };
});
}
handleLoadData() {
return __async(this, null, function* () {
const { logger: logger2, cache, isSilent, cacheHitHttpCode, payload } = yield this.getOptions();
const req = yield this.getRequest();
if (isRequestNotReadMessage(req)) {
logger2 == null ? void 0 : logger2.debug(`FetchEnvConfig: ${req.message}`);
return false;
}
this.path = urlSanitize(req.url);
logger2 == null ? void 0 : logger2.debug(`fetching config from ${this.path}`);
let res = yield this.fetchRequestOrCacheResponse(req);
if (!res) {
logger2 == null ? void 0 : logger2.info(`client is offline and not have cached response for FetchEnvConfig`);
return false;
}
if (res.ok && cache) {
logger2 == null ? void 0 : logger2.debug(`storing response in cache for FetchEnvConfig`);
yield cache.storeRequest(req, res);
}
if (res.status >= 400) {
res = yield this.checkIfValidCacheResponse(req, res);
if (res.status >= 400) {
if (isSilent) {
logger2 == null ? void 0 : logger2.info(`http error ${res.status.toString()} from FetchEnvConfig`);
return false;
}
throw new VariableError(`http error ${res.status.toString()} from FetchEnvConfig`);
}
}
if (res.status === cacheHitHttpCode) {
res = yield this.handleNotModifiedCache(req, res);
}
const contentType = res.headers.get("content-type");
if ((contentType == null ? void 0 : contentType.startsWith("application/json")) && payload === "json") {
applyStringMap(yield this.handleJson(res), this.data);
logger2 == null ? void 0 : logger2.debug("successfully loaded config from FetchEnvConfig");
return true;
}
if (isSilent) {
logger2 == null ? void 0 : logger2.info(`unsupported content-type ${String(contentType)} from FetchEnvConfig`);
return false;
}
throw new VariableError(`unsupported content-type ${String(contentType)} from FetchEnvConfig`);
});
}
/**
* if client is offline, we will try return the cached response else add cache validation (ETag) and try get the response from the fetch request
* @param {Request} req - request to fetch
* @returns {Promise<Response | undefined>} - response or undefined
*/
fetchRequestOrCacheResponse(req) {
return __async(this, null, function* () {
const { logger: logger2, cache, fetchClient } = yield this.getOptions();
if (cache) {
const cacheRes = yield cache.fetchRequest(req);
if (!cache.isOnline()) {
if (cacheRes) {
logger2 == null ? void 0 : logger2.debug(`client is offline, returned cached response for FetchEnvConfig`);
return cacheRes;
}
return void 0;
}
if (cacheRes) {
const etag = cacheRes.headers.get("etag");
if (etag) {
req.headers.set("If-None-Match", etag);
}
}
}
return fetchClient(req);
});
}
/**
* on error, check if we have a valid cached response
* @param {Request} req - request to fetch
* @param {Response} res - response from fetch
* @returns {Promise<Response>} - response
*/
checkIfValidCacheResponse(req, res) {
return __async(this, null, function* () {
const { cache } = yield this.getOptions();
if (cache) {
const cacheRes = yield cache.fetchRequest(req);
if (cacheRes) {
return cacheRes;
}
}
return res;
});
}
/**
* if we get a 304, get the cached response
* @param {Request} req - request to fetch
* @param {Response} res - response from fetch
* @returns {Promise<Response>} - response
*/
handleNotModifiedCache(req, res) {
return __async(this, null, function* () {
const { cache, logger: logger2 } = yield this.getOptions();
if (cache) {
const cacheRes = yield cache.fetchRequest(req);
if (cacheRes) {
logger2 == null ? void 0 : logger2.debug(`returned cached response for FetchEnvConfig`);
return cacheRes;
}
throw new VariableError(`http error ${res.status.toString()} from FetchEnvConfig (using cached version)`);
}
return res;
});
}
handleJson(res) {
return __async(this, null, function* () {
const { validate, isSilent, logger: logger2 } = yield this.getOptions();
try {
const contentType = res.headers.get("content-type");
if (!(contentType == null ? void 0 : contentType.startsWith("application/json"))) {
throw new Error(`unsupported content-type ${String(contentType)}`);
}
const rawData = yield res.json();
if (!isValidObject(rawData)) {
throw new Error(`is not valid JSON object`);
}
const data = buildStringObject(rawData);
if (validate) {
return yield validate(data);
}
return data;
} catch (error) {
const err = error instanceof Error ? error : new Error("unknown error");
if (isSilent) {
logger2 == null ? void 0 : logger2.info(`FetchEnvConfig JSON error: ${err.message}`);
return Promise.resolve({});
}
throw new VariableError(`FetchEnvConfig JSON error: ${err.message}`);
}
});
}
getRequest() {
return __async(this, null, function* () {
return typeof this.request === "function" ? this.request() : this.request;
});
}
};
// src/loaders/MemoryConfigLoader.ts
var MemoryConfigLoader = class extends ConfigLoader {
constructor(initialData, options = {}, overrideKeys = {}, type = "memory") {
super(options, overrideKeys);
this.defaultOptions = {
disabled: false,
logger: void 0
};
this.data = new Map(Object.entries(initialData));
this.loaderType = type;
}
set(key, value) {
return __async(this, null, function* () {
var _a;
const options = yield this.getOptions();
(_a = options.logger) == null ? void 0 : _a.debug(this.buildErrorStr(`setting key ${String(key)} to '${String(value)}'`));
if (this.data.get(key) !== value) {
this.valueSeen.delete(String(key));
}
this.data.set(key, value);
this.emit("updated");
});
}
get(key) {
return __async(this, null, function* () {
var _a;
const options = yield this.getOptions();
(_a = options.logger) == null ? void 0 : _a.debug(this.buildErrorStr(`getting key ${String(key)}`));
return this.data.get(key);
});
}
handleLoaderValue(lookupKey) {
return { value: this.data.get(lookupKey), path: `key:${lookupKey}` };
}
};
// src/loaders/SwitchLoader.ts
var SwitchLoader = class extends ConfigLoader {
constructor(configs, props = {}, type = "switch") {
super(props);
this.keys = /* @__PURE__ */ new Set();
this.defaultOptions = {
disabled: false,
logger: void 0
};
this.config = configs;
this.loaderType = type;
}
activateSwitch(key) {
return __async(this, null, function* () {
var _a;
const options = yield this.getOptions();
(_a = options.logger) == null ? void 0 : _a.debug(this.buildErrorStr(`activating key '${String(key)}' => [${this.getConfigKeys(key).join(", ")}]`));
this.keys.add(key);
this.emit("updated");
});
}
deactivateSwitch(key) {
return __async(this, null, function* () {
var _a;
const options = yield this.getOptions();
(_a = options.logger) == null ? void 0 : _a.debug(this.buildErrorStr(`deactivating key '${String(key)}' => [${this.getConfigKeys(key).join(", ")}]`));
this.keys.delete(key);
this.emit("updated");
});
}
getCurrentKeys() {
return this.keys;
}
handleLoaderValue(lookupKey) {
let output;
for (const key of Array.from(this.keys)) {
const currentValue = this.config[key][lookupKey];
if (currentValue) {
output = { value: currentValue, path: `switch:${String(key)}:${lookupKey}` };
}
}
return output;
}
getConfigKeys(key) {
return Object.keys(this.config[key]);
}
};
// src/parsers/JsonConfigParser.ts
var JsonConfigParser = class {
constructor({ protectedKeys, validate, showProtectedKeys } = {}) {
this.name = "jsonConfigParser";
this.protectedKeys = new Set(protectedKeys);
this.validate = validate;
this.showProtectedKeys = showProtectedKeys;
}
parse({ value }) {
return this.buildBaseRecord(JSON.parse(value));
}
postValidate(_0) {
return __async(this, arguments, function* ({ value }) {
var _a;
return yield (_a = this.validate) == null ? void 0 : _a.call(this, value);
});
}
toString(config) {
return JSON.stringify(
Object.entries(config).reduce((last, [key, value]) => {
if (value) {
last[key] = value;
}
return last;
}, {})
);
}
toLogString(config) {
return JSON.stringify(
Object.entries(config).reduce((last, [key, value]) => {
if (value) {
if (this.protectedKeys.has(key)) {
last[key] = buildHiddenValue(String(value), this.showProtectedKeys);
} else {
last[key] = value;
}
}
return last;
}, {})
);
}
/**
* Build a string record from the given data
* @param {unknown} data - to be validated as object
* @returns {object} A record with string values
*/
buildBaseRecord(data) {
if (typeof data !== "object" || data === null || Array.isArray(data)) {
return {};
}
return data;
}
};
// src/parsers/UrlParser.ts
var import_ts_common3 = require("@luolapeikko/ts-common");
var UrlParser = class {
/**
* Create a new UrlParser
* @param {UrlParserProps} properties - Properties for the UrlParser
*/
constructor({ urlSanitize: urlSanitize2 } = {}) {
this.name = "urlParser";
this.urlSanitize = urlSanitize2 != null ? urlSanitize2 : false;
}
parse({ value }) {
try {
return new URL(value);
} catch (err) {
throw import_ts_common3.ErrorCore.from(err);
}
}
toString(value) {
return value.href;
}
toLogString(value) {
if (this.urlSanitize) {
return this.handleUrlSanitize(value.href);
}
return value.toString();
}
/**
* Build a URL object from a string and sanitize the username and password
* @param {string} value string to parse
* @returns {URL} URL object with sanitized username and password
*/
handleUrlSanitize(value) {
const url = new URL(value);
url.password = "*".repeat(url.password.length);
url.username = "*".repeat(url.username.length);
return url.href;
}
};
// src/lib/primitiveUtils.ts
var import_result_option3 = require("@luolapeikko/result-option");
function getInteger(value) {
const parsed = parseInt(value, 10);
if (isNaN(parsed)) {
return (0, import_result_option3.Err)(new TypeError(`${value} is not an integer string`));
}
return (0, import_result_option3.Ok)(parsed);
}
var booleanTrueStringValues = ["true", "1", "yes", "y", "on"];
var booleanFalseStringValues = ["false", "0", "no", "n", "off"];
var allBooleanStringValues = [...booleanFalseStringValues, ...booleanTrueStringValues];
function getBoolean(value) {
if (typeof value === "boolean") {
return (0, import_result_option3.Ok)(value);
}
const output = value.toLowerCase();
if (!allBooleanStringValues.includes(output)) {
return (0, import_result_option3.Err)(new TypeError(`${value} is not a boolean string`));
}
return (0, import_result_option3.Ok)(booleanTrueStringValues.includes(output));
}
function getString(value) {
return (0, import_result_option3.Ok)(value);
}
function getFloat(value) {
const parsed = parseFloat(value);
if (isNaN(parsed)) {
return (0, import_result_option3.Err)(new TypeError(`${value} is not a float string`));
}
return (0, import_result_option3.Ok)(parsed);
}
function getBigInt(value) {
try {
return (0, import_result_option3.Ok)(BigInt(value));
} catch (_err) {
return (0, import_result_option3.Err)(new TypeError(`${value} is not a bigint string`));
}
}
// src/parsers/stringParser.ts
function stringParser(validate) {
return {
name: "stringParser",
parse: ({ value, key }) => {
return getString(value).mapErr((cause) => new TypeError(`value for key ${key} is not a string`, { cause })).unwrap();
},
postValidate: (props) => __async(null, null, function* () {
var _a;
if (!((_a = yield validate) == null ? void 0 : _a(props.value))) {
return void 0;
}
return props.value;
}),
preValidate: ({ key, value }) => {
if (typeof value !== "string") {
throw new TypeError(`value for key ${key} is not a string`);
}
},
toString: (value) => {
return value;
}
};
}
// src/parsers/SemicolonConfigParser.ts
var SemicolonConfigParser = class {
constructor({ keepCase, protectedKeys, validate, showProtectedKeys } = {}) {
this.name = "semicolonConfigParser";
this.protectedKeys = new Set(protectedKeys);
this.validate = validate;
this.keepCase = keepCase != null ? keepCase : true;
this.showProtectedKeys = showProtectedKeys;
}
parse({ value }) {
return Promise.resolve(parseSemicolonConfig(value, this.keepCase));
}
postValidate(_0) {
return __async(this, arguments, function* ({ value }) {
var _a;
return yield (_a = this.validate) == null ? void 0 : _a.call(this, value);
});
}
toString(config, options) {
return stringifySemicolonConfig(config, options == null ? void 0 : options.uriEncode);
}
toLogString(config) {
return logStringifySemicolonConfig(config, this.showProtectedKeys, this.protectedKeys);
}
};
// src/parsers/booleanParser.ts
function booleanParser(validate) {
return {
name: "booleanParser",
parse: ({ key, value }) => {
return getBoolean(value).mapErr((cause) => new TypeError(`value for key ${key} is not a boolean string`, { cause })).unwrap();
},
postValidate: (props) => __async(null, null, function* () {
var _a;
if (!((_a = yield validate) == null ? void 0 : _a(props.value))) {
return void 0;
}
return props.value;
}),
preValidate: ({ key, value }) => {
if (typeof value === "boolean") {
return;
}
if (typeof value !== "string") {
throw new TypeError(`value for key ${key} is not a string`);
}
},
toString: (value) => {
return value.toString();
}
};
}
// src/parsers/floatParser.ts
function floatParser(validate) {
return {
name: "floatParser",
parse: ({ key, value }) => {
return getFloat(value).mapErr((cause) => new TypeError(`value for key ${key} is not a float string`, { cause })).unwrap();
},
postValidate: (props) => __async(null, null, function* () {
var _a;
if (!((_a = yield validate) == null ? void 0 : _a(props.value))) {
return void 0;
}
return props.value;
}),
preValidate: ({ key, value }) => {
if (typeof value !== "string") {
throw new TypeError(`value for key ${key} is not a string`);
}
},
toString: (value) => {
return value.toString();
}
};
}
// src/parsers/integerParser.ts
function integerParser(validate) {
return {
name: "integerParser",
parse: ({ key, value }) => {
return getInteger(value).mapErr((cause) => new TypeError(`value for key ${key} is not an integer string`, { cause })).unwrap();
},
postValidate: (props) => __async(null, null, function* () {
var _a;
if (!((_a = yield validate) == null ? void 0 : _a(props.value))) {
return void 0;
}
return props.value;
}),
toString: (value) => {
return value.toString();
}
};
}
// src/parsers/arrayParser.ts
function arrayParser(parse, separator = ";", validate) {
return {
name: "arraySeparatorParser",
parse: (props) => {
return Promise.all(props.value.split(separator).map((v) => parse.parse(__spreadProps(__spreadValues({}, props), { value: v }))));
},
postValidate: (props) => __async(null, null, function* () {
if (!validate) {
return void 0;
}
const valueList = yield Promise.all(
props.value.map((v) => __async(null, null, function* () {
var _a;
if (!((_a = yield validate) == null ? void 0 : _a(v))) {
return void 0;
}
return v;
}))
);
return valueList.filter((v) => v !== void 0);
}),
toString: (value) => {
return value.map((v) => parse.toString(v)).join(separator);
}
};
}
// src/parsers/bigIntParser.ts
function bigIntParser(validate) {
return {
name: "bigIntParser",
parse: ({ key, value }) => {
return getBigInt(value).mapErr((cause) => new TypeError(`value for key ${key} is not an integer string`, { cause })).unwrap();
},
postValidate: (props) => __async(null, null, function* () {
var _a;
if (!((_a = yield validate) == null ? void 0 : _a(props.value))) {
return void 0;
}
return props.value;
}),
toString: (value) => {
return value.toString();
}
};
}
// src/VariableLookupError.ts
var VariableLookupError = class extends VariableError {
/**
* Create a new VariableLookupError
* @param {string} variableKey - The variable key.
* @param {string} message - The error message.
* @param {ErrorOptions} [options] - The error options
*/
constructor(variableKey, message, options) {
super(message, options);
this.variableKey = variableKey;
this.name = "VariableLookupError";
}
};
// src/ConfigMap.ts
var import_result_option4 = require("@luolapeikko/result-option");
var import_ts_common4 = require("@luolapeikko/ts-common");
var ConfigMap = class {
/**
* ConfigMap constructor
* @param {EnvMapSchema<Data>} schema - schema of config map
* @param {Iterable<IConfigLoader>} loaders - iterable of config loaders
* @param {ConfigOptions} options - optional config options (logger, namespace)
*/
constructor(schema, loaders, options = { logger: void 0, namespace: void 0 }) {
this.schema = schema;
this.options = options;
this.loaders = loaders;
}
/**
* Set logger value
* @param {ILoggerLike | undefined} logger - logger like object
*/
setLogger(logger2) {
this.options.logger = logger2;
}
/**
* get env object from config map
* @returns {Promise<TypeValueRecords<Data>>} Promise of env object or undefined
* @example
* const valueObject: LoaderTypeValue<number> = await config.getObject('PORT');
* console.log(valueObject.type, valueObject.value); // 'env', 3000
* @param {string} key - key of config map
* @param {EncodeOptions} [encodeOptions] - optional encode options
*/
getObject(key, encodeOptions) {
return __async(this, null, function* () {
var _a;
if (typeof key !== "string") {
throw new VariableError(`ConfigMap key ${String(key)} is not a string`);
}
const entry = this.schema[key];
if (!entry) {
throw new VariableLookupError(key, `ConfigMap key ${String(key)} not found in config map`);
}
const { parser, defaultValue, params, undefinedThrowsError, undefinedErrorMessage } = entry;
const loaders = Array.from(yield import_ts_common4.LoadableCore.resolve(this.loaders));
const configObject = yield getConfigObject(key, loaders, parser, defaultValue, params, this.options, encodeOptions);
if (undefinedThrowsError && configObject.value === void 0) {
(_a = buildOptions(this.options).logger) == null ? void 0 : _a.info(`ConfigMap key ${String(key)} is undefined (expect to throw error)`);
throw new VariableError(undefinedErrorMessage != null ? undefinedErrorMessage : `ConfigMap key ${String(key)} is undefined`);
}
return configObject;
});
}
/**
* get env object from config map as Result
* @returns {Promise<Result<TypeValueRecords<Data>>>} Result Promise of env object or undefined
* @example
* const valueObject: Result<LoaderTypeValue<number>> = await config.getObjectResult('PORT');
* if (valueObject.isOk()) {
* const {type, value} = valueObject.ok();
* console.log(type, value); // 'env', 3000
* }
* @param {string} key - key of config map
* @param {EncodeOptions} [encodeOptions] - optional encode options
*/
getObjectResult(key, encodeOptions) {
return __async(this, null, function* () {
try {
return (0, import_result_option4.Ok)(yield this.getObject(key, encodeOptions));
} catch (err) {
return (0, import_result_option4.Err)(err);
}
});
}
/**
* get env value from config map
* @returns {Promise<Data[Key]>} Promise of value or undefined
* @example
* const port: number = await config.get('PORT');
* @param {string} key - key of config map
* @param {EncodeOptions} [encodeOptions] - optional encode options
*/
get(key, encodeOptions) {
return __async(this, null, function* () {
return (yield this.getObject(key, encodeOptions)).value;
});
}
/**
* get env value as string from config map
* @returns {Promise<string | undefined>} Promise of string value or undefined
* @example
* const port: string = await config.getString('PORT');
* @param {string} key - key of config map
* @param {EncodeOptions} [encodeOptions] - optional encode options
*/
getString(key, encodeOptions) {
return __async(this, null, function* () {
return (yield this.getObject(key, encodeOptions)).stringValue;
});
}
/**
* get env value from config map as Result
* @returns {Promise<Result<Data[Key]>>} Result Promise of value or undefined
* @example
* const port: Result<number> = await config.getResult('PORT');
* if (port.isOk()) {
* console.log('port', port.ok());
* }
* @param {string} key - key of config map
* @param {EncodeOptions} [encodeOptions] - optional encode options
*/
getResult(key, encodeOptions) {
return __async(this, null, function* () {
try {
return (0, import_result_option4.Ok)(yield this.get(key, encodeOptions));
} catch (err) {
return (0, import_result_option4.Err)(err);
}
});
}
/**
* get env value as string from config map as Result
* @returns {Promise<Result<string | undefined>>} Result Promise of string value or undefined
* @example
* const port: Result<string> = await config.getStringResult('PORT');
* if (port.isOk()) {
* console.log('port', port.ok());
* }
* @param {string} key - key of config map
* @param {EncodeOptions} [encodeOptions] - optional encode options
*/
getStringResult(key, encodeOptions) {
return __async(this, null, function* () {
try {
return (0, import_result_option4.Ok)(yield this.getString(key, encodeOptions));
} catch (err) {
return (0, import_result_option4.Err)(err);
}
});
}
/**
* get all env value objects from config map
* @returns {Promise<TypeValueRecords<Data>>} Promise of all values
* @example
* const values: TypeValueRecords<Data> = await config.getAll();
* console.log(values.PORT.type, values.PORT.value); // 'env', 3000
*/
getAllObjects() {
return __async(this, null, function* () {
const values = yield this.getAllPromises();
return values.reduce((result, [key, value]) => {
result[key] = value;
return result;
}, {});
});
}
/**
* get all env values from config map
* @returns {Promise<Data>} Promise of all values
* @example
* const values: Data = await config.getAllValues();
* console.log('PORT', values.PORT); // 3000 (number)
*/
getAllValues() {
return __async(this, null, function* () {
const values = yield this.getAllPromises();
return values.reduce((result, [key, value]) => {
result[key] = value.value;
return result;
}, {});
});
}
/**
* get all env values from config map as string
* @returns {Promise<Record<keyof Data, string>>} Promise of all values as string
* @example
* const values: Record<keyof Data, string> = await config.getAllStringValues();
* console.log('PORT', values.PORT); // '3000' (string)
* @param {EncodeOptions} [encodeOptions] - optional encode options
*/
getAllStringValues(encodeOptions) {
return __async(this, null, function* () {
const values = yield this.getAllPromises(encodeOptions);
return values.reduce(
(result, [key, value]) => {
result[key] = value.stringValue;
return result;
},
{}
);
});
}
/**
* Validate all env values from config map, expect to throw error if error exists
* @param {(data: Data) => void} callback callback function
*/
validateAll(callback) {
return __async(this, null, function* () {
const data = yield this.getAllValues();
callback(data);
});
}
/**
* run lookup to all keys and return all promises
* @param {EncodeOptions} [encodeOptions] - optional encode options
* @returns {Promise<[keyof Data, LoaderTypeValueStrict<Data[keyof Data]>][]>} Promise of all values
*/
getAllPromises(encodeOptions) {
return Promise.all(
Object.keys(this.schema).map((key) => __async(this, null, function* () {
return [key, yield this.getObject(key, encodeOptions)];
}))
);
}
};
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
ConfigLoader,
ConfigMap,
EnvConfigLoader,
FetchConfigLoader,
JsonConfigParser,
MapConfigLoader,
MemoryConfigLoader,
ReactEnvConfigLoader,
RecordConfigLoader,
SemicolonConfigParser,
SwitchLoader,
UrlParser,
VariableError,
VariableLookupError,
applyStringMap,
arrayParser,
bigIntParser,
booleanParser,
buildHiddenAsterisksValueString,
buildHiddenValue,
buildOptions,
buildPartialHiddenValueString,
buildStringMap,
buildStringObject,
clearDefaultValueSeenMap,
createRequestNotReady,
floatParser,
getConfigObject,
getConfigObjectResult,
getConfigVariable,
getConfigVariableResult,
getLogger,
handleSeen,
integerParser,
isRequestNotReadMessage,
isValidObject,
logStringifySemicolonConfig,
parseSemicolonConfig,
printValue,
resolveLogger,
setLogger,
stringParser,
stringifySemicolonConfig,
urlSanitize
});
//# sourceMappingURL=index.js.map

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display