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.13.0
to
0.13.1
+3
-3
dist/index.d.mts

@@ -69,5 +69,5 @@ import { ILoggerLike } from '@avanio/logger-like';

abstract readonly type: Lowercase<string>;
protected abstract defaultOptions: Options;
private watcher;
private timeout;
protected abstract defaultOptions: Options;
constructor(options: Loadable<Partial<Options>>);

@@ -81,2 +81,4 @@ /**

protected handleLoadData(): Promise<boolean>;
private handleFileWatch;
private handleFileChange;
/**

@@ -86,4 +88,2 @@ * Handle the parsing of the file.

protected abstract handleParse(rawData: Buffer, options: Options): Record<string, string | undefined> | Promise<Record<string, string | undefined>>;
private handleFileWatch;
private handleFileChange;
}

@@ -90,0 +90,0 @@

@@ -69,5 +69,5 @@ import { ILoggerLike } from '@avanio/logger-like';

abstract readonly type: Lowercase<string>;
protected abstract defaultOptions: Options;
private watcher;
private timeout;
protected abstract defaultOptions: Options;
constructor(options: Loadable<Partial<Options>>);

@@ -81,2 +81,4 @@ /**

protected handleLoadData(): Promise<boolean>;
private handleFileWatch;
private handleFileChange;
/**

@@ -86,4 +88,2 @@ * Handle the parsing of the file.

protected abstract handleParse(rawData: Buffer, options: Options): Record<string, string | undefined> | Promise<Record<string, string | undefined>>;
private handleFileWatch;
private handleFileChange;
}

@@ -90,0 +90,0 @@

@@ -47,3 +47,3 @@ "use strict";

type;
valuePromises = {};
valuePromises = /* @__PURE__ */ new Map();
defaultOptions = {

@@ -66,7 +66,7 @@ disabled: false,

}
const targetKey = overrideKey || lookupKey;
const targetKey = overrideKey ?? lookupKey;
const filePath = this.filePath(targetKey, options);
const valuePromise = this.valuePromises[targetKey];
const seen = !!valuePromise;
if (!valuePromise) {
let valuePromise = this.valuePromises.get(targetKey) ?? Promise.resolve(void 0);
const seen = this.valuePromises.has(targetKey);
if (!seen) {
if (!(0, import_fs.existsSync)(filePath)) {

@@ -77,8 +77,8 @@ if (!options.isSilent) {

options.logger?.debug(`ConfigVariables[${this.type}]: ${lookupKey} from ${filePath} not found`);
this.valuePromises[targetKey] = Promise.resolve(void 0);
} else {
this.valuePromises[targetKey] = (0, import_promises.readFile)(filePath, "utf8");
valuePromise = (0, import_promises.readFile)(filePath, "utf8");
}
this.valuePromises.set(targetKey, valuePromise);
}
const value = await this.valuePromises[targetKey];
const value = await valuePromise;
return { type: this.type, result: { path: filePath, value, seen } };

@@ -124,3 +124,3 @@ }

}
const targetKey = overrideKey || lookupKey;
const targetKey = overrideKey ?? lookupKey;
const value = this.data.get(targetKey);

@@ -127,0 +127,0 @@ return { type: this.type, result: { value, path: fileName, seen: this.handleSeen(targetKey, value) } };

@@ -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 {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"]}
{"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 = new Map<string, Promise<string | 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\tlet valuePromise = this.valuePromises.get(targetKey) ?? Promise.resolve(undefined);\n\t\tconst seen = this.valuePromises.has(targetKey); // if valuePromise exists, it means we have seen this key before\n\t\tif (!seen) {\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} else {\n\t\t\t\tvaluePromise = readFile(filePath, 'utf8');\n\t\t\t}\n\t\t\t// store value promise as haven't seen this key before\n\t\t\tthis.valuePromises.set(targetKey, valuePromise);\n\t\t}\n\t\tconst value = await valuePromise;\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\tprotected abstract defaultOptions: Options;\n\tprivate watcher: FSWatcher | undefined;\n\tprivate timeout: ReturnType<typeof setTimeout> | undefined;\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\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\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","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"]}

@@ -8,3 +8,3 @@ // src/DockerSecretsConfigLoader.ts

type;
valuePromises = {};
valuePromises = /* @__PURE__ */ new Map();
defaultOptions = {

@@ -27,7 +27,7 @@ disabled: false,

}
const targetKey = overrideKey || lookupKey;
const targetKey = overrideKey ?? lookupKey;
const filePath = this.filePath(targetKey, options);
const valuePromise = this.valuePromises[targetKey];
const seen = !!valuePromise;
if (!valuePromise) {
let valuePromise = this.valuePromises.get(targetKey) ?? Promise.resolve(void 0);
const seen = this.valuePromises.has(targetKey);
if (!seen) {
if (!existsSync(filePath)) {

@@ -38,8 +38,8 @@ if (!options.isSilent) {

options.logger?.debug(`ConfigVariables[${this.type}]: ${lookupKey} from ${filePath} not found`);
this.valuePromises[targetKey] = Promise.resolve(void 0);
} else {
this.valuePromises[targetKey] = readFile(filePath, "utf8");
valuePromise = readFile(filePath, "utf8");
}
this.valuePromises.set(targetKey, valuePromise);
}
const value = await this.valuePromises[targetKey];
const value = await valuePromise;
return { type: this.type, result: { path: filePath, value, seen } };

@@ -85,3 +85,3 @@ }

}
const targetKey = overrideKey || lookupKey;
const targetKey = overrideKey ?? lookupKey;
const value = this.data.get(targetKey);

@@ -88,0 +88,0 @@ return { type: this.type, result: { value, path: fileName, seen: this.handleSeen(targetKey, value) } };

@@ -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 {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"]}
{"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 = new Map<string, Promise<string | 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\tlet valuePromise = this.valuePromises.get(targetKey) ?? Promise.resolve(undefined);\n\t\tconst seen = this.valuePromises.has(targetKey); // if valuePromise exists, it means we have seen this key before\n\t\tif (!seen) {\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} else {\n\t\t\t\tvaluePromise = readFile(filePath, 'utf8');\n\t\t\t}\n\t\t\t// store value promise as haven't seen this key before\n\t\t\tthis.valuePromises.set(targetKey, valuePromise);\n\t\t}\n\t\tconst value = await valuePromise;\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\tprotected abstract defaultOptions: Options;\n\tprivate watcher: FSWatcher | undefined;\n\tprivate timeout: ReturnType<typeof setTimeout> | undefined;\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\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\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","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.13.0",
"version": "0.13.1",
"description": "nodejs env util",

@@ -26,35 +26,37 @@ "main": "./dist/index.js",

"@avanio/logger-like": "^0.2.11",
"@avanio/variable-util": "^0.13.0",
"@avanio/variable-util": "^0.13.1",
"@cspell/eslint-plugin": "^8.17.5",
"@eslint/js": "^9.22.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",
"@luolapeikko/ts-common": "^0.3.3",
"@stylistic/eslint-plugin": "^4.2.0",
"@stylistic/eslint-plugin-ts": "^4.2.0",
"@tsconfig/node18": "^18.2.4",
"@types/node": "^22.13.5",
"@types/node": "^22.13.10",
"@types/sinon": "^17.0.4",
"@typescript-eslint/eslint-plugin": "^8.25.0",
"@typescript-eslint/parser": "^8.25.0",
"@vitest/coverage-v8": "^3.0.7",
"@typescript-eslint/eslint-plugin": "^8.26.0",
"@typescript-eslint/parser": "^8.26.0",
"@vitest/coverage-v8": "^3.0.8",
"c8": "^10.1.3",
"dotenv": "^16.4.7",
"eslint": "^8.57.1",
"eslint-config-prettier": "^10.0.2",
"eslint-config-standard": "^17.1.0",
"eslint-import-resolver-typescript": "^3.8.0",
"eslint": "^9.22.0",
"eslint-config-prettier": "^10.1.1",
"eslint-import-resolver-typescript": "^3.8.3",
"eslint-plugin-import": "^2.31.0",
"eslint-plugin-prettier": "^5.2.3",
"eslint-plugin-sonarjs": "^0.25.1",
"prettier": "^3.5.2",
"eslint-plugin-sonarjs": "^3.0.2",
"prettier": "^3.5.3",
"sinon": "^19.0.2",
"source-map-support": "^0.5.21",
"tsup": "^8.4.0",
"typescript": "^5.7.3",
"vite": "^6.2.0",
"vitest": "^3.0.7"
"typescript": "^5.8.2",
"typescript-eslint": "^8.26.0",
"vite": "^6.2.1",
"vitest": "^3.0.8"
},
"peerDependencies": {
"@avanio/logger-like": "^0.1 || ^0.2",
"@avanio/logger-like": ">= 0.1.0",
"@avanio/variable-util": "^0.13",
"@luolapeikko/result-option": "^1.0",
"@luolapeikko/ts-common": "^0.1 || ^0.2",
"@luolapeikko/ts-common": ">= 0.2.0",
"dotenv": "^16"

@@ -61,0 +63,0 @@ },