UNPKG

kist

Version:

Package Pipeline Processor

141 lines (140 loc) 5.15 kB
"use strict"; // ============================================================================ // Import // ============================================================================ Object.defineProperty(exports, "__esModule", { value: true }); exports.ConfigStore = void 0; const AbstractProcess_1 = require("../abstract/AbstractProcess"); const defaultConfig_1 = require("./defaultConfig"); // ============================================================================ // Class // ============================================================================ /** * ConfigStore is a singleton that loads and manages the application's configuration. * It prioritizes CLI arguments over configuration file values. */ class ConfigStore extends AbstractProcess_1.AbstractProcess { // Constructor (Private to enforce Singleton Pattern) constructor() { super(); this.config = defaultConfig_1.defaultConfig; this.logDebug("ConfigStore initialized with default configuration."); } /** * Retrieves the singleton instance of ConfigStore. * @returns The singleton instance of ConfigStore. */ static getInstance() { if (!ConfigStore.instance) { ConfigStore.instance = new ConfigStore(); } return ConfigStore.instance; } /** * Retrieves a value from the configuration using dot notation. * * @param key - The key of the configuration to retrieve. * @returns The configuration value or undefined if not found. */ get(key) { const keys = key.split("."); let current = this.config; for (const k of keys) { if (current[k] === undefined) { return undefined; } current = current[k]; } this.logDebug(`Configuration key "${key}" retrieved.`); return current; } /** * Sets a value in the configuration using dot notation. * * @param key - The key of the configuration to set. * @param value - The value to set. */ set(key, value) { const keys = key.split("."); let current = this.config; for (let i = 0; i < keys.length - 1; i++) { const k = keys[i]; // Prevent prototype pollution by blocking reserved keywords if (["__proto__", "constructor", "prototype"].includes(k)) { this.logWarn(`Attempted prototype pollution detected: "${k}"`); return; } // Ensure property exists and is an object if (!Object.prototype.hasOwnProperty.call(current, k) || typeof current[k] !== "object") { current[k] = Object.create(null); // Use a null prototype object } current = current[k]; } const finalKey = keys[keys.length - 1]; // Prevent prototype pollution at the final assignment if (["__proto__", "constructor", "prototype"].includes(finalKey)) { this.logWarn(`Attempted prototype pollution detected: "${finalKey}"`); return; } current[finalKey] = value; this.logDebug(`Set configuration key "${key}" to: ${JSON.stringify(value)}`); } /** * Merges the provided configuration into the existing configuration using deep merge. * * @param newConfig - The new configuration to merge. */ merge(newConfig) { this.config = this.deepMerge(this.config, newConfig); this.logDebug("Configuration successfully merged."); } /** * Retrieves the current configuration object. * @returns The current configuration. */ getConfig() { return this.config; } /** * Prints the current configuration to the console. */ print() { console.log("Current Configuration:", JSON.stringify(this.config, null, 2)); } /** * Deeply merges two objects, preventing prototype pollution. * * @param target - The target object. * @param source - The source object. * @returns The merged object. */ deepMerge(target, source) { if (typeof target !== "object" || target === null) { return source; } for (const key of Object.keys(source)) { // Prevent prototype pollution if (["__proto__", "constructor", "prototype"].includes(key)) { this.logWarn(`Skipping unsafe key during merge: "${key}"`); continue; } if (source[key] && typeof source[key] === "object" && !Array.isArray(source[key])) { if (!Object.prototype.hasOwnProperty.call(target, key) || typeof target[key] !== "object") { target[key] = Object.create(null); } target[key] = this.deepMerge(target[key], source[key]); } else { target[key] = source[key]; } } return target; } } exports.ConfigStore = ConfigStore; // Singleton instance ConfigStore.instance = null;