UNPKG

react-runtime-config

Version:

Provide a typesafe runtime configuration inside a react app

177 lines 6.69 kB
export interface ConfigOptions<TSchema extends Record<string, Config>, TNamespace extends string> { /** * Namespace of the configuration * * This namespace is used to consume the configuration from `window` and `localStorage` */ namespace: string; /** * Schema of the configuration (used for runtime validation) */ schema: TSchema; /** * Storage adapter * * @default window.localStorage */ storage?: Storage; /** * Permit to override any config values in storage * * @default true */ localOverride?: boolean; /** * Namespace for `useConfig()` return methods. * * Example: * ``` * // MyConfig.ts * export const { useConfig } = createConfig({ * configNamespace: "hello" * }); * * // In a react component * const { * getHelloConfig, * setHelloConfig, * getAllHelloConfig, * } = useConfig(); * ``` */ configNamespace?: TNamespace; } export declare type Config = StringConfig | NumberConfig | BooleanConfig | CustomConfig; export interface StringConfig { type: "string"; enum?: string[]; default?: string | (() => string); description?: string; } export interface StringEnumConfig extends StringConfig { /** * List of allowed values */ enum: string[]; } export interface NumberConfig { type: "number"; min?: number; max?: number; default?: number | (() => number); description?: string; } export interface BooleanConfig { type: "boolean"; default?: boolean | (() => boolean); description?: string; } export interface CustomConfig<T = unknown> { type: "custom"; default?: T | (() => T); description?: string; /** * Custom parser. * * Should throw an error if the value can't be parsed */ parser: (value: any) => T; } export declare type ResolvedSchema<TSchema extends Record<string, Config>> = { [key in keyof TSchema]: ResolvedConfigValue<TSchema[key]>; }; export declare type ResolvedConfigValue<TValue extends Config> = TValue extends StringEnumConfig ? TValue["enum"][-1] : TValue extends StringConfig ? string : TValue extends NumberConfig ? number : TValue extends BooleanConfig ? boolean : TValue extends CustomConfig ? ReturnType<TValue["parser"]> : never; export declare const isStringConfig: (config: Config) => config is StringConfig; export declare const isStringEnumConfig: (config: Config) => config is StringEnumConfig; export declare const isNumberConfig: (config: Config) => config is NumberConfig; export declare const isBooleanConfig: (config: Config) => config is BooleanConfig; export declare const isCustomConfig: (config: Config) => config is CustomConfig<unknown>; export interface InjectedProps<TSchema extends Record<string, Config>, TNamespace extends string, TConfig = ResolvedSchema<TSchema>> { namespace: string; configNamespace: TNamespace; schema: TSchema; storage: Storage; localOverride: boolean; getConfig: <K extends keyof TSchema>(key: K) => ResolvedConfigValue<TSchema[K]>; setConfig: <K extends keyof TSchema>(key: K, value: ResolvedConfigValue<TSchema[K]>) => void; getAllConfig: () => TConfig; getWindowValue: <K extends keyof TSchema>(key: K) => ResolvedConfigValue<TSchema[K]> | null; getStorageValue: <K extends keyof TSchema>(key: K) => ResolvedConfigValue<TSchema[K]> | null; } export declare type AdminField<TSchema extends Record<string, Config>, TKey extends keyof TSchema> = TSchema[TKey] & { /** * Schema key of the config */ key: TKey; /** * Full path of the config (with `namespace`) */ path: string; /** * Value stored in `window.{path}` */ windowValue: ResolvedConfigValue<TSchema[TKey]> | null; /** * Value stored in `storage.getItem({path})` */ storageValue: ResolvedConfigValue<TSchema[TKey]> | null; /** * True if a value is stored on the localStorage */ isFromStorage: boolean; /** * Computed value from storage, window, schema[key].default */ value: ResolvedConfigValue<TSchema[TKey]>; /** * Value setter */ set: (value: ResolvedConfigValue<TSchema[TKey]>) => void; }; declare type Lookup<T, K> = K extends keyof T ? T[K] : never; declare type TupleFromInterface<T, K extends Array<keyof T> = Array<keyof T>> = { [I in keyof K]: Lookup<T, K[I]>; }; export declare type AdminFields<TSchema extends Record<string, Config>> = TupleFromInterface<{ [key in keyof TSchema]: AdminField<TSchema, key>; }>; declare type AdminProps<T, U = T> = { key: string; path: string; windowValue: T | null; storageValue: T | null; isFromStorage: boolean; value: T; set: (value: U) => void; }; /** * `useAdminConfig.fields` in a generic version. * * This should be used if you are implementing a generic component * that consume any `fields` as prop. * * Note: "custom" type and "string" with enum are defined as `any` to be * compatible with any schemas. You will need to validate them in your * implementation to retrieve a strict type. */ export declare type GenericAdminFields = Array<(StringConfig & AdminProps<string>) | (NumberConfig & AdminProps<number>) | (BooleanConfig & AdminProps<boolean>) | (StringEnumConfig & AdminProps<string, any>) | (CustomConfig<any> & AdminProps<any>)>; declare type UseConfigReturnType<TSchema extends Record<string, Config>> = { getConfig: <K extends keyof TSchema>(path: K) => ResolvedConfigValue<TSchema[K]>; getAllConfig: () => ResolvedSchema<TSchema>; setConfig: <K extends keyof TSchema>(path: K, value: ResolvedConfigValue<TSchema[K]>) => void; }; /** * Helper to inject a namespace inside the keys of the `useConfig` return type. * * example: * ``` * type Namespaced<{getConfig: any; getAllConfig: any; setConfig: any}, "foo"> * // { getFooConfig: any; getAllFooConfig: any; setFooConfig: any } * ``` */ declare type Namespaced<T, TNamespace extends string> = { [P in keyof T as P extends `getConfig` ? `get${Capitalize<TNamespace>}Config` : P extends "getAllConfig" ? `getAll${Capitalize<TNamespace>}Config` : `set${Capitalize<TNamespace>}Config`]: T[P]; }; export declare type NamespacedUseConfigReturnType<TSchema extends Record<string, Config>, TNamespace extends string> = Namespaced<UseConfigReturnType<TSchema>, TNamespace>; export {}; //# sourceMappingURL=types.d.ts.map