UNPKG

@nexe/config-manager

Version:

Nexe Config Manager - A flexible configuration management solution with multiple sources and hot reload support

138 lines 5.16 kB
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