UNPKG

@difizen/mana-core

Version:

191 lines (168 loc) 5.87 kB
import { Emitter } from '@difizen/mana-common'; import { getOrigin } from '@difizen/mana-observable'; import type { Contribution } from '@difizen/mana-syringe'; import { contrib, inject, singleton } from '@difizen/mana-syringe'; import { ApplicationContribution } from '../application'; import { ConfigurationCache } from './configuration-cache'; import type { ConfigurationNode } from './configuration-protocol'; import { ConfigurationProvider } from './configuration-provider'; import { ConfigurationRegistry } from './configuration-registry'; import type { ConfigurationStorage } from './configuration-storage'; import { SchemaValidator } from './validation'; @singleton({ contrib: [ApplicationContribution] }) export class ConfigurationService implements ApplicationContribution { @contrib(ConfigurationProvider) protected providers: Contribution.Provider<ConfigurationProvider>; @inject(ConfigurationRegistry) protected readonly configurationRegistry: ConfigurationRegistry; @inject(SchemaValidator) protected readonly schemaValidator: SchemaValidator; @inject(ConfigurationCache) protected readonly configurationCache: ConfigurationCache; protected readonly onConfigurationValueChangeEmitter = new Emitter<{ key: string; value: any; }>(); readonly onConfigurationValueChange = this.onConfigurationValueChangeEmitter.event; protected storageMap = new Map<ConfigurationStorage, ConfigurationProvider>(); async onWillStart() { await this.prefetch(); } async prefetch() { const storageArray = this.configurationRegistry.getStorages(); for (const storage of storageArray.sort((a, b) => b.priority - a.priority)) { const provider = this.getConfigurationProviderByStorage(storage); if (!provider || provider.enableCache !== true || !provider.prefetch) { continue; } const nodes = this.configurationRegistry.getConfigurationsByStorage(storage); if (!nodes) { continue; } try { const cacheValues = await provider.prefetch(nodes); nodes.forEach((item, index) => { this.configurationCache.set(provider, item, cacheValues[index]); }); } catch (error) { console.error('prefetch config failed', error); } } } async has<T>( node: ConfigurationNode<T>, options?: { useCache?: boolean }, ): Promise<boolean> { const useCache = options?.useCache ?? true; const storage = this.configurationRegistry.getStorage(node); const provider = this.getConfigurationProviderByStorage(storage); if (!provider) { return false; } if (provider.enableCache && useCache) { const hasCache = this.configurationCache.has(provider, node); if (hasCache) { return true; } } const hasValue = await provider.has(node); if (hasValue) { return true; } return false; } async get<T>( node: ConfigurationNode<T>, defaultValue?: T, options?: { useCache?: boolean }, ): Promise<T> { let result: T = defaultValue ?? node.defaultValue; const useCache = options?.useCache ?? true; const storage = this.configurationRegistry.getStorage(node); const provider = this.getConfigurationProviderByStorage(storage); if (!provider) { return result; } if (provider.enableCache && useCache) { const hasCache = this.configurationCache.has(provider, node); if (hasCache) { result = this.configurationCache.get(provider, node); return result; } } const hasValue = await provider.has(node); if (!hasValue) { return result; } const val = await provider.get<T>(node); result = val; if (provider.enableCache) { this.configurationCache.set(provider, node, val); } return result; } /** * * @param node 配置 * @param value 配置的值 * @param options * @returns */ async set<T>( node: ConfigurationNode<T>, value: T, options?: { useCache?: boolean; validate?: boolean }, ) { if ( options?.validate !== false && !this.schemaValidator.validateNode(node, value) ) { return; } const useCache = options?.useCache ?? true; const setStorage = this.configurationRegistry.getStorage(node); this.configurationRegistry.addStorage(setStorage); const provider = this.getConfigurationProviderByStorage(setStorage); if (!provider) { return; } if (provider.enableCache && useCache) { this.configurationCache.set(provider, node, value); } await provider.set(node, value); this.onConfigurationValueChangeEmitter.fire({ key: node.id, value }); } async remove<T>(node: ConfigurationNode<T>) { const storage = this.configurationRegistry.getStorage(node); const provider = this.getConfigurationProviderByStorage(storage); if (!provider) { return; } if (!provider.has(node)) { return; } this.configurationCache.remove(provider, node); provider.remove(node); } protected getConfigurationProviderByStorage( storage: ConfigurationStorage, ): ConfigurationProvider | undefined { if (this.storageMap.has(storage)) { return this.storageMap.get(storage); } const contribs = this.providers .getContributions() .map((item) => ({ ...item, priority: item.canHandle(getOrigin(storage)) })) .filter((item) => item.priority !== false); if (contribs.length === 0) { return undefined; } // 相比sort性能更高 let maxPriorityProvider = contribs[0]; for (const provider of contribs) { if ((provider.priority as number) > (maxPriorityProvider.priority as number)) { maxPriorityProvider = provider; } } this.storageMap.set(storage, maxPriorityProvider); return maxPriorityProvider; } }