New Research: Supply Chain Attack on Axios Pulls Malicious Dependency from npm.Details →
Socket
Book a DemoSign in
Socket

@avanio/variable-util-node

Package Overview
Dependencies
Maintainers
3
Versions
37
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@avanio/variable-util-node - npm Package Compare versions

Comparing version
0.12.2
to
0.13.0
+6
-4
dist/index.d.mts
import { ILoggerLike } from '@avanio/logger-like';
import { ConfigLoader, LoaderValue, IConfigLoaderProps, ValidateCallback, RecordConfigLoader } from '@avanio/variable-util';
import { ConfigLoader, LoaderValue, IConfigLoaderProps, ValidateCallback, MapConfigLoader, VariableError } from '@avanio/variable-util';
import { Loadable } from '@luolapeikko/ts-common';
import { IResult } from '@luolapeikko/result-option';

@@ -66,3 +67,3 @@ interface DockerSecretsConfigLoaderOptions {

*/
declare abstract class AbstractFileRecordLoader<Options extends AbstractFileRecordLoaderOptions<string> = AbstractFileRecordLoaderOptions<string>> extends RecordConfigLoader<string | undefined, Partial<Options>, Options> {
declare abstract class AbstractFileRecordLoader<Options extends AbstractFileRecordLoaderOptions<string> = AbstractFileRecordLoaderOptions<string>> extends MapConfigLoader<string, Partial<Options>, Options> {
abstract readonly type: Lowercase<string>;

@@ -77,4 +78,5 @@ private watcher;

close(): Promise<void>;
protected handleLoader(rootKey: string, key?: string): Promise<LoaderValue>;
protected handleData(): Promise<Record<string, string | undefined>>;
protected handleLoader(lookupKey: string, overrideKey: string | undefined): Promise<LoaderValue>;
protected handleData(): Promise<IResult<Record<string, string | undefined>, VariableError>>;
protected handleLoadData(): Promise<boolean>;
/**

@@ -81,0 +83,0 @@ * Handle the parsing of the file.

import { ILoggerLike } from '@avanio/logger-like';
import { ConfigLoader, LoaderValue, IConfigLoaderProps, ValidateCallback, RecordConfigLoader } from '@avanio/variable-util';
import { ConfigLoader, LoaderValue, IConfigLoaderProps, ValidateCallback, MapConfigLoader, VariableError } from '@avanio/variable-util';
import { Loadable } from '@luolapeikko/ts-common';
import { IResult } from '@luolapeikko/result-option';

@@ -66,3 +67,3 @@ interface DockerSecretsConfigLoaderOptions {

*/
declare abstract class AbstractFileRecordLoader<Options extends AbstractFileRecordLoaderOptions<string> = AbstractFileRecordLoaderOptions<string>> extends RecordConfigLoader<string | undefined, Partial<Options>, Options> {
declare abstract class AbstractFileRecordLoader<Options extends AbstractFileRecordLoaderOptions<string> = AbstractFileRecordLoaderOptions<string>> extends MapConfigLoader<string, Partial<Options>, Options> {
abstract readonly type: Lowercase<string>;

@@ -77,4 +78,5 @@ private watcher;

close(): Promise<void>;
protected handleLoader(rootKey: string, key?: string): Promise<LoaderValue>;
protected handleData(): Promise<Record<string, string | undefined>>;
protected handleLoader(lookupKey: string, overrideKey: string | undefined): Promise<LoaderValue>;
protected handleData(): Promise<IResult<Record<string, string | undefined>, VariableError>>;
protected handleLoadData(): Promise<boolean>;
/**

@@ -81,0 +83,0 @@ * Handle the parsing of the file.

+33
-25

@@ -92,4 +92,5 @@ "use strict";

var import_variable_util2 = require("@avanio/variable-util");
var import_result_option = require("@luolapeikko/result-option");
var import_ts_common = require("@luolapeikko/ts-common");
var AbstractFileRecordLoader = class extends import_variable_util2.RecordConfigLoader {
var AbstractFileRecordLoader = class extends import_variable_util2.MapConfigLoader {
watcher;

@@ -112,3 +113,3 @@ timeout;

}
async handleLoader(rootKey, key) {
async handleLoader(lookupKey, overrideKey) {
const { disabled, fileName } = await this.getOptions();

@@ -118,9 +119,9 @@ if (disabled) {

}
if (!this.dataPromise) {
this.dataPromise = this.handleData();
if (!this._isLoaded) {
await this.loadData();
this._isLoaded = true;
}
const data = await this.dataPromise;
const targetKey = key || rootKey;
const currentValue = data[targetKey];
return { type: this.type, result: { value: currentValue, path: fileName, seen: this.handleSeen(targetKey, currentValue) } };
const targetKey = overrideKey || lookupKey;
const value = this.data.get(targetKey);
return { type: this.type, result: { value, path: fileName, seen: this.handleSeen(targetKey, value) } };
}

@@ -130,28 +131,35 @@ async handleData() {

options.logger?.debug(this.buildErrorStr(`loading file ${options.fileName}`));
if (options.disabled) {
return {};
}
if (!(0, import_fs2.existsSync)(options.fileName)) {
const msg = this.buildErrorStr(`file ${options.fileName} not found`);
if (options.isSilent) {
options.logger?.debug(msg);
return {};
}
throw new import_variable_util2.VariableError(msg);
return (0, import_result_option.Err)(new import_variable_util2.VariableError(this.buildErrorStr(`file ${options.fileName} not found`)));
}
let buffer;
try {
const data = await this.handleParse(await (0, import_promises2.readFile)(options.fileName), options);
buffer = await (0, import_promises2.readFile)(options.fileName);
} catch (unknownErr) {
return (0, import_result_option.Err)(new import_variable_util2.VariableError((0, import_ts_common.toError)(unknownErr).message));
}
try {
let data = await this.handleParse(buffer, options);
if (options.validate) {
return await options.validate(data);
data = await options.validate(data);
}
this.handleFileWatch(options);
return data;
return (0, import_result_option.Ok)(data);
} catch (_err) {
const msg = this.buildErrorStr(`file ${options.fileName} is not a valid ${options.fileType}`);
if (options.isSilent) {
options.logger?.info(msg);
return {};
return (0, import_result_option.Err)(new import_variable_util2.VariableError(this.buildErrorStr(`file ${options.fileName} is not a valid ${options.fileType}`)));
}
}
async handleLoadData() {
const { logger, isSilent } = await this.getOptions();
const res = await this.handleData();
if (res.isErr) {
if (!isSilent) {
res.unwrap();
} else {
logger?.debug(res.err());
}
throw new import_variable_util2.VariableError(msg);
return false;
}
(0, import_variable_util2.applyStringMap)(res.ok(), this.data);
return true;
}

@@ -158,0 +166,0 @@ handleFileWatch(options) {

@@ -1,1 +0,1 @@

{"version":3,"sources":["../src/index.ts","../src/DockerSecretsConfigLoader.ts","../src/AbstractFileRecordLoader.ts","../src/FileConfigLoader.ts","../src/DotEnvLoader.ts"],"sourcesContent":["export * from './DockerSecretsConfigLoader';\nexport * from './FileConfigLoader';\nexport * from './DotEnvLoader';\nexport * from './AbstractFileRecordLoader';\n","import {existsSync} from 'fs';\nimport {readFile} from 'fs/promises';\nimport * as path from 'path';\nimport type {ILoggerLike} from '@avanio/logger-like';\nimport {ConfigLoader, type LoaderValue, VariableLookupError} from '@avanio/variable-util';\nimport {type Loadable} from '@luolapeikko/ts-common';\n\nexport interface DockerSecretsConfigLoaderOptions {\n\t/** force file name to lower case */\n\tfileLowerCase: boolean;\n\t/** path to docker secrets, default is '/run/secrets' */\n\tpath: string;\n\t/** set to false if need errors */\n\tisSilent: boolean;\n\t/** optional logger */\n\tlogger: ILoggerLike | undefined;\n\t/** set to true to disable loader, default is false */\n\tdisabled: boolean;\n}\n\n/**\n * Loader for docker secrets, reads secrets from the `/run/secrets` directory.\n * @since v0.8.0\n */\nexport class DockerSecretsConfigLoader extends ConfigLoader<string | undefined, Partial<DockerSecretsConfigLoaderOptions>, DockerSecretsConfigLoaderOptions> {\n\tpublic readonly type: Lowercase<string>;\n\tprivate valuePromises: Record<string, Promise<string | undefined> | undefined> = {};\n\tprotected defaultOptions: DockerSecretsConfigLoaderOptions = {\n\t\tdisabled: false,\n\t\tfileLowerCase: false,\n\t\tisSilent: true,\n\t\tlogger: undefined,\n\t\tpath: '/run/secrets',\n\t};\n\n\tpublic constructor(options: Loadable<Partial<DockerSecretsConfigLoaderOptions>>, type: Lowercase<string> = 'docker-secrets') {\n\t\tsuper(options);\n\t\tthis.getLoader = this.getLoader.bind(this);\n\t\tthis.type = type;\n\t}\n\n\tprotected async handleLoader(lookupKey: string, overrideKey?: string): Promise<LoaderValue> {\n\t\tconst options = await this.getOptions();\n\t\tif (options.disabled) {\n\t\t\treturn {type: this.type, result: undefined};\n\t\t}\n\t\tconst targetKey = overrideKey || lookupKey;\n\t\tconst filePath = this.filePath(targetKey, options);\n\t\tconst valuePromise = this.valuePromises[targetKey];\n\t\tconst seen = !!valuePromise; // if valuePromise exists, it means we have seen this key before\n\t\tif (!valuePromise) {\n\t\t\tif (!existsSync(filePath)) {\n\t\t\t\tif (!options.isSilent) {\n\t\t\t\t\tthrow new VariableLookupError(targetKey, `ConfigVariables[${this.type}]: ${lookupKey} from ${filePath} not found`);\n\t\t\t\t}\n\t\t\t\toptions.logger?.debug(`ConfigVariables[${this.type}]: ${lookupKey} from ${filePath} not found`);\n\t\t\t\tthis.valuePromises[targetKey] = Promise.resolve(undefined);\n\t\t\t} else {\n\t\t\t\tthis.valuePromises[targetKey] = readFile(filePath, 'utf8');\n\t\t\t}\n\t\t}\n\t\tconst value = await this.valuePromises[targetKey];\n\t\treturn {type: this.type, result: {path: filePath, value, seen}};\n\t}\n\n\tprivate filePath(key: string, options: DockerSecretsConfigLoaderOptions): string {\n\t\treturn path.join(path.resolve(options.path), options.fileLowerCase ? key.toLowerCase() : key);\n\t}\n}\n","import {existsSync, type FSWatcher, watch} from 'fs';\nimport {readFile} from 'fs/promises';\nimport type {ILoggerLike} from '@avanio/logger-like';\nimport {type IConfigLoaderProps, type LoaderValue, RecordConfigLoader, type ValidateCallback, VariableError} from '@avanio/variable-util';\nimport {type Loadable, toError} from '@luolapeikko/ts-common';\n\n/**\n * Options for the AbstractFileRecordLoader.\n * @since v0.8.0\n */\nexport interface AbstractFileRecordLoaderOptions<FileType extends string> extends IConfigLoaderProps {\n\tfileType: FileType;\n\t/** file name to load */\n\tfileName: string;\n\t/** set to false if need errors */\n\tisSilent: boolean;\n\t/** optional logger */\n\tlogger: ILoggerLike | undefined;\n\t/** set to true to watch file for changes */\n\twatch: boolean;\n\t/** set to true to disable loader */\n\tdisabled: boolean;\n\t/**\n\t * optional validator for data (Record<string, string | undefined>)\n\t *\n\t * @example\n\t * // using zod\n\t * const stringRecordSchema = z.record(z.string().min(1), z.string());\n\t * const validate: ValidateCallback<Record<string, string>> = async (data) => {\n\t * const result = await stringRecordSchema.safeParseAsync(data);\n\t * if (!result.success) {\n\t * return {success: false, message: result.error.message};\n\t * }\n\t * return {success: true};\n\t * };\n\t */\n\tvalidate: ValidateCallback<Record<string, string | undefined>, Record<string, string | undefined>> | undefined;\n}\n\n/**\n * Abstract class for loading records from a file.\n * @since v0.8.0\n */\nexport abstract class AbstractFileRecordLoader<\n\tOptions extends AbstractFileRecordLoaderOptions<string> = AbstractFileRecordLoaderOptions<string>,\n> extends RecordConfigLoader<string | undefined, Partial<Options>, Options> {\n\tabstract readonly type: Lowercase<string>;\n\tprivate watcher: FSWatcher | undefined;\n\tprivate timeout: ReturnType<typeof setTimeout> | undefined;\n\n\tprotected abstract defaultOptions: Options;\n\n\tpublic constructor(options: Loadable<Partial<Options>>) {\n\t\tsuper(options);\n\t\tthis.getLoader = this.getLoader.bind(this);\n\t\tthis.handleFileChange = this.handleFileChange.bind(this);\n\t}\n\n\t/**\n\t * If the loader is watching the file, it will stop watching.\n\t */\n\tpublic async close(): Promise<void> {\n\t\tif (this.watcher) {\n\t\t\tconst {logger, fileName} = await this.getOptions();\n\t\t\tlogger?.debug(this.buildErrorStr(`closing file watcher for ${fileName}`));\n\t\t\tthis.watcher.close();\n\t\t}\n\t}\n\n\tprotected async handleLoader(rootKey: string, key?: string): Promise<LoaderValue> {\n\t\tconst {disabled, fileName} = await this.getOptions();\n\t\tif (disabled) {\n\t\t\treturn {type: this.type, result: undefined};\n\t\t}\n\t\tif (!this.dataPromise) {\n\t\t\tthis.dataPromise = this.handleData();\n\t\t}\n\t\tconst data = await this.dataPromise;\n\t\tconst targetKey = key || rootKey;\n\t\tconst currentValue = data[targetKey];\n\t\treturn {type: this.type, result: {value: currentValue, path: fileName, seen: this.handleSeen(targetKey, currentValue)}};\n\t}\n\n\tprotected async handleData(): Promise<Record<string, string | undefined>> {\n\t\tconst options = await this.getOptions();\n\t\toptions.logger?.debug(this.buildErrorStr(`loading file ${options.fileName}`));\n\t\t// if file is disabled, return empty object\n\t\tif (options.disabled) {\n\t\t\treturn {};\n\t\t}\n\t\tif (!existsSync(options.fileName)) {\n\t\t\tconst msg = this.buildErrorStr(`file ${options.fileName} not found`);\n\t\t\tif (options.isSilent) {\n\t\t\t\toptions.logger?.debug(msg);\n\t\t\t\treturn {};\n\t\t\t}\n\t\t\tthrow new VariableError(msg);\n\t\t}\n\t\ttry {\n\t\t\tconst data = await this.handleParse(await readFile(options.fileName), options);\n\t\t\tif (options.validate) {\n\t\t\t\treturn await options.validate(data);\n\t\t\t}\n\t\t\tthis.handleFileWatch(options); // add watch after successful load\n\t\t\treturn data;\n\t\t} catch (_err) {\n\t\t\tconst msg = this.buildErrorStr(`file ${options.fileName} is not a valid ${options.fileType}`);\n\t\t\tif (options.isSilent) {\n\t\t\t\toptions.logger?.info(msg);\n\t\t\t\treturn {};\n\t\t\t}\n\t\t\tthrow new VariableError(msg);\n\t\t}\n\t}\n\n\t/**\n\t * Handle the parsing of the file.\n\t */\n\tprotected abstract handleParse(rawData: Buffer, options: Options): Record<string, string | undefined> | Promise<Record<string, string | undefined>>;\n\n\tprivate handleFileWatch(options: Options): void {\n\t\tif (options.watch && !this.watcher) {\n\t\t\toptions.logger?.debug(this.buildErrorStr(`opening file watcher for ${options.fileName}`));\n\t\t\tthis.watcher = watch(options.fileName, () => {\n\t\t\t\tif (this.timeout) {\n\t\t\t\t\tclearTimeout(this.timeout);\n\t\t\t\t}\n\t\t\t\t// delay to prevent multiple reloads\n\t\t\t\tthis.timeout = setTimeout(() => {\n\t\t\t\t\tvoid this.handleFileChange(options);\n\t\t\t\t}, 200);\n\t\t\t});\n\t\t}\n\t}\n\n\tprivate async handleFileChange(options: Options): Promise<void> {\n\t\ttry {\n\t\t\toptions.logger?.debug(this.buildErrorStr(`file ${options.fileName} changed`));\n\t\t\tawait this.reload();\n\t\t} catch (err) {\n\t\t\toptions.logger?.error(this.buildErrorStr(`error reloading file ${options.fileName}: ${toError(err).message}`));\n\t\t}\n\t}\n}\n","import {type Loadable} from '@luolapeikko/ts-common';\nimport {AbstractFileRecordLoader, type AbstractFileRecordLoaderOptions} from './AbstractFileRecordLoader';\n\n/**\n * A file-based configuration loader that reads a JSON file.\n * @since v0.9.1\n */\nexport class FileConfigLoader extends AbstractFileRecordLoader<AbstractFileRecordLoaderOptions<'json'>> {\n\tpublic readonly type: Lowercase<string>;\n\n\tprotected defaultOptions: AbstractFileRecordLoaderOptions<'json'> = {\n\t\tdisabled: false,\n\t\tfileName: 'config.json',\n\t\tfileType: 'json',\n\t\tisSilent: true,\n\t\tlogger: undefined,\n\t\tvalidate: undefined,\n\t\twatch: false,\n\t};\n\n\tpublic constructor(options: Loadable<Partial<AbstractFileRecordLoaderOptions<'json'>>>, type: Lowercase<string> = 'file') {\n\t\tsuper(options);\n\t\tthis.type = type;\n\t}\n\n\tprotected handleParse(rawData: Buffer, options: AbstractFileRecordLoaderOptions<'json'>): Record<string, string | undefined> {\n\t\tconst data: unknown = JSON.parse(rawData.toString());\n\t\tif (typeof data !== 'object' || data === null || Array.isArray(data)) {\n\t\t\toptions.logger?.error(`ConfigVariables[${this.type}]: Invalid JSON data from ${options.fileName}`);\n\t\t\treturn {};\n\t\t}\n\t\treturn this.convertObjectToStringRecord(data);\n\t}\n\n\t/**\n\t * Converts an object to a record of strings as env values are always strings.\n\t */\n\tprivate convertObjectToStringRecord(data: object): Record<string, string> {\n\t\treturn Object.entries(data).reduce<Record<string, string>>((acc, [key, value]) => {\n\t\t\tif (value) {\n\t\t\t\tacc[key] = String(value);\n\t\t\t}\n\t\t\treturn acc;\n\t\t}, {});\n\t}\n}\n","import {type Loadable} from '@luolapeikko/ts-common';\nimport {parse} from 'dotenv';\nimport {AbstractFileRecordLoader, type AbstractFileRecordLoaderOptions} from './AbstractFileRecordLoader';\n\n/**\n * Loader for dotenv files, using the `dotenv` packages parser.\n * @since v0.6.1\n */\nexport class DotEnvLoader extends AbstractFileRecordLoader {\n\tpublic readonly type: Lowercase<string>;\n\n\tprotected defaultOptions: AbstractFileRecordLoaderOptions<'env'> = {\n\t\tdisabled: false,\n\t\tfileName: '.env',\n\t\tfileType: 'env',\n\t\tisSilent: true,\n\t\tlogger: undefined,\n\t\tvalidate: undefined,\n\t\twatch: false,\n\t};\n\n\tpublic constructor(options: Loadable<Partial<AbstractFileRecordLoaderOptions<'env'>>>, type: Lowercase<string> = 'dotenv') {\n\t\tsuper(options);\n\t\tthis.type = type;\n\t}\n\n\tprotected handleParse(rawData: Buffer): Record<string, string | undefined> {\n\t\treturn parse(rawData);\n\t}\n}\n"],"mappings":"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","names":["import_fs","import_promises","import_variable_util"]}
{"version":3,"sources":["../src/index.ts","../src/DockerSecretsConfigLoader.ts","../src/AbstractFileRecordLoader.ts","../src/FileConfigLoader.ts","../src/DotEnvLoader.ts"],"sourcesContent":["export * from './DockerSecretsConfigLoader';\nexport * from './FileConfigLoader';\nexport * from './DotEnvLoader';\nexport * from './AbstractFileRecordLoader';\n","import {existsSync} from 'fs';\nimport {readFile} from 'fs/promises';\nimport * as path from 'path';\nimport type {ILoggerLike} from '@avanio/logger-like';\nimport {ConfigLoader, type LoaderValue, VariableLookupError} from '@avanio/variable-util';\nimport {type Loadable} from '@luolapeikko/ts-common';\n\nexport interface DockerSecretsConfigLoaderOptions {\n\t/** force file name to lower case */\n\tfileLowerCase: boolean;\n\t/** path to docker secrets, default is '/run/secrets' */\n\tpath: string;\n\t/** set to false if need errors */\n\tisSilent: boolean;\n\t/** optional logger */\n\tlogger: ILoggerLike | undefined;\n\t/** set to true to disable loader, default is false */\n\tdisabled: boolean;\n}\n\n/**\n * Loader for docker secrets, reads secrets from the `/run/secrets` directory.\n * @since v0.8.0\n */\nexport class DockerSecretsConfigLoader extends ConfigLoader<string | undefined, Partial<DockerSecretsConfigLoaderOptions>, DockerSecretsConfigLoaderOptions> {\n\tpublic readonly type: Lowercase<string>;\n\tprivate valuePromises: Record<string, Promise<string | undefined> | undefined> = {};\n\tprotected defaultOptions: DockerSecretsConfigLoaderOptions = {\n\t\tdisabled: false,\n\t\tfileLowerCase: false,\n\t\tisSilent: true,\n\t\tlogger: undefined,\n\t\tpath: '/run/secrets',\n\t};\n\n\tpublic constructor(options: Loadable<Partial<DockerSecretsConfigLoaderOptions>>, type: Lowercase<string> = 'docker-secrets') {\n\t\tsuper(options);\n\t\tthis.getLoader = this.getLoader.bind(this);\n\t\tthis.type = type;\n\t}\n\n\tprotected async handleLoader(lookupKey: string, overrideKey?: string): Promise<LoaderValue> {\n\t\tconst options = await this.getOptions();\n\t\tif (options.disabled) {\n\t\t\treturn {type: this.type, result: undefined};\n\t\t}\n\t\tconst targetKey = overrideKey || lookupKey;\n\t\tconst filePath = this.filePath(targetKey, options);\n\t\tconst valuePromise = this.valuePromises[targetKey];\n\t\tconst seen = !!valuePromise; // if valuePromise exists, it means we have seen this key before\n\t\tif (!valuePromise) {\n\t\t\tif (!existsSync(filePath)) {\n\t\t\t\tif (!options.isSilent) {\n\t\t\t\t\tthrow new VariableLookupError(targetKey, `ConfigVariables[${this.type}]: ${lookupKey} from ${filePath} not found`);\n\t\t\t\t}\n\t\t\t\toptions.logger?.debug(`ConfigVariables[${this.type}]: ${lookupKey} from ${filePath} not found`);\n\t\t\t\tthis.valuePromises[targetKey] = Promise.resolve(undefined);\n\t\t\t} else {\n\t\t\t\tthis.valuePromises[targetKey] = readFile(filePath, 'utf8');\n\t\t\t}\n\t\t}\n\t\tconst value = await this.valuePromises[targetKey];\n\t\treturn {type: this.type, result: {path: filePath, value, seen}};\n\t}\n\n\tprivate filePath(key: string, options: DockerSecretsConfigLoaderOptions): string {\n\t\treturn path.join(path.resolve(options.path), options.fileLowerCase ? key.toLowerCase() : key);\n\t}\n}\n","import {existsSync, type FSWatcher, watch} from 'fs';\nimport {readFile} from 'fs/promises';\nimport type {ILoggerLike} from '@avanio/logger-like';\nimport {applyStringMap, type IConfigLoaderProps, type LoaderValue, MapConfigLoader, type ValidateCallback, VariableError} from '@avanio/variable-util';\nimport {Err, type IResult, Ok} from '@luolapeikko/result-option';\nimport {type Loadable, toError} from '@luolapeikko/ts-common';\n\n/**\n * Options for the AbstractFileRecordLoader.\n * @since v0.8.0\n */\nexport interface AbstractFileRecordLoaderOptions<FileType extends string> extends IConfigLoaderProps {\n\tfileType: FileType;\n\t/** file name to load */\n\tfileName: string;\n\t/** set to false if need errors */\n\tisSilent: boolean;\n\t/** optional logger */\n\tlogger: ILoggerLike | undefined;\n\t/** set to true to watch file for changes */\n\twatch: boolean;\n\t/** set to true to disable loader */\n\tdisabled: boolean;\n\t/**\n\t * optional validator for data (Record<string, string | undefined>)\n\t *\n\t * @example\n\t * // using zod\n\t * const stringRecordSchema = z.record(z.string().min(1), z.string());\n\t * const validate: ValidateCallback<Record<string, string>> = async (data) => {\n\t * const result = await stringRecordSchema.safeParseAsync(data);\n\t * if (!result.success) {\n\t * return {success: false, message: result.error.message};\n\t * }\n\t * return {success: true};\n\t * };\n\t */\n\tvalidate: ValidateCallback<Record<string, string | undefined>, Record<string, string | undefined>> | undefined;\n}\n\n/**\n * Abstract class for loading records from a file.\n * @since v0.8.0\n */\nexport abstract class AbstractFileRecordLoader<\n\tOptions extends AbstractFileRecordLoaderOptions<string> = AbstractFileRecordLoaderOptions<string>,\n> extends MapConfigLoader<string, Partial<Options>, Options> {\n\tabstract readonly type: Lowercase<string>;\n\tprivate watcher: FSWatcher | undefined;\n\tprivate timeout: ReturnType<typeof setTimeout> | undefined;\n\n\tprotected abstract defaultOptions: Options;\n\n\tpublic constructor(options: Loadable<Partial<Options>>) {\n\t\tsuper(options);\n\t\tthis.getLoader = this.getLoader.bind(this);\n\t\tthis.handleFileChange = this.handleFileChange.bind(this);\n\t}\n\n\t/**\n\t * If the loader is watching the file, it will stop watching.\n\t */\n\tpublic async close(): Promise<void> {\n\t\tif (this.watcher) {\n\t\t\tconst {logger, fileName} = await this.getOptions();\n\t\t\tlogger?.debug(this.buildErrorStr(`closing file watcher for ${fileName}`));\n\t\t\tthis.watcher.close();\n\t\t}\n\t}\n\n\tprotected async handleLoader(lookupKey: string, overrideKey: string | undefined): Promise<LoaderValue> {\n\t\tconst {disabled, fileName} = await this.getOptions();\n\t\tif (disabled) {\n\t\t\treturn {type: this.type, result: undefined};\n\t\t}\n\t\tif (!this._isLoaded) {\n\t\t\tawait this.loadData();\n\t\t\tthis._isLoaded = true; // only load data once to prevent spamming\n\t\t}\n\t\tconst targetKey = overrideKey || lookupKey; // optional override key, else use actual lookupKey\n\t\tconst value = this.data.get(targetKey);\n\t\treturn {type: this.type, result: {value, path: fileName, seen: this.handleSeen(targetKey, value)}};\n\t}\n\n\tprotected async handleData(): Promise<IResult<Record<string, string | undefined>, VariableError>> {\n\t\tconst options = await this.getOptions();\n\t\toptions.logger?.debug(this.buildErrorStr(`loading file ${options.fileName}`));\n\t\tif (!existsSync(options.fileName)) {\n\t\t\treturn Err(new VariableError(this.buildErrorStr(`file ${options.fileName} not found`)));\n\t\t}\n\t\tlet buffer;\n\t\ttry {\n\t\t\tbuffer = await readFile(options.fileName);\n\t\t} catch (unknownErr) {\n\t\t\treturn Err(new VariableError(toError(unknownErr).message));\n\t\t}\n\t\ttry {\n\t\t\tlet data = await this.handleParse(buffer, options);\n\t\t\tif (options.validate) {\n\t\t\t\tdata = await options.validate(data);\n\t\t\t}\n\t\t\tthis.handleFileWatch(options); // add watch after successful load\n\t\t\treturn Ok(data);\n\t\t} catch (_err) {\n\t\t\treturn Err(new VariableError(this.buildErrorStr(`file ${options.fileName} is not a valid ${options.fileType}`)));\n\t\t}\n\t}\n\n\tprotected async handleLoadData(): Promise<boolean> {\n\t\tconst {logger, isSilent} = await this.getOptions();\n\t\tconst res = await this.handleData();\n\t\tif (res.isErr) {\n\t\t\tif (!isSilent) {\n\t\t\t\tres.unwrap();\n\t\t\t} else {\n\t\t\t\tlogger?.debug(res.err());\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\t\tapplyStringMap(res.ok(), this.data);\n\t\treturn true;\n\t}\n\n\t/**\n\t * Handle the parsing of the file.\n\t */\n\tprotected abstract handleParse(rawData: Buffer, options: Options): Record<string, string | undefined> | Promise<Record<string, string | undefined>>;\n\n\tprivate handleFileWatch(options: Options): void {\n\t\tif (options.watch && !this.watcher) {\n\t\t\toptions.logger?.debug(this.buildErrorStr(`opening file watcher for ${options.fileName}`));\n\t\t\tthis.watcher = watch(options.fileName, () => {\n\t\t\t\tif (this.timeout) {\n\t\t\t\t\tclearTimeout(this.timeout);\n\t\t\t\t}\n\t\t\t\t// delay to prevent multiple reloads\n\t\t\t\tthis.timeout = setTimeout(() => {\n\t\t\t\t\tvoid this.handleFileChange(options);\n\t\t\t\t}, 200);\n\t\t\t});\n\t\t}\n\t}\n\n\tprivate async handleFileChange(options: Options): Promise<void> {\n\t\ttry {\n\t\t\toptions.logger?.debug(this.buildErrorStr(`file ${options.fileName} changed`));\n\t\t\tawait this.reload();\n\t\t} catch (err) {\n\t\t\toptions.logger?.error(this.buildErrorStr(`error reloading file ${options.fileName}: ${toError(err).message}`));\n\t\t}\n\t}\n}\n","import {type Loadable} from '@luolapeikko/ts-common';\nimport {AbstractFileRecordLoader, type AbstractFileRecordLoaderOptions} from './AbstractFileRecordLoader';\n\n/**\n * A file-based configuration loader that reads a JSON file.\n * @since v0.9.1\n */\nexport class FileConfigLoader extends AbstractFileRecordLoader<AbstractFileRecordLoaderOptions<'json'>> {\n\tpublic readonly type: Lowercase<string>;\n\n\tprotected defaultOptions: AbstractFileRecordLoaderOptions<'json'> = {\n\t\tdisabled: false,\n\t\tfileName: 'config.json',\n\t\tfileType: 'json',\n\t\tisSilent: true,\n\t\tlogger: undefined,\n\t\tvalidate: undefined,\n\t\twatch: false,\n\t};\n\n\tpublic constructor(options: Loadable<Partial<AbstractFileRecordLoaderOptions<'json'>>>, type: Lowercase<string> = 'file') {\n\t\tsuper(options);\n\t\tthis.type = type;\n\t}\n\n\tprotected handleParse(rawData: Buffer, options: AbstractFileRecordLoaderOptions<'json'>): Record<string, string | undefined> {\n\t\tconst data: unknown = JSON.parse(rawData.toString());\n\t\tif (typeof data !== 'object' || data === null || Array.isArray(data)) {\n\t\t\toptions.logger?.error(`ConfigVariables[${this.type}]: Invalid JSON data from ${options.fileName}`);\n\t\t\treturn {};\n\t\t}\n\t\treturn this.convertObjectToStringRecord(data);\n\t}\n\n\t/**\n\t * Converts an object to a record of strings as env values are always strings.\n\t */\n\tprivate convertObjectToStringRecord(data: object): Record<string, string> {\n\t\treturn Object.entries(data).reduce<Record<string, string>>((acc, [key, value]) => {\n\t\t\tif (value) {\n\t\t\t\tacc[key] = String(value);\n\t\t\t}\n\t\t\treturn acc;\n\t\t}, {});\n\t}\n}\n","import {type Loadable} from '@luolapeikko/ts-common';\nimport {parse} from 'dotenv';\nimport {AbstractFileRecordLoader, type AbstractFileRecordLoaderOptions} from './AbstractFileRecordLoader';\n\n/**\n * Loader for dotenv files, using the `dotenv` packages parser.\n * @since v0.6.1\n */\nexport class DotEnvLoader extends AbstractFileRecordLoader {\n\tpublic readonly type: Lowercase<string>;\n\n\tprotected defaultOptions: AbstractFileRecordLoaderOptions<'env'> = {\n\t\tdisabled: false,\n\t\tfileName: '.env',\n\t\tfileType: 'env',\n\t\tisSilent: true,\n\t\tlogger: undefined,\n\t\tvalidate: undefined,\n\t\twatch: false,\n\t};\n\n\tpublic constructor(options: Loadable<Partial<AbstractFileRecordLoaderOptions<'env'>>>, type: Lowercase<string> = 'dotenv') {\n\t\tsuper(options);\n\t\tthis.type = type;\n\t}\n\n\tprotected handleParse(rawData: Buffer): Record<string, string | undefined> {\n\t\treturn parse(rawData);\n\t}\n}\n"],"mappings":"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","names":["import_fs","import_promises","import_variable_util"]}

@@ -52,5 +52,6 @@ // src/DockerSecretsConfigLoader.ts

import { readFile as readFile2 } from "fs/promises";
import { RecordConfigLoader, VariableError } from "@avanio/variable-util";
import { applyStringMap, MapConfigLoader, VariableError } from "@avanio/variable-util";
import { Err, Ok } from "@luolapeikko/result-option";
import { toError } from "@luolapeikko/ts-common";
var AbstractFileRecordLoader = class extends RecordConfigLoader {
var AbstractFileRecordLoader = class extends MapConfigLoader {
watcher;

@@ -73,3 +74,3 @@ timeout;

}
async handleLoader(rootKey, key) {
async handleLoader(lookupKey, overrideKey) {
const { disabled, fileName } = await this.getOptions();

@@ -79,9 +80,9 @@ if (disabled) {

}
if (!this.dataPromise) {
this.dataPromise = this.handleData();
if (!this._isLoaded) {
await this.loadData();
this._isLoaded = true;
}
const data = await this.dataPromise;
const targetKey = key || rootKey;
const currentValue = data[targetKey];
return { type: this.type, result: { value: currentValue, path: fileName, seen: this.handleSeen(targetKey, currentValue) } };
const targetKey = overrideKey || lookupKey;
const value = this.data.get(targetKey);
return { type: this.type, result: { value, path: fileName, seen: this.handleSeen(targetKey, value) } };
}

@@ -91,28 +92,35 @@ async handleData() {

options.logger?.debug(this.buildErrorStr(`loading file ${options.fileName}`));
if (options.disabled) {
return {};
}
if (!existsSync2(options.fileName)) {
const msg = this.buildErrorStr(`file ${options.fileName} not found`);
if (options.isSilent) {
options.logger?.debug(msg);
return {};
}
throw new VariableError(msg);
return Err(new VariableError(this.buildErrorStr(`file ${options.fileName} not found`)));
}
let buffer;
try {
const data = await this.handleParse(await readFile2(options.fileName), options);
buffer = await readFile2(options.fileName);
} catch (unknownErr) {
return Err(new VariableError(toError(unknownErr).message));
}
try {
let data = await this.handleParse(buffer, options);
if (options.validate) {
return await options.validate(data);
data = await options.validate(data);
}
this.handleFileWatch(options);
return data;
return Ok(data);
} catch (_err) {
const msg = this.buildErrorStr(`file ${options.fileName} is not a valid ${options.fileType}`);
if (options.isSilent) {
options.logger?.info(msg);
return {};
return Err(new VariableError(this.buildErrorStr(`file ${options.fileName} is not a valid ${options.fileType}`)));
}
}
async handleLoadData() {
const { logger, isSilent } = await this.getOptions();
const res = await this.handleData();
if (res.isErr) {
if (!isSilent) {
res.unwrap();
} else {
logger?.debug(res.err());
}
throw new VariableError(msg);
return false;
}
applyStringMap(res.ok(), this.data);
return true;
}

@@ -119,0 +127,0 @@ handleFileWatch(options) {

@@ -1,1 +0,1 @@

{"version":3,"sources":["../src/DockerSecretsConfigLoader.ts","../src/AbstractFileRecordLoader.ts","../src/FileConfigLoader.ts","../src/DotEnvLoader.ts"],"sourcesContent":["import {existsSync} from 'fs';\nimport {readFile} from 'fs/promises';\nimport * as path from 'path';\nimport type {ILoggerLike} from '@avanio/logger-like';\nimport {ConfigLoader, type LoaderValue, VariableLookupError} from '@avanio/variable-util';\nimport {type Loadable} from '@luolapeikko/ts-common';\n\nexport interface DockerSecretsConfigLoaderOptions {\n\t/** force file name to lower case */\n\tfileLowerCase: boolean;\n\t/** path to docker secrets, default is '/run/secrets' */\n\tpath: string;\n\t/** set to false if need errors */\n\tisSilent: boolean;\n\t/** optional logger */\n\tlogger: ILoggerLike | undefined;\n\t/** set to true to disable loader, default is false */\n\tdisabled: boolean;\n}\n\n/**\n * Loader for docker secrets, reads secrets from the `/run/secrets` directory.\n * @since v0.8.0\n */\nexport class DockerSecretsConfigLoader extends ConfigLoader<string | undefined, Partial<DockerSecretsConfigLoaderOptions>, DockerSecretsConfigLoaderOptions> {\n\tpublic readonly type: Lowercase<string>;\n\tprivate valuePromises: Record<string, Promise<string | undefined> | undefined> = {};\n\tprotected defaultOptions: DockerSecretsConfigLoaderOptions = {\n\t\tdisabled: false,\n\t\tfileLowerCase: false,\n\t\tisSilent: true,\n\t\tlogger: undefined,\n\t\tpath: '/run/secrets',\n\t};\n\n\tpublic constructor(options: Loadable<Partial<DockerSecretsConfigLoaderOptions>>, type: Lowercase<string> = 'docker-secrets') {\n\t\tsuper(options);\n\t\tthis.getLoader = this.getLoader.bind(this);\n\t\tthis.type = type;\n\t}\n\n\tprotected async handleLoader(lookupKey: string, overrideKey?: string): Promise<LoaderValue> {\n\t\tconst options = await this.getOptions();\n\t\tif (options.disabled) {\n\t\t\treturn {type: this.type, result: undefined};\n\t\t}\n\t\tconst targetKey = overrideKey || lookupKey;\n\t\tconst filePath = this.filePath(targetKey, options);\n\t\tconst valuePromise = this.valuePromises[targetKey];\n\t\tconst seen = !!valuePromise; // if valuePromise exists, it means we have seen this key before\n\t\tif (!valuePromise) {\n\t\t\tif (!existsSync(filePath)) {\n\t\t\t\tif (!options.isSilent) {\n\t\t\t\t\tthrow new VariableLookupError(targetKey, `ConfigVariables[${this.type}]: ${lookupKey} from ${filePath} not found`);\n\t\t\t\t}\n\t\t\t\toptions.logger?.debug(`ConfigVariables[${this.type}]: ${lookupKey} from ${filePath} not found`);\n\t\t\t\tthis.valuePromises[targetKey] = Promise.resolve(undefined);\n\t\t\t} else {\n\t\t\t\tthis.valuePromises[targetKey] = readFile(filePath, 'utf8');\n\t\t\t}\n\t\t}\n\t\tconst value = await this.valuePromises[targetKey];\n\t\treturn {type: this.type, result: {path: filePath, value, seen}};\n\t}\n\n\tprivate filePath(key: string, options: DockerSecretsConfigLoaderOptions): string {\n\t\treturn path.join(path.resolve(options.path), options.fileLowerCase ? key.toLowerCase() : key);\n\t}\n}\n","import {existsSync, type FSWatcher, watch} from 'fs';\nimport {readFile} from 'fs/promises';\nimport type {ILoggerLike} from '@avanio/logger-like';\nimport {type IConfigLoaderProps, type LoaderValue, RecordConfigLoader, type ValidateCallback, VariableError} from '@avanio/variable-util';\nimport {type Loadable, toError} from '@luolapeikko/ts-common';\n\n/**\n * Options for the AbstractFileRecordLoader.\n * @since v0.8.0\n */\nexport interface AbstractFileRecordLoaderOptions<FileType extends string> extends IConfigLoaderProps {\n\tfileType: FileType;\n\t/** file name to load */\n\tfileName: string;\n\t/** set to false if need errors */\n\tisSilent: boolean;\n\t/** optional logger */\n\tlogger: ILoggerLike | undefined;\n\t/** set to true to watch file for changes */\n\twatch: boolean;\n\t/** set to true to disable loader */\n\tdisabled: boolean;\n\t/**\n\t * optional validator for data (Record<string, string | undefined>)\n\t *\n\t * @example\n\t * // using zod\n\t * const stringRecordSchema = z.record(z.string().min(1), z.string());\n\t * const validate: ValidateCallback<Record<string, string>> = async (data) => {\n\t * const result = await stringRecordSchema.safeParseAsync(data);\n\t * if (!result.success) {\n\t * return {success: false, message: result.error.message};\n\t * }\n\t * return {success: true};\n\t * };\n\t */\n\tvalidate: ValidateCallback<Record<string, string | undefined>, Record<string, string | undefined>> | undefined;\n}\n\n/**\n * Abstract class for loading records from a file.\n * @since v0.8.0\n */\nexport abstract class AbstractFileRecordLoader<\n\tOptions extends AbstractFileRecordLoaderOptions<string> = AbstractFileRecordLoaderOptions<string>,\n> extends RecordConfigLoader<string | undefined, Partial<Options>, Options> {\n\tabstract readonly type: Lowercase<string>;\n\tprivate watcher: FSWatcher | undefined;\n\tprivate timeout: ReturnType<typeof setTimeout> | undefined;\n\n\tprotected abstract defaultOptions: Options;\n\n\tpublic constructor(options: Loadable<Partial<Options>>) {\n\t\tsuper(options);\n\t\tthis.getLoader = this.getLoader.bind(this);\n\t\tthis.handleFileChange = this.handleFileChange.bind(this);\n\t}\n\n\t/**\n\t * If the loader is watching the file, it will stop watching.\n\t */\n\tpublic async close(): Promise<void> {\n\t\tif (this.watcher) {\n\t\t\tconst {logger, fileName} = await this.getOptions();\n\t\t\tlogger?.debug(this.buildErrorStr(`closing file watcher for ${fileName}`));\n\t\t\tthis.watcher.close();\n\t\t}\n\t}\n\n\tprotected async handleLoader(rootKey: string, key?: string): Promise<LoaderValue> {\n\t\tconst {disabled, fileName} = await this.getOptions();\n\t\tif (disabled) {\n\t\t\treturn {type: this.type, result: undefined};\n\t\t}\n\t\tif (!this.dataPromise) {\n\t\t\tthis.dataPromise = this.handleData();\n\t\t}\n\t\tconst data = await this.dataPromise;\n\t\tconst targetKey = key || rootKey;\n\t\tconst currentValue = data[targetKey];\n\t\treturn {type: this.type, result: {value: currentValue, path: fileName, seen: this.handleSeen(targetKey, currentValue)}};\n\t}\n\n\tprotected async handleData(): Promise<Record<string, string | undefined>> {\n\t\tconst options = await this.getOptions();\n\t\toptions.logger?.debug(this.buildErrorStr(`loading file ${options.fileName}`));\n\t\t// if file is disabled, return empty object\n\t\tif (options.disabled) {\n\t\t\treturn {};\n\t\t}\n\t\tif (!existsSync(options.fileName)) {\n\t\t\tconst msg = this.buildErrorStr(`file ${options.fileName} not found`);\n\t\t\tif (options.isSilent) {\n\t\t\t\toptions.logger?.debug(msg);\n\t\t\t\treturn {};\n\t\t\t}\n\t\t\tthrow new VariableError(msg);\n\t\t}\n\t\ttry {\n\t\t\tconst data = await this.handleParse(await readFile(options.fileName), options);\n\t\t\tif (options.validate) {\n\t\t\t\treturn await options.validate(data);\n\t\t\t}\n\t\t\tthis.handleFileWatch(options); // add watch after successful load\n\t\t\treturn data;\n\t\t} catch (_err) {\n\t\t\tconst msg = this.buildErrorStr(`file ${options.fileName} is not a valid ${options.fileType}`);\n\t\t\tif (options.isSilent) {\n\t\t\t\toptions.logger?.info(msg);\n\t\t\t\treturn {};\n\t\t\t}\n\t\t\tthrow new VariableError(msg);\n\t\t}\n\t}\n\n\t/**\n\t * Handle the parsing of the file.\n\t */\n\tprotected abstract handleParse(rawData: Buffer, options: Options): Record<string, string | undefined> | Promise<Record<string, string | undefined>>;\n\n\tprivate handleFileWatch(options: Options): void {\n\t\tif (options.watch && !this.watcher) {\n\t\t\toptions.logger?.debug(this.buildErrorStr(`opening file watcher for ${options.fileName}`));\n\t\t\tthis.watcher = watch(options.fileName, () => {\n\t\t\t\tif (this.timeout) {\n\t\t\t\t\tclearTimeout(this.timeout);\n\t\t\t\t}\n\t\t\t\t// delay to prevent multiple reloads\n\t\t\t\tthis.timeout = setTimeout(() => {\n\t\t\t\t\tvoid this.handleFileChange(options);\n\t\t\t\t}, 200);\n\t\t\t});\n\t\t}\n\t}\n\n\tprivate async handleFileChange(options: Options): Promise<void> {\n\t\ttry {\n\t\t\toptions.logger?.debug(this.buildErrorStr(`file ${options.fileName} changed`));\n\t\t\tawait this.reload();\n\t\t} catch (err) {\n\t\t\toptions.logger?.error(this.buildErrorStr(`error reloading file ${options.fileName}: ${toError(err).message}`));\n\t\t}\n\t}\n}\n","import {type Loadable} from '@luolapeikko/ts-common';\nimport {AbstractFileRecordLoader, type AbstractFileRecordLoaderOptions} from './AbstractFileRecordLoader';\n\n/**\n * A file-based configuration loader that reads a JSON file.\n * @since v0.9.1\n */\nexport class FileConfigLoader extends AbstractFileRecordLoader<AbstractFileRecordLoaderOptions<'json'>> {\n\tpublic readonly type: Lowercase<string>;\n\n\tprotected defaultOptions: AbstractFileRecordLoaderOptions<'json'> = {\n\t\tdisabled: false,\n\t\tfileName: 'config.json',\n\t\tfileType: 'json',\n\t\tisSilent: true,\n\t\tlogger: undefined,\n\t\tvalidate: undefined,\n\t\twatch: false,\n\t};\n\n\tpublic constructor(options: Loadable<Partial<AbstractFileRecordLoaderOptions<'json'>>>, type: Lowercase<string> = 'file') {\n\t\tsuper(options);\n\t\tthis.type = type;\n\t}\n\n\tprotected handleParse(rawData: Buffer, options: AbstractFileRecordLoaderOptions<'json'>): Record<string, string | undefined> {\n\t\tconst data: unknown = JSON.parse(rawData.toString());\n\t\tif (typeof data !== 'object' || data === null || Array.isArray(data)) {\n\t\t\toptions.logger?.error(`ConfigVariables[${this.type}]: Invalid JSON data from ${options.fileName}`);\n\t\t\treturn {};\n\t\t}\n\t\treturn this.convertObjectToStringRecord(data);\n\t}\n\n\t/**\n\t * Converts an object to a record of strings as env values are always strings.\n\t */\n\tprivate convertObjectToStringRecord(data: object): Record<string, string> {\n\t\treturn Object.entries(data).reduce<Record<string, string>>((acc, [key, value]) => {\n\t\t\tif (value) {\n\t\t\t\tacc[key] = String(value);\n\t\t\t}\n\t\t\treturn acc;\n\t\t}, {});\n\t}\n}\n","import {type Loadable} from '@luolapeikko/ts-common';\nimport {parse} from 'dotenv';\nimport {AbstractFileRecordLoader, type AbstractFileRecordLoaderOptions} from './AbstractFileRecordLoader';\n\n/**\n * Loader for dotenv files, using the `dotenv` packages parser.\n * @since v0.6.1\n */\nexport class DotEnvLoader extends AbstractFileRecordLoader {\n\tpublic readonly type: Lowercase<string>;\n\n\tprotected defaultOptions: AbstractFileRecordLoaderOptions<'env'> = {\n\t\tdisabled: false,\n\t\tfileName: '.env',\n\t\tfileType: 'env',\n\t\tisSilent: true,\n\t\tlogger: undefined,\n\t\tvalidate: undefined,\n\t\twatch: false,\n\t};\n\n\tpublic constructor(options: Loadable<Partial<AbstractFileRecordLoaderOptions<'env'>>>, type: Lowercase<string> = 'dotenv') {\n\t\tsuper(options);\n\t\tthis.type = type;\n\t}\n\n\tprotected handleParse(rawData: Buffer): Record<string, string | undefined> {\n\t\treturn parse(rawData);\n\t}\n}\n"],"mappings":"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","names":["existsSync","readFile"]}
{"version":3,"sources":["../src/DockerSecretsConfigLoader.ts","../src/AbstractFileRecordLoader.ts","../src/FileConfigLoader.ts","../src/DotEnvLoader.ts"],"sourcesContent":["import {existsSync} from 'fs';\nimport {readFile} from 'fs/promises';\nimport * as path from 'path';\nimport type {ILoggerLike} from '@avanio/logger-like';\nimport {ConfigLoader, type LoaderValue, VariableLookupError} from '@avanio/variable-util';\nimport {type Loadable} from '@luolapeikko/ts-common';\n\nexport interface DockerSecretsConfigLoaderOptions {\n\t/** force file name to lower case */\n\tfileLowerCase: boolean;\n\t/** path to docker secrets, default is '/run/secrets' */\n\tpath: string;\n\t/** set to false if need errors */\n\tisSilent: boolean;\n\t/** optional logger */\n\tlogger: ILoggerLike | undefined;\n\t/** set to true to disable loader, default is false */\n\tdisabled: boolean;\n}\n\n/**\n * Loader for docker secrets, reads secrets from the `/run/secrets` directory.\n * @since v0.8.0\n */\nexport class DockerSecretsConfigLoader extends ConfigLoader<string | undefined, Partial<DockerSecretsConfigLoaderOptions>, DockerSecretsConfigLoaderOptions> {\n\tpublic readonly type: Lowercase<string>;\n\tprivate valuePromises: Record<string, Promise<string | undefined> | undefined> = {};\n\tprotected defaultOptions: DockerSecretsConfigLoaderOptions = {\n\t\tdisabled: false,\n\t\tfileLowerCase: false,\n\t\tisSilent: true,\n\t\tlogger: undefined,\n\t\tpath: '/run/secrets',\n\t};\n\n\tpublic constructor(options: Loadable<Partial<DockerSecretsConfigLoaderOptions>>, type: Lowercase<string> = 'docker-secrets') {\n\t\tsuper(options);\n\t\tthis.getLoader = this.getLoader.bind(this);\n\t\tthis.type = type;\n\t}\n\n\tprotected async handleLoader(lookupKey: string, overrideKey?: string): Promise<LoaderValue> {\n\t\tconst options = await this.getOptions();\n\t\tif (options.disabled) {\n\t\t\treturn {type: this.type, result: undefined};\n\t\t}\n\t\tconst targetKey = overrideKey || lookupKey;\n\t\tconst filePath = this.filePath(targetKey, options);\n\t\tconst valuePromise = this.valuePromises[targetKey];\n\t\tconst seen = !!valuePromise; // if valuePromise exists, it means we have seen this key before\n\t\tif (!valuePromise) {\n\t\t\tif (!existsSync(filePath)) {\n\t\t\t\tif (!options.isSilent) {\n\t\t\t\t\tthrow new VariableLookupError(targetKey, `ConfigVariables[${this.type}]: ${lookupKey} from ${filePath} not found`);\n\t\t\t\t}\n\t\t\t\toptions.logger?.debug(`ConfigVariables[${this.type}]: ${lookupKey} from ${filePath} not found`);\n\t\t\t\tthis.valuePromises[targetKey] = Promise.resolve(undefined);\n\t\t\t} else {\n\t\t\t\tthis.valuePromises[targetKey] = readFile(filePath, 'utf8');\n\t\t\t}\n\t\t}\n\t\tconst value = await this.valuePromises[targetKey];\n\t\treturn {type: this.type, result: {path: filePath, value, seen}};\n\t}\n\n\tprivate filePath(key: string, options: DockerSecretsConfigLoaderOptions): string {\n\t\treturn path.join(path.resolve(options.path), options.fileLowerCase ? key.toLowerCase() : key);\n\t}\n}\n","import {existsSync, type FSWatcher, watch} from 'fs';\nimport {readFile} from 'fs/promises';\nimport type {ILoggerLike} from '@avanio/logger-like';\nimport {applyStringMap, type IConfigLoaderProps, type LoaderValue, MapConfigLoader, type ValidateCallback, VariableError} from '@avanio/variable-util';\nimport {Err, type IResult, Ok} from '@luolapeikko/result-option';\nimport {type Loadable, toError} from '@luolapeikko/ts-common';\n\n/**\n * Options for the AbstractFileRecordLoader.\n * @since v0.8.0\n */\nexport interface AbstractFileRecordLoaderOptions<FileType extends string> extends IConfigLoaderProps {\n\tfileType: FileType;\n\t/** file name to load */\n\tfileName: string;\n\t/** set to false if need errors */\n\tisSilent: boolean;\n\t/** optional logger */\n\tlogger: ILoggerLike | undefined;\n\t/** set to true to watch file for changes */\n\twatch: boolean;\n\t/** set to true to disable loader */\n\tdisabled: boolean;\n\t/**\n\t * optional validator for data (Record<string, string | undefined>)\n\t *\n\t * @example\n\t * // using zod\n\t * const stringRecordSchema = z.record(z.string().min(1), z.string());\n\t * const validate: ValidateCallback<Record<string, string>> = async (data) => {\n\t * const result = await stringRecordSchema.safeParseAsync(data);\n\t * if (!result.success) {\n\t * return {success: false, message: result.error.message};\n\t * }\n\t * return {success: true};\n\t * };\n\t */\n\tvalidate: ValidateCallback<Record<string, string | undefined>, Record<string, string | undefined>> | undefined;\n}\n\n/**\n * Abstract class for loading records from a file.\n * @since v0.8.0\n */\nexport abstract class AbstractFileRecordLoader<\n\tOptions extends AbstractFileRecordLoaderOptions<string> = AbstractFileRecordLoaderOptions<string>,\n> extends MapConfigLoader<string, Partial<Options>, Options> {\n\tabstract readonly type: Lowercase<string>;\n\tprivate watcher: FSWatcher | undefined;\n\tprivate timeout: ReturnType<typeof setTimeout> | undefined;\n\n\tprotected abstract defaultOptions: Options;\n\n\tpublic constructor(options: Loadable<Partial<Options>>) {\n\t\tsuper(options);\n\t\tthis.getLoader = this.getLoader.bind(this);\n\t\tthis.handleFileChange = this.handleFileChange.bind(this);\n\t}\n\n\t/**\n\t * If the loader is watching the file, it will stop watching.\n\t */\n\tpublic async close(): Promise<void> {\n\t\tif (this.watcher) {\n\t\t\tconst {logger, fileName} = await this.getOptions();\n\t\t\tlogger?.debug(this.buildErrorStr(`closing file watcher for ${fileName}`));\n\t\t\tthis.watcher.close();\n\t\t}\n\t}\n\n\tprotected async handleLoader(lookupKey: string, overrideKey: string | undefined): Promise<LoaderValue> {\n\t\tconst {disabled, fileName} = await this.getOptions();\n\t\tif (disabled) {\n\t\t\treturn {type: this.type, result: undefined};\n\t\t}\n\t\tif (!this._isLoaded) {\n\t\t\tawait this.loadData();\n\t\t\tthis._isLoaded = true; // only load data once to prevent spamming\n\t\t}\n\t\tconst targetKey = overrideKey || lookupKey; // optional override key, else use actual lookupKey\n\t\tconst value = this.data.get(targetKey);\n\t\treturn {type: this.type, result: {value, path: fileName, seen: this.handleSeen(targetKey, value)}};\n\t}\n\n\tprotected async handleData(): Promise<IResult<Record<string, string | undefined>, VariableError>> {\n\t\tconst options = await this.getOptions();\n\t\toptions.logger?.debug(this.buildErrorStr(`loading file ${options.fileName}`));\n\t\tif (!existsSync(options.fileName)) {\n\t\t\treturn Err(new VariableError(this.buildErrorStr(`file ${options.fileName} not found`)));\n\t\t}\n\t\tlet buffer;\n\t\ttry {\n\t\t\tbuffer = await readFile(options.fileName);\n\t\t} catch (unknownErr) {\n\t\t\treturn Err(new VariableError(toError(unknownErr).message));\n\t\t}\n\t\ttry {\n\t\t\tlet data = await this.handleParse(buffer, options);\n\t\t\tif (options.validate) {\n\t\t\t\tdata = await options.validate(data);\n\t\t\t}\n\t\t\tthis.handleFileWatch(options); // add watch after successful load\n\t\t\treturn Ok(data);\n\t\t} catch (_err) {\n\t\t\treturn Err(new VariableError(this.buildErrorStr(`file ${options.fileName} is not a valid ${options.fileType}`)));\n\t\t}\n\t}\n\n\tprotected async handleLoadData(): Promise<boolean> {\n\t\tconst {logger, isSilent} = await this.getOptions();\n\t\tconst res = await this.handleData();\n\t\tif (res.isErr) {\n\t\t\tif (!isSilent) {\n\t\t\t\tres.unwrap();\n\t\t\t} else {\n\t\t\t\tlogger?.debug(res.err());\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\t\tapplyStringMap(res.ok(), this.data);\n\t\treturn true;\n\t}\n\n\t/**\n\t * Handle the parsing of the file.\n\t */\n\tprotected abstract handleParse(rawData: Buffer, options: Options): Record<string, string | undefined> | Promise<Record<string, string | undefined>>;\n\n\tprivate handleFileWatch(options: Options): void {\n\t\tif (options.watch && !this.watcher) {\n\t\t\toptions.logger?.debug(this.buildErrorStr(`opening file watcher for ${options.fileName}`));\n\t\t\tthis.watcher = watch(options.fileName, () => {\n\t\t\t\tif (this.timeout) {\n\t\t\t\t\tclearTimeout(this.timeout);\n\t\t\t\t}\n\t\t\t\t// delay to prevent multiple reloads\n\t\t\t\tthis.timeout = setTimeout(() => {\n\t\t\t\t\tvoid this.handleFileChange(options);\n\t\t\t\t}, 200);\n\t\t\t});\n\t\t}\n\t}\n\n\tprivate async handleFileChange(options: Options): Promise<void> {\n\t\ttry {\n\t\t\toptions.logger?.debug(this.buildErrorStr(`file ${options.fileName} changed`));\n\t\t\tawait this.reload();\n\t\t} catch (err) {\n\t\t\toptions.logger?.error(this.buildErrorStr(`error reloading file ${options.fileName}: ${toError(err).message}`));\n\t\t}\n\t}\n}\n","import {type Loadable} from '@luolapeikko/ts-common';\nimport {AbstractFileRecordLoader, type AbstractFileRecordLoaderOptions} from './AbstractFileRecordLoader';\n\n/**\n * A file-based configuration loader that reads a JSON file.\n * @since v0.9.1\n */\nexport class FileConfigLoader extends AbstractFileRecordLoader<AbstractFileRecordLoaderOptions<'json'>> {\n\tpublic readonly type: Lowercase<string>;\n\n\tprotected defaultOptions: AbstractFileRecordLoaderOptions<'json'> = {\n\t\tdisabled: false,\n\t\tfileName: 'config.json',\n\t\tfileType: 'json',\n\t\tisSilent: true,\n\t\tlogger: undefined,\n\t\tvalidate: undefined,\n\t\twatch: false,\n\t};\n\n\tpublic constructor(options: Loadable<Partial<AbstractFileRecordLoaderOptions<'json'>>>, type: Lowercase<string> = 'file') {\n\t\tsuper(options);\n\t\tthis.type = type;\n\t}\n\n\tprotected handleParse(rawData: Buffer, options: AbstractFileRecordLoaderOptions<'json'>): Record<string, string | undefined> {\n\t\tconst data: unknown = JSON.parse(rawData.toString());\n\t\tif (typeof data !== 'object' || data === null || Array.isArray(data)) {\n\t\t\toptions.logger?.error(`ConfigVariables[${this.type}]: Invalid JSON data from ${options.fileName}`);\n\t\t\treturn {};\n\t\t}\n\t\treturn this.convertObjectToStringRecord(data);\n\t}\n\n\t/**\n\t * Converts an object to a record of strings as env values are always strings.\n\t */\n\tprivate convertObjectToStringRecord(data: object): Record<string, string> {\n\t\treturn Object.entries(data).reduce<Record<string, string>>((acc, [key, value]) => {\n\t\t\tif (value) {\n\t\t\t\tacc[key] = String(value);\n\t\t\t}\n\t\t\treturn acc;\n\t\t}, {});\n\t}\n}\n","import {type Loadable} from '@luolapeikko/ts-common';\nimport {parse} from 'dotenv';\nimport {AbstractFileRecordLoader, type AbstractFileRecordLoaderOptions} from './AbstractFileRecordLoader';\n\n/**\n * Loader for dotenv files, using the `dotenv` packages parser.\n * @since v0.6.1\n */\nexport class DotEnvLoader extends AbstractFileRecordLoader {\n\tpublic readonly type: Lowercase<string>;\n\n\tprotected defaultOptions: AbstractFileRecordLoaderOptions<'env'> = {\n\t\tdisabled: false,\n\t\tfileName: '.env',\n\t\tfileType: 'env',\n\t\tisSilent: true,\n\t\tlogger: undefined,\n\t\tvalidate: undefined,\n\t\twatch: false,\n\t};\n\n\tpublic constructor(options: Loadable<Partial<AbstractFileRecordLoaderOptions<'env'>>>, type: Lowercase<string> = 'dotenv') {\n\t\tsuper(options);\n\t\tthis.type = type;\n\t}\n\n\tprotected handleParse(rawData: Buffer): Record<string, string | undefined> {\n\t\treturn parse(rawData);\n\t}\n}\n"],"mappings":"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","names":["existsSync","readFile"]}
{
"name": "@avanio/variable-util-node",
"version": "0.12.2",
"version": "0.13.0",
"description": "nodejs env util",

@@ -26,31 +26,34 @@ "main": "./dist/index.js",

"@avanio/logger-like": "^0.2.11",
"@avanio/variable-util": "^0.12.2",
"@luolapeikko/ts-common": "^0.2.6",
"@stylistic/eslint-plugin": "^2.13.0",
"@stylistic/eslint-plugin-ts": "^2.13.0",
"@avanio/variable-util": "^0.13.0",
"@luolapeikko/result-option": "^1.0.5",
"@luolapeikko/ts-common": "^0.2.8",
"@stylistic/eslint-plugin": "^3.1.0",
"@stylistic/eslint-plugin-ts": "^3.1.0",
"@tsconfig/node18": "^18.2.4",
"@types/node": "^18.19.70",
"@types/sinon": "^17.0.3",
"@typescript-eslint/eslint-plugin": "^8.20.0",
"@typescript-eslint/parser": "^8.20.0",
"@vitest/coverage-v8": "^2.1.8",
"@types/node": "^22.13.5",
"@types/sinon": "^17.0.4",
"@typescript-eslint/eslint-plugin": "^8.25.0",
"@typescript-eslint/parser": "^8.25.0",
"@vitest/coverage-v8": "^3.0.7",
"c8": "^10.1.3",
"dotenv": "^16.4.7",
"eslint": "^8.57.1",
"eslint-config-prettier": "^9.1.0",
"eslint-config-prettier": "^10.0.2",
"eslint-config-standard": "^17.1.0",
"eslint-plugin-deprecation": "^3.0.0",
"eslint-plugin-prettier": "^5.2.2",
"eslint-import-resolver-typescript": "^3.8.0",
"eslint-plugin-import": "^2.31.0",
"eslint-plugin-prettier": "^5.2.3",
"eslint-plugin-sonarjs": "^0.25.1",
"prettier": "^3.4.2",
"prettier": "^3.5.2",
"sinon": "^19.0.2",
"source-map-support": "^0.5.21",
"tsup": "^8.3.5",
"tsup": "^8.4.0",
"typescript": "^5.7.3",
"vite": "^6.0.7",
"vitest": "^2.1.8"
"vite": "^6.2.0",
"vitest": "^3.0.7"
},
"peerDependencies": {
"@avanio/logger-like": "^0.1 || ^0.2",
"@avanio/variable-util": "^0.12",
"@avanio/variable-util": "^0.13",
"@luolapeikko/result-option": "^1.0",
"@luolapeikko/ts-common": "^0.1 || ^0.2",

@@ -57,0 +60,0 @@ "dotenv": "^16"