lowcode-engine/packages/core/src/configuration.ts
2024-05-28 11:07:01 +08:00

155 lines
3.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { get as lodashGet, isPlainObject, cloneDeep } from 'lodash-es';
import { type PlainObject } from '@alilc/lowcode-shared/src/types';
import { invariant } from '@alilc/lowcode-shared/src/utils';
export class Configuration<Config extends PlainObject, K extends keyof Config = keyof Config> {
private config: Config;
private setterValidator: ((key: K, value: Config[K]) => boolean | string) | undefined;
private waits = new Map<
K,
{
once?: boolean;
resolve: (data: any) => void;
}[]
>();
constructor(config: Config, setterValidator?: (key: K, value: Config[K]) => boolean | string) {
invariant(config, 'config must exist', 'Configuration');
this.config = cloneDeep(config);
if (setterValidator) {
invariant(
typeof setterValidator === 'function',
'setterValidator must be a function',
'Configuration',
);
this.setterValidator = setterValidator;
}
}
/**
* 判断指定 key 是否有值
* @param key
*/
has(key: K): boolean {
return this.config[key] !== undefined;
}
/**
* 获取指定 key 的值
* @param key
* @param defaultValue
*/
get<T = any>(key: K, defaultValue?: T): T | undefined {
return lodashGet(this.config, key, defaultValue);
}
/**
* 设置指定 key 的值
* @param key
* @param value
*/
set(key: K, value: any) {
if (this.setterValidator) {
const valid = this.setterValidator(key, value);
invariant(
valid === false || typeof valid === 'string',
`failed to config ${key.toString()}, only predefined options can be set under strict mode, predefined options: ${valid ? valid : ''}`,
'Configuration',
);
}
this.config[key] = value;
this.notifyGot(key);
}
/**
* 批量设值set 的对象版本
* @param config
*/
setConfig(config: Partial<Config>) {
if (isPlainObject(config)) {
Object.keys(config).forEach((key) => {
this.set(key as K, config[key]);
});
}
}
/**
* 获取指定 key 的值,若此时还未赋值,则等待,若已有值,则直接返回值
* 注:此函数返回 Promise 实例只会执行fullfill一次
* @param key
* @returns
*/
onceGot(key: K) {
const val = this.get(key);
if (val !== undefined) {
return Promise.resolve(val);
}
return new Promise((resolve) => {
this.setWait(key, resolve, true);
});
}
/**
* 获取指定 key 的值,函数回调模式,若多次被赋值,回调会被多次调用
* @param key
* @param fn
* @returns
*/
onGot(key: K, fn: (data: Config[K]) => void): () => void {
const val = this.config[key];
if (val !== undefined) {
fn(val);
}
this.setWait(key, fn);
return () => {
this.delWait(key, fn);
};
}
private notifyGot(key: K): void {
let waits = this.waits.get(key);
if (!waits) {
return;
}
waits = waits.slice().reverse();
let i = waits.length;
while (i--) {
waits[i].resolve(this.get(key));
if (waits[i].once) {
waits.splice(i, 1);
}
}
if (waits.length > 0) {
this.waits.set(key, waits);
} else {
this.waits.delete(key);
}
}
private setWait(key: K, resolve: (data: any) => void, once?: boolean) {
const waits = this.waits.get(key);
if (waits) {
waits.push({ resolve, once });
} else {
this.waits.set(key, [{ resolve, once }]);
}
}
private delWait(key: K, fn: any) {
const waits = this.waits.get(key);
if (!waits) {
return;
}
let i = waits.length;
while (i--) {
if (waits[i].resolve === fn) {
waits.splice(i, 1);
}
}
if (waits.length < 1) {
this.waits.delete(key);
}
}
}