kist
Version:
Package Pipeline Processor
141 lines (140 loc) • 5.15 kB
JavaScript
;
// ============================================================================
// 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;