You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

122 lines
4.0 KiB

type ConfigScope = "global" | "user" | "both";
type ConfigValueType = "string" | "number" | "boolean" | "json";
export type SupportedValue = string | number | boolean | Record<string, unknown> | unknown[];
export type ConfigDefinition<T extends SupportedValue = SupportedValue> = {
key: string;
scope: ConfigScope;
valueType: ConfigValueType;
defaultValue: T;
userOverridable: boolean;
validate?: (value: T) => boolean;
};
function defineConfig<T extends SupportedValue>(config: ConfigDefinition<T>): ConfigDefinition<T> {
return config;
}
const CONFIG_REGISTRY = {
siteName: defineConfig<string>({
key: "siteName",
scope: "global",
valueType: "string",
defaultValue: "Person Panel",
userOverridable: false,
validate: (value: string) => value.trim().length > 0 && value.length <= 64,
}),
allowRegister: defineConfig<boolean>({
key: "allowRegister",
scope: "global",
valueType: "boolean",
defaultValue: true,
userOverridable: false,
}),
theme: defineConfig<string>({
key: "theme",
scope: "both",
valueType: "string",
defaultValue: "light",
userOverridable: true,
validate: (value: string) => ["light", "dark", "system"].includes(value),
}),
dashboardLayout: defineConfig<unknown[]>({
key: "dashboardLayout",
scope: "user",
valueType: "json",
defaultValue: [],
userOverridable: true,
}),
} as const;
export type KnownConfigKey = keyof typeof CONFIG_REGISTRY;
type KnownConfigDefinition<K extends KnownConfigKey> = (typeof CONFIG_REGISTRY)[K];
export type KnownConfigValue<K extends KnownConfigKey> = KnownConfigDefinition<K>["defaultValue"];
export type KnownConfigItem = (typeof CONFIG_REGISTRY)[KnownConfigKey];
export const KNOWN_CONFIG_KEYS = Object.keys(CONFIG_REGISTRY) as KnownConfigKey[];
export function isKnownConfigKey(key: string): key is KnownConfigKey {
return key in CONFIG_REGISTRY;
}
export function getConfigDefinition<K extends KnownConfigKey>(key: K): KnownConfigDefinition<K> {
return CONFIG_REGISTRY[key];
}
export function getConfigDefaultValue<K extends KnownConfigKey>(key: K): KnownConfigValue<K> {
return CONFIG_REGISTRY[key].defaultValue;
}
export function getConfigScope<K extends KnownConfigKey>(key: K) {
return CONFIG_REGISTRY[key].scope;
}
export function canUserOverride<K extends KnownConfigKey>(key: K) {
return CONFIG_REGISTRY[key].userOverridable;
}
export function getConfigValueType<K extends KnownConfigKey>(key: K) {
return CONFIG_REGISTRY[key].valueType;
}
function isSupportedValueType(valueType: ConfigValueType, value: unknown) {
if (valueType === "string") return typeof value === "string";
if (valueType === "number") return typeof value === "number" && Number.isFinite(value);
if (valueType === "boolean") return typeof value === "boolean";
if (valueType === "json") return value !== undefined;
return false;
}
export function validateConfigValue<K extends KnownConfigKey>(key: K, value: unknown): value is KnownConfigValue<K> {
const definition = getConfigDefinition(key);
if (!isSupportedValueType(definition.valueType, value)) {
return false;
}
const validator = (definition as ConfigDefinition<KnownConfigValue<K>>).validate;
if (!validator) {
return true;
}
return validator(value as KnownConfigValue<K>);
}
export function serializeConfigValue<K extends KnownConfigKey>(key: K, value: KnownConfigValue<K>): string {
const valueType = getConfigValueType(key);
if (valueType === "json") {
return JSON.stringify(value);
}
return String(value);
}
export function deserializeConfigValue<K extends KnownConfigKey>(key: K, rawValue: string): KnownConfigValue<K> {
const valueType = getConfigValueType(key);
switch (valueType) {
case "string":
return rawValue as KnownConfigValue<K>;
case "number":
return Number(rawValue) as unknown as KnownConfigValue<K>;
case "boolean":
return (rawValue === "true") as unknown as KnownConfigValue<K>;
case "json":
default:
return JSON.parse(rawValue) as KnownConfigValue<K>;
}
}