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
1.2.0
to
1.2.1
+4
-1
dist/index.js

@@ -82,2 +82,5 @@ "use strict";

// src/FileConfigLoader.ts
var import_ts_common2 = require("@luolapeikko/ts-common");
// src/AbstractFileRecordLoader.ts

@@ -206,3 +209,3 @@ var import_fs2 = require("fs");

return Object.entries(data).reduce((acc, [key, value]) => {
if (value) {
if (import_ts_common2.UndefCore.isNotNullish(value)) {
acc[key] = String(value);

@@ -209,0 +212,0 @@ }

+1
-1

@@ -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, type OverrideKeyMap, 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 * @template OverrideMap Type of the override keys\n * @since v1.0.0\n */\nexport class DockerSecretsConfigLoader<OverrideMap extends OverrideKeyMap = OverrideKeyMap> extends ConfigLoader<\n\tDockerSecretsConfigLoaderOptions,\n\tOverrideMap\n> {\n\tpublic readonly loaderType: 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(\n\t\toptions: Loadable<Partial<DockerSecretsConfigLoaderOptions>>,\n\t\toverrideKeys?: Partial<OverrideMap>,\n\t\tloaderType: Lowercase<string> = 'docker-secrets',\n\t) {\n\t\tsuper(options, overrideKeys);\n\t\tthis.loaderType = loaderType;\n\t}\n\n\tprotected async handleLoaderValue(lookupKey: string): Promise<LoaderValue> {\n\t\tconst options = await this.getOptions();\n\t\tconst filePath = this.filePath(lookupKey, options);\n\t\tlet valuePromise = this.valuePromises.get(lookupKey) ?? Promise.resolve(undefined);\n\t\tconst seen = this.valuePromises.has(lookupKey); // 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(lookupKey, `ConfigVariables[${this.loaderType}]: ${lookupKey} from ${filePath} not found`);\n\t\t\t\t}\n\t\t\t\toptions.logger?.debug(`ConfigVariables[${this.loaderType}]: ${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(lookupKey, valuePromise);\n\t\t}\n\t\treturn {path: filePath, value: await valuePromise};\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 {\n\tapplyStringMap,\n\ttype IConfigLoaderProps,\n\ttype LoaderValue,\n\tMapConfigLoader,\n\ttype OverrideKeyMap,\n\ttype ValidateCallback,\n\tVariableError,\n} from '@avanio/variable-util';\nimport {Err, type IResult, Ok} from '@luolapeikko/result-option';\nimport {ErrorCore, type Loadable} from '@luolapeikko/ts-common';\n\n/**\n * Options for the AbstractFileRecordLoader.\n * @template FileType Type of the file\n * @since v1.0.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 * @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 * @template Options Options for the loader\n * @template OverrideMap Type of the override keys\n * @since v1.0.0\n */\nexport abstract class AbstractFileRecordLoader<\n\tOptions extends AbstractFileRecordLoaderOptions<string> = AbstractFileRecordLoaderOptions<string>,\n\tOverrideMap extends OverrideKeyMap = OverrideKeyMap,\n> extends MapConfigLoader<Options, OverrideMap> {\n\tabstract readonly loaderType: 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>>, overrideKeys?: Partial<OverrideMap>) {\n\t\tsuper(options, overrideKeys);\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 handleLoaderValue(lookupKey: string): Promise<LoaderValue> {\n\t\tconst {fileName} = await this.getOptions();\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 value = this.data.get(lookupKey);\n\t\treturn {value, path: fileName};\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 (cause) {\n\t\t\treturn Err(new VariableError(ErrorCore.from(cause).message, {cause}));\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 (cause) {\n\t\t\treturn Err(new VariableError(this.buildErrorStr(`file ${options.fileName} is not a valid ${options.fileType}`), {cause}));\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}: ${ErrorCore.from(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 OverrideKeyMap} from '@avanio/variable-util';\nimport {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 * @template OverrideMap Type of the override keys\n * @since v1.0.0\n */\nexport class FileConfigLoader<OverrideMap extends OverrideKeyMap = OverrideKeyMap> extends AbstractFileRecordLoader<\n\tAbstractFileRecordLoaderOptions<'json'>,\n\tOverrideMap\n> {\n\tpublic readonly loaderType: 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(\n\t\toptions: Loadable<Partial<AbstractFileRecordLoaderOptions<'json'>>>,\n\t\toverrideKeys?: Partial<OverrideMap>,\n\t\ttype: Lowercase<string> = 'file',\n\t) {\n\t\tsuper(options, overrideKeys);\n\t\tthis.loaderType = 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.loaderType}]: 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 * @param {object} data The object to convert\n\t * @returns {Record<string, string>} The converted object\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 OverrideKeyMap} from '@avanio/variable-util';\nimport {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 * @template OverrideMap Type of the override keys\n * @since v1.0.0\n */\nexport class DotEnvLoader<OverrideMap extends OverrideKeyMap = OverrideKeyMap> extends AbstractFileRecordLoader<\n\tAbstractFileRecordLoaderOptions<string>,\n\tOverrideMap\n> {\n\tpublic readonly loaderType: 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(\n\t\toptions: Loadable<Partial<AbstractFileRecordLoaderOptions<'env'>>>,\n\t\toverrideKeys?: Partial<OverrideMap>,\n\t\ttype: Lowercase<string> = 'dotenv',\n\t) {\n\t\tsuper(options, overrideKeys);\n\t\tthis.loaderType = 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/FileConfigLoader.ts","../src/AbstractFileRecordLoader.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, type OverrideKeyMap, 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 * @template OverrideMap Type of the override keys\n * @since v1.0.0\n */\nexport class DockerSecretsConfigLoader<OverrideMap extends OverrideKeyMap = OverrideKeyMap> extends ConfigLoader<\n\tDockerSecretsConfigLoaderOptions,\n\tOverrideMap\n> {\n\tpublic readonly loaderType: 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(\n\t\toptions: Loadable<Partial<DockerSecretsConfigLoaderOptions>>,\n\t\toverrideKeys?: Partial<OverrideMap>,\n\t\tloaderType: Lowercase<string> = 'docker-secrets',\n\t) {\n\t\tsuper(options, overrideKeys);\n\t\tthis.loaderType = loaderType;\n\t}\n\n\tprotected async handleLoaderValue(lookupKey: string): Promise<LoaderValue> {\n\t\tconst options = await this.getOptions();\n\t\tconst filePath = this.filePath(lookupKey, options);\n\t\tlet valuePromise = this.valuePromises.get(lookupKey) ?? Promise.resolve(undefined);\n\t\tconst seen = this.valuePromises.has(lookupKey); // 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(lookupKey, `ConfigVariables[${this.loaderType}]: ${lookupKey} from ${filePath} not found`);\n\t\t\t\t}\n\t\t\t\toptions.logger?.debug(`ConfigVariables[${this.loaderType}]: ${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(lookupKey, valuePromise);\n\t\t}\n\t\treturn {path: filePath, value: await valuePromise};\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 {type OverrideKeyMap} from '@avanio/variable-util';\nimport {type Loadable, UndefCore} from '@luolapeikko/ts-common';\nimport {AbstractFileRecordLoader, type AbstractFileRecordLoaderOptions} from './AbstractFileRecordLoader';\n\n/**\n * A file-based configuration loader that reads a JSON file.\n * @template OverrideMap Type of the override keys\n * @since v1.0.0\n */\nexport class FileConfigLoader<OverrideMap extends OverrideKeyMap = OverrideKeyMap> extends AbstractFileRecordLoader<\n\tAbstractFileRecordLoaderOptions<'json'>,\n\tOverrideMap\n> {\n\tpublic readonly loaderType: 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(\n\t\toptions: Loadable<Partial<AbstractFileRecordLoaderOptions<'json'>>>,\n\t\toverrideKeys?: Partial<OverrideMap>,\n\t\ttype: Lowercase<string> = 'file',\n\t) {\n\t\tsuper(options, overrideKeys);\n\t\tthis.loaderType = 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.loaderType}]: 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 * @param {object} data The object to convert\n\t * @returns {Record<string, string>} The converted object\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 (UndefCore.isNotNullish(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 {existsSync, type FSWatcher, watch} from 'fs';\nimport {readFile} from 'fs/promises';\nimport type {ILoggerLike} from '@avanio/logger-like';\nimport {\n\tapplyStringMap,\n\ttype IConfigLoaderProps,\n\ttype LoaderValue,\n\tMapConfigLoader,\n\ttype OverrideKeyMap,\n\ttype ValidateCallback,\n\tVariableError,\n} from '@avanio/variable-util';\nimport {Err, type IResult, Ok} from '@luolapeikko/result-option';\nimport {ErrorCore, type Loadable} from '@luolapeikko/ts-common';\n\n/**\n * Options for the AbstractFileRecordLoader.\n * @template FileType Type of the file\n * @since v1.0.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 * @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 * @template Options Options for the loader\n * @template OverrideMap Type of the override keys\n * @since v1.0.0\n */\nexport abstract class AbstractFileRecordLoader<\n\tOptions extends AbstractFileRecordLoaderOptions<string> = AbstractFileRecordLoaderOptions<string>,\n\tOverrideMap extends OverrideKeyMap = OverrideKeyMap,\n> extends MapConfigLoader<Options, OverrideMap> {\n\tabstract readonly loaderType: 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>>, overrideKeys?: Partial<OverrideMap>) {\n\t\tsuper(options, overrideKeys);\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 handleLoaderValue(lookupKey: string): Promise<LoaderValue> {\n\t\tconst {fileName} = await this.getOptions();\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 value = this.data.get(lookupKey);\n\t\treturn {value, path: fileName};\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 (cause) {\n\t\t\treturn Err(new VariableError(ErrorCore.from(cause).message, {cause}));\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 (cause) {\n\t\t\treturn Err(new VariableError(this.buildErrorStr(`file ${options.fileName} is not a valid ${options.fileType}`), {cause}));\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}: ${ErrorCore.from(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 OverrideKeyMap} from '@avanio/variable-util';\nimport {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 * @template OverrideMap Type of the override keys\n * @since v1.0.0\n */\nexport class DotEnvLoader<OverrideMap extends OverrideKeyMap = OverrideKeyMap> extends AbstractFileRecordLoader<\n\tAbstractFileRecordLoaderOptions<string>,\n\tOverrideMap\n> {\n\tpublic readonly loaderType: 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(\n\t\toptions: Loadable<Partial<AbstractFileRecordLoaderOptions<'env'>>>,\n\t\toverrideKeys?: Partial<OverrideMap>,\n\t\ttype: Lowercase<string> = 'dotenv',\n\t) {\n\t\tsuper(options, overrideKeys);\n\t\tthis.loaderType = 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_ts_common","import_fs","import_promises","import_variable_util"]}

@@ -43,2 +43,5 @@ // src/DockerSecretsConfigLoader.ts

// src/FileConfigLoader.ts
import { UndefCore } from "@luolapeikko/ts-common";
// src/AbstractFileRecordLoader.ts

@@ -171,3 +174,3 @@ import { existsSync as existsSync2, watch } from "fs";

return Object.entries(data).reduce((acc, [key, value]) => {
if (value) {
if (UndefCore.isNotNullish(value)) {
acc[key] = String(value);

@@ -174,0 +177,0 @@ }

@@ -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, type OverrideKeyMap, 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 * @template OverrideMap Type of the override keys\n * @since v1.0.0\n */\nexport class DockerSecretsConfigLoader<OverrideMap extends OverrideKeyMap = OverrideKeyMap> extends ConfigLoader<\n\tDockerSecretsConfigLoaderOptions,\n\tOverrideMap\n> {\n\tpublic readonly loaderType: 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(\n\t\toptions: Loadable<Partial<DockerSecretsConfigLoaderOptions>>,\n\t\toverrideKeys?: Partial<OverrideMap>,\n\t\tloaderType: Lowercase<string> = 'docker-secrets',\n\t) {\n\t\tsuper(options, overrideKeys);\n\t\tthis.loaderType = loaderType;\n\t}\n\n\tprotected async handleLoaderValue(lookupKey: string): Promise<LoaderValue> {\n\t\tconst options = await this.getOptions();\n\t\tconst filePath = this.filePath(lookupKey, options);\n\t\tlet valuePromise = this.valuePromises.get(lookupKey) ?? Promise.resolve(undefined);\n\t\tconst seen = this.valuePromises.has(lookupKey); // 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(lookupKey, `ConfigVariables[${this.loaderType}]: ${lookupKey} from ${filePath} not found`);\n\t\t\t\t}\n\t\t\t\toptions.logger?.debug(`ConfigVariables[${this.loaderType}]: ${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(lookupKey, valuePromise);\n\t\t}\n\t\treturn {path: filePath, value: await valuePromise};\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 {\n\tapplyStringMap,\n\ttype IConfigLoaderProps,\n\ttype LoaderValue,\n\tMapConfigLoader,\n\ttype OverrideKeyMap,\n\ttype ValidateCallback,\n\tVariableError,\n} from '@avanio/variable-util';\nimport {Err, type IResult, Ok} from '@luolapeikko/result-option';\nimport {ErrorCore, type Loadable} from '@luolapeikko/ts-common';\n\n/**\n * Options for the AbstractFileRecordLoader.\n * @template FileType Type of the file\n * @since v1.0.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 * @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 * @template Options Options for the loader\n * @template OverrideMap Type of the override keys\n * @since v1.0.0\n */\nexport abstract class AbstractFileRecordLoader<\n\tOptions extends AbstractFileRecordLoaderOptions<string> = AbstractFileRecordLoaderOptions<string>,\n\tOverrideMap extends OverrideKeyMap = OverrideKeyMap,\n> extends MapConfigLoader<Options, OverrideMap> {\n\tabstract readonly loaderType: 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>>, overrideKeys?: Partial<OverrideMap>) {\n\t\tsuper(options, overrideKeys);\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 handleLoaderValue(lookupKey: string): Promise<LoaderValue> {\n\t\tconst {fileName} = await this.getOptions();\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 value = this.data.get(lookupKey);\n\t\treturn {value, path: fileName};\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 (cause) {\n\t\t\treturn Err(new VariableError(ErrorCore.from(cause).message, {cause}));\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 (cause) {\n\t\t\treturn Err(new VariableError(this.buildErrorStr(`file ${options.fileName} is not a valid ${options.fileType}`), {cause}));\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}: ${ErrorCore.from(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 OverrideKeyMap} from '@avanio/variable-util';\nimport {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 * @template OverrideMap Type of the override keys\n * @since v1.0.0\n */\nexport class FileConfigLoader<OverrideMap extends OverrideKeyMap = OverrideKeyMap> extends AbstractFileRecordLoader<\n\tAbstractFileRecordLoaderOptions<'json'>,\n\tOverrideMap\n> {\n\tpublic readonly loaderType: 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(\n\t\toptions: Loadable<Partial<AbstractFileRecordLoaderOptions<'json'>>>,\n\t\toverrideKeys?: Partial<OverrideMap>,\n\t\ttype: Lowercase<string> = 'file',\n\t) {\n\t\tsuper(options, overrideKeys);\n\t\tthis.loaderType = 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.loaderType}]: 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 * @param {object} data The object to convert\n\t * @returns {Record<string, string>} The converted object\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 OverrideKeyMap} from '@avanio/variable-util';\nimport {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 * @template OverrideMap Type of the override keys\n * @since v1.0.0\n */\nexport class DotEnvLoader<OverrideMap extends OverrideKeyMap = OverrideKeyMap> extends AbstractFileRecordLoader<\n\tAbstractFileRecordLoaderOptions<string>,\n\tOverrideMap\n> {\n\tpublic readonly loaderType: 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(\n\t\toptions: Loadable<Partial<AbstractFileRecordLoaderOptions<'env'>>>,\n\t\toverrideKeys?: Partial<OverrideMap>,\n\t\ttype: Lowercase<string> = 'dotenv',\n\t) {\n\t\tsuper(options, overrideKeys);\n\t\tthis.loaderType = 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/FileConfigLoader.ts","../src/AbstractFileRecordLoader.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, type OverrideKeyMap, 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 * @template OverrideMap Type of the override keys\n * @since v1.0.0\n */\nexport class DockerSecretsConfigLoader<OverrideMap extends OverrideKeyMap = OverrideKeyMap> extends ConfigLoader<\n\tDockerSecretsConfigLoaderOptions,\n\tOverrideMap\n> {\n\tpublic readonly loaderType: 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(\n\t\toptions: Loadable<Partial<DockerSecretsConfigLoaderOptions>>,\n\t\toverrideKeys?: Partial<OverrideMap>,\n\t\tloaderType: Lowercase<string> = 'docker-secrets',\n\t) {\n\t\tsuper(options, overrideKeys);\n\t\tthis.loaderType = loaderType;\n\t}\n\n\tprotected async handleLoaderValue(lookupKey: string): Promise<LoaderValue> {\n\t\tconst options = await this.getOptions();\n\t\tconst filePath = this.filePath(lookupKey, options);\n\t\tlet valuePromise = this.valuePromises.get(lookupKey) ?? Promise.resolve(undefined);\n\t\tconst seen = this.valuePromises.has(lookupKey); // 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(lookupKey, `ConfigVariables[${this.loaderType}]: ${lookupKey} from ${filePath} not found`);\n\t\t\t\t}\n\t\t\t\toptions.logger?.debug(`ConfigVariables[${this.loaderType}]: ${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(lookupKey, valuePromise);\n\t\t}\n\t\treturn {path: filePath, value: await valuePromise};\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 {type OverrideKeyMap} from '@avanio/variable-util';\nimport {type Loadable, UndefCore} from '@luolapeikko/ts-common';\nimport {AbstractFileRecordLoader, type AbstractFileRecordLoaderOptions} from './AbstractFileRecordLoader';\n\n/**\n * A file-based configuration loader that reads a JSON file.\n * @template OverrideMap Type of the override keys\n * @since v1.0.0\n */\nexport class FileConfigLoader<OverrideMap extends OverrideKeyMap = OverrideKeyMap> extends AbstractFileRecordLoader<\n\tAbstractFileRecordLoaderOptions<'json'>,\n\tOverrideMap\n> {\n\tpublic readonly loaderType: 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(\n\t\toptions: Loadable<Partial<AbstractFileRecordLoaderOptions<'json'>>>,\n\t\toverrideKeys?: Partial<OverrideMap>,\n\t\ttype: Lowercase<string> = 'file',\n\t) {\n\t\tsuper(options, overrideKeys);\n\t\tthis.loaderType = 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.loaderType}]: 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 * @param {object} data The object to convert\n\t * @returns {Record<string, string>} The converted object\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 (UndefCore.isNotNullish(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 {existsSync, type FSWatcher, watch} from 'fs';\nimport {readFile} from 'fs/promises';\nimport type {ILoggerLike} from '@avanio/logger-like';\nimport {\n\tapplyStringMap,\n\ttype IConfigLoaderProps,\n\ttype LoaderValue,\n\tMapConfigLoader,\n\ttype OverrideKeyMap,\n\ttype ValidateCallback,\n\tVariableError,\n} from '@avanio/variable-util';\nimport {Err, type IResult, Ok} from '@luolapeikko/result-option';\nimport {ErrorCore, type Loadable} from '@luolapeikko/ts-common';\n\n/**\n * Options for the AbstractFileRecordLoader.\n * @template FileType Type of the file\n * @since v1.0.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 * @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 * @template Options Options for the loader\n * @template OverrideMap Type of the override keys\n * @since v1.0.0\n */\nexport abstract class AbstractFileRecordLoader<\n\tOptions extends AbstractFileRecordLoaderOptions<string> = AbstractFileRecordLoaderOptions<string>,\n\tOverrideMap extends OverrideKeyMap = OverrideKeyMap,\n> extends MapConfigLoader<Options, OverrideMap> {\n\tabstract readonly loaderType: 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>>, overrideKeys?: Partial<OverrideMap>) {\n\t\tsuper(options, overrideKeys);\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 handleLoaderValue(lookupKey: string): Promise<LoaderValue> {\n\t\tconst {fileName} = await this.getOptions();\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 value = this.data.get(lookupKey);\n\t\treturn {value, path: fileName};\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 (cause) {\n\t\t\treturn Err(new VariableError(ErrorCore.from(cause).message, {cause}));\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 (cause) {\n\t\t\treturn Err(new VariableError(this.buildErrorStr(`file ${options.fileName} is not a valid ${options.fileType}`), {cause}));\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}: ${ErrorCore.from(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 OverrideKeyMap} from '@avanio/variable-util';\nimport {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 * @template OverrideMap Type of the override keys\n * @since v1.0.0\n */\nexport class DotEnvLoader<OverrideMap extends OverrideKeyMap = OverrideKeyMap> extends AbstractFileRecordLoader<\n\tAbstractFileRecordLoaderOptions<string>,\n\tOverrideMap\n> {\n\tpublic readonly loaderType: 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(\n\t\toptions: Loadable<Partial<AbstractFileRecordLoaderOptions<'env'>>>,\n\t\toverrideKeys?: Partial<OverrideMap>,\n\t\ttype: Lowercase<string> = 'dotenv',\n\t) {\n\t\tsuper(options, overrideKeys);\n\t\tthis.loaderType = 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": "1.2.0",
"version": "1.2.1",
"description": "nodejs env util",

@@ -5,0 +5,0 @@ "main": "./dist/index.js",