@nexe/config-manager
Version:
Nexe Config Manager - A flexible configuration management solution with multiple sources and hot reload support
138 lines • 5.16 kB
JavaScript
import { __decorate } from "tslib";
import 'reflect-metadata';
import { injectable } from 'tsyringe';
import { CONFIG_PROPERTY_METADATA, CONFIG_SECTION_METADATA, } from './interfaces';
/**
* 配置管理器实现
*/
let ConfigManager = class ConfigManager {
constructor() {
this.sources = [];
this.subscriptions = new Map();
}
/**
* 添加配置源
*/
addSource(source, priority = 0) {
this.sources.push({ source, priority });
// 根据优先级排序,高优先级的在前面
this.sources.sort((a, b) => b.priority - a.priority);
// 如果配置源支持热更新,设置变更监听
if (source.supportsHotReload() && source.subscribe) {
this.setupSourceSubscription(source);
}
}
/**
* 获取配置值
*/
async get(key, defaultValue, options) {
for (const { source } of this.sources) {
try {
// 如果指定了配置源名称,只从该配置源获取
if (options?.sourceName && source.getName() !== options.sourceName) {
continue;
}
const value = await source.load(key, options?.pathPrefix);
if (value !== undefined && value !== null) {
return value;
}
}
catch (error) {
// eslint-disable-next-line no-console
console.warn(`配置源 ${source.getName()} 加载键 ${key} 时出错:`, error);
}
}
return defaultValue;
}
/**
* 获取强类型配置对象
*/
async getConfig(configClass, options) {
const instance = new configClass();
const section = Reflect.getMetadata(CONFIG_SECTION_METADATA, configClass);
const properties = Reflect.getMetadata(CONFIG_PROPERTY_METADATA, configClass) ?? [];
for (const { key, propertyKey } of properties) {
const configKey = section ? `${section}.${key}` : key;
const value = await this.get(configKey, undefined, options);
if (value !== undefined) {
instance[propertyKey] = value;
}
}
return instance;
}
/**
* 订阅配置变更
*/
subscribe(key, callback, options) {
const subscriptionKey = options?.pathPrefix
? `${options.pathPrefix}:${key}`
: key;
const callbacks = this.subscriptions.get(subscriptionKey) ?? [];
callbacks.push(callback);
this.subscriptions.set(subscriptionKey, callbacks);
// 只订阅第一个支持热更新的配置源,避免重复订阅
let subscribed = false;
for (const { source } of this.sources) {
if (source.supportsHotReload() && source.subscribe && !subscribed) {
// 如果配置源支持动态路径,传递路径前缀
if (source.supportsDynamicPath && source.supportsDynamicPath()) {
source.subscribe(key, newValue => {
// 通知所有订阅此键的回调
const currentCallbacks = this.subscriptions.get(subscriptionKey) ?? [];
currentCallbacks.forEach(cb => cb(newValue));
}, options?.pathPrefix);
}
else {
source.subscribe(key, newValue => {
// 通知所有订阅此键的回调
const currentCallbacks = this.subscriptions.get(subscriptionKey) ?? [];
currentCallbacks.forEach(cb => cb(newValue));
});
}
subscribed = true;
break; // 只订阅第一个配置源
}
}
}
/**
* 取消订阅配置变更
*/
unsubscribe(key, options) {
const subscriptionKey = options?.pathPrefix
? `${options.pathPrefix}:${key}`
: key;
this.subscriptions.delete(subscriptionKey);
// 通知配置源取消订阅
for (const { source } of this.sources) {
if (source.supportsHotReload() && source.unsubscribe) {
if (source.supportsDynamicPath && source.supportsDynamicPath()) {
source.unsubscribe(key, options?.pathPrefix);
}
else {
source.unsubscribe(key);
}
}
}
}
/**
* 设置配置源的变更订阅
*/
setupSourceSubscription(source) {
// 确保 subscribe 方法存在
const subscribeMethod = source.subscribe;
if (!subscribeMethod) {
return;
}
// 为每个订阅的键设置变更监听
this.subscriptions.forEach((callbacks, key) => {
subscribeMethod.call(source, key, newValue => {
callbacks.forEach(callback => callback(newValue));
});
});
}
};
ConfigManager = __decorate([
injectable()
], ConfigManager);
export { ConfigManager };
//# sourceMappingURL=config-manager.js.map