@theunwalked/cardigantime 0.0.6 → 0.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cardigantime.cjs +17 -16
- package/dist/cardigantime.cjs.map +1 -1
- package/dist/read.js +9 -9
- package/dist/read.js.map +1 -1
- package/dist/util/hierarchical.js +6 -6
- package/dist/util/hierarchical.js.map +1 -1
- package/dist/validate.js +2 -1
- package/dist/validate.js.map +1 -1
- package/package.json +1 -1
package/dist/cardigantime.cjs
CHANGED
|
@@ -483,7 +483,7 @@ const create$1 = (params)=>{
|
|
|
483
483
|
currentDir = parentDir;
|
|
484
484
|
level++;
|
|
485
485
|
}
|
|
486
|
-
logger === null || logger === void 0 ? void 0 : logger.
|
|
486
|
+
logger === null || logger === void 0 ? void 0 : logger.verbose(`Discovery complete. Found ${discoveredDirs.length} config directories`);
|
|
487
487
|
return discoveredDirs;
|
|
488
488
|
}
|
|
489
489
|
/**
|
|
@@ -500,7 +500,7 @@ const create$1 = (params)=>{
|
|
|
500
500
|
});
|
|
501
501
|
const configFilePath = path.join(configDir, configFileName);
|
|
502
502
|
try {
|
|
503
|
-
logger === null || logger === void 0 ? void 0 : logger.
|
|
503
|
+
logger === null || logger === void 0 ? void 0 : logger.verbose(`Attempting to load config file: ${configFilePath}`);
|
|
504
504
|
const exists = await storage.exists(configFilePath);
|
|
505
505
|
if (!exists) {
|
|
506
506
|
logger === null || logger === void 0 ? void 0 : logger.debug(`Config file does not exist: ${configFilePath}`);
|
|
@@ -514,7 +514,7 @@ const create$1 = (params)=>{
|
|
|
514
514
|
const yamlContent = await storage.readFile(configFilePath, encoding);
|
|
515
515
|
const parsedYaml = yaml__namespace.load(yamlContent);
|
|
516
516
|
if (parsedYaml !== null && typeof parsedYaml === 'object') {
|
|
517
|
-
logger === null || logger === void 0 ? void 0 : logger.
|
|
517
|
+
logger === null || logger === void 0 ? void 0 : logger.verbose(`Successfully loaded config from: ${configFilePath}`);
|
|
518
518
|
return parsedYaml;
|
|
519
519
|
} else {
|
|
520
520
|
logger === null || logger === void 0 ? void 0 : logger.debug(`Config file contains invalid format: ${configFilePath}`);
|
|
@@ -623,11 +623,11 @@ const create$1 = (params)=>{
|
|
|
623
623
|
* ```
|
|
624
624
|
*/ async function loadHierarchicalConfig(options) {
|
|
625
625
|
const { configFileName, encoding = 'utf8', logger } = options;
|
|
626
|
-
logger === null || logger === void 0 ? void 0 : logger.
|
|
626
|
+
logger === null || logger === void 0 ? void 0 : logger.verbose('Starting hierarchical configuration loading');
|
|
627
627
|
// Discover all configuration directories
|
|
628
628
|
const discoveredDirs = await discoverConfigDirectories(options);
|
|
629
629
|
if (discoveredDirs.length === 0) {
|
|
630
|
-
logger === null || logger === void 0 ? void 0 : logger.
|
|
630
|
+
logger === null || logger === void 0 ? void 0 : logger.verbose('No configuration directories found');
|
|
631
631
|
return {
|
|
632
632
|
config: {},
|
|
633
633
|
discoveredDirs: [],
|
|
@@ -658,7 +658,7 @@ const create$1 = (params)=>{
|
|
|
658
658
|
}
|
|
659
659
|
// Merge all configurations with proper precedence
|
|
660
660
|
const mergedConfig = deepMergeConfigs(configs);
|
|
661
|
-
logger === null || logger === void 0 ? void 0 : logger.
|
|
661
|
+
logger === null || logger === void 0 ? void 0 : logger.verbose(`Hierarchical loading complete. Merged ${configs.length} configurations`);
|
|
662
662
|
return {
|
|
663
663
|
config: mergedConfig,
|
|
664
664
|
discoveredDirs,
|
|
@@ -765,11 +765,11 @@ const create$1 = (params)=>{
|
|
|
765
765
|
throw new Error('Configuration directory must be specified');
|
|
766
766
|
}
|
|
767
767
|
const resolvedConfigDir = validateConfigDirectory$1(rawConfigDir);
|
|
768
|
-
logger.
|
|
768
|
+
logger.verbose('Resolved config directory');
|
|
769
769
|
let rawFileConfig = {};
|
|
770
770
|
// Check if hierarchical configuration discovery is enabled
|
|
771
771
|
if (options.features.includes('hierarchical')) {
|
|
772
|
-
logger.
|
|
772
|
+
logger.verbose('Hierarchical configuration discovery enabled');
|
|
773
773
|
try {
|
|
774
774
|
// Extract the config directory name from the path for hierarchical discovery
|
|
775
775
|
const configDirName = path__namespace.basename(resolvedConfigDir);
|
|
@@ -784,12 +784,12 @@ const create$1 = (params)=>{
|
|
|
784
784
|
});
|
|
785
785
|
rawFileConfig = hierarchicalResult.config;
|
|
786
786
|
if (hierarchicalResult.discoveredDirs.length > 0) {
|
|
787
|
-
logger.
|
|
787
|
+
logger.verbose(`Hierarchical discovery found ${hierarchicalResult.discoveredDirs.length} configuration directories`);
|
|
788
788
|
hierarchicalResult.discoveredDirs.forEach((dir)=>{
|
|
789
789
|
logger.debug(` Level ${dir.level}: ${dir.path}`);
|
|
790
790
|
});
|
|
791
791
|
} else {
|
|
792
|
-
logger.
|
|
792
|
+
logger.verbose('No configuration directories found in hierarchy');
|
|
793
793
|
}
|
|
794
794
|
if (hierarchicalResult.errors.length > 0) {
|
|
795
795
|
hierarchicalResult.errors.forEach((error)=>logger.warn(`Hierarchical config warning: ${error}`));
|
|
@@ -797,12 +797,12 @@ const create$1 = (params)=>{
|
|
|
797
797
|
} catch (error) {
|
|
798
798
|
logger.error('Hierarchical configuration loading failed: ' + (error.message || 'Unknown error'));
|
|
799
799
|
// Fall back to single directory mode
|
|
800
|
-
logger.
|
|
800
|
+
logger.verbose('Falling back to single directory configuration loading');
|
|
801
801
|
rawFileConfig = await loadSingleDirectoryConfig(resolvedConfigDir, options, logger);
|
|
802
802
|
}
|
|
803
803
|
} else {
|
|
804
804
|
// Use traditional single directory configuration loading
|
|
805
|
-
logger.
|
|
805
|
+
logger.verbose('Using single directory configuration loading');
|
|
806
806
|
rawFileConfig = await loadSingleDirectoryConfig(resolvedConfigDir, options, logger);
|
|
807
807
|
}
|
|
808
808
|
const config = clean({
|
|
@@ -825,7 +825,7 @@ const create$1 = (params)=>{
|
|
|
825
825
|
log: logger.debug
|
|
826
826
|
});
|
|
827
827
|
const configFile = validatePath(options.defaults.configFile, resolvedConfigDir);
|
|
828
|
-
logger.
|
|
828
|
+
logger.verbose('Attempting to load config file for cardigantime');
|
|
829
829
|
let rawFileConfig = {};
|
|
830
830
|
try {
|
|
831
831
|
const yamlContent = await storage.readFile(configFile, options.defaults.encoding);
|
|
@@ -833,13 +833,13 @@ const create$1 = (params)=>{
|
|
|
833
833
|
const parsedYaml = yaml__namespace.load(yamlContent);
|
|
834
834
|
if (parsedYaml !== null && typeof parsedYaml === 'object') {
|
|
835
835
|
rawFileConfig = parsedYaml;
|
|
836
|
-
logger.
|
|
836
|
+
logger.verbose('Loaded configuration file successfully');
|
|
837
837
|
} else if (parsedYaml !== null) {
|
|
838
838
|
logger.warn('Ignoring invalid configuration format. Expected an object, got ' + typeof parsedYaml);
|
|
839
839
|
}
|
|
840
840
|
} catch (error) {
|
|
841
841
|
if (error.code === 'ENOENT' || /not found|no such file/i.test(error.message)) {
|
|
842
|
-
logger.
|
|
842
|
+
logger.verbose('Configuration file not found. Using empty configuration.');
|
|
843
843
|
} else {
|
|
844
844
|
// SECURITY FIX: Don't expose internal paths or detailed error information
|
|
845
845
|
logger.error('Failed to load or parse configuration file: ' + (error.message || 'Unknown error'));
|
|
@@ -1152,7 +1152,8 @@ class ConfigurationError extends Error {
|
|
|
1152
1152
|
checkForExtraKeys(config, fullSchema, logger);
|
|
1153
1153
|
if (!validationResult.success) {
|
|
1154
1154
|
const formattedError = JSON.stringify(validationResult.error.format(), null, 2);
|
|
1155
|
-
logger.error('Configuration validation failed
|
|
1155
|
+
logger.error('Configuration validation failed. Check logs for details.');
|
|
1156
|
+
logger.silly('Configuration validation failed: %s', formattedError);
|
|
1156
1157
|
throw ConfigurationError.validation('Configuration validation failed. Check logs for details.', validationResult.error);
|
|
1157
1158
|
}
|
|
1158
1159
|
return;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cardigantime.cjs","sources":["../src/error/ArgumentError.ts","../src/configure.ts","../src/constants.ts","../src/error/FileSystemError.ts","../src/util/storage.ts","../src/util/hierarchical.ts","../src/read.ts","../src/error/ConfigurationError.ts","../src/types.ts","../src/validate.ts","../src/cardigantime.ts"],"sourcesContent":["/**\n * Error thrown when CLI arguments or function parameters are invalid.\n * \n * This error provides specific context about which argument failed validation\n * and why, making it easier for users to fix their command-line usage or\n * for developers to debug parameter issues.\n * \n * @example\n * ```typescript\n * throw new ArgumentError('config-directory', 'Path cannot be empty');\n * // Error message: \"Path cannot be empty\"\n * // error.argument: \"config-directory\"\n * ```\n */\nexport class ArgumentError extends Error {\n /** The name of the argument that caused the error */\n private argumentName: string;\n\n /**\n * Creates a new ArgumentError instance.\n * \n * @param argumentName - The name of the invalid argument\n * @param message - Description of why the argument is invalid\n */\n constructor(argumentName: string, message: string) {\n super(`${message}`);\n this.name = 'ArgumentError';\n this.argumentName = argumentName;\n }\n\n /**\n * Gets the name of the argument that caused this error.\n * \n * @returns The argument name\n */\n get argument(): string {\n return this.argumentName;\n }\n}","import { Command } from \"commander\";\nimport { z } from \"zod\";\nimport { ArgumentError } from \"./error/ArgumentError\";\nimport { Options } from \"./types\";\nexport { ArgumentError };\n\n/**\n * Validates a configuration directory path to ensure it's safe and valid.\n * \n * Performs security and safety checks including:\n * - Non-empty string validation\n * - Null byte injection prevention\n * - Path length validation\n * - Type checking\n * \n * @param configDirectory - The configuration directory path to validate\n * @param _testThrowNonArgumentError - Internal testing parameter to simulate non-ArgumentError exceptions\n * @returns The trimmed and validated configuration directory path\n * @throws {ArgumentError} When the directory path is invalid\n * \n * @example\n * ```typescript\n * const validDir = validateConfigDirectory('./config'); // Returns './config'\n * const invalidDir = validateConfigDirectory(''); // Throws ArgumentError\n * ```\n */\nexport function validateConfigDirectory(configDirectory: string, _testThrowNonArgumentError?: boolean): string {\n if (_testThrowNonArgumentError) {\n throw new Error('Test non-ArgumentError for coverage');\n }\n\n if (!configDirectory) {\n throw new ArgumentError('configDirectory', 'Configuration directory cannot be empty');\n }\n\n if (typeof configDirectory !== 'string') {\n throw new ArgumentError('configDirectory', 'Configuration directory must be a string');\n }\n\n const trimmed = configDirectory.trim();\n if (trimmed.length === 0) {\n throw new ArgumentError('configDirectory', 'Configuration directory cannot be empty or whitespace only');\n }\n\n // Check for obviously invalid paths\n if (trimmed.includes('\\0')) {\n throw new ArgumentError('configDirectory', 'Configuration directory contains invalid null character');\n }\n\n // Validate path length (reasonable limit)\n if (trimmed.length > 1000) {\n throw new ArgumentError('configDirectory', 'Configuration directory path is too long (max 1000 characters)');\n }\n\n return trimmed;\n}\n\n/**\n * Configures a Commander.js command with Cardigantime's CLI options.\n * \n * This function adds command-line options that allow users to override\n * configuration settings at runtime, such as:\n * - --config-directory: Override the default configuration directory\n * \n * The function validates both the command object and the options to ensure\n * they meet the requirements for proper integration.\n * \n * @template T - The Zod schema shape type for configuration validation\n * @param command - The Commander.js Command instance to configure\n * @param options - Cardigantime options containing defaults and schema\n * @param _testThrowNonArgumentError - Internal testing parameter\n * @returns Promise resolving to the configured Command instance\n * @throws {ArgumentError} When command or options are invalid\n * \n * @example\n * ```typescript\n * import { Command } from 'commander';\n * import { configure } from './configure';\n * \n * const program = new Command();\n * const configuredProgram = await configure(program, options);\n * \n * // Now the program accepts: --config-directory <path>\n * ```\n */\nexport const configure = async <T extends z.ZodRawShape>(\n command: Command,\n options: Options<T>,\n _testThrowNonArgumentError?: boolean\n): Promise<Command> => {\n // Validate the command object\n if (!command) {\n throw new ArgumentError('command', 'Command instance is required');\n }\n\n if (typeof command.option !== 'function') {\n throw new ArgumentError('command', 'Command must be a valid Commander.js Command instance');\n }\n\n // Validate options\n if (!options) {\n throw new ArgumentError('options', 'Options object is required');\n }\n\n if (!options.defaults) {\n throw new ArgumentError('options.defaults', 'Options must include defaults configuration');\n }\n\n if (!options.defaults.configDirectory) {\n throw new ArgumentError('options.defaults.configDirectory', 'Default config directory is required');\n }\n\n // Validate the default config directory\n const validatedDefaultDir = validateConfigDirectory(options.defaults.configDirectory, _testThrowNonArgumentError);\n\n let retCommand = command;\n\n // Add the config directory option with validation\n retCommand = retCommand.option(\n '-c, --config-directory <configDirectory>',\n 'Configuration directory path',\n (value: string) => {\n try {\n return validateConfigDirectory(value, _testThrowNonArgumentError);\n } catch (error) {\n if (error instanceof ArgumentError) {\n // Re-throw with more specific context for CLI usage\n throw new ArgumentError('config-directory', `Invalid --config-directory: ${error.message}`);\n }\n throw error;\n }\n },\n validatedDefaultDir\n );\n\n return retCommand;\n}\n\n\n\n\n","import { DefaultOptions, Feature, Logger } from \"./types\";\n\n/** Version string populated at build time with git and system information */\nexport const VERSION = '__VERSION__ (__GIT_BRANCH__/__GIT_COMMIT__ __GIT_TAGS__ __GIT_COMMIT_DATE__) __SYSTEM_INFO__';\n\n/** The program name used in CLI help and error messages */\nexport const PROGRAM_NAME = 'cardigantime';\n\n/** Default file encoding for reading configuration files */\nexport const DEFAULT_ENCODING = 'utf8';\n\n/** Default configuration file name to look for in the config directory */\nexport const DEFAULT_CONFIG_FILE = 'config.yaml';\n\n/**\n * Default configuration options applied when creating a Cardigantime instance.\n * These provide sensible defaults that work for most use cases.\n */\nexport const DEFAULT_OPTIONS: Partial<DefaultOptions> = {\n configFile: DEFAULT_CONFIG_FILE,\n isRequired: false,\n encoding: DEFAULT_ENCODING,\n}\n\n/**\n * Default features enabled when creating a Cardigantime instance.\n * Currently includes only the 'config' feature for configuration file support.\n */\nexport const DEFAULT_FEATURES: Feature[] = ['config'];\n\n/**\n * Default logger implementation using console methods.\n * Provides basic logging functionality when no custom logger is specified.\n * The verbose and silly methods are no-ops to avoid excessive output.\n */\nexport const DEFAULT_LOGGER: Logger = {\n // eslint-disable-next-line no-console\n debug: console.debug,\n // eslint-disable-next-line no-console\n info: console.info,\n // eslint-disable-next-line no-console\n warn: console.warn,\n // eslint-disable-next-line no-console\n error: console.error,\n\n verbose: () => { },\n\n silly: () => { },\n}\n","/**\n * Error thrown when file system operations fail\n */\nexport class FileSystemError extends Error {\n public readonly errorType: 'not_found' | 'not_readable' | 'not_writable' | 'creation_failed' | 'operation_failed';\n public readonly path: string;\n public readonly operation: string;\n public readonly originalError?: Error;\n\n constructor(\n errorType: 'not_found' | 'not_readable' | 'not_writable' | 'creation_failed' | 'operation_failed',\n message: string,\n path: string,\n operation: string,\n originalError?: Error\n ) {\n super(message);\n this.name = 'FileSystemError';\n this.errorType = errorType;\n this.path = path;\n this.operation = operation;\n this.originalError = originalError;\n }\n\n /**\n * Creates an error for when a required directory doesn't exist\n */\n static directoryNotFound(path: string, isRequired: boolean = false): FileSystemError {\n const message = isRequired\n ? 'Configuration directory does not exist and is required'\n : 'Configuration directory not found';\n return new FileSystemError('not_found', message, path, 'directory_access');\n }\n\n /**\n * Creates an error for when a directory exists but isn't readable\n */\n static directoryNotReadable(path: string): FileSystemError {\n const message = 'Configuration directory exists but is not readable';\n return new FileSystemError('not_readable', message, path, 'directory_read');\n }\n\n /**\n * Creates an error for directory creation failures\n */\n static directoryCreationFailed(path: string, originalError: Error): FileSystemError {\n const message = 'Failed to create directory: ' + (originalError.message || 'Unknown error');\n return new FileSystemError('creation_failed', message, path, 'directory_create', originalError);\n }\n\n /**\n * Creates an error for file operation failures (glob, etc.)\n */\n static operationFailed(operation: string, path: string, originalError: Error): FileSystemError {\n const message = `Failed to ${operation}: ${originalError.message || 'Unknown error'}`;\n return new FileSystemError('operation_failed', message, path, operation, originalError);\n }\n\n /**\n * Creates an error for when a file is not found\n */\n static fileNotFound(path: string): FileSystemError {\n const message = 'Configuration file not found';\n return new FileSystemError('not_found', message, path, 'file_read');\n }\n} ","// eslint-disable-next-line no-restricted-imports\nimport * as fs from 'fs';\nimport { glob } from 'glob';\nimport path from 'path';\nimport crypto from 'crypto';\nimport { FileSystemError } from '../error/FileSystemError';\n/**\n * This module exists to isolate filesystem operations from the rest of the codebase.\n * This makes testing easier by avoiding direct fs mocking in jest configuration.\n * \n * Additionally, abstracting storage operations allows for future flexibility - \n * this export utility may need to work with storage systems other than the local filesystem\n * (e.g. S3, Google Cloud Storage, etc).\n */\n\nexport interface Utility {\n exists: (path: string) => Promise<boolean>;\n isDirectory: (path: string) => Promise<boolean>;\n isFile: (path: string) => Promise<boolean>;\n isReadable: (path: string) => Promise<boolean>;\n isWritable: (path: string) => Promise<boolean>;\n isFileReadable: (path: string) => Promise<boolean>;\n isDirectoryWritable: (path: string) => Promise<boolean>;\n isDirectoryReadable: (path: string) => Promise<boolean>;\n createDirectory: (path: string) => Promise<void>;\n readFile: (path: string, encoding: string) => Promise<string>;\n readStream: (path: string) => Promise<fs.ReadStream>;\n writeFile: (path: string, data: string | Buffer, encoding: string) => Promise<void>;\n forEachFileIn: (directory: string, callback: (path: string) => Promise<void>, options?: { pattern: string }) => Promise<void>;\n hashFile: (path: string, length: number) => Promise<string>;\n listFiles: (directory: string) => Promise<string[]>;\n}\n\nexport const create = (params: { log?: (message: string, ...args: any[]) => void }): Utility => {\n\n // eslint-disable-next-line no-console\n const log = params.log || console.log;\n\n const exists = async (path: string): Promise<boolean> => {\n try {\n await fs.promises.stat(path);\n return true;\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n } catch (error: any) {\n return false;\n }\n }\n\n const isDirectory = async (path: string): Promise<boolean> => {\n const stats = await fs.promises.stat(path);\n if (!stats.isDirectory()) {\n log(`${path} is not a directory`);\n return false;\n }\n return true;\n }\n\n const isFile = async (path: string): Promise<boolean> => {\n const stats = await fs.promises.stat(path);\n if (!stats.isFile()) {\n log(`${path} is not a file`);\n return false;\n }\n return true;\n }\n\n const isReadable = async (path: string): Promise<boolean> => {\n try {\n await fs.promises.access(path, fs.constants.R_OK);\n } catch (error: any) {\n log(`${path} is not readable: %s %s`, error.message, error.stack);\n return false;\n }\n return true;\n }\n\n const isWritable = async (path: string): Promise<boolean> => {\n try {\n await fs.promises.access(path, fs.constants.W_OK);\n } catch (error: any) {\n log(`${path} is not writable: %s %s`, error.message, error.stack);\n return false;\n }\n return true;\n }\n\n const isFileReadable = async (path: string): Promise<boolean> => {\n return await exists(path) && await isFile(path) && await isReadable(path);\n }\n\n const isDirectoryWritable = async (path: string): Promise<boolean> => {\n return await exists(path) && await isDirectory(path) && await isWritable(path);\n }\n\n const isDirectoryReadable = async (path: string): Promise<boolean> => {\n return await exists(path) && await isDirectory(path) && await isReadable(path);\n }\n\n const createDirectory = async (path: string): Promise<void> => {\n try {\n await fs.promises.mkdir(path, { recursive: true });\n } catch (mkdirError: any) {\n throw FileSystemError.directoryCreationFailed(path, mkdirError);\n }\n }\n\n const readFile = async (path: string, encoding: string): Promise<string> => {\n // Validate encoding parameter\n const validEncodings = ['utf8', 'utf-8', 'ascii', 'latin1', 'base64', 'hex', 'utf16le', 'ucs2', 'ucs-2'];\n if (!validEncodings.includes(encoding.toLowerCase())) {\n throw new Error('Invalid encoding specified');\n }\n\n // Check file size before reading to prevent DoS\n try {\n const stats = await fs.promises.stat(path);\n const maxFileSize = 10 * 1024 * 1024; // 10MB limit\n if (stats.size > maxFileSize) {\n throw new Error('File too large to process');\n }\n } catch (error: any) {\n if (error.code === 'ENOENT') {\n throw FileSystemError.fileNotFound(path);\n }\n throw error;\n }\n\n return await fs.promises.readFile(path, { encoding: encoding as BufferEncoding });\n }\n\n const writeFile = async (path: string, data: string | Buffer, encoding: string): Promise<void> => {\n await fs.promises.writeFile(path, data, { encoding: encoding as BufferEncoding });\n }\n\n const forEachFileIn = async (directory: string, callback: (file: string) => Promise<void>, options: { pattern: string | string[] } = { pattern: '*.*' }): Promise<void> => {\n try {\n const files = await glob(options.pattern, { cwd: directory, nodir: true });\n for (const file of files) {\n await callback(path.join(directory, file));\n }\n } catch (err: any) {\n throw FileSystemError.operationFailed(`glob pattern ${options.pattern}`, directory, err);\n }\n }\n\n const readStream = async (path: string): Promise<fs.ReadStream> => {\n return fs.createReadStream(path);\n }\n\n const hashFile = async (path: string, length: number): Promise<string> => {\n const file = await readFile(path, 'utf8');\n return crypto.createHash('sha256').update(file).digest('hex').slice(0, length);\n }\n\n const listFiles = async (directory: string): Promise<string[]> => {\n return await fs.promises.readdir(directory);\n }\n\n return {\n exists,\n isDirectory,\n isFile,\n isReadable,\n isWritable,\n isFileReadable,\n isDirectoryWritable,\n isDirectoryReadable,\n createDirectory,\n readFile,\n readStream,\n writeFile,\n forEachFileIn,\n hashFile,\n listFiles,\n };\n}","import path from 'path';\nimport * as yaml from 'js-yaml';\nimport { create as createStorage } from './storage';\nimport { Logger } from '../types';\n\n/**\n * Represents a discovered configuration directory with its path and precedence level.\n */\nexport interface DiscoveredConfigDir {\n /** Absolute path to the configuration directory */\n path: string;\n /** Distance from the starting directory (0 = closest/highest precedence) */\n level: number;\n}\n\n/**\n * Options for hierarchical configuration discovery.\n */\nexport interface HierarchicalDiscoveryOptions {\n /** Name of the configuration directory to look for (e.g., '.kodrdriv') */\n configDirName: string;\n /** Name of the configuration file within each directory */\n configFileName: string;\n /** Maximum number of parent directories to traverse (default: 10) */\n maxLevels?: number;\n /** Starting directory for discovery (default: process.cwd()) */\n startingDir?: string;\n /** File encoding for reading configuration files */\n encoding?: string;\n /** Logger for debugging */\n logger?: Logger;\n}\n\n/**\n * Result of loading configurations from multiple directories.\n */\nexport interface HierarchicalConfigResult {\n /** Merged configuration object with proper precedence */\n config: object;\n /** Array of directories where configuration was found */\n discoveredDirs: DiscoveredConfigDir[];\n /** Array of any errors encountered during loading (non-fatal) */\n errors: string[];\n}\n\n/**\n * Discovers configuration directories by traversing up the directory tree.\n * \n * Starting from the specified directory (or current working directory),\n * this function searches for directories with the given name, continuing\n * up the directory tree until it reaches the filesystem root or the\n * maximum number of levels.\n * \n * @param options Configuration options for discovery\n * @returns Promise resolving to array of discovered configuration directories\n * \n * @example\n * ```typescript\n * const dirs = await discoverConfigDirectories({\n * configDirName: '.kodrdriv',\n * configFileName: 'config.yaml',\n * maxLevels: 5\n * });\n * // Returns: [\n * // { path: '/project/.kodrdriv', level: 0 },\n * // { path: '/project/parent/.kodrdriv', level: 1 }\n * // ]\n * ```\n */\nexport async function discoverConfigDirectories(\n options: HierarchicalDiscoveryOptions\n): Promise<DiscoveredConfigDir[]> {\n const {\n configDirName,\n maxLevels = 10,\n startingDir = process.cwd(),\n logger\n } = options;\n\n const storage = createStorage({ log: logger?.debug || (() => { }) });\n const discoveredDirs: DiscoveredConfigDir[] = [];\n\n let currentDir = path.resolve(startingDir);\n let level = 0;\n const visited = new Set<string>(); // Prevent infinite loops with symlinks\n\n logger?.debug(`Starting hierarchical discovery from: ${currentDir}`);\n\n while (level < maxLevels) {\n // Prevent infinite loops with symlinks\n const realPath = path.resolve(currentDir);\n if (visited.has(realPath)) {\n logger?.debug(`Already visited ${realPath}, stopping discovery`);\n break;\n }\n visited.add(realPath);\n\n const configDirPath = path.join(currentDir, configDirName);\n logger?.debug(`Checking for config directory: ${configDirPath}`);\n\n try {\n const exists = await storage.exists(configDirPath);\n const isReadable = exists && await storage.isDirectoryReadable(configDirPath);\n\n if (exists && isReadable) {\n discoveredDirs.push({\n path: configDirPath,\n level\n });\n logger?.debug(`Found config directory at level ${level}: ${configDirPath}`);\n } else if (exists && !isReadable) {\n logger?.debug(`Config directory exists but is not readable: ${configDirPath}`);\n }\n } catch (error: any) {\n logger?.debug(`Error checking config directory ${configDirPath}: ${error.message}`);\n }\n\n // Move up one directory level\n const parentDir = path.dirname(currentDir);\n\n // Check if we've reached the root directory\n if (parentDir === currentDir) {\n logger?.debug('Reached filesystem root, stopping discovery');\n break;\n }\n\n currentDir = parentDir;\n level++;\n }\n\n logger?.debug(`Discovery complete. Found ${discoveredDirs.length} config directories`);\n return discoveredDirs;\n}\n\n/**\n * Loads and parses a configuration file from a directory.\n * \n * @param configDir Path to the configuration directory\n * @param configFileName Name of the configuration file\n * @param encoding File encoding\n * @param logger Optional logger\n * @returns Promise resolving to parsed configuration object or null if not found\n */\nexport async function loadConfigFromDirectory(\n configDir: string,\n configFileName: string,\n encoding: string = 'utf8',\n logger?: Logger\n): Promise<object | null> {\n const storage = createStorage({ log: logger?.debug || (() => { }) });\n const configFilePath = path.join(configDir, configFileName);\n\n try {\n logger?.debug(`Attempting to load config file: ${configFilePath}`);\n\n const exists = await storage.exists(configFilePath);\n if (!exists) {\n logger?.debug(`Config file does not exist: ${configFilePath}`);\n return null;\n }\n\n const isReadable = await storage.isFileReadable(configFilePath);\n if (!isReadable) {\n logger?.debug(`Config file exists but is not readable: ${configFilePath}`);\n return null;\n }\n\n const yamlContent = await storage.readFile(configFilePath, encoding);\n const parsedYaml = yaml.load(yamlContent);\n\n if (parsedYaml !== null && typeof parsedYaml === 'object') {\n logger?.debug(`Successfully loaded config from: ${configFilePath}`);\n return parsedYaml as object;\n } else {\n logger?.debug(`Config file contains invalid format: ${configFilePath}`);\n return null;\n }\n } catch (error: any) {\n logger?.debug(`Error loading config from ${configFilePath}: ${error.message}`);\n return null;\n }\n}\n\n/**\n * Deep merges multiple configuration objects with proper precedence.\n * \n * Objects are merged from lowest precedence to highest precedence,\n * meaning that properties in later objects override properties in earlier objects.\n * Arrays are replaced entirely (not merged).\n * \n * @param configs Array of configuration objects, ordered from lowest to highest precedence\n * @returns Merged configuration object\n * \n * @example\n * ```typescript\n * const merged = deepMergeConfigs([\n * { api: { timeout: 5000 }, debug: true }, // Lower precedence\n * { api: { retries: 3 }, features: ['auth'] }, // Higher precedence\n * ]);\n * // Result: { api: { timeout: 5000, retries: 3 }, debug: true, features: ['auth'] }\n * ```\n */\nexport function deepMergeConfigs(configs: object[]): object {\n if (configs.length === 0) {\n return {};\n }\n\n if (configs.length === 1) {\n return { ...configs[0] };\n }\n\n return configs.reduce((merged, current) => {\n return deepMergeTwo(merged, current);\n }, {});\n}\n\n/**\n * Deep merges two objects with proper precedence.\n * \n * @param target Target object (lower precedence)\n * @param source Source object (higher precedence)\n * @returns Merged object\n */\nfunction deepMergeTwo(target: any, source: any): any {\n // Handle null/undefined\n if (source == null) return target;\n if (target == null) return source;\n\n // Handle non-objects (primitives, arrays, functions, etc.)\n if (typeof source !== 'object' || typeof target !== 'object') {\n return source; // Source takes precedence\n }\n\n // Handle arrays - replace entirely, don't merge\n if (Array.isArray(source)) {\n return [...source];\n }\n\n if (Array.isArray(target)) {\n return source; // Source object replaces target array\n }\n\n // Deep merge objects\n const result = { ...target };\n\n for (const key in source) {\n if (Object.prototype.hasOwnProperty.call(source, key)) {\n if (Object.prototype.hasOwnProperty.call(result, key) &&\n typeof result[key] === 'object' &&\n typeof source[key] === 'object' &&\n !Array.isArray(source[key]) &&\n !Array.isArray(result[key])) {\n // Recursively merge nested objects\n result[key] = deepMergeTwo(result[key], source[key]);\n } else {\n // Replace with source value (higher precedence)\n result[key] = source[key];\n }\n }\n }\n\n return result;\n}\n\n/**\n * Loads configurations from multiple directories and merges them with proper precedence.\n * \n * This is the main function for hierarchical configuration loading. It:\n * 1. Discovers configuration directories up the directory tree\n * 2. Loads configuration files from each discovered directory\n * 3. Merges them with proper precedence (closer directories win)\n * 4. Returns the merged configuration with metadata\n * \n * @param options Configuration options for hierarchical loading\n * @returns Promise resolving to hierarchical configuration result\n * \n * @example\n * ```typescript\n * const result = await loadHierarchicalConfig({\n * configDirName: '.kodrdriv',\n * configFileName: 'config.yaml',\n * startingDir: '/project/subdir',\n * maxLevels: 5\n * });\n * \n * // result.config contains merged configuration\n * // result.discoveredDirs shows where configs were found\n * // result.errors contains any non-fatal errors\n * ```\n */\nexport async function loadHierarchicalConfig(\n options: HierarchicalDiscoveryOptions\n): Promise<HierarchicalConfigResult> {\n const { configFileName, encoding = 'utf8', logger } = options;\n\n logger?.debug('Starting hierarchical configuration loading');\n\n // Discover all configuration directories\n const discoveredDirs = await discoverConfigDirectories(options);\n\n if (discoveredDirs.length === 0) {\n logger?.debug('No configuration directories found');\n return {\n config: {},\n discoveredDirs: [],\n errors: []\n };\n }\n\n // Load configurations from each directory\n const configs: object[] = [];\n const errors: string[] = [];\n\n // Sort by level (highest level first = lowest precedence first)\n const sortedDirs = [...discoveredDirs].sort((a, b) => b.level - a.level);\n\n for (const dir of sortedDirs) {\n try {\n const config = await loadConfigFromDirectory(\n dir.path,\n configFileName,\n encoding,\n logger\n );\n\n if (config !== null) {\n configs.push(config);\n logger?.debug(`Loaded config from level ${dir.level}: ${dir.path}`);\n } else {\n logger?.debug(`No valid config found at level ${dir.level}: ${dir.path}`);\n }\n } catch (error: any) {\n const errorMsg = `Failed to load config from ${dir.path}: ${error.message}`;\n errors.push(errorMsg);\n logger?.debug(errorMsg);\n }\n }\n\n // Merge all configurations with proper precedence\n const mergedConfig = deepMergeConfigs(configs);\n\n logger?.debug(`Hierarchical loading complete. Merged ${configs.length} configurations`);\n\n return {\n config: mergedConfig,\n discoveredDirs,\n errors\n };\n} ","import * as yaml from 'js-yaml';\nimport * as path from 'path';\nimport { z, ZodObject } from 'zod';\nimport { Args, ConfigSchema, Options } from './types';\nimport * as Storage from './util/storage';\nimport { loadHierarchicalConfig } from './util/hierarchical';\n\n/**\n * Removes undefined values from an object to create a clean configuration.\n * This is used to merge configuration sources while avoiding undefined pollution.\n * \n * @param obj - The object to clean\n * @returns A new object with undefined values filtered out\n */\nfunction clean(obj: any) {\n return Object.fromEntries(\n Object.entries(obj).filter(([_, v]) => v !== undefined)\n );\n}\n\n/**\n * Validates and secures a user-provided path to prevent path traversal attacks.\n * \n * Security checks include:\n * - Path traversal prevention (blocks '..')\n * - Absolute path detection\n * - Path separator validation\n * \n * @param userPath - The user-provided path component\n * @param basePath - The base directory to join the path with\n * @returns The safely joined and normalized path\n * @throws {Error} When path traversal or absolute paths are detected\n */\nfunction validatePath(userPath: string, basePath: string): string {\n if (!userPath || !basePath) {\n throw new Error('Invalid path parameters');\n }\n\n const normalized = path.normalize(userPath);\n\n // Prevent path traversal attacks\n if (normalized.includes('..') || path.isAbsolute(normalized)) {\n throw new Error('Invalid path: path traversal detected');\n }\n\n // Ensure the path doesn't start with a path separator\n if (normalized.startsWith('/') || normalized.startsWith('\\\\')) {\n throw new Error('Invalid path: absolute path detected');\n }\n\n return path.join(basePath, normalized);\n}\n\n/**\n * Validates a configuration directory path for security and basic formatting.\n * \n * Performs validation to prevent:\n * - Null byte injection attacks\n * - Extremely long paths that could cause DoS\n * - Empty or invalid directory specifications\n * \n * @param configDir - The configuration directory path to validate\n * @returns The normalized configuration directory path\n * @throws {Error} When the directory path is invalid or potentially dangerous\n */\nfunction validateConfigDirectory(configDir: string): string {\n if (!configDir) {\n throw new Error('Configuration directory is required');\n }\n\n // Check for null bytes which could be used for path injection\n if (configDir.includes('\\0')) {\n throw new Error('Invalid path: null byte detected');\n }\n\n const normalized = path.normalize(configDir);\n\n // Basic validation - could be expanded based on requirements\n if (normalized.length > 1000) {\n throw new Error('Configuration directory path too long');\n }\n\n return normalized;\n}\n\n/**\n * Reads configuration from files and merges it with CLI arguments.\n * \n * This function implements the core configuration loading logic:\n * 1. Validates and resolves the configuration directory path\n * 2. Attempts to read the YAML configuration file\n * 3. Safely parses the YAML content with security protections\n * 4. Merges file configuration with runtime arguments\n * 5. Returns a typed configuration object\n * \n * The function handles missing files gracefully and provides detailed\n * logging for troubleshooting configuration issues.\n * \n * @template T - The Zod schema shape type for configuration validation\n * @param args - Parsed command-line arguments containing potential config overrides\n * @param options - Cardigantime options with defaults, schema, and logger\n * @returns Promise resolving to the merged and typed configuration object\n * @throws {Error} When configuration directory is invalid or required files cannot be read\n * \n * @example\n * ```typescript\n * const config = await read(cliArgs, {\n * defaults: { configDirectory: './config', configFile: 'app.yaml' },\n * configShape: MySchema.shape,\n * logger: console,\n * features: ['config']\n * });\n * // config is fully typed based on your schema\n * ```\n */\nexport const read = async <T extends z.ZodRawShape>(args: Args, options: Options<T>): Promise<z.infer<ZodObject<T & typeof ConfigSchema.shape>>> => {\n const logger = options.logger;\n\n const rawConfigDir = args.configDirectory || options.defaults?.configDirectory;\n if (!rawConfigDir) {\n throw new Error('Configuration directory must be specified');\n }\n\n const resolvedConfigDir = validateConfigDirectory(rawConfigDir);\n logger.debug('Resolved config directory');\n\n let rawFileConfig: object = {};\n\n // Check if hierarchical configuration discovery is enabled\n if (options.features.includes('hierarchical')) {\n logger.debug('Hierarchical configuration discovery enabled');\n\n try {\n // Extract the config directory name from the path for hierarchical discovery\n const configDirName = path.basename(resolvedConfigDir);\n const startingDir = path.dirname(resolvedConfigDir);\n\n logger.debug(`Using hierarchical discovery: configDirName=${configDirName}, startingDir=${startingDir}`);\n\n const hierarchicalResult = await loadHierarchicalConfig({\n configDirName,\n configFileName: options.defaults.configFile,\n startingDir,\n encoding: options.defaults.encoding,\n logger\n });\n\n rawFileConfig = hierarchicalResult.config;\n\n if (hierarchicalResult.discoveredDirs.length > 0) {\n logger.debug(`Hierarchical discovery found ${hierarchicalResult.discoveredDirs.length} configuration directories`);\n hierarchicalResult.discoveredDirs.forEach(dir => {\n logger.debug(` Level ${dir.level}: ${dir.path}`);\n });\n } else {\n logger.debug('No configuration directories found in hierarchy');\n }\n\n if (hierarchicalResult.errors.length > 0) {\n hierarchicalResult.errors.forEach(error => logger.warn(`Hierarchical config warning: ${error}`));\n }\n\n } catch (error: any) {\n logger.error('Hierarchical configuration loading failed: ' + (error.message || 'Unknown error'));\n // Fall back to single directory mode\n logger.debug('Falling back to single directory configuration loading');\n rawFileConfig = await loadSingleDirectoryConfig(resolvedConfigDir, options, logger);\n }\n } else {\n // Use traditional single directory configuration loading\n logger.debug('Using single directory configuration loading');\n rawFileConfig = await loadSingleDirectoryConfig(resolvedConfigDir, options, logger);\n }\n\n const config: z.infer<ZodObject<T & typeof ConfigSchema.shape>> = clean({\n ...rawFileConfig,\n ...{\n configDirectory: resolvedConfigDir,\n }\n }) as z.infer<ZodObject<T & typeof ConfigSchema.shape>>;\n\n return config;\n}\n\n/**\n * Loads configuration from a single directory (traditional mode).\n * \n * @param resolvedConfigDir - The resolved configuration directory path\n * @param options - Cardigantime options\n * @param logger - Logger instance\n * @returns Promise resolving to the configuration object\n */\nasync function loadSingleDirectoryConfig<T extends z.ZodRawShape>(\n resolvedConfigDir: string,\n options: Options<T>,\n logger: any\n): Promise<object> {\n const storage = Storage.create({ log: logger.debug });\n const configFile = validatePath(options.defaults.configFile, resolvedConfigDir);\n logger.debug('Attempting to load config file for cardigantime');\n\n let rawFileConfig: object = {};\n\n try {\n const yamlContent = await storage.readFile(configFile, options.defaults.encoding);\n\n // SECURITY FIX: Use safer parsing options to prevent code execution vulnerabilities\n const parsedYaml = yaml.load(yamlContent);\n\n if (parsedYaml !== null && typeof parsedYaml === 'object') {\n rawFileConfig = parsedYaml;\n logger.debug('Loaded configuration file successfully');\n } else if (parsedYaml !== null) {\n logger.warn('Ignoring invalid configuration format. Expected an object, got ' + typeof parsedYaml);\n }\n } catch (error: any) {\n if (error.code === 'ENOENT' || /not found|no such file/i.test(error.message)) {\n logger.debug('Configuration file not found. Using empty configuration.');\n } else {\n // SECURITY FIX: Don't expose internal paths or detailed error information\n logger.error('Failed to load or parse configuration file: ' + (error.message || 'Unknown error'));\n }\n }\n\n return rawFileConfig;\n}","/**\n * Error thrown when configuration validation fails\n */\nexport class ConfigurationError extends Error {\n public readonly errorType: 'validation' | 'schema' | 'extra_keys';\n public readonly details?: any;\n public readonly configPath?: string;\n\n constructor(\n errorType: 'validation' | 'schema' | 'extra_keys',\n message: string,\n details?: any,\n configPath?: string\n ) {\n super(message);\n this.name = 'ConfigurationError';\n this.errorType = errorType;\n this.details = details;\n this.configPath = configPath;\n }\n\n /**\n * Creates a validation error for when config doesn't match the schema\n */\n static validation(message: string, zodError?: any, configPath?: string): ConfigurationError {\n return new ConfigurationError('validation', message, zodError, configPath);\n }\n\n /**\n * Creates an error for when extra/unknown keys are found\n */\n static extraKeys(extraKeys: string[], allowedKeys: string[], configPath?: string): ConfigurationError {\n const message = `Unknown configuration keys found: ${extraKeys.join(', ')}. Allowed keys are: ${allowedKeys.join(', ')}`;\n return new ConfigurationError('extra_keys', message, { extraKeys, allowedKeys }, configPath);\n }\n\n /**\n * Creates a schema error for when the configuration schema itself is invalid\n */\n static schema(message: string, details?: any): ConfigurationError {\n return new ConfigurationError('schema', message, details);\n }\n} ","import { Command } from \"commander\";\nimport { ZodObject } from \"zod\";\n\nimport { z } from \"zod\";\n\n/**\n * Available features that can be enabled in Cardigantime.\n * Currently supports:\n * - 'config': Configuration file reading and validation\n * - 'hierarchical': Hierarchical configuration discovery and layering\n */\nexport type Feature = 'config' | 'hierarchical';\n\n/**\n * Default configuration options for Cardigantime.\n * These define the basic behavior of configuration loading.\n */\nexport interface DefaultOptions {\n /** Directory path where configuration files are located */\n configDirectory: string;\n /** Name of the configuration file (e.g., 'config.yaml', 'app.yml') */\n configFile: string;\n /** Whether the configuration directory must exist. If true, throws error if directory doesn't exist */\n isRequired: boolean;\n /** File encoding for reading configuration files (e.g., 'utf8', 'ascii') */\n encoding: string;\n}\n\n/**\n * Complete options object passed to Cardigantime functions.\n * Combines defaults, features, schema shape, and logger.\n * \n * @template T - The Zod schema shape type for configuration validation\n */\nexport interface Options<T extends z.ZodRawShape> {\n /** Default configuration options */\n defaults: DefaultOptions,\n /** Array of enabled features */\n features: Feature[],\n /** Zod schema shape for validating user configuration */\n configShape: T;\n /** Logger instance for debugging and error reporting */\n logger: Logger;\n}\n\n/**\n * Logger interface for Cardigantime's internal logging.\n * Compatible with popular logging libraries like Winston, Bunyan, etc.\n */\nexport interface Logger {\n /** Debug-level logging for detailed troubleshooting information */\n debug: (message: string, ...args: any[]) => void;\n /** Info-level logging for general information */\n info: (message: string, ...args: any[]) => void;\n /** Warning-level logging for non-critical issues */\n warn: (message: string, ...args: any[]) => void;\n /** Error-level logging for critical problems */\n error: (message: string, ...args: any[]) => void;\n /** Verbose-level logging for extensive detail */\n verbose: (message: string, ...args: any[]) => void;\n /** Silly-level logging for maximum detail */\n silly: (message: string, ...args: any[]) => void;\n}\n\n/**\n * Main Cardigantime interface providing configuration management functionality.\n * \n * @template T - The Zod schema shape type for configuration validation\n */\nexport interface Cardigantime<T extends z.ZodRawShape> {\n /** \n * Adds Cardigantime's CLI options to a Commander.js command.\n * This includes options like --config-directory for runtime config path overrides.\n */\n configure: (command: Command) => Promise<Command>;\n /** Sets a custom logger for debugging and error reporting */\n setLogger: (logger: Logger) => void;\n /** \n * Reads configuration from files and merges with CLI arguments.\n * Returns a fully typed configuration object.\n */\n read: (args: Args) => Promise<z.infer<ZodObject<T & typeof ConfigSchema.shape>>>;\n /** \n * Validates the merged configuration against the Zod schema.\n * Throws ConfigurationError if validation fails.\n */\n validate: (config: z.infer<ZodObject<T & typeof ConfigSchema.shape>>) => Promise<void>;\n}\n\n/**\n * Parsed command-line arguments object, typically from Commander.js opts().\n * Keys correspond to CLI option names with values from user input.\n */\nexport interface Args {\n [key: string]: any;\n}\n\n/**\n * Base Zod schema for core Cardigantime configuration.\n * Contains the minimum required configuration fields.\n */\nexport const ConfigSchema = z.object({\n /** The resolved configuration directory path */\n configDirectory: z.string(),\n});\n\n/**\n * Base configuration type derived from the core schema.\n */\nexport type Config = z.infer<typeof ConfigSchema>;\n","import { z, ZodObject } from \"zod\";\nimport { ArgumentError } from \"./error/ArgumentError\";\nimport { ConfigurationError } from \"./error/ConfigurationError\";\nimport { FileSystemError } from \"./error/FileSystemError\";\nimport { ConfigSchema, Logger, Options } from \"./types\";\nimport * as Storage from \"./util/storage\";\nexport { ArgumentError, ConfigurationError, FileSystemError };\n\n/**\n * Recursively extracts all keys from a Zod schema in dot notation.\n * \n * This function traverses a Zod schema structure and builds a flat list\n * of all possible keys, using dot notation for nested objects. It handles\n * optional/nullable types by unwrapping them and supports arrays by\n * introspecting their element type.\n * \n * Special handling for:\n * - ZodOptional/ZodNullable: Unwraps to get the underlying type\n * - ZodAny/ZodRecord: Accepts any keys, returns the prefix or empty array\n * - ZodArray: Introspects the element type\n * - ZodObject: Recursively processes all shape properties\n * \n * @param schema - The Zod schema to introspect\n * @param prefix - Internal parameter for building nested key paths\n * @returns Array of strings representing all possible keys in dot notation\n * \n * @example\n * ```typescript\n * const schema = z.object({\n * user: z.object({\n * name: z.string(),\n * settings: z.object({ theme: z.string() })\n * }),\n * debug: z.boolean()\n * });\n * \n * const keys = listZodKeys(schema);\n * // Returns: ['user.name', 'user.settings.theme', 'debug']\n * ```\n */\nexport const listZodKeys = (schema: z.ZodTypeAny, prefix = ''): string[] => {\n // Check if schema has unwrap method (which both ZodOptional and ZodNullable have)\n if (schema._def && (schema._def.typeName === 'ZodOptional' || schema._def.typeName === 'ZodNullable')) {\n // Use type assertion to handle the unwrap method\n const unwrappable = schema as z.ZodOptional<any> | z.ZodNullable<any>;\n return listZodKeys(unwrappable.unwrap(), prefix);\n }\n\n // Handle ZodAny and ZodRecord - these accept any keys, so don't introspect\n if (schema._def && (schema._def.typeName === 'ZodAny' || schema._def.typeName === 'ZodRecord')) {\n return prefix ? [prefix] : [];\n }\n\n if (schema._def && schema._def.typeName === 'ZodArray') {\n // Use type assertion to handle the element property\n const arraySchema = schema as z.ZodArray<any>;\n return listZodKeys(arraySchema.element, prefix);\n }\n if (schema._def && schema._def.typeName === 'ZodObject') {\n // Use type assertion to handle the shape property\n const objectSchema = schema as z.ZodObject<any>;\n return Object.entries(objectSchema.shape).flatMap(([key, subschema]) => {\n const fullKey = prefix ? `${prefix}.${key}` : key;\n const nested = listZodKeys(subschema as z.ZodTypeAny, fullKey);\n return nested.length ? nested : fullKey;\n });\n }\n return [];\n}\n\n/**\n * Type guard to check if a value is a plain object (not array, null, or other types).\n * \n * @param value - The value to check\n * @returns True if the value is a plain object\n */\nconst isPlainObject = (value: unknown): value is Record<string, unknown> => {\n // Check if it's an object, not null, and not an array.\n return value !== null && typeof value === 'object' && !Array.isArray(value);\n};\n\n/**\n * Generates a list of all keys within a JavaScript object, using dot notation for nested keys.\n * Mimics the behavior of listZodKeys but operates on plain objects.\n * For arrays, it inspects the first element that is a plain object to determine nested keys.\n * If an array contains no plain objects, or is empty, the key for the array itself is listed.\n *\n * @param obj The object to introspect.\n * @param prefix Internal use for recursion: the prefix for the current nesting level.\n * @returns An array of strings representing all keys in dot notation.\n */\nexport const listObjectKeys = (obj: Record<string, unknown>, prefix = ''): string[] => {\n const keys = new Set<string>(); // Use Set to automatically handle duplicates from array recursion\n\n for (const key in obj) {\n // Ensure it's an own property, not from the prototype chain\n if (Object.prototype.hasOwnProperty.call(obj, key)) {\n const value = obj[key];\n const fullKey = prefix ? `${prefix}.${key}` : key;\n\n if (Array.isArray(value)) {\n // Find the first element that is a plain object to determine structure\n const firstObjectElement = value.find(isPlainObject);\n if (firstObjectElement) {\n // Recurse into the structure of the first object element found\n const nestedKeys = listObjectKeys(firstObjectElement, fullKey);\n nestedKeys.forEach(k => keys.add(k));\n } else {\n // Array is empty or contains no plain objects, list the array key itself\n keys.add(fullKey);\n }\n } else if (isPlainObject(value)) {\n // Recurse into nested plain objects\n const nestedKeys = listObjectKeys(value, fullKey);\n nestedKeys.forEach(k => keys.add(k));\n } else {\n // It's a primitive, null, or other non-plain object/array type\n keys.add(fullKey);\n }\n }\n }\n return Array.from(keys); // Convert Set back to Array\n};\n\n/**\n * Validates that the configuration object contains only keys allowed by the schema.\n * \n * This function prevents configuration errors by detecting typos or extra keys\n * that aren't defined in the Zod schema. It intelligently handles:\n * - ZodRecord types that accept arbitrary keys\n * - Nested objects and their key structures\n * - Arrays and their element key structures\n * \n * The function throws a ConfigurationError if extra keys are found, providing\n * helpful information about what keys are allowed vs. what was found.\n * \n * @param mergedSources - The merged configuration object to validate\n * @param fullSchema - The complete Zod schema including base and user schemas\n * @param logger - Logger for error reporting\n * @throws {ConfigurationError} When extra keys are found that aren't in the schema\n * \n * @example\n * ```typescript\n * const schema = z.object({ name: z.string(), age: z.number() });\n * const config = { name: 'John', age: 30, typo: 'invalid' };\n * \n * checkForExtraKeys(config, schema, console);\n * // Throws: ConfigurationError with details about 'typo' being an extra key\n * ```\n */\nexport const checkForExtraKeys = (mergedSources: object, fullSchema: ZodObject<any>, logger: Logger | typeof console): void => {\n const allowedKeys = new Set(listZodKeys(fullSchema));\n const actualKeys = listObjectKeys(mergedSources as Record<string, unknown>);\n\n // Filter out keys that are under a record type (ZodRecord accepts any keys)\n const recordPrefixes = new Set<string>();\n\n // Find all prefixes that are ZodRecord types\n const findRecordPrefixes = (schema: z.ZodTypeAny, prefix = ''): void => {\n if (schema._def && (schema._def.typeName === 'ZodOptional' || schema._def.typeName === 'ZodNullable')) {\n const unwrappable = schema as z.ZodOptional<any> | z.ZodNullable<any>;\n findRecordPrefixes(unwrappable.unwrap(), prefix);\n return;\n }\n\n if (schema._def && (schema._def.typeName === 'ZodAny' || schema._def.typeName === 'ZodRecord')) {\n if (prefix) recordPrefixes.add(prefix);\n return;\n }\n\n if (schema._def && schema._def.typeName === 'ZodObject') {\n const objectSchema = schema as z.ZodObject<any>;\n Object.entries(objectSchema.shape).forEach(([key, subschema]) => {\n const fullKey = prefix ? `${prefix}.${key}` : key;\n findRecordPrefixes(subschema as z.ZodTypeAny, fullKey);\n });\n }\n };\n\n findRecordPrefixes(fullSchema);\n\n // Filter out keys that are under record prefixes\n const extraKeys = actualKeys.filter(key => {\n if (allowedKeys.has(key)) return false;\n\n // Check if this key is under a record prefix\n for (const recordPrefix of recordPrefixes) {\n if (key.startsWith(recordPrefix + '.')) {\n return false; // This key is allowed under a record\n }\n }\n\n return true; // This is an extra key\n });\n\n if (extraKeys.length > 0) {\n const allowedKeysArray = Array.from(allowedKeys);\n const error = ConfigurationError.extraKeys(extraKeys, allowedKeysArray);\n logger.error(error.message);\n throw error;\n }\n}\n\n/**\n * Validates that a configuration directory exists and is accessible.\n * \n * This function performs file system checks to ensure the configuration\n * directory can be used. It handles the isRequired flag to determine\n * whether a missing directory should cause an error or be silently ignored.\n * \n * @param configDirectory - Path to the configuration directory\n * @param isRequired - Whether the directory must exist\n * @param logger - Optional logger for debug information\n * @throws {FileSystemError} When the directory is required but missing or unreadable\n */\nconst validateConfigDirectory = async (configDirectory: string, isRequired: boolean, logger?: Logger): Promise<void> => {\n const storage = Storage.create({ log: logger?.debug || (() => { }) });\n const exists = await storage.exists(configDirectory);\n if (!exists) {\n if (isRequired) {\n throw FileSystemError.directoryNotFound(configDirectory, true);\n }\n } else if (exists) {\n const isReadable = await storage.isDirectoryReadable(configDirectory);\n if (!isReadable) {\n throw FileSystemError.directoryNotReadable(configDirectory);\n }\n }\n}\n\n/**\n * Validates a configuration object against the combined Zod schema.\n * \n * This is the main validation function that:\n * 1. Validates the configuration directory (if config feature enabled)\n * 2. Combines the base ConfigSchema with user-provided schema shape\n * 3. Checks for extra keys not defined in the schema\n * 4. Validates all values against their schema definitions\n * 5. Provides detailed error reporting for validation failures\n * \n * The validation is comprehensive and catches common configuration errors\n * including typos, missing required fields, wrong types, and invalid values.\n * \n * @template T - The Zod schema shape type for configuration validation\n * @param config - The merged configuration object to validate\n * @param options - Cardigantime options containing schema, defaults, and logger\n * @throws {ConfigurationError} When configuration validation fails\n * @throws {FileSystemError} When configuration directory validation fails\n * \n * @example\n * ```typescript\n * const schema = z.object({\n * apiKey: z.string().min(1),\n * timeout: z.number().positive(),\n * });\n * \n * await validate(config, {\n * configShape: schema.shape,\n * defaults: { configDirectory: './config', isRequired: true },\n * logger: console,\n * features: ['config']\n * });\n * // Throws detailed errors if validation fails\n * ```\n */\nexport const validate = async <T extends z.ZodRawShape>(config: z.infer<ZodObject<T & typeof ConfigSchema.shape>>, options: Options<T>): Promise<void> => {\n const logger = options.logger;\n\n if (options.features.includes('config') && config.configDirectory) {\n await validateConfigDirectory(config.configDirectory, options.defaults.isRequired, logger);\n }\n\n // Combine the base schema with the user-provided shape\n const fullSchema = z.object({\n ...ConfigSchema.shape,\n ...options.configShape,\n });\n\n // Validate the merged sources against the full schema\n const validationResult = fullSchema.safeParse(config);\n\n // Check for extraneous keys\n checkForExtraKeys(config, fullSchema, logger);\n\n if (!validationResult.success) {\n const formattedError = JSON.stringify(validationResult.error.format(), null, 2);\n logger.error('Configuration validation failed: %s', formattedError);\n throw ConfigurationError.validation('Configuration validation failed. Check logs for details.', validationResult.error);\n }\n\n return;\n}\n\n","import { Command } from 'commander';\nimport { Args, DefaultOptions, Feature, Cardigantime, Logger, Options } from 'types';\nimport { z, ZodObject } from 'zod';\nimport { configure } from './configure';\nimport { DEFAULT_FEATURES, DEFAULT_LOGGER, DEFAULT_OPTIONS } from './constants';\nimport { read } from './read';\nimport { ConfigSchema } from 'types';\nimport { validate } from './validate';\n\nexport * from './types';\nexport { ArgumentError, ConfigurationError, FileSystemError } from './validate';\n\n/**\n * Creates a new Cardigantime instance for configuration management.\n * \n * Cardigantime handles the complete configuration lifecycle including:\n * - Reading configuration from YAML files\n * - Validating configuration against Zod schemas\n * - Merging CLI arguments with file configuration and defaults\n * - Providing type-safe configuration objects\n * \n * @template T - The Zod schema shape type for your configuration\n * @param pOptions - Configuration options for the Cardigantime instance\n * @param pOptions.defaults - Default configuration settings\n * @param pOptions.defaults.configDirectory - Directory to search for configuration files (required)\n * @param pOptions.defaults.configFile - Name of the configuration file (optional, defaults to 'config.yaml')\n * @param pOptions.defaults.isRequired - Whether the config directory must exist (optional, defaults to false)\n * @param pOptions.defaults.encoding - File encoding for reading config files (optional, defaults to 'utf8')\n * @param pOptions.features - Array of features to enable (optional, defaults to ['config'])\n * @param pOptions.configShape - Zod schema shape defining your configuration structure (required)\n * @param pOptions.logger - Custom logger implementation (optional, defaults to console logger)\n * @returns A Cardigantime instance with methods for configure, read, validate, and setLogger\n * \n * @example\n * ```typescript\n * import { create } from '@theunwalked/cardigantime';\n * import { z } from 'zod';\n * \n * const MyConfigSchema = z.object({\n * apiKey: z.string().min(1),\n * timeout: z.number().default(5000),\n * debug: z.boolean().default(false),\n * });\n * \n * const cardigantime = create({\n * defaults: {\n * configDirectory: './config',\n * configFile: 'myapp.yaml',\n * },\n * configShape: MyConfigSchema.shape,\n * });\n * ```\n */\nexport const create = <T extends z.ZodRawShape>(pOptions: {\n defaults: Pick<DefaultOptions, 'configDirectory'> & Partial<Omit<DefaultOptions, 'configDirectory'>>,\n features?: Feature[],\n configShape: T, // Make configShape mandatory\n logger?: Logger,\n}): Cardigantime<T> => {\n\n\n const defaults: DefaultOptions = { ...DEFAULT_OPTIONS, ...pOptions.defaults } as DefaultOptions;\n const features = pOptions.features || DEFAULT_FEATURES;\n const configShape = pOptions.configShape;\n let logger = pOptions.logger || DEFAULT_LOGGER;\n\n const options: Options<T> = {\n defaults,\n features,\n configShape, // Store the shape\n logger,\n }\n\n const setLogger = (pLogger: Logger) => {\n logger = pLogger;\n options.logger = pLogger;\n }\n\n return {\n setLogger,\n configure: (command: Command) => configure(command, options),\n validate: (config: z.infer<ZodObject<T & typeof ConfigSchema.shape>>) => validate(config, options),\n read: (args: Args) => read(args, options),\n }\n}\n\n\n\n\n\n"],"names":["_define_property","ArgumentError","Error","argument","argumentName","message","name","validateConfigDirectory","configDirectory","_testThrowNonArgumentError","trimmed","trim","length","includes","configure","command","options","option","defaults","validatedDefaultDir","retCommand","value","error","DEFAULT_ENCODING","DEFAULT_CONFIG_FILE","DEFAULT_OPTIONS","configFile","isRequired","encoding","DEFAULT_FEATURES","DEFAULT_LOGGER","debug","console","info","warn","verbose","silly","FileSystemError","directoryNotFound","path","directoryNotReadable","directoryCreationFailed","originalError","operationFailed","operation","fileNotFound","errorType","create","params","log","exists","fs","promises","stat","isDirectory","stats","isFile","isReadable","access","constants","R_OK","stack","isWritable","W_OK","isFileReadable","isDirectoryWritable","isDirectoryReadable","createDirectory","mkdir","recursive","mkdirError","readFile","validEncodings","toLowerCase","maxFileSize","size","code","writeFile","data","forEachFileIn","directory","callback","pattern","files","glob","cwd","nodir","file","join","err","readStream","createReadStream","hashFile","crypto","createHash","update","digest","slice","listFiles","readdir","discoverConfigDirectories","configDirName","maxLevels","startingDir","process","logger","storage","createStorage","discoveredDirs","currentDir","resolve","level","visited","Set","realPath","has","add","configDirPath","push","parentDir","dirname","loadConfigFromDirectory","configDir","configFileName","configFilePath","yamlContent","parsedYaml","yaml","load","deepMergeConfigs","configs","reduce","merged","current","deepMergeTwo","target","source","Array","isArray","result","key","Object","prototype","hasOwnProperty","call","loadHierarchicalConfig","config","errors","sortedDirs","sort","a","b","dir","errorMsg","mergedConfig","clean","obj","fromEntries","entries","filter","_","v","undefined","validatePath","userPath","basePath","normalized","normalize","isAbsolute","startsWith","read","args","rawConfigDir","resolvedConfigDir","rawFileConfig","features","basename","hierarchicalResult","forEach","loadSingleDirectoryConfig","Storage","test","ConfigurationError","validation","zodError","configPath","extraKeys","allowedKeys","schema","details","ConfigSchema","z","object","string","listZodKeys","prefix","_def","typeName","unwrappable","unwrap","arraySchema","element","objectSchema","shape","flatMap","subschema","fullKey","nested","isPlainObject","listObjectKeys","keys","firstObjectElement","find","nestedKeys","k","from","checkForExtraKeys","mergedSources","fullSchema","actualKeys","recordPrefixes","findRecordPrefixes","recordPrefix","allowedKeysArray","validate","configShape","validationResult","safeParse","success","formattedError","JSON","stringify","format","pOptions","setLogger","pLogger"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"cardigantime.cjs","sources":["../src/error/ArgumentError.ts","../src/configure.ts","../src/constants.ts","../src/error/FileSystemError.ts","../src/util/storage.ts","../src/util/hierarchical.ts","../src/read.ts","../src/error/ConfigurationError.ts","../src/types.ts","../src/validate.ts","../src/cardigantime.ts"],"sourcesContent":["/**\n * Error thrown when CLI arguments or function parameters are invalid.\n * \n * This error provides specific context about which argument failed validation\n * and why, making it easier for users to fix their command-line usage or\n * for developers to debug parameter issues.\n * \n * @example\n * ```typescript\n * throw new ArgumentError('config-directory', 'Path cannot be empty');\n * // Error message: \"Path cannot be empty\"\n * // error.argument: \"config-directory\"\n * ```\n */\nexport class ArgumentError extends Error {\n /** The name of the argument that caused the error */\n private argumentName: string;\n\n /**\n * Creates a new ArgumentError instance.\n * \n * @param argumentName - The name of the invalid argument\n * @param message - Description of why the argument is invalid\n */\n constructor(argumentName: string, message: string) {\n super(`${message}`);\n this.name = 'ArgumentError';\n this.argumentName = argumentName;\n }\n\n /**\n * Gets the name of the argument that caused this error.\n * \n * @returns The argument name\n */\n get argument(): string {\n return this.argumentName;\n }\n}","import { Command } from \"commander\";\nimport { z } from \"zod\";\nimport { ArgumentError } from \"./error/ArgumentError\";\nimport { Options } from \"./types\";\nexport { ArgumentError };\n\n/**\n * Validates a configuration directory path to ensure it's safe and valid.\n * \n * Performs security and safety checks including:\n * - Non-empty string validation\n * - Null byte injection prevention\n * - Path length validation\n * - Type checking\n * \n * @param configDirectory - The configuration directory path to validate\n * @param _testThrowNonArgumentError - Internal testing parameter to simulate non-ArgumentError exceptions\n * @returns The trimmed and validated configuration directory path\n * @throws {ArgumentError} When the directory path is invalid\n * \n * @example\n * ```typescript\n * const validDir = validateConfigDirectory('./config'); // Returns './config'\n * const invalidDir = validateConfigDirectory(''); // Throws ArgumentError\n * ```\n */\nexport function validateConfigDirectory(configDirectory: string, _testThrowNonArgumentError?: boolean): string {\n if (_testThrowNonArgumentError) {\n throw new Error('Test non-ArgumentError for coverage');\n }\n\n if (!configDirectory) {\n throw new ArgumentError('configDirectory', 'Configuration directory cannot be empty');\n }\n\n if (typeof configDirectory !== 'string') {\n throw new ArgumentError('configDirectory', 'Configuration directory must be a string');\n }\n\n const trimmed = configDirectory.trim();\n if (trimmed.length === 0) {\n throw new ArgumentError('configDirectory', 'Configuration directory cannot be empty or whitespace only');\n }\n\n // Check for obviously invalid paths\n if (trimmed.includes('\\0')) {\n throw new ArgumentError('configDirectory', 'Configuration directory contains invalid null character');\n }\n\n // Validate path length (reasonable limit)\n if (trimmed.length > 1000) {\n throw new ArgumentError('configDirectory', 'Configuration directory path is too long (max 1000 characters)');\n }\n\n return trimmed;\n}\n\n/**\n * Configures a Commander.js command with Cardigantime's CLI options.\n * \n * This function adds command-line options that allow users to override\n * configuration settings at runtime, such as:\n * - --config-directory: Override the default configuration directory\n * \n * The function validates both the command object and the options to ensure\n * they meet the requirements for proper integration.\n * \n * @template T - The Zod schema shape type for configuration validation\n * @param command - The Commander.js Command instance to configure\n * @param options - Cardigantime options containing defaults and schema\n * @param _testThrowNonArgumentError - Internal testing parameter\n * @returns Promise resolving to the configured Command instance\n * @throws {ArgumentError} When command or options are invalid\n * \n * @example\n * ```typescript\n * import { Command } from 'commander';\n * import { configure } from './configure';\n * \n * const program = new Command();\n * const configuredProgram = await configure(program, options);\n * \n * // Now the program accepts: --config-directory <path>\n * ```\n */\nexport const configure = async <T extends z.ZodRawShape>(\n command: Command,\n options: Options<T>,\n _testThrowNonArgumentError?: boolean\n): Promise<Command> => {\n // Validate the command object\n if (!command) {\n throw new ArgumentError('command', 'Command instance is required');\n }\n\n if (typeof command.option !== 'function') {\n throw new ArgumentError('command', 'Command must be a valid Commander.js Command instance');\n }\n\n // Validate options\n if (!options) {\n throw new ArgumentError('options', 'Options object is required');\n }\n\n if (!options.defaults) {\n throw new ArgumentError('options.defaults', 'Options must include defaults configuration');\n }\n\n if (!options.defaults.configDirectory) {\n throw new ArgumentError('options.defaults.configDirectory', 'Default config directory is required');\n }\n\n // Validate the default config directory\n const validatedDefaultDir = validateConfigDirectory(options.defaults.configDirectory, _testThrowNonArgumentError);\n\n let retCommand = command;\n\n // Add the config directory option with validation\n retCommand = retCommand.option(\n '-c, --config-directory <configDirectory>',\n 'Configuration directory path',\n (value: string) => {\n try {\n return validateConfigDirectory(value, _testThrowNonArgumentError);\n } catch (error) {\n if (error instanceof ArgumentError) {\n // Re-throw with more specific context for CLI usage\n throw new ArgumentError('config-directory', `Invalid --config-directory: ${error.message}`);\n }\n throw error;\n }\n },\n validatedDefaultDir\n );\n\n return retCommand;\n}\n\n\n\n\n","import { DefaultOptions, Feature, Logger } from \"./types\";\n\n/** Version string populated at build time with git and system information */\nexport const VERSION = '__VERSION__ (__GIT_BRANCH__/__GIT_COMMIT__ __GIT_TAGS__ __GIT_COMMIT_DATE__) __SYSTEM_INFO__';\n\n/** The program name used in CLI help and error messages */\nexport const PROGRAM_NAME = 'cardigantime';\n\n/** Default file encoding for reading configuration files */\nexport const DEFAULT_ENCODING = 'utf8';\n\n/** Default configuration file name to look for in the config directory */\nexport const DEFAULT_CONFIG_FILE = 'config.yaml';\n\n/**\n * Default configuration options applied when creating a Cardigantime instance.\n * These provide sensible defaults that work for most use cases.\n */\nexport const DEFAULT_OPTIONS: Partial<DefaultOptions> = {\n configFile: DEFAULT_CONFIG_FILE,\n isRequired: false,\n encoding: DEFAULT_ENCODING,\n}\n\n/**\n * Default features enabled when creating a Cardigantime instance.\n * Currently includes only the 'config' feature for configuration file support.\n */\nexport const DEFAULT_FEATURES: Feature[] = ['config'];\n\n/**\n * Default logger implementation using console methods.\n * Provides basic logging functionality when no custom logger is specified.\n * The verbose and silly methods are no-ops to avoid excessive output.\n */\nexport const DEFAULT_LOGGER: Logger = {\n // eslint-disable-next-line no-console\n debug: console.debug,\n // eslint-disable-next-line no-console\n info: console.info,\n // eslint-disable-next-line no-console\n warn: console.warn,\n // eslint-disable-next-line no-console\n error: console.error,\n\n verbose: () => { },\n\n silly: () => { },\n}\n","/**\n * Error thrown when file system operations fail\n */\nexport class FileSystemError extends Error {\n public readonly errorType: 'not_found' | 'not_readable' | 'not_writable' | 'creation_failed' | 'operation_failed';\n public readonly path: string;\n public readonly operation: string;\n public readonly originalError?: Error;\n\n constructor(\n errorType: 'not_found' | 'not_readable' | 'not_writable' | 'creation_failed' | 'operation_failed',\n message: string,\n path: string,\n operation: string,\n originalError?: Error\n ) {\n super(message);\n this.name = 'FileSystemError';\n this.errorType = errorType;\n this.path = path;\n this.operation = operation;\n this.originalError = originalError;\n }\n\n /**\n * Creates an error for when a required directory doesn't exist\n */\n static directoryNotFound(path: string, isRequired: boolean = false): FileSystemError {\n const message = isRequired\n ? 'Configuration directory does not exist and is required'\n : 'Configuration directory not found';\n return new FileSystemError('not_found', message, path, 'directory_access');\n }\n\n /**\n * Creates an error for when a directory exists but isn't readable\n */\n static directoryNotReadable(path: string): FileSystemError {\n const message = 'Configuration directory exists but is not readable';\n return new FileSystemError('not_readable', message, path, 'directory_read');\n }\n\n /**\n * Creates an error for directory creation failures\n */\n static directoryCreationFailed(path: string, originalError: Error): FileSystemError {\n const message = 'Failed to create directory: ' + (originalError.message || 'Unknown error');\n return new FileSystemError('creation_failed', message, path, 'directory_create', originalError);\n }\n\n /**\n * Creates an error for file operation failures (glob, etc.)\n */\n static operationFailed(operation: string, path: string, originalError: Error): FileSystemError {\n const message = `Failed to ${operation}: ${originalError.message || 'Unknown error'}`;\n return new FileSystemError('operation_failed', message, path, operation, originalError);\n }\n\n /**\n * Creates an error for when a file is not found\n */\n static fileNotFound(path: string): FileSystemError {\n const message = 'Configuration file not found';\n return new FileSystemError('not_found', message, path, 'file_read');\n }\n} ","// eslint-disable-next-line no-restricted-imports\nimport * as fs from 'fs';\nimport { glob } from 'glob';\nimport path from 'path';\nimport crypto from 'crypto';\nimport { FileSystemError } from '../error/FileSystemError';\n/**\n * This module exists to isolate filesystem operations from the rest of the codebase.\n * This makes testing easier by avoiding direct fs mocking in jest configuration.\n * \n * Additionally, abstracting storage operations allows for future flexibility - \n * this export utility may need to work with storage systems other than the local filesystem\n * (e.g. S3, Google Cloud Storage, etc).\n */\n\nexport interface Utility {\n exists: (path: string) => Promise<boolean>;\n isDirectory: (path: string) => Promise<boolean>;\n isFile: (path: string) => Promise<boolean>;\n isReadable: (path: string) => Promise<boolean>;\n isWritable: (path: string) => Promise<boolean>;\n isFileReadable: (path: string) => Promise<boolean>;\n isDirectoryWritable: (path: string) => Promise<boolean>;\n isDirectoryReadable: (path: string) => Promise<boolean>;\n createDirectory: (path: string) => Promise<void>;\n readFile: (path: string, encoding: string) => Promise<string>;\n readStream: (path: string) => Promise<fs.ReadStream>;\n writeFile: (path: string, data: string | Buffer, encoding: string) => Promise<void>;\n forEachFileIn: (directory: string, callback: (path: string) => Promise<void>, options?: { pattern: string }) => Promise<void>;\n hashFile: (path: string, length: number) => Promise<string>;\n listFiles: (directory: string) => Promise<string[]>;\n}\n\nexport const create = (params: { log?: (message: string, ...args: any[]) => void }): Utility => {\n\n // eslint-disable-next-line no-console\n const log = params.log || console.log;\n\n const exists = async (path: string): Promise<boolean> => {\n try {\n await fs.promises.stat(path);\n return true;\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n } catch (error: any) {\n return false;\n }\n }\n\n const isDirectory = async (path: string): Promise<boolean> => {\n const stats = await fs.promises.stat(path);\n if (!stats.isDirectory()) {\n log(`${path} is not a directory`);\n return false;\n }\n return true;\n }\n\n const isFile = async (path: string): Promise<boolean> => {\n const stats = await fs.promises.stat(path);\n if (!stats.isFile()) {\n log(`${path} is not a file`);\n return false;\n }\n return true;\n }\n\n const isReadable = async (path: string): Promise<boolean> => {\n try {\n await fs.promises.access(path, fs.constants.R_OK);\n } catch (error: any) {\n log(`${path} is not readable: %s %s`, error.message, error.stack);\n return false;\n }\n return true;\n }\n\n const isWritable = async (path: string): Promise<boolean> => {\n try {\n await fs.promises.access(path, fs.constants.W_OK);\n } catch (error: any) {\n log(`${path} is not writable: %s %s`, error.message, error.stack);\n return false;\n }\n return true;\n }\n\n const isFileReadable = async (path: string): Promise<boolean> => {\n return await exists(path) && await isFile(path) && await isReadable(path);\n }\n\n const isDirectoryWritable = async (path: string): Promise<boolean> => {\n return await exists(path) && await isDirectory(path) && await isWritable(path);\n }\n\n const isDirectoryReadable = async (path: string): Promise<boolean> => {\n return await exists(path) && await isDirectory(path) && await isReadable(path);\n }\n\n const createDirectory = async (path: string): Promise<void> => {\n try {\n await fs.promises.mkdir(path, { recursive: true });\n } catch (mkdirError: any) {\n throw FileSystemError.directoryCreationFailed(path, mkdirError);\n }\n }\n\n const readFile = async (path: string, encoding: string): Promise<string> => {\n // Validate encoding parameter\n const validEncodings = ['utf8', 'utf-8', 'ascii', 'latin1', 'base64', 'hex', 'utf16le', 'ucs2', 'ucs-2'];\n if (!validEncodings.includes(encoding.toLowerCase())) {\n throw new Error('Invalid encoding specified');\n }\n\n // Check file size before reading to prevent DoS\n try {\n const stats = await fs.promises.stat(path);\n const maxFileSize = 10 * 1024 * 1024; // 10MB limit\n if (stats.size > maxFileSize) {\n throw new Error('File too large to process');\n }\n } catch (error: any) {\n if (error.code === 'ENOENT') {\n throw FileSystemError.fileNotFound(path);\n }\n throw error;\n }\n\n return await fs.promises.readFile(path, { encoding: encoding as BufferEncoding });\n }\n\n const writeFile = async (path: string, data: string | Buffer, encoding: string): Promise<void> => {\n await fs.promises.writeFile(path, data, { encoding: encoding as BufferEncoding });\n }\n\n const forEachFileIn = async (directory: string, callback: (file: string) => Promise<void>, options: { pattern: string | string[] } = { pattern: '*.*' }): Promise<void> => {\n try {\n const files = await glob(options.pattern, { cwd: directory, nodir: true });\n for (const file of files) {\n await callback(path.join(directory, file));\n }\n } catch (err: any) {\n throw FileSystemError.operationFailed(`glob pattern ${options.pattern}`, directory, err);\n }\n }\n\n const readStream = async (path: string): Promise<fs.ReadStream> => {\n return fs.createReadStream(path);\n }\n\n const hashFile = async (path: string, length: number): Promise<string> => {\n const file = await readFile(path, 'utf8');\n return crypto.createHash('sha256').update(file).digest('hex').slice(0, length);\n }\n\n const listFiles = async (directory: string): Promise<string[]> => {\n return await fs.promises.readdir(directory);\n }\n\n return {\n exists,\n isDirectory,\n isFile,\n isReadable,\n isWritable,\n isFileReadable,\n isDirectoryWritable,\n isDirectoryReadable,\n createDirectory,\n readFile,\n readStream,\n writeFile,\n forEachFileIn,\n hashFile,\n listFiles,\n };\n}","import path from 'path';\nimport * as yaml from 'js-yaml';\nimport { create as createStorage } from './storage';\nimport { Logger } from '../types';\n\n/**\n * Represents a discovered configuration directory with its path and precedence level.\n */\nexport interface DiscoveredConfigDir {\n /** Absolute path to the configuration directory */\n path: string;\n /** Distance from the starting directory (0 = closest/highest precedence) */\n level: number;\n}\n\n/**\n * Options for hierarchical configuration discovery.\n */\nexport interface HierarchicalDiscoveryOptions {\n /** Name of the configuration directory to look for (e.g., '.kodrdriv') */\n configDirName: string;\n /** Name of the configuration file within each directory */\n configFileName: string;\n /** Maximum number of parent directories to traverse (default: 10) */\n maxLevels?: number;\n /** Starting directory for discovery (default: process.cwd()) */\n startingDir?: string;\n /** File encoding for reading configuration files */\n encoding?: string;\n /** Logger for debugging */\n logger?: Logger;\n}\n\n/**\n * Result of loading configurations from multiple directories.\n */\nexport interface HierarchicalConfigResult {\n /** Merged configuration object with proper precedence */\n config: object;\n /** Array of directories where configuration was found */\n discoveredDirs: DiscoveredConfigDir[];\n /** Array of any errors encountered during loading (non-fatal) */\n errors: string[];\n}\n\n/**\n * Discovers configuration directories by traversing up the directory tree.\n * \n * Starting from the specified directory (or current working directory),\n * this function searches for directories with the given name, continuing\n * up the directory tree until it reaches the filesystem root or the\n * maximum number of levels.\n * \n * @param options Configuration options for discovery\n * @returns Promise resolving to array of discovered configuration directories\n * \n * @example\n * ```typescript\n * const dirs = await discoverConfigDirectories({\n * configDirName: '.kodrdriv',\n * configFileName: 'config.yaml',\n * maxLevels: 5\n * });\n * // Returns: [\n * // { path: '/project/.kodrdriv', level: 0 },\n * // { path: '/project/parent/.kodrdriv', level: 1 }\n * // ]\n * ```\n */\nexport async function discoverConfigDirectories(\n options: HierarchicalDiscoveryOptions\n): Promise<DiscoveredConfigDir[]> {\n const {\n configDirName,\n maxLevels = 10,\n startingDir = process.cwd(),\n logger\n } = options;\n\n const storage = createStorage({ log: logger?.debug || (() => { }) });\n const discoveredDirs: DiscoveredConfigDir[] = [];\n\n let currentDir = path.resolve(startingDir);\n let level = 0;\n const visited = new Set<string>(); // Prevent infinite loops with symlinks\n\n logger?.debug(`Starting hierarchical discovery from: ${currentDir}`);\n\n while (level < maxLevels) {\n // Prevent infinite loops with symlinks\n const realPath = path.resolve(currentDir);\n if (visited.has(realPath)) {\n logger?.debug(`Already visited ${realPath}, stopping discovery`);\n break;\n }\n visited.add(realPath);\n\n const configDirPath = path.join(currentDir, configDirName);\n logger?.debug(`Checking for config directory: ${configDirPath}`);\n\n try {\n const exists = await storage.exists(configDirPath);\n const isReadable = exists && await storage.isDirectoryReadable(configDirPath);\n\n if (exists && isReadable) {\n discoveredDirs.push({\n path: configDirPath,\n level\n });\n logger?.debug(`Found config directory at level ${level}: ${configDirPath}`);\n } else if (exists && !isReadable) {\n logger?.debug(`Config directory exists but is not readable: ${configDirPath}`);\n }\n } catch (error: any) {\n logger?.debug(`Error checking config directory ${configDirPath}: ${error.message}`);\n }\n\n // Move up one directory level\n const parentDir = path.dirname(currentDir);\n\n // Check if we've reached the root directory\n if (parentDir === currentDir) {\n logger?.debug('Reached filesystem root, stopping discovery');\n break;\n }\n\n currentDir = parentDir;\n level++;\n }\n\n logger?.verbose(`Discovery complete. Found ${discoveredDirs.length} config directories`);\n return discoveredDirs;\n}\n\n/**\n * Loads and parses a configuration file from a directory.\n * \n * @param configDir Path to the configuration directory\n * @param configFileName Name of the configuration file\n * @param encoding File encoding\n * @param logger Optional logger\n * @returns Promise resolving to parsed configuration object or null if not found\n */\nexport async function loadConfigFromDirectory(\n configDir: string,\n configFileName: string,\n encoding: string = 'utf8',\n logger?: Logger\n): Promise<object | null> {\n const storage = createStorage({ log: logger?.debug || (() => { }) });\n const configFilePath = path.join(configDir, configFileName);\n\n try {\n logger?.verbose(`Attempting to load config file: ${configFilePath}`);\n\n const exists = await storage.exists(configFilePath);\n if (!exists) {\n logger?.debug(`Config file does not exist: ${configFilePath}`);\n return null;\n }\n\n const isReadable = await storage.isFileReadable(configFilePath);\n if (!isReadable) {\n logger?.debug(`Config file exists but is not readable: ${configFilePath}`);\n return null;\n }\n\n const yamlContent = await storage.readFile(configFilePath, encoding);\n const parsedYaml = yaml.load(yamlContent);\n\n if (parsedYaml !== null && typeof parsedYaml === 'object') {\n logger?.verbose(`Successfully loaded config from: ${configFilePath}`);\n return parsedYaml as object;\n } else {\n logger?.debug(`Config file contains invalid format: ${configFilePath}`);\n return null;\n }\n } catch (error: any) {\n logger?.debug(`Error loading config from ${configFilePath}: ${error.message}`);\n return null;\n }\n}\n\n/**\n * Deep merges multiple configuration objects with proper precedence.\n * \n * Objects are merged from lowest precedence to highest precedence,\n * meaning that properties in later objects override properties in earlier objects.\n * Arrays are replaced entirely (not merged).\n * \n * @param configs Array of configuration objects, ordered from lowest to highest precedence\n * @returns Merged configuration object\n * \n * @example\n * ```typescript\n * const merged = deepMergeConfigs([\n * { api: { timeout: 5000 }, debug: true }, // Lower precedence\n * { api: { retries: 3 }, features: ['auth'] }, // Higher precedence\n * ]);\n * // Result: { api: { timeout: 5000, retries: 3 }, debug: true, features: ['auth'] }\n * ```\n */\nexport function deepMergeConfigs(configs: object[]): object {\n if (configs.length === 0) {\n return {};\n }\n\n if (configs.length === 1) {\n return { ...configs[0] };\n }\n\n return configs.reduce((merged, current) => {\n return deepMergeTwo(merged, current);\n }, {});\n}\n\n/**\n * Deep merges two objects with proper precedence.\n * \n * @param target Target object (lower precedence)\n * @param source Source object (higher precedence)\n * @returns Merged object\n */\nfunction deepMergeTwo(target: any, source: any): any {\n // Handle null/undefined\n if (source == null) return target;\n if (target == null) return source;\n\n // Handle non-objects (primitives, arrays, functions, etc.)\n if (typeof source !== 'object' || typeof target !== 'object') {\n return source; // Source takes precedence\n }\n\n // Handle arrays - replace entirely, don't merge\n if (Array.isArray(source)) {\n return [...source];\n }\n\n if (Array.isArray(target)) {\n return source; // Source object replaces target array\n }\n\n // Deep merge objects\n const result = { ...target };\n\n for (const key in source) {\n if (Object.prototype.hasOwnProperty.call(source, key)) {\n if (Object.prototype.hasOwnProperty.call(result, key) &&\n typeof result[key] === 'object' &&\n typeof source[key] === 'object' &&\n !Array.isArray(source[key]) &&\n !Array.isArray(result[key])) {\n // Recursively merge nested objects\n result[key] = deepMergeTwo(result[key], source[key]);\n } else {\n // Replace with source value (higher precedence)\n result[key] = source[key];\n }\n }\n }\n\n return result;\n}\n\n/**\n * Loads configurations from multiple directories and merges them with proper precedence.\n * \n * This is the main function for hierarchical configuration loading. It:\n * 1. Discovers configuration directories up the directory tree\n * 2. Loads configuration files from each discovered directory\n * 3. Merges them with proper precedence (closer directories win)\n * 4. Returns the merged configuration with metadata\n * \n * @param options Configuration options for hierarchical loading\n * @returns Promise resolving to hierarchical configuration result\n * \n * @example\n * ```typescript\n * const result = await loadHierarchicalConfig({\n * configDirName: '.kodrdriv',\n * configFileName: 'config.yaml',\n * startingDir: '/project/subdir',\n * maxLevels: 5\n * });\n * \n * // result.config contains merged configuration\n * // result.discoveredDirs shows where configs were found\n * // result.errors contains any non-fatal errors\n * ```\n */\nexport async function loadHierarchicalConfig(\n options: HierarchicalDiscoveryOptions\n): Promise<HierarchicalConfigResult> {\n const { configFileName, encoding = 'utf8', logger } = options;\n\n logger?.verbose('Starting hierarchical configuration loading');\n\n // Discover all configuration directories\n const discoveredDirs = await discoverConfigDirectories(options);\n\n if (discoveredDirs.length === 0) {\n logger?.verbose('No configuration directories found');\n return {\n config: {},\n discoveredDirs: [],\n errors: []\n };\n }\n\n // Load configurations from each directory\n const configs: object[] = [];\n const errors: string[] = [];\n\n // Sort by level (highest level first = lowest precedence first)\n const sortedDirs = [...discoveredDirs].sort((a, b) => b.level - a.level);\n\n for (const dir of sortedDirs) {\n try {\n const config = await loadConfigFromDirectory(\n dir.path,\n configFileName,\n encoding,\n logger\n );\n\n if (config !== null) {\n configs.push(config);\n logger?.debug(`Loaded config from level ${dir.level}: ${dir.path}`);\n } else {\n logger?.debug(`No valid config found at level ${dir.level}: ${dir.path}`);\n }\n } catch (error: any) {\n const errorMsg = `Failed to load config from ${dir.path}: ${error.message}`;\n errors.push(errorMsg);\n logger?.debug(errorMsg);\n }\n }\n\n // Merge all configurations with proper precedence\n const mergedConfig = deepMergeConfigs(configs);\n\n logger?.verbose(`Hierarchical loading complete. Merged ${configs.length} configurations`);\n\n return {\n config: mergedConfig,\n discoveredDirs,\n errors\n };\n} ","import * as yaml from 'js-yaml';\nimport * as path from 'path';\nimport { z, ZodObject } from 'zod';\nimport { Args, ConfigSchema, Options } from './types';\nimport * as Storage from './util/storage';\nimport { loadHierarchicalConfig } from './util/hierarchical';\n\n/**\n * Removes undefined values from an object to create a clean configuration.\n * This is used to merge configuration sources while avoiding undefined pollution.\n * \n * @param obj - The object to clean\n * @returns A new object with undefined values filtered out\n */\nfunction clean(obj: any) {\n return Object.fromEntries(\n Object.entries(obj).filter(([_, v]) => v !== undefined)\n );\n}\n\n/**\n * Validates and secures a user-provided path to prevent path traversal attacks.\n * \n * Security checks include:\n * - Path traversal prevention (blocks '..')\n * - Absolute path detection\n * - Path separator validation\n * \n * @param userPath - The user-provided path component\n * @param basePath - The base directory to join the path with\n * @returns The safely joined and normalized path\n * @throws {Error} When path traversal or absolute paths are detected\n */\nfunction validatePath(userPath: string, basePath: string): string {\n if (!userPath || !basePath) {\n throw new Error('Invalid path parameters');\n }\n\n const normalized = path.normalize(userPath);\n\n // Prevent path traversal attacks\n if (normalized.includes('..') || path.isAbsolute(normalized)) {\n throw new Error('Invalid path: path traversal detected');\n }\n\n // Ensure the path doesn't start with a path separator\n if (normalized.startsWith('/') || normalized.startsWith('\\\\')) {\n throw new Error('Invalid path: absolute path detected');\n }\n\n return path.join(basePath, normalized);\n}\n\n/**\n * Validates a configuration directory path for security and basic formatting.\n * \n * Performs validation to prevent:\n * - Null byte injection attacks\n * - Extremely long paths that could cause DoS\n * - Empty or invalid directory specifications\n * \n * @param configDir - The configuration directory path to validate\n * @returns The normalized configuration directory path\n * @throws {Error} When the directory path is invalid or potentially dangerous\n */\nfunction validateConfigDirectory(configDir: string): string {\n if (!configDir) {\n throw new Error('Configuration directory is required');\n }\n\n // Check for null bytes which could be used for path injection\n if (configDir.includes('\\0')) {\n throw new Error('Invalid path: null byte detected');\n }\n\n const normalized = path.normalize(configDir);\n\n // Basic validation - could be expanded based on requirements\n if (normalized.length > 1000) {\n throw new Error('Configuration directory path too long');\n }\n\n return normalized;\n}\n\n/**\n * Reads configuration from files and merges it with CLI arguments.\n * \n * This function implements the core configuration loading logic:\n * 1. Validates and resolves the configuration directory path\n * 2. Attempts to read the YAML configuration file\n * 3. Safely parses the YAML content with security protections\n * 4. Merges file configuration with runtime arguments\n * 5. Returns a typed configuration object\n * \n * The function handles missing files gracefully and provides detailed\n * logging for troubleshooting configuration issues.\n * \n * @template T - The Zod schema shape type for configuration validation\n * @param args - Parsed command-line arguments containing potential config overrides\n * @param options - Cardigantime options with defaults, schema, and logger\n * @returns Promise resolving to the merged and typed configuration object\n * @throws {Error} When configuration directory is invalid or required files cannot be read\n * \n * @example\n * ```typescript\n * const config = await read(cliArgs, {\n * defaults: { configDirectory: './config', configFile: 'app.yaml' },\n * configShape: MySchema.shape,\n * logger: console,\n * features: ['config']\n * });\n * // config is fully typed based on your schema\n * ```\n */\nexport const read = async <T extends z.ZodRawShape>(args: Args, options: Options<T>): Promise<z.infer<ZodObject<T & typeof ConfigSchema.shape>>> => {\n const logger = options.logger;\n\n const rawConfigDir = args.configDirectory || options.defaults?.configDirectory;\n if (!rawConfigDir) {\n throw new Error('Configuration directory must be specified');\n }\n\n const resolvedConfigDir = validateConfigDirectory(rawConfigDir);\n logger.verbose('Resolved config directory');\n\n let rawFileConfig: object = {};\n\n // Check if hierarchical configuration discovery is enabled\n if (options.features.includes('hierarchical')) {\n logger.verbose('Hierarchical configuration discovery enabled');\n\n try {\n // Extract the config directory name from the path for hierarchical discovery\n const configDirName = path.basename(resolvedConfigDir);\n const startingDir = path.dirname(resolvedConfigDir);\n\n logger.debug(`Using hierarchical discovery: configDirName=${configDirName}, startingDir=${startingDir}`);\n\n const hierarchicalResult = await loadHierarchicalConfig({\n configDirName,\n configFileName: options.defaults.configFile,\n startingDir,\n encoding: options.defaults.encoding,\n logger\n });\n\n rawFileConfig = hierarchicalResult.config;\n\n if (hierarchicalResult.discoveredDirs.length > 0) {\n logger.verbose(`Hierarchical discovery found ${hierarchicalResult.discoveredDirs.length} configuration directories`);\n hierarchicalResult.discoveredDirs.forEach(dir => {\n logger.debug(` Level ${dir.level}: ${dir.path}`);\n });\n } else {\n logger.verbose('No configuration directories found in hierarchy');\n }\n\n if (hierarchicalResult.errors.length > 0) {\n hierarchicalResult.errors.forEach(error => logger.warn(`Hierarchical config warning: ${error}`));\n }\n\n } catch (error: any) {\n logger.error('Hierarchical configuration loading failed: ' + (error.message || 'Unknown error'));\n // Fall back to single directory mode\n logger.verbose('Falling back to single directory configuration loading');\n rawFileConfig = await loadSingleDirectoryConfig(resolvedConfigDir, options, logger);\n }\n } else {\n // Use traditional single directory configuration loading\n logger.verbose('Using single directory configuration loading');\n rawFileConfig = await loadSingleDirectoryConfig(resolvedConfigDir, options, logger);\n }\n\n const config: z.infer<ZodObject<T & typeof ConfigSchema.shape>> = clean({\n ...rawFileConfig,\n ...{\n configDirectory: resolvedConfigDir,\n }\n }) as z.infer<ZodObject<T & typeof ConfigSchema.shape>>;\n\n return config;\n}\n\n/**\n * Loads configuration from a single directory (traditional mode).\n * \n * @param resolvedConfigDir - The resolved configuration directory path\n * @param options - Cardigantime options\n * @param logger - Logger instance\n * @returns Promise resolving to the configuration object\n */\nasync function loadSingleDirectoryConfig<T extends z.ZodRawShape>(\n resolvedConfigDir: string,\n options: Options<T>,\n logger: any\n): Promise<object> {\n const storage = Storage.create({ log: logger.debug });\n const configFile = validatePath(options.defaults.configFile, resolvedConfigDir);\n logger.verbose('Attempting to load config file for cardigantime');\n\n let rawFileConfig: object = {};\n\n try {\n const yamlContent = await storage.readFile(configFile, options.defaults.encoding);\n\n // SECURITY FIX: Use safer parsing options to prevent code execution vulnerabilities\n const parsedYaml = yaml.load(yamlContent);\n\n if (parsedYaml !== null && typeof parsedYaml === 'object') {\n rawFileConfig = parsedYaml;\n logger.verbose('Loaded configuration file successfully');\n } else if (parsedYaml !== null) {\n logger.warn('Ignoring invalid configuration format. Expected an object, got ' + typeof parsedYaml);\n }\n } catch (error: any) {\n if (error.code === 'ENOENT' || /not found|no such file/i.test(error.message)) {\n logger.verbose('Configuration file not found. Using empty configuration.');\n } else {\n // SECURITY FIX: Don't expose internal paths or detailed error information\n logger.error('Failed to load or parse configuration file: ' + (error.message || 'Unknown error'));\n }\n }\n\n return rawFileConfig;\n}","/**\n * Error thrown when configuration validation fails\n */\nexport class ConfigurationError extends Error {\n public readonly errorType: 'validation' | 'schema' | 'extra_keys';\n public readonly details?: any;\n public readonly configPath?: string;\n\n constructor(\n errorType: 'validation' | 'schema' | 'extra_keys',\n message: string,\n details?: any,\n configPath?: string\n ) {\n super(message);\n this.name = 'ConfigurationError';\n this.errorType = errorType;\n this.details = details;\n this.configPath = configPath;\n }\n\n /**\n * Creates a validation error for when config doesn't match the schema\n */\n static validation(message: string, zodError?: any, configPath?: string): ConfigurationError {\n return new ConfigurationError('validation', message, zodError, configPath);\n }\n\n /**\n * Creates an error for when extra/unknown keys are found\n */\n static extraKeys(extraKeys: string[], allowedKeys: string[], configPath?: string): ConfigurationError {\n const message = `Unknown configuration keys found: ${extraKeys.join(', ')}. Allowed keys are: ${allowedKeys.join(', ')}`;\n return new ConfigurationError('extra_keys', message, { extraKeys, allowedKeys }, configPath);\n }\n\n /**\n * Creates a schema error for when the configuration schema itself is invalid\n */\n static schema(message: string, details?: any): ConfigurationError {\n return new ConfigurationError('schema', message, details);\n }\n} ","import { Command } from \"commander\";\nimport { ZodObject } from \"zod\";\n\nimport { z } from \"zod\";\n\n/**\n * Available features that can be enabled in Cardigantime.\n * Currently supports:\n * - 'config': Configuration file reading and validation\n * - 'hierarchical': Hierarchical configuration discovery and layering\n */\nexport type Feature = 'config' | 'hierarchical';\n\n/**\n * Default configuration options for Cardigantime.\n * These define the basic behavior of configuration loading.\n */\nexport interface DefaultOptions {\n /** Directory path where configuration files are located */\n configDirectory: string;\n /** Name of the configuration file (e.g., 'config.yaml', 'app.yml') */\n configFile: string;\n /** Whether the configuration directory must exist. If true, throws error if directory doesn't exist */\n isRequired: boolean;\n /** File encoding for reading configuration files (e.g., 'utf8', 'ascii') */\n encoding: string;\n}\n\n/**\n * Complete options object passed to Cardigantime functions.\n * Combines defaults, features, schema shape, and logger.\n * \n * @template T - The Zod schema shape type for configuration validation\n */\nexport interface Options<T extends z.ZodRawShape> {\n /** Default configuration options */\n defaults: DefaultOptions,\n /** Array of enabled features */\n features: Feature[],\n /** Zod schema shape for validating user configuration */\n configShape: T;\n /** Logger instance for debugging and error reporting */\n logger: Logger;\n}\n\n/**\n * Logger interface for Cardigantime's internal logging.\n * Compatible with popular logging libraries like Winston, Bunyan, etc.\n */\nexport interface Logger {\n /** Debug-level logging for detailed troubleshooting information */\n debug: (message: string, ...args: any[]) => void;\n /** Info-level logging for general information */\n info: (message: string, ...args: any[]) => void;\n /** Warning-level logging for non-critical issues */\n warn: (message: string, ...args: any[]) => void;\n /** Error-level logging for critical problems */\n error: (message: string, ...args: any[]) => void;\n /** Verbose-level logging for extensive detail */\n verbose: (message: string, ...args: any[]) => void;\n /** Silly-level logging for maximum detail */\n silly: (message: string, ...args: any[]) => void;\n}\n\n/**\n * Main Cardigantime interface providing configuration management functionality.\n * \n * @template T - The Zod schema shape type for configuration validation\n */\nexport interface Cardigantime<T extends z.ZodRawShape> {\n /** \n * Adds Cardigantime's CLI options to a Commander.js command.\n * This includes options like --config-directory for runtime config path overrides.\n */\n configure: (command: Command) => Promise<Command>;\n /** Sets a custom logger for debugging and error reporting */\n setLogger: (logger: Logger) => void;\n /** \n * Reads configuration from files and merges with CLI arguments.\n * Returns a fully typed configuration object.\n */\n read: (args: Args) => Promise<z.infer<ZodObject<T & typeof ConfigSchema.shape>>>;\n /** \n * Validates the merged configuration against the Zod schema.\n * Throws ConfigurationError if validation fails.\n */\n validate: (config: z.infer<ZodObject<T & typeof ConfigSchema.shape>>) => Promise<void>;\n}\n\n/**\n * Parsed command-line arguments object, typically from Commander.js opts().\n * Keys correspond to CLI option names with values from user input.\n */\nexport interface Args {\n [key: string]: any;\n}\n\n/**\n * Base Zod schema for core Cardigantime configuration.\n * Contains the minimum required configuration fields.\n */\nexport const ConfigSchema = z.object({\n /** The resolved configuration directory path */\n configDirectory: z.string(),\n});\n\n/**\n * Base configuration type derived from the core schema.\n */\nexport type Config = z.infer<typeof ConfigSchema>;\n","import { z, ZodObject } from \"zod\";\nimport { ArgumentError } from \"./error/ArgumentError\";\nimport { ConfigurationError } from \"./error/ConfigurationError\";\nimport { FileSystemError } from \"./error/FileSystemError\";\nimport { ConfigSchema, Logger, Options } from \"./types\";\nimport * as Storage from \"./util/storage\";\nexport { ArgumentError, ConfigurationError, FileSystemError };\n\n/**\n * Recursively extracts all keys from a Zod schema in dot notation.\n * \n * This function traverses a Zod schema structure and builds a flat list\n * of all possible keys, using dot notation for nested objects. It handles\n * optional/nullable types by unwrapping them and supports arrays by\n * introspecting their element type.\n * \n * Special handling for:\n * - ZodOptional/ZodNullable: Unwraps to get the underlying type\n * - ZodAny/ZodRecord: Accepts any keys, returns the prefix or empty array\n * - ZodArray: Introspects the element type\n * - ZodObject: Recursively processes all shape properties\n * \n * @param schema - The Zod schema to introspect\n * @param prefix - Internal parameter for building nested key paths\n * @returns Array of strings representing all possible keys in dot notation\n * \n * @example\n * ```typescript\n * const schema = z.object({\n * user: z.object({\n * name: z.string(),\n * settings: z.object({ theme: z.string() })\n * }),\n * debug: z.boolean()\n * });\n * \n * const keys = listZodKeys(schema);\n * // Returns: ['user.name', 'user.settings.theme', 'debug']\n * ```\n */\nexport const listZodKeys = (schema: z.ZodTypeAny, prefix = ''): string[] => {\n // Check if schema has unwrap method (which both ZodOptional and ZodNullable have)\n if (schema._def && (schema._def.typeName === 'ZodOptional' || schema._def.typeName === 'ZodNullable')) {\n // Use type assertion to handle the unwrap method\n const unwrappable = schema as z.ZodOptional<any> | z.ZodNullable<any>;\n return listZodKeys(unwrappable.unwrap(), prefix);\n }\n\n // Handle ZodAny and ZodRecord - these accept any keys, so don't introspect\n if (schema._def && (schema._def.typeName === 'ZodAny' || schema._def.typeName === 'ZodRecord')) {\n return prefix ? [prefix] : [];\n }\n\n if (schema._def && schema._def.typeName === 'ZodArray') {\n // Use type assertion to handle the element property\n const arraySchema = schema as z.ZodArray<any>;\n return listZodKeys(arraySchema.element, prefix);\n }\n if (schema._def && schema._def.typeName === 'ZodObject') {\n // Use type assertion to handle the shape property\n const objectSchema = schema as z.ZodObject<any>;\n return Object.entries(objectSchema.shape).flatMap(([key, subschema]) => {\n const fullKey = prefix ? `${prefix}.${key}` : key;\n const nested = listZodKeys(subschema as z.ZodTypeAny, fullKey);\n return nested.length ? nested : fullKey;\n });\n }\n return [];\n}\n\n/**\n * Type guard to check if a value is a plain object (not array, null, or other types).\n * \n * @param value - The value to check\n * @returns True if the value is a plain object\n */\nconst isPlainObject = (value: unknown): value is Record<string, unknown> => {\n // Check if it's an object, not null, and not an array.\n return value !== null && typeof value === 'object' && !Array.isArray(value);\n};\n\n/**\n * Generates a list of all keys within a JavaScript object, using dot notation for nested keys.\n * Mimics the behavior of listZodKeys but operates on plain objects.\n * For arrays, it inspects the first element that is a plain object to determine nested keys.\n * If an array contains no plain objects, or is empty, the key for the array itself is listed.\n *\n * @param obj The object to introspect.\n * @param prefix Internal use for recursion: the prefix for the current nesting level.\n * @returns An array of strings representing all keys in dot notation.\n */\nexport const listObjectKeys = (obj: Record<string, unknown>, prefix = ''): string[] => {\n const keys = new Set<string>(); // Use Set to automatically handle duplicates from array recursion\n\n for (const key in obj) {\n // Ensure it's an own property, not from the prototype chain\n if (Object.prototype.hasOwnProperty.call(obj, key)) {\n const value = obj[key];\n const fullKey = prefix ? `${prefix}.${key}` : key;\n\n if (Array.isArray(value)) {\n // Find the first element that is a plain object to determine structure\n const firstObjectElement = value.find(isPlainObject);\n if (firstObjectElement) {\n // Recurse into the structure of the first object element found\n const nestedKeys = listObjectKeys(firstObjectElement, fullKey);\n nestedKeys.forEach(k => keys.add(k));\n } else {\n // Array is empty or contains no plain objects, list the array key itself\n keys.add(fullKey);\n }\n } else if (isPlainObject(value)) {\n // Recurse into nested plain objects\n const nestedKeys = listObjectKeys(value, fullKey);\n nestedKeys.forEach(k => keys.add(k));\n } else {\n // It's a primitive, null, or other non-plain object/array type\n keys.add(fullKey);\n }\n }\n }\n return Array.from(keys); // Convert Set back to Array\n};\n\n/**\n * Validates that the configuration object contains only keys allowed by the schema.\n * \n * This function prevents configuration errors by detecting typos or extra keys\n * that aren't defined in the Zod schema. It intelligently handles:\n * - ZodRecord types that accept arbitrary keys\n * - Nested objects and their key structures\n * - Arrays and their element key structures\n * \n * The function throws a ConfigurationError if extra keys are found, providing\n * helpful information about what keys are allowed vs. what was found.\n * \n * @param mergedSources - The merged configuration object to validate\n * @param fullSchema - The complete Zod schema including base and user schemas\n * @param logger - Logger for error reporting\n * @throws {ConfigurationError} When extra keys are found that aren't in the schema\n * \n * @example\n * ```typescript\n * const schema = z.object({ name: z.string(), age: z.number() });\n * const config = { name: 'John', age: 30, typo: 'invalid' };\n * \n * checkForExtraKeys(config, schema, console);\n * // Throws: ConfigurationError with details about 'typo' being an extra key\n * ```\n */\nexport const checkForExtraKeys = (mergedSources: object, fullSchema: ZodObject<any>, logger: Logger | typeof console): void => {\n const allowedKeys = new Set(listZodKeys(fullSchema));\n const actualKeys = listObjectKeys(mergedSources as Record<string, unknown>);\n\n // Filter out keys that are under a record type (ZodRecord accepts any keys)\n const recordPrefixes = new Set<string>();\n\n // Find all prefixes that are ZodRecord types\n const findRecordPrefixes = (schema: z.ZodTypeAny, prefix = ''): void => {\n if (schema._def && (schema._def.typeName === 'ZodOptional' || schema._def.typeName === 'ZodNullable')) {\n const unwrappable = schema as z.ZodOptional<any> | z.ZodNullable<any>;\n findRecordPrefixes(unwrappable.unwrap(), prefix);\n return;\n }\n\n if (schema._def && (schema._def.typeName === 'ZodAny' || schema._def.typeName === 'ZodRecord')) {\n if (prefix) recordPrefixes.add(prefix);\n return;\n }\n\n if (schema._def && schema._def.typeName === 'ZodObject') {\n const objectSchema = schema as z.ZodObject<any>;\n Object.entries(objectSchema.shape).forEach(([key, subschema]) => {\n const fullKey = prefix ? `${prefix}.${key}` : key;\n findRecordPrefixes(subschema as z.ZodTypeAny, fullKey);\n });\n }\n };\n\n findRecordPrefixes(fullSchema);\n\n // Filter out keys that are under record prefixes\n const extraKeys = actualKeys.filter(key => {\n if (allowedKeys.has(key)) return false;\n\n // Check if this key is under a record prefix\n for (const recordPrefix of recordPrefixes) {\n if (key.startsWith(recordPrefix + '.')) {\n return false; // This key is allowed under a record\n }\n }\n\n return true; // This is an extra key\n });\n\n if (extraKeys.length > 0) {\n const allowedKeysArray = Array.from(allowedKeys);\n const error = ConfigurationError.extraKeys(extraKeys, allowedKeysArray);\n logger.error(error.message);\n throw error;\n }\n}\n\n/**\n * Validates that a configuration directory exists and is accessible.\n * \n * This function performs file system checks to ensure the configuration\n * directory can be used. It handles the isRequired flag to determine\n * whether a missing directory should cause an error or be silently ignored.\n * \n * @param configDirectory - Path to the configuration directory\n * @param isRequired - Whether the directory must exist\n * @param logger - Optional logger for debug information\n * @throws {FileSystemError} When the directory is required but missing or unreadable\n */\nconst validateConfigDirectory = async (configDirectory: string, isRequired: boolean, logger?: Logger): Promise<void> => {\n const storage = Storage.create({ log: logger?.debug || (() => { }) });\n const exists = await storage.exists(configDirectory);\n if (!exists) {\n if (isRequired) {\n throw FileSystemError.directoryNotFound(configDirectory, true);\n }\n } else if (exists) {\n const isReadable = await storage.isDirectoryReadable(configDirectory);\n if (!isReadable) {\n throw FileSystemError.directoryNotReadable(configDirectory);\n }\n }\n}\n\n/**\n * Validates a configuration object against the combined Zod schema.\n * \n * This is the main validation function that:\n * 1. Validates the configuration directory (if config feature enabled)\n * 2. Combines the base ConfigSchema with user-provided schema shape\n * 3. Checks for extra keys not defined in the schema\n * 4. Validates all values against their schema definitions\n * 5. Provides detailed error reporting for validation failures\n * \n * The validation is comprehensive and catches common configuration errors\n * including typos, missing required fields, wrong types, and invalid values.\n * \n * @template T - The Zod schema shape type for configuration validation\n * @param config - The merged configuration object to validate\n * @param options - Cardigantime options containing schema, defaults, and logger\n * @throws {ConfigurationError} When configuration validation fails\n * @throws {FileSystemError} When configuration directory validation fails\n * \n * @example\n * ```typescript\n * const schema = z.object({\n * apiKey: z.string().min(1),\n * timeout: z.number().positive(),\n * });\n * \n * await validate(config, {\n * configShape: schema.shape,\n * defaults: { configDirectory: './config', isRequired: true },\n * logger: console,\n * features: ['config']\n * });\n * // Throws detailed errors if validation fails\n * ```\n */\nexport const validate = async <T extends z.ZodRawShape>(config: z.infer<ZodObject<T & typeof ConfigSchema.shape>>, options: Options<T>): Promise<void> => {\n const logger = options.logger;\n\n if (options.features.includes('config') && config.configDirectory) {\n await validateConfigDirectory(config.configDirectory, options.defaults.isRequired, logger);\n }\n\n // Combine the base schema with the user-provided shape\n const fullSchema = z.object({\n ...ConfigSchema.shape,\n ...options.configShape,\n });\n\n // Validate the merged sources against the full schema\n const validationResult = fullSchema.safeParse(config);\n\n // Check for extraneous keys\n checkForExtraKeys(config, fullSchema, logger);\n\n if (!validationResult.success) {\n const formattedError = JSON.stringify(validationResult.error.format(), null, 2);\n logger.error('Configuration validation failed. Check logs for details.');\n logger.silly('Configuration validation failed: %s', formattedError);\n throw ConfigurationError.validation('Configuration validation failed. Check logs for details.', validationResult.error);\n }\n\n return;\n}\n\n","import { Command } from 'commander';\nimport { Args, DefaultOptions, Feature, Cardigantime, Logger, Options } from 'types';\nimport { z, ZodObject } from 'zod';\nimport { configure } from './configure';\nimport { DEFAULT_FEATURES, DEFAULT_LOGGER, DEFAULT_OPTIONS } from './constants';\nimport { read } from './read';\nimport { ConfigSchema } from 'types';\nimport { validate } from './validate';\n\nexport * from './types';\nexport { ArgumentError, ConfigurationError, FileSystemError } from './validate';\n\n/**\n * Creates a new Cardigantime instance for configuration management.\n * \n * Cardigantime handles the complete configuration lifecycle including:\n * - Reading configuration from YAML files\n * - Validating configuration against Zod schemas\n * - Merging CLI arguments with file configuration and defaults\n * - Providing type-safe configuration objects\n * \n * @template T - The Zod schema shape type for your configuration\n * @param pOptions - Configuration options for the Cardigantime instance\n * @param pOptions.defaults - Default configuration settings\n * @param pOptions.defaults.configDirectory - Directory to search for configuration files (required)\n * @param pOptions.defaults.configFile - Name of the configuration file (optional, defaults to 'config.yaml')\n * @param pOptions.defaults.isRequired - Whether the config directory must exist (optional, defaults to false)\n * @param pOptions.defaults.encoding - File encoding for reading config files (optional, defaults to 'utf8')\n * @param pOptions.features - Array of features to enable (optional, defaults to ['config'])\n * @param pOptions.configShape - Zod schema shape defining your configuration structure (required)\n * @param pOptions.logger - Custom logger implementation (optional, defaults to console logger)\n * @returns A Cardigantime instance with methods for configure, read, validate, and setLogger\n * \n * @example\n * ```typescript\n * import { create } from '@theunwalked/cardigantime';\n * import { z } from 'zod';\n * \n * const MyConfigSchema = z.object({\n * apiKey: z.string().min(1),\n * timeout: z.number().default(5000),\n * debug: z.boolean().default(false),\n * });\n * \n * const cardigantime = create({\n * defaults: {\n * configDirectory: './config',\n * configFile: 'myapp.yaml',\n * },\n * configShape: MyConfigSchema.shape,\n * });\n * ```\n */\nexport const create = <T extends z.ZodRawShape>(pOptions: {\n defaults: Pick<DefaultOptions, 'configDirectory'> & Partial<Omit<DefaultOptions, 'configDirectory'>>,\n features?: Feature[],\n configShape: T, // Make configShape mandatory\n logger?: Logger,\n}): Cardigantime<T> => {\n\n\n const defaults: DefaultOptions = { ...DEFAULT_OPTIONS, ...pOptions.defaults } as DefaultOptions;\n const features = pOptions.features || DEFAULT_FEATURES;\n const configShape = pOptions.configShape;\n let logger = pOptions.logger || DEFAULT_LOGGER;\n\n const options: Options<T> = {\n defaults,\n features,\n configShape, // Store the shape\n logger,\n }\n\n const setLogger = (pLogger: Logger) => {\n logger = pLogger;\n options.logger = pLogger;\n }\n\n return {\n setLogger,\n configure: (command: Command) => configure(command, options),\n validate: (config: z.infer<ZodObject<T & typeof ConfigSchema.shape>>) => validate(config, options),\n read: (args: Args) => read(args, options),\n }\n}\n\n\n\n\n\n"],"names":["_define_property","ArgumentError","Error","argument","argumentName","message","name","validateConfigDirectory","configDirectory","_testThrowNonArgumentError","trimmed","trim","length","includes","configure","command","options","option","defaults","validatedDefaultDir","retCommand","value","error","DEFAULT_ENCODING","DEFAULT_CONFIG_FILE","DEFAULT_OPTIONS","configFile","isRequired","encoding","DEFAULT_FEATURES","DEFAULT_LOGGER","debug","console","info","warn","verbose","silly","FileSystemError","directoryNotFound","path","directoryNotReadable","directoryCreationFailed","originalError","operationFailed","operation","fileNotFound","errorType","create","params","log","exists","fs","promises","stat","isDirectory","stats","isFile","isReadable","access","constants","R_OK","stack","isWritable","W_OK","isFileReadable","isDirectoryWritable","isDirectoryReadable","createDirectory","mkdir","recursive","mkdirError","readFile","validEncodings","toLowerCase","maxFileSize","size","code","writeFile","data","forEachFileIn","directory","callback","pattern","files","glob","cwd","nodir","file","join","err","readStream","createReadStream","hashFile","crypto","createHash","update","digest","slice","listFiles","readdir","discoverConfigDirectories","configDirName","maxLevels","startingDir","process","logger","storage","createStorage","discoveredDirs","currentDir","resolve","level","visited","Set","realPath","has","add","configDirPath","push","parentDir","dirname","loadConfigFromDirectory","configDir","configFileName","configFilePath","yamlContent","parsedYaml","yaml","load","deepMergeConfigs","configs","reduce","merged","current","deepMergeTwo","target","source","Array","isArray","result","key","Object","prototype","hasOwnProperty","call","loadHierarchicalConfig","config","errors","sortedDirs","sort","a","b","dir","errorMsg","mergedConfig","clean","obj","fromEntries","entries","filter","_","v","undefined","validatePath","userPath","basePath","normalized","normalize","isAbsolute","startsWith","read","args","rawConfigDir","resolvedConfigDir","rawFileConfig","features","basename","hierarchicalResult","forEach","loadSingleDirectoryConfig","Storage","test","ConfigurationError","validation","zodError","configPath","extraKeys","allowedKeys","schema","details","ConfigSchema","z","object","string","listZodKeys","prefix","_def","typeName","unwrappable","unwrap","arraySchema","element","objectSchema","shape","flatMap","subschema","fullKey","nested","isPlainObject","listObjectKeys","keys","firstObjectElement","find","nestedKeys","k","from","checkForExtraKeys","mergedSources","fullSchema","actualKeys","recordPrefixes","findRecordPrefixes","recordPrefix","allowedKeysArray","validate","configShape","validationResult","safeParse","success","formattedError","JSON","stringify","format","pOptions","setLogger","pLogger"],"mappings":"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"}
|
package/dist/read.js
CHANGED
|
@@ -102,11 +102,11 @@ import { loadHierarchicalConfig } from './util/hierarchical.js';
|
|
|
102
102
|
throw new Error('Configuration directory must be specified');
|
|
103
103
|
}
|
|
104
104
|
const resolvedConfigDir = validateConfigDirectory(rawConfigDir);
|
|
105
|
-
logger.
|
|
105
|
+
logger.verbose('Resolved config directory');
|
|
106
106
|
let rawFileConfig = {};
|
|
107
107
|
// Check if hierarchical configuration discovery is enabled
|
|
108
108
|
if (options.features.includes('hierarchical')) {
|
|
109
|
-
logger.
|
|
109
|
+
logger.verbose('Hierarchical configuration discovery enabled');
|
|
110
110
|
try {
|
|
111
111
|
// Extract the config directory name from the path for hierarchical discovery
|
|
112
112
|
const configDirName = path.basename(resolvedConfigDir);
|
|
@@ -121,12 +121,12 @@ import { loadHierarchicalConfig } from './util/hierarchical.js';
|
|
|
121
121
|
});
|
|
122
122
|
rawFileConfig = hierarchicalResult.config;
|
|
123
123
|
if (hierarchicalResult.discoveredDirs.length > 0) {
|
|
124
|
-
logger.
|
|
124
|
+
logger.verbose(`Hierarchical discovery found ${hierarchicalResult.discoveredDirs.length} configuration directories`);
|
|
125
125
|
hierarchicalResult.discoveredDirs.forEach((dir)=>{
|
|
126
126
|
logger.debug(` Level ${dir.level}: ${dir.path}`);
|
|
127
127
|
});
|
|
128
128
|
} else {
|
|
129
|
-
logger.
|
|
129
|
+
logger.verbose('No configuration directories found in hierarchy');
|
|
130
130
|
}
|
|
131
131
|
if (hierarchicalResult.errors.length > 0) {
|
|
132
132
|
hierarchicalResult.errors.forEach((error)=>logger.warn(`Hierarchical config warning: ${error}`));
|
|
@@ -134,12 +134,12 @@ import { loadHierarchicalConfig } from './util/hierarchical.js';
|
|
|
134
134
|
} catch (error) {
|
|
135
135
|
logger.error('Hierarchical configuration loading failed: ' + (error.message || 'Unknown error'));
|
|
136
136
|
// Fall back to single directory mode
|
|
137
|
-
logger.
|
|
137
|
+
logger.verbose('Falling back to single directory configuration loading');
|
|
138
138
|
rawFileConfig = await loadSingleDirectoryConfig(resolvedConfigDir, options, logger);
|
|
139
139
|
}
|
|
140
140
|
} else {
|
|
141
141
|
// Use traditional single directory configuration loading
|
|
142
|
-
logger.
|
|
142
|
+
logger.verbose('Using single directory configuration loading');
|
|
143
143
|
rawFileConfig = await loadSingleDirectoryConfig(resolvedConfigDir, options, logger);
|
|
144
144
|
}
|
|
145
145
|
const config = clean({
|
|
@@ -162,7 +162,7 @@ import { loadHierarchicalConfig } from './util/hierarchical.js';
|
|
|
162
162
|
log: logger.debug
|
|
163
163
|
});
|
|
164
164
|
const configFile = validatePath(options.defaults.configFile, resolvedConfigDir);
|
|
165
|
-
logger.
|
|
165
|
+
logger.verbose('Attempting to load config file for cardigantime');
|
|
166
166
|
let rawFileConfig = {};
|
|
167
167
|
try {
|
|
168
168
|
const yamlContent = await storage.readFile(configFile, options.defaults.encoding);
|
|
@@ -170,13 +170,13 @@ import { loadHierarchicalConfig } from './util/hierarchical.js';
|
|
|
170
170
|
const parsedYaml = yaml.load(yamlContent);
|
|
171
171
|
if (parsedYaml !== null && typeof parsedYaml === 'object') {
|
|
172
172
|
rawFileConfig = parsedYaml;
|
|
173
|
-
logger.
|
|
173
|
+
logger.verbose('Loaded configuration file successfully');
|
|
174
174
|
} else if (parsedYaml !== null) {
|
|
175
175
|
logger.warn('Ignoring invalid configuration format. Expected an object, got ' + typeof parsedYaml);
|
|
176
176
|
}
|
|
177
177
|
} catch (error) {
|
|
178
178
|
if (error.code === 'ENOENT' || /not found|no such file/i.test(error.message)) {
|
|
179
|
-
logger.
|
|
179
|
+
logger.verbose('Configuration file not found. Using empty configuration.');
|
|
180
180
|
} else {
|
|
181
181
|
// SECURITY FIX: Don't expose internal paths or detailed error information
|
|
182
182
|
logger.error('Failed to load or parse configuration file: ' + (error.message || 'Unknown error'));
|
package/dist/read.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"read.js","sources":["../src/read.ts"],"sourcesContent":["import * as yaml from 'js-yaml';\nimport * as path from 'path';\nimport { z, ZodObject } from 'zod';\nimport { Args, ConfigSchema, Options } from './types';\nimport * as Storage from './util/storage';\nimport { loadHierarchicalConfig } from './util/hierarchical';\n\n/**\n * Removes undefined values from an object to create a clean configuration.\n * This is used to merge configuration sources while avoiding undefined pollution.\n * \n * @param obj - The object to clean\n * @returns A new object with undefined values filtered out\n */\nfunction clean(obj: any) {\n return Object.fromEntries(\n Object.entries(obj).filter(([_, v]) => v !== undefined)\n );\n}\n\n/**\n * Validates and secures a user-provided path to prevent path traversal attacks.\n * \n * Security checks include:\n * - Path traversal prevention (blocks '..')\n * - Absolute path detection\n * - Path separator validation\n * \n * @param userPath - The user-provided path component\n * @param basePath - The base directory to join the path with\n * @returns The safely joined and normalized path\n * @throws {Error} When path traversal or absolute paths are detected\n */\nfunction validatePath(userPath: string, basePath: string): string {\n if (!userPath || !basePath) {\n throw new Error('Invalid path parameters');\n }\n\n const normalized = path.normalize(userPath);\n\n // Prevent path traversal attacks\n if (normalized.includes('..') || path.isAbsolute(normalized)) {\n throw new Error('Invalid path: path traversal detected');\n }\n\n // Ensure the path doesn't start with a path separator\n if (normalized.startsWith('/') || normalized.startsWith('\\\\')) {\n throw new Error('Invalid path: absolute path detected');\n }\n\n return path.join(basePath, normalized);\n}\n\n/**\n * Validates a configuration directory path for security and basic formatting.\n * \n * Performs validation to prevent:\n * - Null byte injection attacks\n * - Extremely long paths that could cause DoS\n * - Empty or invalid directory specifications\n * \n * @param configDir - The configuration directory path to validate\n * @returns The normalized configuration directory path\n * @throws {Error} When the directory path is invalid or potentially dangerous\n */\nfunction validateConfigDirectory(configDir: string): string {\n if (!configDir) {\n throw new Error('Configuration directory is required');\n }\n\n // Check for null bytes which could be used for path injection\n if (configDir.includes('\\0')) {\n throw new Error('Invalid path: null byte detected');\n }\n\n const normalized = path.normalize(configDir);\n\n // Basic validation - could be expanded based on requirements\n if (normalized.length > 1000) {\n throw new Error('Configuration directory path too long');\n }\n\n return normalized;\n}\n\n/**\n * Reads configuration from files and merges it with CLI arguments.\n * \n * This function implements the core configuration loading logic:\n * 1. Validates and resolves the configuration directory path\n * 2. Attempts to read the YAML configuration file\n * 3. Safely parses the YAML content with security protections\n * 4. Merges file configuration with runtime arguments\n * 5. Returns a typed configuration object\n * \n * The function handles missing files gracefully and provides detailed\n * logging for troubleshooting configuration issues.\n * \n * @template T - The Zod schema shape type for configuration validation\n * @param args - Parsed command-line arguments containing potential config overrides\n * @param options - Cardigantime options with defaults, schema, and logger\n * @returns Promise resolving to the merged and typed configuration object\n * @throws {Error} When configuration directory is invalid or required files cannot be read\n * \n * @example\n * ```typescript\n * const config = await read(cliArgs, {\n * defaults: { configDirectory: './config', configFile: 'app.yaml' },\n * configShape: MySchema.shape,\n * logger: console,\n * features: ['config']\n * });\n * // config is fully typed based on your schema\n * ```\n */\nexport const read = async <T extends z.ZodRawShape>(args: Args, options: Options<T>): Promise<z.infer<ZodObject<T & typeof ConfigSchema.shape>>> => {\n const logger = options.logger;\n\n const rawConfigDir = args.configDirectory || options.defaults?.configDirectory;\n if (!rawConfigDir) {\n throw new Error('Configuration directory must be specified');\n }\n\n const resolvedConfigDir = validateConfigDirectory(rawConfigDir);\n logger.debug('Resolved config directory');\n\n let rawFileConfig: object = {};\n\n // Check if hierarchical configuration discovery is enabled\n if (options.features.includes('hierarchical')) {\n logger.debug('Hierarchical configuration discovery enabled');\n\n try {\n // Extract the config directory name from the path for hierarchical discovery\n const configDirName = path.basename(resolvedConfigDir);\n const startingDir = path.dirname(resolvedConfigDir);\n\n logger.debug(`Using hierarchical discovery: configDirName=${configDirName}, startingDir=${startingDir}`);\n\n const hierarchicalResult = await loadHierarchicalConfig({\n configDirName,\n configFileName: options.defaults.configFile,\n startingDir,\n encoding: options.defaults.encoding,\n logger\n });\n\n rawFileConfig = hierarchicalResult.config;\n\n if (hierarchicalResult.discoveredDirs.length > 0) {\n logger.debug(`Hierarchical discovery found ${hierarchicalResult.discoveredDirs.length} configuration directories`);\n hierarchicalResult.discoveredDirs.forEach(dir => {\n logger.debug(` Level ${dir.level}: ${dir.path}`);\n });\n } else {\n logger.debug('No configuration directories found in hierarchy');\n }\n\n if (hierarchicalResult.errors.length > 0) {\n hierarchicalResult.errors.forEach(error => logger.warn(`Hierarchical config warning: ${error}`));\n }\n\n } catch (error: any) {\n logger.error('Hierarchical configuration loading failed: ' + (error.message || 'Unknown error'));\n // Fall back to single directory mode\n logger.debug('Falling back to single directory configuration loading');\n rawFileConfig = await loadSingleDirectoryConfig(resolvedConfigDir, options, logger);\n }\n } else {\n // Use traditional single directory configuration loading\n logger.debug('Using single directory configuration loading');\n rawFileConfig = await loadSingleDirectoryConfig(resolvedConfigDir, options, logger);\n }\n\n const config: z.infer<ZodObject<T & typeof ConfigSchema.shape>> = clean({\n ...rawFileConfig,\n ...{\n configDirectory: resolvedConfigDir,\n }\n }) as z.infer<ZodObject<T & typeof ConfigSchema.shape>>;\n\n return config;\n}\n\n/**\n * Loads configuration from a single directory (traditional mode).\n * \n * @param resolvedConfigDir - The resolved configuration directory path\n * @param options - Cardigantime options\n * @param logger - Logger instance\n * @returns Promise resolving to the configuration object\n */\nasync function loadSingleDirectoryConfig<T extends z.ZodRawShape>(\n resolvedConfigDir: string,\n options: Options<T>,\n logger: any\n): Promise<object> {\n const storage = Storage.create({ log: logger.debug });\n const configFile = validatePath(options.defaults.configFile, resolvedConfigDir);\n logger.debug('Attempting to load config file for cardigantime');\n\n let rawFileConfig: object = {};\n\n try {\n const yamlContent = await storage.readFile(configFile, options.defaults.encoding);\n\n // SECURITY FIX: Use safer parsing options to prevent code execution vulnerabilities\n const parsedYaml = yaml.load(yamlContent);\n\n if (parsedYaml !== null && typeof parsedYaml === 'object') {\n rawFileConfig = parsedYaml;\n logger.debug('Loaded configuration file successfully');\n } else if (parsedYaml !== null) {\n logger.warn('Ignoring invalid configuration format. Expected an object, got ' + typeof parsedYaml);\n }\n } catch (error: any) {\n if (error.code === 'ENOENT' || /not found|no such file/i.test(error.message)) {\n logger.debug('Configuration file not found. Using empty configuration.');\n } else {\n // SECURITY FIX: Don't expose internal paths or detailed error information\n logger.error('Failed to load or parse configuration file: ' + (error.message || 'Unknown error'));\n }\n }\n\n return rawFileConfig;\n}"],"names":["clean","obj","Object","fromEntries","entries","filter","_","v","undefined","validatePath","userPath","basePath","Error","normalized","path","normalize","includes","isAbsolute","startsWith","join","validateConfigDirectory","configDir","length","read","args","options","logger","rawConfigDir","configDirectory","defaults","resolvedConfigDir","debug","rawFileConfig","features","configDirName","basename","startingDir","dirname","hierarchicalResult","loadHierarchicalConfig","configFileName","configFile","encoding","config","discoveredDirs","forEach","dir","level","errors","error","warn","message","loadSingleDirectoryConfig","storage","Storage","log","yamlContent","readFile","parsedYaml","yaml","load","code","test"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"read.js","sources":["../src/read.ts"],"sourcesContent":["import * as yaml from 'js-yaml';\nimport * as path from 'path';\nimport { z, ZodObject } from 'zod';\nimport { Args, ConfigSchema, Options } from './types';\nimport * as Storage from './util/storage';\nimport { loadHierarchicalConfig } from './util/hierarchical';\n\n/**\n * Removes undefined values from an object to create a clean configuration.\n * This is used to merge configuration sources while avoiding undefined pollution.\n * \n * @param obj - The object to clean\n * @returns A new object with undefined values filtered out\n */\nfunction clean(obj: any) {\n return Object.fromEntries(\n Object.entries(obj).filter(([_, v]) => v !== undefined)\n );\n}\n\n/**\n * Validates and secures a user-provided path to prevent path traversal attacks.\n * \n * Security checks include:\n * - Path traversal prevention (blocks '..')\n * - Absolute path detection\n * - Path separator validation\n * \n * @param userPath - The user-provided path component\n * @param basePath - The base directory to join the path with\n * @returns The safely joined and normalized path\n * @throws {Error} When path traversal or absolute paths are detected\n */\nfunction validatePath(userPath: string, basePath: string): string {\n if (!userPath || !basePath) {\n throw new Error('Invalid path parameters');\n }\n\n const normalized = path.normalize(userPath);\n\n // Prevent path traversal attacks\n if (normalized.includes('..') || path.isAbsolute(normalized)) {\n throw new Error('Invalid path: path traversal detected');\n }\n\n // Ensure the path doesn't start with a path separator\n if (normalized.startsWith('/') || normalized.startsWith('\\\\')) {\n throw new Error('Invalid path: absolute path detected');\n }\n\n return path.join(basePath, normalized);\n}\n\n/**\n * Validates a configuration directory path for security and basic formatting.\n * \n * Performs validation to prevent:\n * - Null byte injection attacks\n * - Extremely long paths that could cause DoS\n * - Empty or invalid directory specifications\n * \n * @param configDir - The configuration directory path to validate\n * @returns The normalized configuration directory path\n * @throws {Error} When the directory path is invalid or potentially dangerous\n */\nfunction validateConfigDirectory(configDir: string): string {\n if (!configDir) {\n throw new Error('Configuration directory is required');\n }\n\n // Check for null bytes which could be used for path injection\n if (configDir.includes('\\0')) {\n throw new Error('Invalid path: null byte detected');\n }\n\n const normalized = path.normalize(configDir);\n\n // Basic validation - could be expanded based on requirements\n if (normalized.length > 1000) {\n throw new Error('Configuration directory path too long');\n }\n\n return normalized;\n}\n\n/**\n * Reads configuration from files and merges it with CLI arguments.\n * \n * This function implements the core configuration loading logic:\n * 1. Validates and resolves the configuration directory path\n * 2. Attempts to read the YAML configuration file\n * 3. Safely parses the YAML content with security protections\n * 4. Merges file configuration with runtime arguments\n * 5. Returns a typed configuration object\n * \n * The function handles missing files gracefully and provides detailed\n * logging for troubleshooting configuration issues.\n * \n * @template T - The Zod schema shape type for configuration validation\n * @param args - Parsed command-line arguments containing potential config overrides\n * @param options - Cardigantime options with defaults, schema, and logger\n * @returns Promise resolving to the merged and typed configuration object\n * @throws {Error} When configuration directory is invalid or required files cannot be read\n * \n * @example\n * ```typescript\n * const config = await read(cliArgs, {\n * defaults: { configDirectory: './config', configFile: 'app.yaml' },\n * configShape: MySchema.shape,\n * logger: console,\n * features: ['config']\n * });\n * // config is fully typed based on your schema\n * ```\n */\nexport const read = async <T extends z.ZodRawShape>(args: Args, options: Options<T>): Promise<z.infer<ZodObject<T & typeof ConfigSchema.shape>>> => {\n const logger = options.logger;\n\n const rawConfigDir = args.configDirectory || options.defaults?.configDirectory;\n if (!rawConfigDir) {\n throw new Error('Configuration directory must be specified');\n }\n\n const resolvedConfigDir = validateConfigDirectory(rawConfigDir);\n logger.verbose('Resolved config directory');\n\n let rawFileConfig: object = {};\n\n // Check if hierarchical configuration discovery is enabled\n if (options.features.includes('hierarchical')) {\n logger.verbose('Hierarchical configuration discovery enabled');\n\n try {\n // Extract the config directory name from the path for hierarchical discovery\n const configDirName = path.basename(resolvedConfigDir);\n const startingDir = path.dirname(resolvedConfigDir);\n\n logger.debug(`Using hierarchical discovery: configDirName=${configDirName}, startingDir=${startingDir}`);\n\n const hierarchicalResult = await loadHierarchicalConfig({\n configDirName,\n configFileName: options.defaults.configFile,\n startingDir,\n encoding: options.defaults.encoding,\n logger\n });\n\n rawFileConfig = hierarchicalResult.config;\n\n if (hierarchicalResult.discoveredDirs.length > 0) {\n logger.verbose(`Hierarchical discovery found ${hierarchicalResult.discoveredDirs.length} configuration directories`);\n hierarchicalResult.discoveredDirs.forEach(dir => {\n logger.debug(` Level ${dir.level}: ${dir.path}`);\n });\n } else {\n logger.verbose('No configuration directories found in hierarchy');\n }\n\n if (hierarchicalResult.errors.length > 0) {\n hierarchicalResult.errors.forEach(error => logger.warn(`Hierarchical config warning: ${error}`));\n }\n\n } catch (error: any) {\n logger.error('Hierarchical configuration loading failed: ' + (error.message || 'Unknown error'));\n // Fall back to single directory mode\n logger.verbose('Falling back to single directory configuration loading');\n rawFileConfig = await loadSingleDirectoryConfig(resolvedConfigDir, options, logger);\n }\n } else {\n // Use traditional single directory configuration loading\n logger.verbose('Using single directory configuration loading');\n rawFileConfig = await loadSingleDirectoryConfig(resolvedConfigDir, options, logger);\n }\n\n const config: z.infer<ZodObject<T & typeof ConfigSchema.shape>> = clean({\n ...rawFileConfig,\n ...{\n configDirectory: resolvedConfigDir,\n }\n }) as z.infer<ZodObject<T & typeof ConfigSchema.shape>>;\n\n return config;\n}\n\n/**\n * Loads configuration from a single directory (traditional mode).\n * \n * @param resolvedConfigDir - The resolved configuration directory path\n * @param options - Cardigantime options\n * @param logger - Logger instance\n * @returns Promise resolving to the configuration object\n */\nasync function loadSingleDirectoryConfig<T extends z.ZodRawShape>(\n resolvedConfigDir: string,\n options: Options<T>,\n logger: any\n): Promise<object> {\n const storage = Storage.create({ log: logger.debug });\n const configFile = validatePath(options.defaults.configFile, resolvedConfigDir);\n logger.verbose('Attempting to load config file for cardigantime');\n\n let rawFileConfig: object = {};\n\n try {\n const yamlContent = await storage.readFile(configFile, options.defaults.encoding);\n\n // SECURITY FIX: Use safer parsing options to prevent code execution vulnerabilities\n const parsedYaml = yaml.load(yamlContent);\n\n if (parsedYaml !== null && typeof parsedYaml === 'object') {\n rawFileConfig = parsedYaml;\n logger.verbose('Loaded configuration file successfully');\n } else if (parsedYaml !== null) {\n logger.warn('Ignoring invalid configuration format. Expected an object, got ' + typeof parsedYaml);\n }\n } catch (error: any) {\n if (error.code === 'ENOENT' || /not found|no such file/i.test(error.message)) {\n logger.verbose('Configuration file not found. Using empty configuration.');\n } else {\n // SECURITY FIX: Don't expose internal paths or detailed error information\n logger.error('Failed to load or parse configuration file: ' + (error.message || 'Unknown error'));\n }\n }\n\n return rawFileConfig;\n}"],"names":["clean","obj","Object","fromEntries","entries","filter","_","v","undefined","validatePath","userPath","basePath","Error","normalized","path","normalize","includes","isAbsolute","startsWith","join","validateConfigDirectory","configDir","length","read","args","options","logger","rawConfigDir","configDirectory","defaults","resolvedConfigDir","verbose","rawFileConfig","features","configDirName","basename","startingDir","dirname","debug","hierarchicalResult","loadHierarchicalConfig","configFileName","configFile","encoding","config","discoveredDirs","forEach","dir","level","errors","error","warn","message","loadSingleDirectoryConfig","storage","Storage","log","yamlContent","readFile","parsedYaml","yaml","load","code","test"],"mappings":"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"}
|
|
@@ -70,7 +70,7 @@ import { create } from './storage.js';
|
|
|
70
70
|
currentDir = parentDir;
|
|
71
71
|
level++;
|
|
72
72
|
}
|
|
73
|
-
logger === null || logger === void 0 ? void 0 : logger.
|
|
73
|
+
logger === null || logger === void 0 ? void 0 : logger.verbose(`Discovery complete. Found ${discoveredDirs.length} config directories`);
|
|
74
74
|
return discoveredDirs;
|
|
75
75
|
}
|
|
76
76
|
/**
|
|
@@ -87,7 +87,7 @@ import { create } from './storage.js';
|
|
|
87
87
|
});
|
|
88
88
|
const configFilePath = path__default.join(configDir, configFileName);
|
|
89
89
|
try {
|
|
90
|
-
logger === null || logger === void 0 ? void 0 : logger.
|
|
90
|
+
logger === null || logger === void 0 ? void 0 : logger.verbose(`Attempting to load config file: ${configFilePath}`);
|
|
91
91
|
const exists = await storage.exists(configFilePath);
|
|
92
92
|
if (!exists) {
|
|
93
93
|
logger === null || logger === void 0 ? void 0 : logger.debug(`Config file does not exist: ${configFilePath}`);
|
|
@@ -101,7 +101,7 @@ import { create } from './storage.js';
|
|
|
101
101
|
const yamlContent = await storage.readFile(configFilePath, encoding);
|
|
102
102
|
const parsedYaml = yaml.load(yamlContent);
|
|
103
103
|
if (parsedYaml !== null && typeof parsedYaml === 'object') {
|
|
104
|
-
logger === null || logger === void 0 ? void 0 : logger.
|
|
104
|
+
logger === null || logger === void 0 ? void 0 : logger.verbose(`Successfully loaded config from: ${configFilePath}`);
|
|
105
105
|
return parsedYaml;
|
|
106
106
|
} else {
|
|
107
107
|
logger === null || logger === void 0 ? void 0 : logger.debug(`Config file contains invalid format: ${configFilePath}`);
|
|
@@ -210,11 +210,11 @@ import { create } from './storage.js';
|
|
|
210
210
|
* ```
|
|
211
211
|
*/ async function loadHierarchicalConfig(options) {
|
|
212
212
|
const { configFileName, encoding = 'utf8', logger } = options;
|
|
213
|
-
logger === null || logger === void 0 ? void 0 : logger.
|
|
213
|
+
logger === null || logger === void 0 ? void 0 : logger.verbose('Starting hierarchical configuration loading');
|
|
214
214
|
// Discover all configuration directories
|
|
215
215
|
const discoveredDirs = await discoverConfigDirectories(options);
|
|
216
216
|
if (discoveredDirs.length === 0) {
|
|
217
|
-
logger === null || logger === void 0 ? void 0 : logger.
|
|
217
|
+
logger === null || logger === void 0 ? void 0 : logger.verbose('No configuration directories found');
|
|
218
218
|
return {
|
|
219
219
|
config: {},
|
|
220
220
|
discoveredDirs: [],
|
|
@@ -245,7 +245,7 @@ import { create } from './storage.js';
|
|
|
245
245
|
}
|
|
246
246
|
// Merge all configurations with proper precedence
|
|
247
247
|
const mergedConfig = deepMergeConfigs(configs);
|
|
248
|
-
logger === null || logger === void 0 ? void 0 : logger.
|
|
248
|
+
logger === null || logger === void 0 ? void 0 : logger.verbose(`Hierarchical loading complete. Merged ${configs.length} configurations`);
|
|
249
249
|
return {
|
|
250
250
|
config: mergedConfig,
|
|
251
251
|
discoveredDirs,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hierarchical.js","sources":["../../src/util/hierarchical.ts"],"sourcesContent":["import path from 'path';\nimport * as yaml from 'js-yaml';\nimport { create as createStorage } from './storage';\nimport { Logger } from '../types';\n\n/**\n * Represents a discovered configuration directory with its path and precedence level.\n */\nexport interface DiscoveredConfigDir {\n /** Absolute path to the configuration directory */\n path: string;\n /** Distance from the starting directory (0 = closest/highest precedence) */\n level: number;\n}\n\n/**\n * Options for hierarchical configuration discovery.\n */\nexport interface HierarchicalDiscoveryOptions {\n /** Name of the configuration directory to look for (e.g., '.kodrdriv') */\n configDirName: string;\n /** Name of the configuration file within each directory */\n configFileName: string;\n /** Maximum number of parent directories to traverse (default: 10) */\n maxLevels?: number;\n /** Starting directory for discovery (default: process.cwd()) */\n startingDir?: string;\n /** File encoding for reading configuration files */\n encoding?: string;\n /** Logger for debugging */\n logger?: Logger;\n}\n\n/**\n * Result of loading configurations from multiple directories.\n */\nexport interface HierarchicalConfigResult {\n /** Merged configuration object with proper precedence */\n config: object;\n /** Array of directories where configuration was found */\n discoveredDirs: DiscoveredConfigDir[];\n /** Array of any errors encountered during loading (non-fatal) */\n errors: string[];\n}\n\n/**\n * Discovers configuration directories by traversing up the directory tree.\n * \n * Starting from the specified directory (or current working directory),\n * this function searches for directories with the given name, continuing\n * up the directory tree until it reaches the filesystem root or the\n * maximum number of levels.\n * \n * @param options Configuration options for discovery\n * @returns Promise resolving to array of discovered configuration directories\n * \n * @example\n * ```typescript\n * const dirs = await discoverConfigDirectories({\n * configDirName: '.kodrdriv',\n * configFileName: 'config.yaml',\n * maxLevels: 5\n * });\n * // Returns: [\n * // { path: '/project/.kodrdriv', level: 0 },\n * // { path: '/project/parent/.kodrdriv', level: 1 }\n * // ]\n * ```\n */\nexport async function discoverConfigDirectories(\n options: HierarchicalDiscoveryOptions\n): Promise<DiscoveredConfigDir[]> {\n const {\n configDirName,\n maxLevels = 10,\n startingDir = process.cwd(),\n logger\n } = options;\n\n const storage = createStorage({ log: logger?.debug || (() => { }) });\n const discoveredDirs: DiscoveredConfigDir[] = [];\n\n let currentDir = path.resolve(startingDir);\n let level = 0;\n const visited = new Set<string>(); // Prevent infinite loops with symlinks\n\n logger?.debug(`Starting hierarchical discovery from: ${currentDir}`);\n\n while (level < maxLevels) {\n // Prevent infinite loops with symlinks\n const realPath = path.resolve(currentDir);\n if (visited.has(realPath)) {\n logger?.debug(`Already visited ${realPath}, stopping discovery`);\n break;\n }\n visited.add(realPath);\n\n const configDirPath = path.join(currentDir, configDirName);\n logger?.debug(`Checking for config directory: ${configDirPath}`);\n\n try {\n const exists = await storage.exists(configDirPath);\n const isReadable = exists && await storage.isDirectoryReadable(configDirPath);\n\n if (exists && isReadable) {\n discoveredDirs.push({\n path: configDirPath,\n level\n });\n logger?.debug(`Found config directory at level ${level}: ${configDirPath}`);\n } else if (exists && !isReadable) {\n logger?.debug(`Config directory exists but is not readable: ${configDirPath}`);\n }\n } catch (error: any) {\n logger?.debug(`Error checking config directory ${configDirPath}: ${error.message}`);\n }\n\n // Move up one directory level\n const parentDir = path.dirname(currentDir);\n\n // Check if we've reached the root directory\n if (parentDir === currentDir) {\n logger?.debug('Reached filesystem root, stopping discovery');\n break;\n }\n\n currentDir = parentDir;\n level++;\n }\n\n logger?.debug(`Discovery complete. Found ${discoveredDirs.length} config directories`);\n return discoveredDirs;\n}\n\n/**\n * Loads and parses a configuration file from a directory.\n * \n * @param configDir Path to the configuration directory\n * @param configFileName Name of the configuration file\n * @param encoding File encoding\n * @param logger Optional logger\n * @returns Promise resolving to parsed configuration object or null if not found\n */\nexport async function loadConfigFromDirectory(\n configDir: string,\n configFileName: string,\n encoding: string = 'utf8',\n logger?: Logger\n): Promise<object | null> {\n const storage = createStorage({ log: logger?.debug || (() => { }) });\n const configFilePath = path.join(configDir, configFileName);\n\n try {\n logger?.debug(`Attempting to load config file: ${configFilePath}`);\n\n const exists = await storage.exists(configFilePath);\n if (!exists) {\n logger?.debug(`Config file does not exist: ${configFilePath}`);\n return null;\n }\n\n const isReadable = await storage.isFileReadable(configFilePath);\n if (!isReadable) {\n logger?.debug(`Config file exists but is not readable: ${configFilePath}`);\n return null;\n }\n\n const yamlContent = await storage.readFile(configFilePath, encoding);\n const parsedYaml = yaml.load(yamlContent);\n\n if (parsedYaml !== null && typeof parsedYaml === 'object') {\n logger?.debug(`Successfully loaded config from: ${configFilePath}`);\n return parsedYaml as object;\n } else {\n logger?.debug(`Config file contains invalid format: ${configFilePath}`);\n return null;\n }\n } catch (error: any) {\n logger?.debug(`Error loading config from ${configFilePath}: ${error.message}`);\n return null;\n }\n}\n\n/**\n * Deep merges multiple configuration objects with proper precedence.\n * \n * Objects are merged from lowest precedence to highest precedence,\n * meaning that properties in later objects override properties in earlier objects.\n * Arrays are replaced entirely (not merged).\n * \n * @param configs Array of configuration objects, ordered from lowest to highest precedence\n * @returns Merged configuration object\n * \n * @example\n * ```typescript\n * const merged = deepMergeConfigs([\n * { api: { timeout: 5000 }, debug: true }, // Lower precedence\n * { api: { retries: 3 }, features: ['auth'] }, // Higher precedence\n * ]);\n * // Result: { api: { timeout: 5000, retries: 3 }, debug: true, features: ['auth'] }\n * ```\n */\nexport function deepMergeConfigs(configs: object[]): object {\n if (configs.length === 0) {\n return {};\n }\n\n if (configs.length === 1) {\n return { ...configs[0] };\n }\n\n return configs.reduce((merged, current) => {\n return deepMergeTwo(merged, current);\n }, {});\n}\n\n/**\n * Deep merges two objects with proper precedence.\n * \n * @param target Target object (lower precedence)\n * @param source Source object (higher precedence)\n * @returns Merged object\n */\nfunction deepMergeTwo(target: any, source: any): any {\n // Handle null/undefined\n if (source == null) return target;\n if (target == null) return source;\n\n // Handle non-objects (primitives, arrays, functions, etc.)\n if (typeof source !== 'object' || typeof target !== 'object') {\n return source; // Source takes precedence\n }\n\n // Handle arrays - replace entirely, don't merge\n if (Array.isArray(source)) {\n return [...source];\n }\n\n if (Array.isArray(target)) {\n return source; // Source object replaces target array\n }\n\n // Deep merge objects\n const result = { ...target };\n\n for (const key in source) {\n if (Object.prototype.hasOwnProperty.call(source, key)) {\n if (Object.prototype.hasOwnProperty.call(result, key) &&\n typeof result[key] === 'object' &&\n typeof source[key] === 'object' &&\n !Array.isArray(source[key]) &&\n !Array.isArray(result[key])) {\n // Recursively merge nested objects\n result[key] = deepMergeTwo(result[key], source[key]);\n } else {\n // Replace with source value (higher precedence)\n result[key] = source[key];\n }\n }\n }\n\n return result;\n}\n\n/**\n * Loads configurations from multiple directories and merges them with proper precedence.\n * \n * This is the main function for hierarchical configuration loading. It:\n * 1. Discovers configuration directories up the directory tree\n * 2. Loads configuration files from each discovered directory\n * 3. Merges them with proper precedence (closer directories win)\n * 4. Returns the merged configuration with metadata\n * \n * @param options Configuration options for hierarchical loading\n * @returns Promise resolving to hierarchical configuration result\n * \n * @example\n * ```typescript\n * const result = await loadHierarchicalConfig({\n * configDirName: '.kodrdriv',\n * configFileName: 'config.yaml',\n * startingDir: '/project/subdir',\n * maxLevels: 5\n * });\n * \n * // result.config contains merged configuration\n * // result.discoveredDirs shows where configs were found\n * // result.errors contains any non-fatal errors\n * ```\n */\nexport async function loadHierarchicalConfig(\n options: HierarchicalDiscoveryOptions\n): Promise<HierarchicalConfigResult> {\n const { configFileName, encoding = 'utf8', logger } = options;\n\n logger?.debug('Starting hierarchical configuration loading');\n\n // Discover all configuration directories\n const discoveredDirs = await discoverConfigDirectories(options);\n\n if (discoveredDirs.length === 0) {\n logger?.debug('No configuration directories found');\n return {\n config: {},\n discoveredDirs: [],\n errors: []\n };\n }\n\n // Load configurations from each directory\n const configs: object[] = [];\n const errors: string[] = [];\n\n // Sort by level (highest level first = lowest precedence first)\n const sortedDirs = [...discoveredDirs].sort((a, b) => b.level - a.level);\n\n for (const dir of sortedDirs) {\n try {\n const config = await loadConfigFromDirectory(\n dir.path,\n configFileName,\n encoding,\n logger\n );\n\n if (config !== null) {\n configs.push(config);\n logger?.debug(`Loaded config from level ${dir.level}: ${dir.path}`);\n } else {\n logger?.debug(`No valid config found at level ${dir.level}: ${dir.path}`);\n }\n } catch (error: any) {\n const errorMsg = `Failed to load config from ${dir.path}: ${error.message}`;\n errors.push(errorMsg);\n logger?.debug(errorMsg);\n }\n }\n\n // Merge all configurations with proper precedence\n const mergedConfig = deepMergeConfigs(configs);\n\n logger?.debug(`Hierarchical loading complete. Merged ${configs.length} configurations`);\n\n return {\n config: mergedConfig,\n discoveredDirs,\n errors\n };\n} "],"names":["discoverConfigDirectories","options","configDirName","maxLevels","startingDir","process","cwd","logger","storage","createStorage","log","debug","discoveredDirs","currentDir","path","resolve","level","visited","Set","realPath","has","add","configDirPath","join","exists","isReadable","isDirectoryReadable","push","error","message","parentDir","dirname","length","loadConfigFromDirectory","configDir","configFileName","encoding","configFilePath","isFileReadable","yamlContent","readFile","parsedYaml","yaml","load","deepMergeConfigs","configs","reduce","merged","current","deepMergeTwo","target","source","Array","isArray","result","key","Object","prototype","hasOwnProperty","call","loadHierarchicalConfig","config","errors","sortedDirs","sort","a","b","dir","errorMsg","mergedConfig"],"mappings":";;;;AA6CA;;;;;;;;;;;;;;;;;;;;;;;IAwBO,eAAeA,yBAAAA,CAClBC,OAAqC,EAAA;AAErC,IAAA,MAAM,EACFC,aAAa,EACbC,SAAAA,GAAY,EAAE,EACdC,WAAAA,GAAcC,OAAAA,CAAQC,GAAG,EAAE,EAC3BC,MAAM,EACT,GAAGN,OAAAA;AAEJ,IAAA,MAAMO,UAAUC,MAAAA,CAAc;QAAEC,GAAAA,EAAKH,CAAAA,mBAAAA,MAAAA,KAAAA,MAAAA,GAAAA,MAAAA,GAAAA,MAAAA,CAAQI,KAAK,MAAK,MAAQ;AAAG,KAAA,CAAA;AAClE,IAAA,MAAMC,iBAAwC,EAAE;IAEhD,IAAIC,UAAAA,GAAaC,aAAAA,CAAKC,OAAO,CAACX,WAAAA,CAAAA;AAC9B,IAAA,IAAIY,KAAAA,GAAQ,CAAA;IACZ,MAAMC,OAAAA,GAAU,IAAIC,GAAAA,EAAAA,CAAAA;AAEpBX,IAAAA,MAAAA,KAAAA,IAAAA,IAAAA,6BAAAA,MAAAA,CAAQI,KAAK,CAAC,CAAC,sCAAsC,EAAEE,UAAAA,CAAAA,CAAY,CAAA;AAEnE,IAAA,MAAOG,QAAQb,SAAAA,CAAW;;QAEtB,MAAMgB,QAAAA,GAAWL,aAAAA,CAAKC,OAAO,CAACF,UAAAA,CAAAA;QAC9B,IAAII,OAAAA,CAAQG,GAAG,CAACD,QAAAA,CAAAA,EAAW;YACvBZ,MAAAA,KAAAA,IAAAA,IAAAA,MAAAA,KAAAA,MAAAA,GAAAA,MAAAA,GAAAA,OAAQI,KAAK,CAAC,CAAC,gBAAgB,EAAEQ,QAAAA,CAAS,oBAAoB,CAAC,CAAA;AAC/D,YAAA;AACJ;AACAF,QAAAA,OAAAA,CAAQI,GAAG,CAACF,QAAAA,CAAAA;AAEZ,QAAA,MAAMG,aAAAA,GAAgBR,aAAAA,CAAKS,IAAI,CAACV,UAAAA,EAAYX,aAAAA,CAAAA;AAC5CK,QAAAA,MAAAA,KAAAA,IAAAA,IAAAA,6BAAAA,MAAAA,CAAQI,KAAK,CAAC,CAAC,+BAA+B,EAAEW,aAAAA,CAAAA,CAAe,CAAA;QAE/D,IAAI;AACA,YAAA,MAAME,MAAAA,GAAS,MAAMhB,OAAAA,CAAQgB,MAAM,CAACF,aAAAA,CAAAA;AACpC,YAAA,MAAMG,UAAAA,GAAaD,MAAAA,IAAU,MAAMhB,OAAAA,CAAQkB,mBAAmB,CAACJ,aAAAA,CAAAA;AAE/D,YAAA,IAAIE,UAAUC,UAAAA,EAAY;AACtBb,gBAAAA,cAAAA,CAAee,IAAI,CAAC;oBAChBb,IAAAA,EAAMQ,aAAAA;AACNN,oBAAAA;AACJ,iBAAA,CAAA;gBACAT,MAAAA,KAAAA,IAAAA,IAAAA,MAAAA,KAAAA,KAAAA,CAAAA,GAAAA,KAAAA,CAAAA,GAAAA,MAAAA,CAAQI,KAAK,CAAC,CAAC,gCAAgC,EAAEK,KAAAA,CAAM,EAAE,EAAEM,aAAAA,CAAAA,CAAe,CAAA;aAC9E,MAAO,IAAIE,MAAAA,IAAU,CAACC,UAAAA,EAAY;AAC9BlB,gBAAAA,MAAAA,KAAAA,IAAAA,IAAAA,6BAAAA,MAAAA,CAAQI,KAAK,CAAC,CAAC,6CAA6C,EAAEW,aAAAA,CAAAA,CAAe,CAAA;AACjF;AACJ,SAAA,CAAE,OAAOM,KAAAA,EAAY;AACjBrB,YAAAA,MAAAA,KAAAA,IAAAA,IAAAA,MAAAA,KAAAA,MAAAA,GAAAA,MAAAA,GAAAA,MAAAA,CAAQI,KAAK,CAAC,CAAC,gCAAgC,EAAEW,aAAAA,CAAc,EAAE,EAAEM,KAAAA,CAAMC,OAAO,CAAA,CAAE,CAAA;AACtF;;QAGA,MAAMC,SAAAA,GAAYhB,aAAAA,CAAKiB,OAAO,CAAClB,UAAAA,CAAAA;;AAG/B,QAAA,IAAIiB,cAAcjB,UAAAA,EAAY;YAC1BN,MAAAA,KAAAA,IAAAA,IAAAA,MAAAA,KAAAA,MAAAA,GAAAA,MAAAA,GAAAA,MAAAA,CAAQI,KAAK,CAAC,6CAAA,CAAA;AACd,YAAA;AACJ;QAEAE,UAAAA,GAAaiB,SAAAA;AACbd,QAAAA,KAAAA,EAAAA;AACJ;IAEAT,MAAAA,KAAAA,IAAAA,IAAAA,MAAAA,KAAAA,MAAAA,GAAAA,MAAAA,GAAAA,MAAAA,CAAQI,KAAK,CAAC,CAAC,0BAA0B,EAAEC,cAAAA,CAAeoB,MAAM,CAAC,mBAAmB,CAAC,CAAA;IACrF,OAAOpB,cAAAA;AACX;AAEA;;;;;;;;IASO,eAAeqB,uBAAAA,CAClBC,SAAiB,EACjBC,cAAsB,EACtBC,QAAAA,GAAmB,MAAM,EACzB7B,MAAe,EAAA;AAEf,IAAA,MAAMC,UAAUC,MAAAA,CAAc;QAAEC,GAAAA,EAAKH,CAAAA,mBAAAA,MAAAA,KAAAA,MAAAA,GAAAA,MAAAA,GAAAA,MAAAA,CAAQI,KAAK,MAAK,MAAQ;AAAG,KAAA,CAAA;AAClE,IAAA,MAAM0B,cAAAA,GAAiBvB,aAAAA,CAAKS,IAAI,CAACW,SAAAA,EAAWC,cAAAA,CAAAA;IAE5C,IAAI;AACA5B,QAAAA,MAAAA,KAAAA,IAAAA,IAAAA,6BAAAA,MAAAA,CAAQI,KAAK,CAAC,CAAC,gCAAgC,EAAE0B,cAAAA,CAAAA,CAAgB,CAAA;AAEjE,QAAA,MAAMb,MAAAA,GAAS,MAAMhB,OAAAA,CAAQgB,MAAM,CAACa,cAAAA,CAAAA;AACpC,QAAA,IAAI,CAACb,MAAAA,EAAQ;AACTjB,YAAAA,MAAAA,KAAAA,IAAAA,IAAAA,6BAAAA,MAAAA,CAAQI,KAAK,CAAC,CAAC,4BAA4B,EAAE0B,cAAAA,CAAAA,CAAgB,CAAA;YAC7D,OAAO,IAAA;AACX;AAEA,QAAA,MAAMZ,UAAAA,GAAa,MAAMjB,OAAAA,CAAQ8B,cAAc,CAACD,cAAAA,CAAAA;AAChD,QAAA,IAAI,CAACZ,UAAAA,EAAY;AACblB,YAAAA,MAAAA,KAAAA,IAAAA,IAAAA,6BAAAA,MAAAA,CAAQI,KAAK,CAAC,CAAC,wCAAwC,EAAE0B,cAAAA,CAAAA,CAAgB,CAAA;YACzE,OAAO,IAAA;AACX;AAEA,QAAA,MAAME,WAAAA,GAAc,MAAM/B,OAAAA,CAAQgC,QAAQ,CAACH,cAAAA,EAAgBD,QAAAA,CAAAA;QAC3D,MAAMK,UAAAA,GAAaC,IAAAA,CAAKC,IAAI,CAACJ,WAAAA,CAAAA;AAE7B,QAAA,IAAIE,UAAAA,KAAe,IAAA,IAAQ,OAAOA,UAAAA,KAAe,QAAA,EAAU;AACvDlC,YAAAA,MAAAA,KAAAA,IAAAA,IAAAA,6BAAAA,MAAAA,CAAQI,KAAK,CAAC,CAAC,iCAAiC,EAAE0B,cAAAA,CAAAA,CAAgB,CAAA;YAClE,OAAOI,UAAAA;SACX,MAAO;AACHlC,YAAAA,MAAAA,KAAAA,IAAAA,IAAAA,6BAAAA,MAAAA,CAAQI,KAAK,CAAC,CAAC,qCAAqC,EAAE0B,cAAAA,CAAAA,CAAgB,CAAA;YACtE,OAAO,IAAA;AACX;AACJ,KAAA,CAAE,OAAOT,KAAAA,EAAY;AACjBrB,QAAAA,MAAAA,KAAAA,IAAAA,IAAAA,MAAAA,KAAAA,MAAAA,GAAAA,MAAAA,GAAAA,MAAAA,CAAQI,KAAK,CAAC,CAAC,0BAA0B,EAAE0B,cAAAA,CAAe,EAAE,EAAET,KAAAA,CAAMC,OAAO,CAAA,CAAE,CAAA;QAC7E,OAAO,IAAA;AACX;AACJ;AAEA;;;;;;;;;;;;;;;;;;IAmBO,SAASe,gBAAAA,CAAiBC,OAAiB,EAAA;IAC9C,IAAIA,OAAAA,CAAQb,MAAM,KAAK,CAAA,EAAG;AACtB,QAAA,OAAO,EAAC;AACZ;IAEA,IAAIa,OAAAA,CAAQb,MAAM,KAAK,CAAA,EAAG;QACtB,OAAO;YAAE,GAAGa,OAAO,CAAC,CAAA;AAAG,SAAA;AAC3B;AAEA,IAAA,OAAOA,OAAAA,CAAQC,MAAM,CAAC,CAACC,MAAAA,EAAQC,OAAAA,GAAAA;AAC3B,QAAA,OAAOC,aAAaF,MAAAA,EAAQC,OAAAA,CAAAA;AAChC,KAAA,EAAG,EAAC,CAAA;AACR;AAEA;;;;;;AAMC,IACD,SAASC,YAAAA,CAAaC,MAAW,EAAEC,MAAW,EAAA;;IAE1C,IAAIA,MAAAA,IAAU,MAAM,OAAOD,MAAAA;IAC3B,IAAIA,MAAAA,IAAU,MAAM,OAAOC,MAAAA;;AAG3B,IAAA,IAAI,OAAOA,MAAAA,KAAW,QAAA,IAAY,OAAOD,WAAW,QAAA,EAAU;AAC1D,QAAA,OAAOC;AACX;;IAGA,IAAIC,KAAAA,CAAMC,OAAO,CAACF,MAAAA,CAAAA,EAAS;QACvB,OAAO;AAAIA,YAAAA,GAAAA;AAAO,SAAA;AACtB;IAEA,IAAIC,KAAAA,CAAMC,OAAO,CAACH,MAAAA,CAAAA,EAAS;AACvB,QAAA,OAAOC;AACX;;AAGA,IAAA,MAAMG,MAAAA,GAAS;AAAE,QAAA,GAAGJ;AAAO,KAAA;IAE3B,IAAK,MAAMK,OAAOJ,MAAAA,CAAQ;QACtB,IAAIK,MAAAA,CAAOC,SAAS,CAACC,cAAc,CAACC,IAAI,CAACR,QAAQI,GAAAA,CAAAA,EAAM;AACnD,YAAA,IAAIC,OAAOC,SAAS,CAACC,cAAc,CAACC,IAAI,CAACL,MAAAA,EAAQC,GAAAA,CAAAA,IAC7C,OAAOD,MAAM,CAACC,GAAAA,CAAI,KAAK,QAAA,IACvB,OAAOJ,MAAM,CAACI,GAAAA,CAAI,KAAK,YACvB,CAACH,KAAAA,CAAMC,OAAO,CAACF,MAAM,CAACI,GAAAA,CAAI,CAAA,IAC1B,CAACH,MAAMC,OAAO,CAACC,MAAM,CAACC,IAAI,CAAA,EAAG;;gBAE7BD,MAAM,CAACC,GAAAA,CAAI,GAAGN,YAAAA,CAAaK,MAAM,CAACC,GAAAA,CAAI,EAAEJ,MAAM,CAACI,GAAAA,CAAI,CAAA;aACvD,MAAO;;AAEHD,gBAAAA,MAAM,CAACC,GAAAA,CAAI,GAAGJ,MAAM,CAACI,GAAAA,CAAI;AAC7B;AACJ;AACJ;IAEA,OAAOD,MAAAA;AACX;AAEA;;;;;;;;;;;;;;;;;;;;;;;;;IA0BO,eAAeM,sBAAAA,CAClB3D,OAAqC,EAAA;IAErC,MAAM,EAAEkC,cAAc,EAAEC,QAAAA,GAAW,MAAM,EAAE7B,MAAM,EAAE,GAAGN,OAAAA;IAEtDM,MAAAA,KAAAA,IAAAA,IAAAA,MAAAA,KAAAA,MAAAA,GAAAA,MAAAA,GAAAA,MAAAA,CAAQI,KAAK,CAAC,6CAAA,CAAA;;IAGd,MAAMC,cAAAA,GAAiB,MAAMZ,yBAAAA,CAA0BC,OAAAA,CAAAA;IAEvD,IAAIW,cAAAA,CAAeoB,MAAM,KAAK,CAAA,EAAG;QAC7BzB,MAAAA,KAAAA,IAAAA,IAAAA,MAAAA,KAAAA,MAAAA,GAAAA,MAAAA,GAAAA,MAAAA,CAAQI,KAAK,CAAC,oCAAA,CAAA;QACd,OAAO;AACHkD,YAAAA,MAAAA,EAAQ,EAAC;AACTjD,YAAAA,cAAAA,EAAgB,EAAE;AAClBkD,YAAAA,MAAAA,EAAQ;AACZ,SAAA;AACJ;;AAGA,IAAA,MAAMjB,UAAoB,EAAE;AAC5B,IAAA,MAAMiB,SAAmB,EAAE;;AAG3B,IAAA,MAAMC,UAAAA,GAAa;AAAInD,QAAAA,GAAAA;KAAe,CAACoD,IAAI,CAAC,CAACC,CAAAA,EAAGC,IAAMA,CAAAA,CAAElD,KAAK,GAAGiD,CAAAA,CAAEjD,KAAK,CAAA;IAEvE,KAAK,MAAMmD,OAAOJ,UAAAA,CAAY;QAC1B,IAAI;AACA,YAAA,MAAMF,SAAS,MAAM5B,uBAAAA,CACjBkC,IAAIrD,IAAI,EACRqB,gBACAC,QAAAA,EACA7B,MAAAA,CAAAA;AAGJ,YAAA,IAAIsD,WAAW,IAAA,EAAM;AACjBhB,gBAAAA,OAAAA,CAAQlB,IAAI,CAACkC,MAAAA,CAAAA;AACbtD,gBAAAA,MAAAA,KAAAA,IAAAA,IAAAA,MAAAA,KAAAA,KAAAA,CAAAA,GAAAA,KAAAA,CAAAA,GAAAA,MAAAA,CAAQI,KAAK,CAAC,CAAC,yBAAyB,EAAEwD,GAAAA,CAAInD,KAAK,CAAC,EAAE,EAAEmD,GAAAA,CAAIrD,IAAI,CAAA,CAAE,CAAA;aACtE,MAAO;AACHP,gBAAAA,MAAAA,KAAAA,IAAAA,IAAAA,MAAAA,KAAAA,KAAAA,CAAAA,GAAAA,KAAAA,CAAAA,GAAAA,MAAAA,CAAQI,KAAK,CAAC,CAAC,+BAA+B,EAAEwD,GAAAA,CAAInD,KAAK,CAAC,EAAE,EAAEmD,GAAAA,CAAIrD,IAAI,CAAA,CAAE,CAAA;AAC5E;AACJ,SAAA,CAAE,OAAOc,KAAAA,EAAY;YACjB,MAAMwC,QAAAA,GAAW,CAAC,2BAA2B,EAAED,GAAAA,CAAIrD,IAAI,CAAC,EAAE,EAAEc,KAAAA,CAAMC,OAAO,CAAA,CAAE;AAC3EiC,YAAAA,MAAAA,CAAOnC,IAAI,CAACyC,QAAAA,CAAAA;YACZ7D,MAAAA,KAAAA,IAAAA,IAAAA,MAAAA,KAAAA,MAAAA,GAAAA,MAAAA,GAAAA,MAAAA,CAAQI,KAAK,CAACyD,QAAAA,CAAAA;AAClB;AACJ;;AAGA,IAAA,MAAMC,eAAezB,gBAAAA,CAAiBC,OAAAA,CAAAA;IAEtCtC,MAAAA,KAAAA,IAAAA,IAAAA,MAAAA,KAAAA,MAAAA,GAAAA,MAAAA,GAAAA,MAAAA,CAAQI,KAAK,CAAC,CAAC,sCAAsC,EAAEkC,OAAAA,CAAQb,MAAM,CAAC,eAAe,CAAC,CAAA;IAEtF,OAAO;QACH6B,MAAAA,EAAQQ,YAAAA;AACRzD,QAAAA,cAAAA;AACAkD,QAAAA;AACJ,KAAA;AACJ;;;;"}
|
|
1
|
+
{"version":3,"file":"hierarchical.js","sources":["../../src/util/hierarchical.ts"],"sourcesContent":["import path from 'path';\nimport * as yaml from 'js-yaml';\nimport { create as createStorage } from './storage';\nimport { Logger } from '../types';\n\n/**\n * Represents a discovered configuration directory with its path and precedence level.\n */\nexport interface DiscoveredConfigDir {\n /** Absolute path to the configuration directory */\n path: string;\n /** Distance from the starting directory (0 = closest/highest precedence) */\n level: number;\n}\n\n/**\n * Options for hierarchical configuration discovery.\n */\nexport interface HierarchicalDiscoveryOptions {\n /** Name of the configuration directory to look for (e.g., '.kodrdriv') */\n configDirName: string;\n /** Name of the configuration file within each directory */\n configFileName: string;\n /** Maximum number of parent directories to traverse (default: 10) */\n maxLevels?: number;\n /** Starting directory for discovery (default: process.cwd()) */\n startingDir?: string;\n /** File encoding for reading configuration files */\n encoding?: string;\n /** Logger for debugging */\n logger?: Logger;\n}\n\n/**\n * Result of loading configurations from multiple directories.\n */\nexport interface HierarchicalConfigResult {\n /** Merged configuration object with proper precedence */\n config: object;\n /** Array of directories where configuration was found */\n discoveredDirs: DiscoveredConfigDir[];\n /** Array of any errors encountered during loading (non-fatal) */\n errors: string[];\n}\n\n/**\n * Discovers configuration directories by traversing up the directory tree.\n * \n * Starting from the specified directory (or current working directory),\n * this function searches for directories with the given name, continuing\n * up the directory tree until it reaches the filesystem root or the\n * maximum number of levels.\n * \n * @param options Configuration options for discovery\n * @returns Promise resolving to array of discovered configuration directories\n * \n * @example\n * ```typescript\n * const dirs = await discoverConfigDirectories({\n * configDirName: '.kodrdriv',\n * configFileName: 'config.yaml',\n * maxLevels: 5\n * });\n * // Returns: [\n * // { path: '/project/.kodrdriv', level: 0 },\n * // { path: '/project/parent/.kodrdriv', level: 1 }\n * // ]\n * ```\n */\nexport async function discoverConfigDirectories(\n options: HierarchicalDiscoveryOptions\n): Promise<DiscoveredConfigDir[]> {\n const {\n configDirName,\n maxLevels = 10,\n startingDir = process.cwd(),\n logger\n } = options;\n\n const storage = createStorage({ log: logger?.debug || (() => { }) });\n const discoveredDirs: DiscoveredConfigDir[] = [];\n\n let currentDir = path.resolve(startingDir);\n let level = 0;\n const visited = new Set<string>(); // Prevent infinite loops with symlinks\n\n logger?.debug(`Starting hierarchical discovery from: ${currentDir}`);\n\n while (level < maxLevels) {\n // Prevent infinite loops with symlinks\n const realPath = path.resolve(currentDir);\n if (visited.has(realPath)) {\n logger?.debug(`Already visited ${realPath}, stopping discovery`);\n break;\n }\n visited.add(realPath);\n\n const configDirPath = path.join(currentDir, configDirName);\n logger?.debug(`Checking for config directory: ${configDirPath}`);\n\n try {\n const exists = await storage.exists(configDirPath);\n const isReadable = exists && await storage.isDirectoryReadable(configDirPath);\n\n if (exists && isReadable) {\n discoveredDirs.push({\n path: configDirPath,\n level\n });\n logger?.debug(`Found config directory at level ${level}: ${configDirPath}`);\n } else if (exists && !isReadable) {\n logger?.debug(`Config directory exists but is not readable: ${configDirPath}`);\n }\n } catch (error: any) {\n logger?.debug(`Error checking config directory ${configDirPath}: ${error.message}`);\n }\n\n // Move up one directory level\n const parentDir = path.dirname(currentDir);\n\n // Check if we've reached the root directory\n if (parentDir === currentDir) {\n logger?.debug('Reached filesystem root, stopping discovery');\n break;\n }\n\n currentDir = parentDir;\n level++;\n }\n\n logger?.verbose(`Discovery complete. Found ${discoveredDirs.length} config directories`);\n return discoveredDirs;\n}\n\n/**\n * Loads and parses a configuration file from a directory.\n * \n * @param configDir Path to the configuration directory\n * @param configFileName Name of the configuration file\n * @param encoding File encoding\n * @param logger Optional logger\n * @returns Promise resolving to parsed configuration object or null if not found\n */\nexport async function loadConfigFromDirectory(\n configDir: string,\n configFileName: string,\n encoding: string = 'utf8',\n logger?: Logger\n): Promise<object | null> {\n const storage = createStorage({ log: logger?.debug || (() => { }) });\n const configFilePath = path.join(configDir, configFileName);\n\n try {\n logger?.verbose(`Attempting to load config file: ${configFilePath}`);\n\n const exists = await storage.exists(configFilePath);\n if (!exists) {\n logger?.debug(`Config file does not exist: ${configFilePath}`);\n return null;\n }\n\n const isReadable = await storage.isFileReadable(configFilePath);\n if (!isReadable) {\n logger?.debug(`Config file exists but is not readable: ${configFilePath}`);\n return null;\n }\n\n const yamlContent = await storage.readFile(configFilePath, encoding);\n const parsedYaml = yaml.load(yamlContent);\n\n if (parsedYaml !== null && typeof parsedYaml === 'object') {\n logger?.verbose(`Successfully loaded config from: ${configFilePath}`);\n return parsedYaml as object;\n } else {\n logger?.debug(`Config file contains invalid format: ${configFilePath}`);\n return null;\n }\n } catch (error: any) {\n logger?.debug(`Error loading config from ${configFilePath}: ${error.message}`);\n return null;\n }\n}\n\n/**\n * Deep merges multiple configuration objects with proper precedence.\n * \n * Objects are merged from lowest precedence to highest precedence,\n * meaning that properties in later objects override properties in earlier objects.\n * Arrays are replaced entirely (not merged).\n * \n * @param configs Array of configuration objects, ordered from lowest to highest precedence\n * @returns Merged configuration object\n * \n * @example\n * ```typescript\n * const merged = deepMergeConfigs([\n * { api: { timeout: 5000 }, debug: true }, // Lower precedence\n * { api: { retries: 3 }, features: ['auth'] }, // Higher precedence\n * ]);\n * // Result: { api: { timeout: 5000, retries: 3 }, debug: true, features: ['auth'] }\n * ```\n */\nexport function deepMergeConfigs(configs: object[]): object {\n if (configs.length === 0) {\n return {};\n }\n\n if (configs.length === 1) {\n return { ...configs[0] };\n }\n\n return configs.reduce((merged, current) => {\n return deepMergeTwo(merged, current);\n }, {});\n}\n\n/**\n * Deep merges two objects with proper precedence.\n * \n * @param target Target object (lower precedence)\n * @param source Source object (higher precedence)\n * @returns Merged object\n */\nfunction deepMergeTwo(target: any, source: any): any {\n // Handle null/undefined\n if (source == null) return target;\n if (target == null) return source;\n\n // Handle non-objects (primitives, arrays, functions, etc.)\n if (typeof source !== 'object' || typeof target !== 'object') {\n return source; // Source takes precedence\n }\n\n // Handle arrays - replace entirely, don't merge\n if (Array.isArray(source)) {\n return [...source];\n }\n\n if (Array.isArray(target)) {\n return source; // Source object replaces target array\n }\n\n // Deep merge objects\n const result = { ...target };\n\n for (const key in source) {\n if (Object.prototype.hasOwnProperty.call(source, key)) {\n if (Object.prototype.hasOwnProperty.call(result, key) &&\n typeof result[key] === 'object' &&\n typeof source[key] === 'object' &&\n !Array.isArray(source[key]) &&\n !Array.isArray(result[key])) {\n // Recursively merge nested objects\n result[key] = deepMergeTwo(result[key], source[key]);\n } else {\n // Replace with source value (higher precedence)\n result[key] = source[key];\n }\n }\n }\n\n return result;\n}\n\n/**\n * Loads configurations from multiple directories and merges them with proper precedence.\n * \n * This is the main function for hierarchical configuration loading. It:\n * 1. Discovers configuration directories up the directory tree\n * 2. Loads configuration files from each discovered directory\n * 3. Merges them with proper precedence (closer directories win)\n * 4. Returns the merged configuration with metadata\n * \n * @param options Configuration options for hierarchical loading\n * @returns Promise resolving to hierarchical configuration result\n * \n * @example\n * ```typescript\n * const result = await loadHierarchicalConfig({\n * configDirName: '.kodrdriv',\n * configFileName: 'config.yaml',\n * startingDir: '/project/subdir',\n * maxLevels: 5\n * });\n * \n * // result.config contains merged configuration\n * // result.discoveredDirs shows where configs were found\n * // result.errors contains any non-fatal errors\n * ```\n */\nexport async function loadHierarchicalConfig(\n options: HierarchicalDiscoveryOptions\n): Promise<HierarchicalConfigResult> {\n const { configFileName, encoding = 'utf8', logger } = options;\n\n logger?.verbose('Starting hierarchical configuration loading');\n\n // Discover all configuration directories\n const discoveredDirs = await discoverConfigDirectories(options);\n\n if (discoveredDirs.length === 0) {\n logger?.verbose('No configuration directories found');\n return {\n config: {},\n discoveredDirs: [],\n errors: []\n };\n }\n\n // Load configurations from each directory\n const configs: object[] = [];\n const errors: string[] = [];\n\n // Sort by level (highest level first = lowest precedence first)\n const sortedDirs = [...discoveredDirs].sort((a, b) => b.level - a.level);\n\n for (const dir of sortedDirs) {\n try {\n const config = await loadConfigFromDirectory(\n dir.path,\n configFileName,\n encoding,\n logger\n );\n\n if (config !== null) {\n configs.push(config);\n logger?.debug(`Loaded config from level ${dir.level}: ${dir.path}`);\n } else {\n logger?.debug(`No valid config found at level ${dir.level}: ${dir.path}`);\n }\n } catch (error: any) {\n const errorMsg = `Failed to load config from ${dir.path}: ${error.message}`;\n errors.push(errorMsg);\n logger?.debug(errorMsg);\n }\n }\n\n // Merge all configurations with proper precedence\n const mergedConfig = deepMergeConfigs(configs);\n\n logger?.verbose(`Hierarchical loading complete. Merged ${configs.length} configurations`);\n\n return {\n config: mergedConfig,\n discoveredDirs,\n errors\n };\n} "],"names":["discoverConfigDirectories","options","configDirName","maxLevels","startingDir","process","cwd","logger","storage","createStorage","log","debug","discoveredDirs","currentDir","path","resolve","level","visited","Set","realPath","has","add","configDirPath","join","exists","isReadable","isDirectoryReadable","push","error","message","parentDir","dirname","verbose","length","loadConfigFromDirectory","configDir","configFileName","encoding","configFilePath","isFileReadable","yamlContent","readFile","parsedYaml","yaml","load","deepMergeConfigs","configs","reduce","merged","current","deepMergeTwo","target","source","Array","isArray","result","key","Object","prototype","hasOwnProperty","call","loadHierarchicalConfig","config","errors","sortedDirs","sort","a","b","dir","errorMsg","mergedConfig"],"mappings":"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"}
|
package/dist/validate.js
CHANGED
|
@@ -257,7 +257,8 @@ import { create } from './util/storage.js';
|
|
|
257
257
|
checkForExtraKeys(config, fullSchema, logger);
|
|
258
258
|
if (!validationResult.success) {
|
|
259
259
|
const formattedError = JSON.stringify(validationResult.error.format(), null, 2);
|
|
260
|
-
logger.error('Configuration validation failed
|
|
260
|
+
logger.error('Configuration validation failed. Check logs for details.');
|
|
261
|
+
logger.silly('Configuration validation failed: %s', formattedError);
|
|
261
262
|
throw ConfigurationError.validation('Configuration validation failed. Check logs for details.', validationResult.error);
|
|
262
263
|
}
|
|
263
264
|
return;
|
package/dist/validate.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validate.js","sources":["../src/validate.ts"],"sourcesContent":["import { z, ZodObject } from \"zod\";\nimport { ArgumentError } from \"./error/ArgumentError\";\nimport { ConfigurationError } from \"./error/ConfigurationError\";\nimport { FileSystemError } from \"./error/FileSystemError\";\nimport { ConfigSchema, Logger, Options } from \"./types\";\nimport * as Storage from \"./util/storage\";\nexport { ArgumentError, ConfigurationError, FileSystemError };\n\n/**\n * Recursively extracts all keys from a Zod schema in dot notation.\n * \n * This function traverses a Zod schema structure and builds a flat list\n * of all possible keys, using dot notation for nested objects. It handles\n * optional/nullable types by unwrapping them and supports arrays by\n * introspecting their element type.\n * \n * Special handling for:\n * - ZodOptional/ZodNullable: Unwraps to get the underlying type\n * - ZodAny/ZodRecord: Accepts any keys, returns the prefix or empty array\n * - ZodArray: Introspects the element type\n * - ZodObject: Recursively processes all shape properties\n * \n * @param schema - The Zod schema to introspect\n * @param prefix - Internal parameter for building nested key paths\n * @returns Array of strings representing all possible keys in dot notation\n * \n * @example\n * ```typescript\n * const schema = z.object({\n * user: z.object({\n * name: z.string(),\n * settings: z.object({ theme: z.string() })\n * }),\n * debug: z.boolean()\n * });\n * \n * const keys = listZodKeys(schema);\n * // Returns: ['user.name', 'user.settings.theme', 'debug']\n * ```\n */\nexport const listZodKeys = (schema: z.ZodTypeAny, prefix = ''): string[] => {\n // Check if schema has unwrap method (which both ZodOptional and ZodNullable have)\n if (schema._def && (schema._def.typeName === 'ZodOptional' || schema._def.typeName === 'ZodNullable')) {\n // Use type assertion to handle the unwrap method\n const unwrappable = schema as z.ZodOptional<any> | z.ZodNullable<any>;\n return listZodKeys(unwrappable.unwrap(), prefix);\n }\n\n // Handle ZodAny and ZodRecord - these accept any keys, so don't introspect\n if (schema._def && (schema._def.typeName === 'ZodAny' || schema._def.typeName === 'ZodRecord')) {\n return prefix ? [prefix] : [];\n }\n\n if (schema._def && schema._def.typeName === 'ZodArray') {\n // Use type assertion to handle the element property\n const arraySchema = schema as z.ZodArray<any>;\n return listZodKeys(arraySchema.element, prefix);\n }\n if (schema._def && schema._def.typeName === 'ZodObject') {\n // Use type assertion to handle the shape property\n const objectSchema = schema as z.ZodObject<any>;\n return Object.entries(objectSchema.shape).flatMap(([key, subschema]) => {\n const fullKey = prefix ? `${prefix}.${key}` : key;\n const nested = listZodKeys(subschema as z.ZodTypeAny, fullKey);\n return nested.length ? nested : fullKey;\n });\n }\n return [];\n}\n\n/**\n * Type guard to check if a value is a plain object (not array, null, or other types).\n * \n * @param value - The value to check\n * @returns True if the value is a plain object\n */\nconst isPlainObject = (value: unknown): value is Record<string, unknown> => {\n // Check if it's an object, not null, and not an array.\n return value !== null && typeof value === 'object' && !Array.isArray(value);\n};\n\n/**\n * Generates a list of all keys within a JavaScript object, using dot notation for nested keys.\n * Mimics the behavior of listZodKeys but operates on plain objects.\n * For arrays, it inspects the first element that is a plain object to determine nested keys.\n * If an array contains no plain objects, or is empty, the key for the array itself is listed.\n *\n * @param obj The object to introspect.\n * @param prefix Internal use for recursion: the prefix for the current nesting level.\n * @returns An array of strings representing all keys in dot notation.\n */\nexport const listObjectKeys = (obj: Record<string, unknown>, prefix = ''): string[] => {\n const keys = new Set<string>(); // Use Set to automatically handle duplicates from array recursion\n\n for (const key in obj) {\n // Ensure it's an own property, not from the prototype chain\n if (Object.prototype.hasOwnProperty.call(obj, key)) {\n const value = obj[key];\n const fullKey = prefix ? `${prefix}.${key}` : key;\n\n if (Array.isArray(value)) {\n // Find the first element that is a plain object to determine structure\n const firstObjectElement = value.find(isPlainObject);\n if (firstObjectElement) {\n // Recurse into the structure of the first object element found\n const nestedKeys = listObjectKeys(firstObjectElement, fullKey);\n nestedKeys.forEach(k => keys.add(k));\n } else {\n // Array is empty or contains no plain objects, list the array key itself\n keys.add(fullKey);\n }\n } else if (isPlainObject(value)) {\n // Recurse into nested plain objects\n const nestedKeys = listObjectKeys(value, fullKey);\n nestedKeys.forEach(k => keys.add(k));\n } else {\n // It's a primitive, null, or other non-plain object/array type\n keys.add(fullKey);\n }\n }\n }\n return Array.from(keys); // Convert Set back to Array\n};\n\n/**\n * Validates that the configuration object contains only keys allowed by the schema.\n * \n * This function prevents configuration errors by detecting typos or extra keys\n * that aren't defined in the Zod schema. It intelligently handles:\n * - ZodRecord types that accept arbitrary keys\n * - Nested objects and their key structures\n * - Arrays and their element key structures\n * \n * The function throws a ConfigurationError if extra keys are found, providing\n * helpful information about what keys are allowed vs. what was found.\n * \n * @param mergedSources - The merged configuration object to validate\n * @param fullSchema - The complete Zod schema including base and user schemas\n * @param logger - Logger for error reporting\n * @throws {ConfigurationError} When extra keys are found that aren't in the schema\n * \n * @example\n * ```typescript\n * const schema = z.object({ name: z.string(), age: z.number() });\n * const config = { name: 'John', age: 30, typo: 'invalid' };\n * \n * checkForExtraKeys(config, schema, console);\n * // Throws: ConfigurationError with details about 'typo' being an extra key\n * ```\n */\nexport const checkForExtraKeys = (mergedSources: object, fullSchema: ZodObject<any>, logger: Logger | typeof console): void => {\n const allowedKeys = new Set(listZodKeys(fullSchema));\n const actualKeys = listObjectKeys(mergedSources as Record<string, unknown>);\n\n // Filter out keys that are under a record type (ZodRecord accepts any keys)\n const recordPrefixes = new Set<string>();\n\n // Find all prefixes that are ZodRecord types\n const findRecordPrefixes = (schema: z.ZodTypeAny, prefix = ''): void => {\n if (schema._def && (schema._def.typeName === 'ZodOptional' || schema._def.typeName === 'ZodNullable')) {\n const unwrappable = schema as z.ZodOptional<any> | z.ZodNullable<any>;\n findRecordPrefixes(unwrappable.unwrap(), prefix);\n return;\n }\n\n if (schema._def && (schema._def.typeName === 'ZodAny' || schema._def.typeName === 'ZodRecord')) {\n if (prefix) recordPrefixes.add(prefix);\n return;\n }\n\n if (schema._def && schema._def.typeName === 'ZodObject') {\n const objectSchema = schema as z.ZodObject<any>;\n Object.entries(objectSchema.shape).forEach(([key, subschema]) => {\n const fullKey = prefix ? `${prefix}.${key}` : key;\n findRecordPrefixes(subschema as z.ZodTypeAny, fullKey);\n });\n }\n };\n\n findRecordPrefixes(fullSchema);\n\n // Filter out keys that are under record prefixes\n const extraKeys = actualKeys.filter(key => {\n if (allowedKeys.has(key)) return false;\n\n // Check if this key is under a record prefix\n for (const recordPrefix of recordPrefixes) {\n if (key.startsWith(recordPrefix + '.')) {\n return false; // This key is allowed under a record\n }\n }\n\n return true; // This is an extra key\n });\n\n if (extraKeys.length > 0) {\n const allowedKeysArray = Array.from(allowedKeys);\n const error = ConfigurationError.extraKeys(extraKeys, allowedKeysArray);\n logger.error(error.message);\n throw error;\n }\n}\n\n/**\n * Validates that a configuration directory exists and is accessible.\n * \n * This function performs file system checks to ensure the configuration\n * directory can be used. It handles the isRequired flag to determine\n * whether a missing directory should cause an error or be silently ignored.\n * \n * @param configDirectory - Path to the configuration directory\n * @param isRequired - Whether the directory must exist\n * @param logger - Optional logger for debug information\n * @throws {FileSystemError} When the directory is required but missing or unreadable\n */\nconst validateConfigDirectory = async (configDirectory: string, isRequired: boolean, logger?: Logger): Promise<void> => {\n const storage = Storage.create({ log: logger?.debug || (() => { }) });\n const exists = await storage.exists(configDirectory);\n if (!exists) {\n if (isRequired) {\n throw FileSystemError.directoryNotFound(configDirectory, true);\n }\n } else if (exists) {\n const isReadable = await storage.isDirectoryReadable(configDirectory);\n if (!isReadable) {\n throw FileSystemError.directoryNotReadable(configDirectory);\n }\n }\n}\n\n/**\n * Validates a configuration object against the combined Zod schema.\n * \n * This is the main validation function that:\n * 1. Validates the configuration directory (if config feature enabled)\n * 2. Combines the base ConfigSchema with user-provided schema shape\n * 3. Checks for extra keys not defined in the schema\n * 4. Validates all values against their schema definitions\n * 5. Provides detailed error reporting for validation failures\n * \n * The validation is comprehensive and catches common configuration errors\n * including typos, missing required fields, wrong types, and invalid values.\n * \n * @template T - The Zod schema shape type for configuration validation\n * @param config - The merged configuration object to validate\n * @param options - Cardigantime options containing schema, defaults, and logger\n * @throws {ConfigurationError} When configuration validation fails\n * @throws {FileSystemError} When configuration directory validation fails\n * \n * @example\n * ```typescript\n * const schema = z.object({\n * apiKey: z.string().min(1),\n * timeout: z.number().positive(),\n * });\n * \n * await validate(config, {\n * configShape: schema.shape,\n * defaults: { configDirectory: './config', isRequired: true },\n * logger: console,\n * features: ['config']\n * });\n * // Throws detailed errors if validation fails\n * ```\n */\nexport const validate = async <T extends z.ZodRawShape>(config: z.infer<ZodObject<T & typeof ConfigSchema.shape>>, options: Options<T>): Promise<void> => {\n const logger = options.logger;\n\n if (options.features.includes('config') && config.configDirectory) {\n await validateConfigDirectory(config.configDirectory, options.defaults.isRequired, logger);\n }\n\n // Combine the base schema with the user-provided shape\n const fullSchema = z.object({\n ...ConfigSchema.shape,\n ...options.configShape,\n });\n\n // Validate the merged sources against the full schema\n const validationResult = fullSchema.safeParse(config);\n\n // Check for extraneous keys\n checkForExtraKeys(config, fullSchema, logger);\n\n if (!validationResult.success) {\n const formattedError = JSON.stringify(validationResult.error.format(), null, 2);\n logger.error('Configuration validation failed: %s', formattedError);\n throw ConfigurationError.validation('Configuration validation failed. Check logs for details.', validationResult.error);\n }\n\n return;\n}\n\n"],"names":["listZodKeys","schema","prefix","_def","typeName","unwrappable","unwrap","arraySchema","element","objectSchema","Object","entries","shape","flatMap","key","subschema","fullKey","nested","length","isPlainObject","value","Array","isArray","listObjectKeys","obj","keys","Set","prototype","hasOwnProperty","call","firstObjectElement","find","nestedKeys","forEach","k","add","from","checkForExtraKeys","mergedSources","fullSchema","logger","allowedKeys","actualKeys","recordPrefixes","findRecordPrefixes","extraKeys","filter","has","recordPrefix","startsWith","allowedKeysArray","error","ConfigurationError","message","validateConfigDirectory","configDirectory","isRequired","storage","Storage","log","debug","exists","FileSystemError","directoryNotFound","isReadable","isDirectoryReadable","directoryNotReadable","validate","config","options","features","includes","defaults","z","object","ConfigSchema","configShape","validationResult","safeParse","success","formattedError","JSON","stringify","format","validation"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"validate.js","sources":["../src/validate.ts"],"sourcesContent":["import { z, ZodObject } from \"zod\";\nimport { ArgumentError } from \"./error/ArgumentError\";\nimport { ConfigurationError } from \"./error/ConfigurationError\";\nimport { FileSystemError } from \"./error/FileSystemError\";\nimport { ConfigSchema, Logger, Options } from \"./types\";\nimport * as Storage from \"./util/storage\";\nexport { ArgumentError, ConfigurationError, FileSystemError };\n\n/**\n * Recursively extracts all keys from a Zod schema in dot notation.\n * \n * This function traverses a Zod schema structure and builds a flat list\n * of all possible keys, using dot notation for nested objects. It handles\n * optional/nullable types by unwrapping them and supports arrays by\n * introspecting their element type.\n * \n * Special handling for:\n * - ZodOptional/ZodNullable: Unwraps to get the underlying type\n * - ZodAny/ZodRecord: Accepts any keys, returns the prefix or empty array\n * - ZodArray: Introspects the element type\n * - ZodObject: Recursively processes all shape properties\n * \n * @param schema - The Zod schema to introspect\n * @param prefix - Internal parameter for building nested key paths\n * @returns Array of strings representing all possible keys in dot notation\n * \n * @example\n * ```typescript\n * const schema = z.object({\n * user: z.object({\n * name: z.string(),\n * settings: z.object({ theme: z.string() })\n * }),\n * debug: z.boolean()\n * });\n * \n * const keys = listZodKeys(schema);\n * // Returns: ['user.name', 'user.settings.theme', 'debug']\n * ```\n */\nexport const listZodKeys = (schema: z.ZodTypeAny, prefix = ''): string[] => {\n // Check if schema has unwrap method (which both ZodOptional and ZodNullable have)\n if (schema._def && (schema._def.typeName === 'ZodOptional' || schema._def.typeName === 'ZodNullable')) {\n // Use type assertion to handle the unwrap method\n const unwrappable = schema as z.ZodOptional<any> | z.ZodNullable<any>;\n return listZodKeys(unwrappable.unwrap(), prefix);\n }\n\n // Handle ZodAny and ZodRecord - these accept any keys, so don't introspect\n if (schema._def && (schema._def.typeName === 'ZodAny' || schema._def.typeName === 'ZodRecord')) {\n return prefix ? [prefix] : [];\n }\n\n if (schema._def && schema._def.typeName === 'ZodArray') {\n // Use type assertion to handle the element property\n const arraySchema = schema as z.ZodArray<any>;\n return listZodKeys(arraySchema.element, prefix);\n }\n if (schema._def && schema._def.typeName === 'ZodObject') {\n // Use type assertion to handle the shape property\n const objectSchema = schema as z.ZodObject<any>;\n return Object.entries(objectSchema.shape).flatMap(([key, subschema]) => {\n const fullKey = prefix ? `${prefix}.${key}` : key;\n const nested = listZodKeys(subschema as z.ZodTypeAny, fullKey);\n return nested.length ? nested : fullKey;\n });\n }\n return [];\n}\n\n/**\n * Type guard to check if a value is a plain object (not array, null, or other types).\n * \n * @param value - The value to check\n * @returns True if the value is a plain object\n */\nconst isPlainObject = (value: unknown): value is Record<string, unknown> => {\n // Check if it's an object, not null, and not an array.\n return value !== null && typeof value === 'object' && !Array.isArray(value);\n};\n\n/**\n * Generates a list of all keys within a JavaScript object, using dot notation for nested keys.\n * Mimics the behavior of listZodKeys but operates on plain objects.\n * For arrays, it inspects the first element that is a plain object to determine nested keys.\n * If an array contains no plain objects, or is empty, the key for the array itself is listed.\n *\n * @param obj The object to introspect.\n * @param prefix Internal use for recursion: the prefix for the current nesting level.\n * @returns An array of strings representing all keys in dot notation.\n */\nexport const listObjectKeys = (obj: Record<string, unknown>, prefix = ''): string[] => {\n const keys = new Set<string>(); // Use Set to automatically handle duplicates from array recursion\n\n for (const key in obj) {\n // Ensure it's an own property, not from the prototype chain\n if (Object.prototype.hasOwnProperty.call(obj, key)) {\n const value = obj[key];\n const fullKey = prefix ? `${prefix}.${key}` : key;\n\n if (Array.isArray(value)) {\n // Find the first element that is a plain object to determine structure\n const firstObjectElement = value.find(isPlainObject);\n if (firstObjectElement) {\n // Recurse into the structure of the first object element found\n const nestedKeys = listObjectKeys(firstObjectElement, fullKey);\n nestedKeys.forEach(k => keys.add(k));\n } else {\n // Array is empty or contains no plain objects, list the array key itself\n keys.add(fullKey);\n }\n } else if (isPlainObject(value)) {\n // Recurse into nested plain objects\n const nestedKeys = listObjectKeys(value, fullKey);\n nestedKeys.forEach(k => keys.add(k));\n } else {\n // It's a primitive, null, or other non-plain object/array type\n keys.add(fullKey);\n }\n }\n }\n return Array.from(keys); // Convert Set back to Array\n};\n\n/**\n * Validates that the configuration object contains only keys allowed by the schema.\n * \n * This function prevents configuration errors by detecting typos or extra keys\n * that aren't defined in the Zod schema. It intelligently handles:\n * - ZodRecord types that accept arbitrary keys\n * - Nested objects and their key structures\n * - Arrays and their element key structures\n * \n * The function throws a ConfigurationError if extra keys are found, providing\n * helpful information about what keys are allowed vs. what was found.\n * \n * @param mergedSources - The merged configuration object to validate\n * @param fullSchema - The complete Zod schema including base and user schemas\n * @param logger - Logger for error reporting\n * @throws {ConfigurationError} When extra keys are found that aren't in the schema\n * \n * @example\n * ```typescript\n * const schema = z.object({ name: z.string(), age: z.number() });\n * const config = { name: 'John', age: 30, typo: 'invalid' };\n * \n * checkForExtraKeys(config, schema, console);\n * // Throws: ConfigurationError with details about 'typo' being an extra key\n * ```\n */\nexport const checkForExtraKeys = (mergedSources: object, fullSchema: ZodObject<any>, logger: Logger | typeof console): void => {\n const allowedKeys = new Set(listZodKeys(fullSchema));\n const actualKeys = listObjectKeys(mergedSources as Record<string, unknown>);\n\n // Filter out keys that are under a record type (ZodRecord accepts any keys)\n const recordPrefixes = new Set<string>();\n\n // Find all prefixes that are ZodRecord types\n const findRecordPrefixes = (schema: z.ZodTypeAny, prefix = ''): void => {\n if (schema._def && (schema._def.typeName === 'ZodOptional' || schema._def.typeName === 'ZodNullable')) {\n const unwrappable = schema as z.ZodOptional<any> | z.ZodNullable<any>;\n findRecordPrefixes(unwrappable.unwrap(), prefix);\n return;\n }\n\n if (schema._def && (schema._def.typeName === 'ZodAny' || schema._def.typeName === 'ZodRecord')) {\n if (prefix) recordPrefixes.add(prefix);\n return;\n }\n\n if (schema._def && schema._def.typeName === 'ZodObject') {\n const objectSchema = schema as z.ZodObject<any>;\n Object.entries(objectSchema.shape).forEach(([key, subschema]) => {\n const fullKey = prefix ? `${prefix}.${key}` : key;\n findRecordPrefixes(subschema as z.ZodTypeAny, fullKey);\n });\n }\n };\n\n findRecordPrefixes(fullSchema);\n\n // Filter out keys that are under record prefixes\n const extraKeys = actualKeys.filter(key => {\n if (allowedKeys.has(key)) return false;\n\n // Check if this key is under a record prefix\n for (const recordPrefix of recordPrefixes) {\n if (key.startsWith(recordPrefix + '.')) {\n return false; // This key is allowed under a record\n }\n }\n\n return true; // This is an extra key\n });\n\n if (extraKeys.length > 0) {\n const allowedKeysArray = Array.from(allowedKeys);\n const error = ConfigurationError.extraKeys(extraKeys, allowedKeysArray);\n logger.error(error.message);\n throw error;\n }\n}\n\n/**\n * Validates that a configuration directory exists and is accessible.\n * \n * This function performs file system checks to ensure the configuration\n * directory can be used. It handles the isRequired flag to determine\n * whether a missing directory should cause an error or be silently ignored.\n * \n * @param configDirectory - Path to the configuration directory\n * @param isRequired - Whether the directory must exist\n * @param logger - Optional logger for debug information\n * @throws {FileSystemError} When the directory is required but missing or unreadable\n */\nconst validateConfigDirectory = async (configDirectory: string, isRequired: boolean, logger?: Logger): Promise<void> => {\n const storage = Storage.create({ log: logger?.debug || (() => { }) });\n const exists = await storage.exists(configDirectory);\n if (!exists) {\n if (isRequired) {\n throw FileSystemError.directoryNotFound(configDirectory, true);\n }\n } else if (exists) {\n const isReadable = await storage.isDirectoryReadable(configDirectory);\n if (!isReadable) {\n throw FileSystemError.directoryNotReadable(configDirectory);\n }\n }\n}\n\n/**\n * Validates a configuration object against the combined Zod schema.\n * \n * This is the main validation function that:\n * 1. Validates the configuration directory (if config feature enabled)\n * 2. Combines the base ConfigSchema with user-provided schema shape\n * 3. Checks for extra keys not defined in the schema\n * 4. Validates all values against their schema definitions\n * 5. Provides detailed error reporting for validation failures\n * \n * The validation is comprehensive and catches common configuration errors\n * including typos, missing required fields, wrong types, and invalid values.\n * \n * @template T - The Zod schema shape type for configuration validation\n * @param config - The merged configuration object to validate\n * @param options - Cardigantime options containing schema, defaults, and logger\n * @throws {ConfigurationError} When configuration validation fails\n * @throws {FileSystemError} When configuration directory validation fails\n * \n * @example\n * ```typescript\n * const schema = z.object({\n * apiKey: z.string().min(1),\n * timeout: z.number().positive(),\n * });\n * \n * await validate(config, {\n * configShape: schema.shape,\n * defaults: { configDirectory: './config', isRequired: true },\n * logger: console,\n * features: ['config']\n * });\n * // Throws detailed errors if validation fails\n * ```\n */\nexport const validate = async <T extends z.ZodRawShape>(config: z.infer<ZodObject<T & typeof ConfigSchema.shape>>, options: Options<T>): Promise<void> => {\n const logger = options.logger;\n\n if (options.features.includes('config') && config.configDirectory) {\n await validateConfigDirectory(config.configDirectory, options.defaults.isRequired, logger);\n }\n\n // Combine the base schema with the user-provided shape\n const fullSchema = z.object({\n ...ConfigSchema.shape,\n ...options.configShape,\n });\n\n // Validate the merged sources against the full schema\n const validationResult = fullSchema.safeParse(config);\n\n // Check for extraneous keys\n checkForExtraKeys(config, fullSchema, logger);\n\n if (!validationResult.success) {\n const formattedError = JSON.stringify(validationResult.error.format(), null, 2);\n logger.error('Configuration validation failed. Check logs for details.');\n logger.silly('Configuration validation failed: %s', formattedError);\n throw ConfigurationError.validation('Configuration validation failed. Check logs for details.', validationResult.error);\n }\n\n return;\n}\n\n"],"names":["listZodKeys","schema","prefix","_def","typeName","unwrappable","unwrap","arraySchema","element","objectSchema","Object","entries","shape","flatMap","key","subschema","fullKey","nested","length","isPlainObject","value","Array","isArray","listObjectKeys","obj","keys","Set","prototype","hasOwnProperty","call","firstObjectElement","find","nestedKeys","forEach","k","add","from","checkForExtraKeys","mergedSources","fullSchema","logger","allowedKeys","actualKeys","recordPrefixes","findRecordPrefixes","extraKeys","filter","has","recordPrefix","startsWith","allowedKeysArray","error","ConfigurationError","message","validateConfigDirectory","configDirectory","isRequired","storage","Storage","log","debug","exists","FileSystemError","directoryNotFound","isReadable","isDirectoryReadable","directoryNotReadable","validate","config","options","features","includes","defaults","z","object","ConfigSchema","configShape","validationResult","safeParse","success","formattedError","JSON","stringify","format","silly","validation"],"mappings":";;;;;;AAQA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+BC,IACM,MAAMA,WAAAA,GAAc,CAACC,MAAAA,EAAsBC,SAAS,EAAE,GAAA;;AAEzD,IAAA,IAAID,OAAOE,IAAI,KAAKF,MAAAA,CAAOE,IAAI,CAACC,QAAQ,KAAK,aAAA,IAAiBH,OAAOE,IAAI,CAACC,QAAQ,KAAK,aAAY,CAAA,EAAI;;AAEnG,QAAA,MAAMC,WAAAA,GAAcJ,MAAAA;QACpB,OAAOD,WAAAA,CAAYK,WAAAA,CAAYC,MAAM,EAAA,EAAIJ,MAAAA,CAAAA;AAC7C;;AAGA,IAAA,IAAID,OAAOE,IAAI,KAAKF,MAAAA,CAAOE,IAAI,CAACC,QAAQ,KAAK,QAAA,IAAYH,OAAOE,IAAI,CAACC,QAAQ,KAAK,WAAU,CAAA,EAAI;AAC5F,QAAA,OAAOF,MAAAA,GAAS;AAACA,YAAAA;AAAO,SAAA,GAAG,EAAE;AACjC;IAEA,IAAID,MAAAA,CAAOE,IAAI,IAAIF,MAAAA,CAAOE,IAAI,CAACC,QAAQ,KAAK,UAAA,EAAY;;AAEpD,QAAA,MAAMG,WAAAA,GAAcN,MAAAA;QACpB,OAAOD,WAAAA,CAAYO,WAAAA,CAAYC,OAAO,EAAEN,MAAAA,CAAAA;AAC5C;IACA,IAAID,MAAAA,CAAOE,IAAI,IAAIF,MAAAA,CAAOE,IAAI,CAACC,QAAQ,KAAK,WAAA,EAAa;;AAErD,QAAA,MAAMK,YAAAA,GAAeR,MAAAA;QACrB,OAAOS,MAAAA,CAAOC,OAAO,CAACF,YAAAA,CAAaG,KAAK,CAAA,CAAEC,OAAO,CAAC,CAAC,CAACC,GAAAA,EAAKC,SAAAA,CAAU,GAAA;AAC/D,YAAA,MAAMC,UAAUd,MAAAA,GAAS,CAAA,EAAGA,OAAO,CAAC,EAAEY,KAAK,GAAGA,GAAAA;YAC9C,MAAMG,MAAAA,GAASjB,YAAYe,SAAAA,EAA2BC,OAAAA,CAAAA;YACtD,OAAOC,MAAAA,CAAOC,MAAM,GAAGD,MAAAA,GAASD,OAAAA;AACpC,SAAA,CAAA;AACJ;AACA,IAAA,OAAO,EAAE;AACb;AAEA;;;;;IAMA,MAAMG,gBAAgB,CAACC,KAAAA,GAAAA;;IAEnB,OAAOA,KAAAA,KAAU,QAAQ,OAAOA,KAAAA,KAAU,YAAY,CAACC,KAAAA,CAAMC,OAAO,CAACF,KAAAA,CAAAA;AACzE,CAAA;AAEA;;;;;;;;;AASC,IACM,MAAMG,cAAAA,GAAiB,CAACC,GAAAA,EAA8BtB,SAAS,EAAE,GAAA;IACpE,MAAMuB,IAAAA,GAAO,IAAIC,GAAAA,EAAAA,CAAAA;IAEjB,IAAK,MAAMZ,OAAOU,GAAAA,CAAK;;QAEnB,IAAId,MAAAA,CAAOiB,SAAS,CAACC,cAAc,CAACC,IAAI,CAACL,KAAKV,GAAAA,CAAAA,EAAM;YAChD,MAAMM,KAAAA,GAAQI,GAAG,CAACV,GAAAA,CAAI;AACtB,YAAA,MAAME,UAAUd,MAAAA,GAAS,CAAA,EAAGA,OAAO,CAAC,EAAEY,KAAK,GAAGA,GAAAA;YAE9C,IAAIO,KAAAA,CAAMC,OAAO,CAACF,KAAAA,CAAAA,EAAQ;;gBAEtB,MAAMU,kBAAAA,GAAqBV,KAAAA,CAAMW,IAAI,CAACZ,aAAAA,CAAAA;AACtC,gBAAA,IAAIW,kBAAAA,EAAoB;;oBAEpB,MAAME,UAAAA,GAAaT,eAAeO,kBAAAA,EAAoBd,OAAAA,CAAAA;AACtDgB,oBAAAA,UAAAA,CAAWC,OAAO,CAACC,CAAAA,CAAAA,GAAKT,IAAAA,CAAKU,GAAG,CAACD,CAAAA,CAAAA,CAAAA;iBACrC,MAAO;;AAEHT,oBAAAA,IAAAA,CAAKU,GAAG,CAACnB,OAAAA,CAAAA;AACb;aACJ,MAAO,IAAIG,cAAcC,KAAAA,CAAAA,EAAQ;;gBAE7B,MAAMY,UAAAA,GAAaT,eAAeH,KAAAA,EAAOJ,OAAAA,CAAAA;AACzCgB,gBAAAA,UAAAA,CAAWC,OAAO,CAACC,CAAAA,CAAAA,GAAKT,IAAAA,CAAKU,GAAG,CAACD,CAAAA,CAAAA,CAAAA;aACrC,MAAO;;AAEHT,gBAAAA,IAAAA,CAAKU,GAAG,CAACnB,OAAAA,CAAAA;AACb;AACJ;AACJ;AACA,IAAA,OAAOK,KAAAA,CAAMe,IAAI,CAACX,IAAAA,CAAAA,CAAAA;AACtB;AAEA;;;;;;;;;;;;;;;;;;;;;;;;;AAyBC,IACM,MAAMY,iBAAAA,GAAoB,CAACC,eAAuBC,UAAAA,EAA4BC,MAAAA,GAAAA;IACjF,MAAMC,WAAAA,GAAc,IAAIf,GAAAA,CAAI1B,WAAAA,CAAYuC,UAAAA,CAAAA,CAAAA;AACxC,IAAA,MAAMG,aAAanB,cAAAA,CAAee,aAAAA,CAAAA;;AAGlC,IAAA,MAAMK,iBAAiB,IAAIjB,GAAAA,EAAAA;;AAG3B,IAAA,MAAMkB,kBAAAA,GAAqB,CAAC3C,MAAAA,EAAsBC,MAAAA,GAAS,EAAE,GAAA;AACzD,QAAA,IAAID,OAAOE,IAAI,KAAKF,MAAAA,CAAOE,IAAI,CAACC,QAAQ,KAAK,aAAA,IAAiBH,OAAOE,IAAI,CAACC,QAAQ,KAAK,aAAY,CAAA,EAAI;AACnG,YAAA,MAAMC,WAAAA,GAAcJ,MAAAA;YACpB2C,kBAAAA,CAAmBvC,WAAAA,CAAYC,MAAM,EAAA,EAAIJ,MAAAA,CAAAA;AACzC,YAAA;AACJ;AAEA,QAAA,IAAID,OAAOE,IAAI,KAAKF,MAAAA,CAAOE,IAAI,CAACC,QAAQ,KAAK,QAAA,IAAYH,OAAOE,IAAI,CAACC,QAAQ,KAAK,WAAU,CAAA,EAAI;YAC5F,IAAIF,MAAAA,EAAQyC,cAAAA,CAAeR,GAAG,CAACjC,MAAAA,CAAAA;AAC/B,YAAA;AACJ;QAEA,IAAID,MAAAA,CAAOE,IAAI,IAAIF,MAAAA,CAAOE,IAAI,CAACC,QAAQ,KAAK,WAAA,EAAa;AACrD,YAAA,MAAMK,YAAAA,GAAeR,MAAAA;YACrBS,MAAAA,CAAOC,OAAO,CAACF,YAAAA,CAAaG,KAAK,CAAA,CAAEqB,OAAO,CAAC,CAAC,CAACnB,GAAAA,EAAKC,SAAAA,CAAU,GAAA;AACxD,gBAAA,MAAMC,UAAUd,MAAAA,GAAS,CAAA,EAAGA,OAAO,CAAC,EAAEY,KAAK,GAAGA,GAAAA;AAC9C8B,gBAAAA,kBAAAA,CAAmB7B,SAAAA,EAA2BC,OAAAA,CAAAA;AAClD,aAAA,CAAA;AACJ;AACJ,KAAA;IAEA4B,kBAAAA,CAAmBL,UAAAA,CAAAA;;AAGnB,IAAA,MAAMM,SAAAA,GAAYH,UAAAA,CAAWI,MAAM,CAAChC,CAAAA,GAAAA,GAAAA;AAChC,QAAA,IAAI2B,WAAAA,CAAYM,GAAG,CAACjC,GAAAA,CAAAA,EAAM,OAAO,KAAA;;QAGjC,KAAK,MAAMkC,gBAAgBL,cAAAA,CAAgB;AACvC,YAAA,IAAI7B,GAAAA,CAAImC,UAAU,CAACD,YAAAA,GAAe,GAAA,CAAA,EAAM;AACpC,gBAAA,OAAO;AACX;AACJ;AAEA,QAAA,OAAO;AACX,KAAA,CAAA;IAEA,IAAIH,SAAAA,CAAU3B,MAAM,GAAG,CAAA,EAAG;QACtB,MAAMgC,gBAAAA,GAAmB7B,KAAAA,CAAMe,IAAI,CAACK,WAAAA,CAAAA;AACpC,QAAA,MAAMU,KAAAA,GAAQC,kBAAAA,CAAmBP,SAAS,CAACA,SAAAA,EAAWK,gBAAAA,CAAAA;QACtDV,MAAAA,CAAOW,KAAK,CAACA,KAAAA,CAAME,OAAO,CAAA;QAC1B,MAAMF,KAAAA;AACV;AACJ;AAEA;;;;;;;;;;;AAWC,IACD,MAAMG,uBAAAA,GAA0B,OAAOC,eAAAA,EAAyBC,UAAAA,EAAqBhB,MAAAA,GAAAA;IACjF,MAAMiB,OAAAA,GAAUC,MAAc,CAAC;QAAEC,GAAAA,EAAKnB,CAAAA,mBAAAA,MAAAA,KAAAA,MAAAA,GAAAA,MAAAA,GAAAA,MAAAA,CAAQoB,KAAK,MAAK,MAAQ;AAAG,KAAA,CAAA;AACnE,IAAA,MAAMC,MAAAA,GAAS,MAAMJ,OAAAA,CAAQI,MAAM,CAACN,eAAAA,CAAAA;AACpC,IAAA,IAAI,CAACM,MAAAA,EAAQ;AACT,QAAA,IAAIL,UAAAA,EAAY;YACZ,MAAMM,eAAAA,CAAgBC,iBAAiB,CAACR,eAAAA,EAAiB,IAAA,CAAA;AAC7D;AACJ,KAAA,MAAO,IAAIM,MAAAA,EAAQ;AACf,QAAA,MAAMG,UAAAA,GAAa,MAAMP,OAAAA,CAAQQ,mBAAmB,CAACV,eAAAA,CAAAA;AACrD,QAAA,IAAI,CAACS,UAAAA,EAAY;YACb,MAAMF,eAAAA,CAAgBI,oBAAoB,CAACX,eAAAA,CAAAA;AAC/C;AACJ;AACJ,CAAA;AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkCC,IACM,MAAMY,QAAAA,GAAW,OAAgCC,MAAAA,EAA2DC,OAAAA,GAAAA;IAC/G,MAAM7B,MAAAA,GAAS6B,QAAQ7B,MAAM;IAE7B,IAAI6B,OAAAA,CAAQC,QAAQ,CAACC,QAAQ,CAAC,QAAA,CAAA,IAAaH,MAAAA,CAAOb,eAAe,EAAE;QAC/D,MAAMD,uBAAAA,CAAwBc,OAAOb,eAAe,EAAEc,QAAQG,QAAQ,CAAChB,UAAU,EAAEhB,MAAAA,CAAAA;AACvF;;IAGA,MAAMD,UAAAA,GAAakC,CAAAA,CAAEC,MAAM,CAAC;AACxB,QAAA,GAAGC,aAAa/D,KAAK;AACrB,QAAA,GAAGyD,QAAQO;AACf,KAAA,CAAA;;IAGA,MAAMC,gBAAAA,GAAmBtC,UAAAA,CAAWuC,SAAS,CAACV,MAAAA,CAAAA;;AAG9C/B,IAAAA,iBAAAA,CAAkB+B,QAAQ7B,UAAAA,EAAYC,MAAAA,CAAAA;IAEtC,IAAI,CAACqC,gBAAAA,CAAiBE,OAAO,EAAE;QAC3B,MAAMC,cAAAA,GAAiBC,KAAKC,SAAS,CAACL,iBAAiB1B,KAAK,CAACgC,MAAM,EAAA,EAAI,IAAA,EAAM,CAAA,CAAA;AAC7E3C,QAAAA,MAAAA,CAAOW,KAAK,CAAC,0DAAA,CAAA;QACbX,MAAAA,CAAO4C,KAAK,CAAC,qCAAA,EAAuCJ,cAAAA,CAAAA;AACpD,QAAA,MAAM5B,kBAAAA,CAAmBiC,UAAU,CAAC,0DAAA,EAA4DR,iBAAiB1B,KAAK,CAAA;AAC1H;AAEA,IAAA;AACJ;;;;"}
|