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

@avanio/variable-util-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.11.0
to
0.12.0
+3
-4
dist/index.d.mts

@@ -1,3 +0,4 @@

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

@@ -107,4 +108,2 @@ interface DockerSecretsConfigLoaderOptions {

declare function getError(error: unknown): Error;
export { AbstractFileRecordLoader, type AbstractFileRecordLoaderOptions, DockerSecretsConfigLoader, type DockerSecretsConfigLoaderOptions, DotEnvLoader, FileConfigLoader, getError };
export { AbstractFileRecordLoader, type AbstractFileRecordLoaderOptions, DockerSecretsConfigLoader, type DockerSecretsConfigLoaderOptions, DotEnvLoader, FileConfigLoader };

@@ -1,3 +0,4 @@

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

@@ -107,4 +108,2 @@ interface DockerSecretsConfigLoaderOptions {

declare function getError(error: unknown): Error;
export { AbstractFileRecordLoader, type AbstractFileRecordLoaderOptions, DockerSecretsConfigLoader, type DockerSecretsConfigLoaderOptions, DotEnvLoader, FileConfigLoader, getError };
export { AbstractFileRecordLoader, type AbstractFileRecordLoaderOptions, DockerSecretsConfigLoader, type DockerSecretsConfigLoaderOptions, DotEnvLoader, FileConfigLoader };

@@ -36,4 +36,3 @@ "use strict";

DotEnvLoader: () => DotEnvLoader,
FileConfigLoader: () => FileConfigLoader,
getError: () => getError
FileConfigLoader: () => FileConfigLoader
});

@@ -93,15 +92,3 @@ module.exports = __toCommonJS(src_exports);

var import_variable_util2 = require("@avanio/variable-util");
// src/errorUtil.ts
function getError(error) {
if (error instanceof Error) {
return error;
}
if (typeof error === "string") {
return new Error(error);
}
return new Error(`Unknown error: ${JSON.stringify(error)}`);
}
// src/AbstractFileRecordLoader.ts
var import_ts_common = require("@luolapeikko/ts-common");
var import_promises2 = require("fs/promises");

@@ -187,3 +174,3 @@ var AbstractFileRecordLoader = class extends import_variable_util2.RecordConfigLoader {

} catch (err) {
options.logger?.error(this.buildErrorStr(`error reloading file ${options.fileName}: ${getError(err).message}`));
options.logger?.error(this.buildErrorStr(`error reloading file ${options.fileName}: ${(0, import_ts_common.toError)(err).message}`));
}

@@ -240,5 +227,4 @@ }

DotEnvLoader,
FileConfigLoader,
getError
FileConfigLoader
});
//# sourceMappingURL=index.js.map

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

{"version":3,"sources":["../src/index.ts","../src/DockerSecretsConfigLoader.ts","../src/AbstractFileRecordLoader.ts","../src/errorUtil.ts","../src/FileConfigLoader.ts","../src/DotEnvLoader.ts"],"sourcesContent":["export * from './DockerSecretsConfigLoader';\nexport * from './FileConfigLoader';\nexport * from './DotEnvLoader';\nexport * from './AbstractFileRecordLoader';\nexport * from './errorUtil';\n","import * as path from 'path';\nimport {ConfigLoader, type Loadable, type LoaderValue, VariableLookupError} from '@avanio/variable-util';\nimport {existsSync} from 'fs';\nimport type {ILoggerLike} from '@avanio/logger-like';\nimport {readFile} from 'fs/promises';\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 {type IConfigLoaderProps, type Loadable, type LoaderValue, RecordConfigLoader, type ValidateCallback, VariableError} from '@avanio/variable-util';\nimport {getError} from './errorUtil';\nimport type {ILoggerLike} from '@avanio/logger-like';\nimport {readFile} from 'fs/promises';\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}: ${getError(err).message}`));\n\t\t}\n\t}\n}\n","export function getError(error: unknown): Error {\n\tif (error instanceof Error) {\n\t\treturn error;\n\t}\n\tif (typeof error === 'string') {\n\t\treturn new Error(error);\n\t}\n\treturn new Error(`Unknown error: ${JSON.stringify(error)}`);\n}\n","import {AbstractFileRecordLoader, type AbstractFileRecordLoaderOptions} from './AbstractFileRecordLoader';\nimport {type Loadable} from '@avanio/variable-util';\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\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 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\tprotected handleParse(rawData: Buffer): Record<string, string | undefined> {\n\t\treturn JSON.parse(rawData.toString()) as Record<string, string | undefined>;\n\t}\n}\n","import {AbstractFileRecordLoader, type AbstractFileRecordLoaderOptions} from './AbstractFileRecordLoader';\nimport {type Loadable} from '@avanio/variable-util';\nimport {parse} from 'dotenv';\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\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 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\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_variable_util","import_promises"]}
{"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 * as path from 'path';\nimport {ConfigLoader, type LoaderValue, VariableLookupError} from '@avanio/variable-util';\nimport {existsSync} from 'fs';\nimport type {ILoggerLike} from '@avanio/logger-like';\nimport {type Loadable} from '@luolapeikko/ts-common';\nimport {readFile} from 'fs/promises';\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 {type IConfigLoaderProps, type LoaderValue, RecordConfigLoader, type ValidateCallback, VariableError} from '@avanio/variable-util';\nimport {type Loadable, toError} from '@luolapeikko/ts-common';\nimport type {ILoggerLike} from '@avanio/logger-like';\nimport {readFile} from 'fs/promises';\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 {AbstractFileRecordLoader, type AbstractFileRecordLoaderOptions} from './AbstractFileRecordLoader';\nimport {type Loadable} from '@luolapeikko/ts-common';\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\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 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\tprotected handleParse(rawData: Buffer): Record<string, string | undefined> {\n\t\treturn JSON.parse(rawData.toString()) as Record<string, string | undefined>;\n\t}\n}\n","import {AbstractFileRecordLoader, type AbstractFileRecordLoaderOptions} from './AbstractFileRecordLoader';\nimport {type Loadable} from '@luolapeikko/ts-common';\nimport {parse} from 'dotenv';\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\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 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\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_variable_util","import_promises"]}

@@ -52,15 +52,3 @@ // src/DockerSecretsConfigLoader.ts

import { RecordConfigLoader, VariableError } from "@avanio/variable-util";
// src/errorUtil.ts
function getError(error) {
if (error instanceof Error) {
return error;
}
if (typeof error === "string") {
return new Error(error);
}
return new Error(`Unknown error: ${JSON.stringify(error)}`);
}
// src/AbstractFileRecordLoader.ts
import { toError } from "@luolapeikko/ts-common";
import { readFile as readFile2 } from "fs/promises";

@@ -146,3 +134,3 @@ var AbstractFileRecordLoader = class extends RecordConfigLoader {

} catch (err) {
options.logger?.error(this.buildErrorStr(`error reloading file ${options.fileName}: ${getError(err).message}`));
options.logger?.error(this.buildErrorStr(`error reloading file ${options.fileName}: ${toError(err).message}`));
}

@@ -198,5 +186,4 @@ }

DotEnvLoader,
FileConfigLoader,
getError
FileConfigLoader
};
//# sourceMappingURL=index.mjs.map

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

{"version":3,"sources":["../src/DockerSecretsConfigLoader.ts","../src/AbstractFileRecordLoader.ts","../src/errorUtil.ts","../src/FileConfigLoader.ts","../src/DotEnvLoader.ts"],"sourcesContent":["import * as path from 'path';\nimport {ConfigLoader, type Loadable, type LoaderValue, VariableLookupError} from '@avanio/variable-util';\nimport {existsSync} from 'fs';\nimport type {ILoggerLike} from '@avanio/logger-like';\nimport {readFile} from 'fs/promises';\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 {type IConfigLoaderProps, type Loadable, type LoaderValue, RecordConfigLoader, type ValidateCallback, VariableError} from '@avanio/variable-util';\nimport {getError} from './errorUtil';\nimport type {ILoggerLike} from '@avanio/logger-like';\nimport {readFile} from 'fs/promises';\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}: ${getError(err).message}`));\n\t\t}\n\t}\n}\n","export function getError(error: unknown): Error {\n\tif (error instanceof Error) {\n\t\treturn error;\n\t}\n\tif (typeof error === 'string') {\n\t\treturn new Error(error);\n\t}\n\treturn new Error(`Unknown error: ${JSON.stringify(error)}`);\n}\n","import {AbstractFileRecordLoader, type AbstractFileRecordLoaderOptions} from './AbstractFileRecordLoader';\nimport {type Loadable} from '@avanio/variable-util';\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\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 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\tprotected handleParse(rawData: Buffer): Record<string, string | undefined> {\n\t\treturn JSON.parse(rawData.toString()) as Record<string, string | undefined>;\n\t}\n}\n","import {AbstractFileRecordLoader, type AbstractFileRecordLoaderOptions} from './AbstractFileRecordLoader';\nimport {type Loadable} from '@avanio/variable-util';\nimport {parse} from 'dotenv';\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\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 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\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","existsSync"]}
{"version":3,"sources":["../src/DockerSecretsConfigLoader.ts","../src/AbstractFileRecordLoader.ts","../src/FileConfigLoader.ts","../src/DotEnvLoader.ts"],"sourcesContent":["import * as path from 'path';\nimport {ConfigLoader, type LoaderValue, VariableLookupError} from '@avanio/variable-util';\nimport {existsSync} from 'fs';\nimport type {ILoggerLike} from '@avanio/logger-like';\nimport {type Loadable} from '@luolapeikko/ts-common';\nimport {readFile} from 'fs/promises';\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 {type IConfigLoaderProps, type LoaderValue, RecordConfigLoader, type ValidateCallback, VariableError} from '@avanio/variable-util';\nimport {type Loadable, toError} from '@luolapeikko/ts-common';\nimport type {ILoggerLike} from '@avanio/logger-like';\nimport {readFile} from 'fs/promises';\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 {AbstractFileRecordLoader, type AbstractFileRecordLoaderOptions} from './AbstractFileRecordLoader';\nimport {type Loadable} from '@luolapeikko/ts-common';\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\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 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\tprotected handleParse(rawData: Buffer): Record<string, string | undefined> {\n\t\treturn JSON.parse(rawData.toString()) as Record<string, string | undefined>;\n\t}\n}\n","import {AbstractFileRecordLoader, type AbstractFileRecordLoaderOptions} from './AbstractFileRecordLoader';\nimport {type Loadable} from '@luolapeikko/ts-common';\nimport {parse} from 'dotenv';\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\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 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\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.11.0",
"version": "0.12.0",
"description": "nodejs env util",

@@ -26,13 +26,14 @@ "main": "./dist/index.js",

"@avanio/logger-like": "^0.2.7",
"@avanio/variable-util": "^0.11.0",
"@stylistic/eslint-plugin": "^2.10.1",
"@stylistic/eslint-plugin-ts": "^2.10.1",
"@avanio/variable-util": "^0.12.0",
"@luolapeikko/ts-common": "^0.2.4",
"@stylistic/eslint-plugin": "^2.12.0",
"@stylistic/eslint-plugin-ts": "^2.12.0",
"@tsconfig/node18": "^18.2.4",
"@types/node": "^18.19.64",
"@types/node": "^18.19.67",
"@types/sinon": "^17.0.3",
"@typescript-eslint/eslint-plugin": "^8.14.0",
"@typescript-eslint/parser": "^8.14.0",
"@vitest/coverage-v8": "^2.1.5",
"@typescript-eslint/eslint-plugin": "^8.17.0",
"@typescript-eslint/parser": "^8.17.0",
"@vitest/coverage-v8": "^2.1.8",
"c8": "^10.1.2",
"dotenv": "^16.4.5",
"dotenv": "^16.4.7",
"eslint": "^8.57.1",

@@ -44,13 +45,14 @@ "eslint-config-prettier": "^9.1.0",

"eslint-plugin-sonarjs": "^0.25.1",
"prettier": "^3.3.3",
"prettier": "^3.4.2",
"sinon": "^19.0.2",
"source-map-support": "^0.5.21",
"tsup": "^8.3.5",
"typescript": "^5.6.3",
"vite": "^5.4.11",
"vitest": "^2.1.4"
"typescript": "^5.7.2",
"vite": "^6.0.3",
"vitest": "^2.1.8"
},
"peerDependencies": {
"@avanio/logger-like": "^0.1 || ^0.2",
"@avanio/variable-util": "^0.11",
"@avanio/variable-util": "^0.12",
"@luolapeikko/ts-common": "^0.1 || ^0.2",
"dotenv": "^16"

@@ -57,0 +59,0 @@ },