wj-config
Version:
Javascript configuration module for NodeJS and browser frameworks such as React that works like ASP.net configuration where data sources are specified (usually JSON files) and environment variables can contribute/overwrite values by following a naming con
667 lines (625 loc) • 35 kB
TypeScript
/**
* Possible value types in properties of a configuration object.
*/
export type SingleConfigurationValue = string | number | Date | boolean | undefined | null;
/**
* Defines the type of configuration leaf properties.
*/
export type ConfigurationValue = SingleConfigurationValue | SingleConfigurationValue[];
/**
* A configuration node.
*/
export interface ConfigurationNode {
[x: string]: ConfigurationValue | ConfigurationNode
}
/**
* Types an object-merging operation's result.
*/
export type MergeResult<T extends Record<string, any>, NewT> = (Omit<T, keyof NewT> & {
[K in keyof NewT]-?: K extends keyof T ?
(
T[K] extends Record<string, any> ?
(NewT[K] extends Record<string, any> ? MergeResult<T[K], NewT[K]> : never) :
(NewT[K] extends Record<string, any> ? never : T[K] | NewT[K])
) : NewT[K]
}) extends infer R ? { [K in keyof R]: R[K] } : never;
/**
* Types individual dictionary values and inflates them.
*/
export type InflateKey<TKey extends string, TValue extends ConfigurationValue, TSep extends string, TPrefix extends string = ""> = TKey extends `${TPrefix}${infer FullKey}` ?
FullKey extends `${infer Key}${TSep}${infer Rest}` ?
{
[K in Key]: InflateKey<Rest, TValue, TSep>
} :
{
[K in FullKey]: TValue;
} : never;
/**
* Inflates entire dictionaries into their corresponding final objects.
*/
export type InflateDictionary<TDic extends Record<string, ConfigurationValue>, TSep extends string, TPrefix extends string = ""> = {
[K in keyof TDic]: (x: InflateKey<K & string, TDic[K], TSep, TPrefix>) => void
} extends Record<keyof TDic, (x: infer I) => void> ? I : never;
/**
* Defines the shape of dictionaries.
*/
export type Dictionary = Record<string, ConfigurationValue>;
/**
* WJ-Config module's entry point. Creates a builder object that is used to specify the various configuration
* sources and settings.
*/
export default function wjConfig(): IBuilder;
/**
* Predicate function that evaluates an arbitrary number of criteria against the data and returns a judgment in terms
* of a Boolean value.
*/
export type Predicate<T> = (data: T) => boolean;
/**
* Defines functions that process a fetched response object and returns configuration data.
*/
export type ProcessFetchResponse<T extends Record<string, any>> = (response: Response) => Promise<T>;
/**
* Defines the capabilities of a configurtion object that contains an environment object in the environment property.
*/
export type IncludeEnvironment<TEnvironments extends string, Key extends string = 'environment'> = {
[K in Key as `${K}`]: IEnvironment<TEnvironments>;
}
/**
* Defines the requirements of objects that wish to provide JSON-parsing services.
*/
export interface IJsonParser<T extends Record<string, any>> {
/**
* Parses the provided JSON string data and returns a JavaScript object.
* @param json The JSON string to parse.
*/
parse(json: string): T;
}
/**
* Defines the capabilities required from data source information objects used in value tracing.
*/
export interface IDataSourceInfo {
/**
* Provides the name of the data source instance that can be used in messages and logs.
*/
name: string;
/**
* Index (position) of the data source object in the builder's list of data sources.
*/
index?: number;
}
/**
* Defines the capabilities required from data sources.
*/
export interface IDataSource<T extends Record<string, any> = Record<string, any>> extends IDataSourceInfo {
/**
* Asynchronously obtains the object that will be used as building block in the creation of the final
* configuration object.
*/
getObject(): Promise<T>;
/**
* Returns a data source information object on demand. This is used when building a configuration object with
* value tracing.
*/
trace(): IDataSourceInfo;
}
/**
* Defines the shape of configuration-tracing objects.
*/
export interface Trace {
[x: string]: IDataSourceInfo | Trace;
}
/**
* Defines the capabilities required from configuration builders.
*/
export interface IBuilder<T extends Record<string, any> = {}> {
/**
* Adds the provided data source to the collection of data sources that will be used to build the
* configuration object.
* @param dataSource The data source to include.
*/
add<NewT extends Record<string, any>>(dataSource: IDataSource<NewT>): IBuilder<MergeResult<T, NewT>>;
/**
* Adds the specified object to the collection of data sources that will be used to build the configuration
* object.
* @param obj Data object to include as part of the final configuration data, or a function that returns said
* object.
*/
addObject<NewT extends Record<string, any>>(obj: NewT | (() => Promise<NewT>)): IBuilder<MergeResult<T, NewT>>;
/**
* Adds the specified dictionary to the collection of data sources that will be used to build the configuration
* object.
* @param dictionary Dictionary object to include (after processing) as part of the final configuration data,
* or a function that returns said object.
* @param hierarchySeparator Optional hierarchy separator. If none is specified, a colon (:) is assumed.
* @param prefix Optional prefix. Only properties that start with the specified prefix are included, and the
* prefix is always removed after the dictionary is processed. If no prefix is provided, then all dictionary
* entries will contribute to the configuration data.
*/
addDictionary<TDic extends Record<string, ConfigurationValue>, TSep extends string = ':'>(dictionary: TDic | (() => Promise<TDic>), hierarchySeparator?: TSep, prefix?: string): IBuilder<MergeResult<T, InflateDictionary<TDic, TSep>>>;
/**
* Adds the specified dictionary to the collection of data sources that will be used to build the configuration
* object.
* @param dictionary Dictionary object to include (after processing) as part of the final configuration data,
* or a function that returns said object.
* @param hierarchySeparator Optional hierarchy separator. If none is specified, a colon (:) is assumed.
* @param predicate Optional predicate function that is called for every property in the dictionary. Only when
* the return value of the predicate is true the property is included in configuration.
*/
addDictionary<TDic extends Record<string, ConfigurationValue>, TSep extends string = ':'>(dictionary: TDic | (() => Promise<TDic>), hierarchySeparator?: TSep, predicate?: Predicate<string>): IBuilder<MergeResult<T, InflateDictionary<TDic, TSep>>>;
/**
* Adds the qualifying environment variables to the collection of data sources that will be used to build the
* configuration object.
* @param env Environment object containing the environment variables to include in the configuration, or a
* function that returns said object.
* @param prefix Optional prefix. Only properties that start with the specified prefix are included, and the
* prefix is always removed after processing. To avoid exposing non-application data as configuration, a prefix
* is always used. If none is specified, the default prefix is OPT_.
*/
addEnvironment<TDic extends Record<string, ConfigurationValue>, TPrefix extends string = 'OPT_'>(env: TDic | (() => Promise<TDic>), prefix?: TPrefix): IBuilder<MergeResult<T, InflateDictionary<TDic, '__', TPrefix>>>;
/**
* Adds a fetch operation to the collection of data sources that will be used to build the configuration object.
* @param url URL to fetch.
* @param required Optional Boolean value indicating if the fetch must produce an object.
* @param init Optional fetch init data. Refer to the fecth() documentation for information.
* @param processFn Optional processing function that must return the configuration data as an object.
*/
addFetched<NewT extends Record<string, any>>(url: URL | (() => Promise<URL>), required?: boolean, init?: RequestInit, processFn?: ProcessFetchResponse<NewT>): IBuilder<MergeResult<T, NewT>>;
/**
* Adds a fetch operation to the collection of data sources that will be used to build the configuration
* object.
* @param request Request object to use when fetching. Refer to the fetch() documentation for information.
* @param required Optional Boolean value indicating if the fetch must produce an object.
* @param init Optional fetch init data. Refer to the fecth() documentation for information.
* @param processFn Optional processing function that must return the configuration data as an object.
*/
addFetched<NewT extends Record<string, any>>(request: RequestInfo | (() => Promise<RequestInfo>), required?: boolean, init?: RequestInit, processFn?: ProcessFetchResponse<NewT>): IBuilder<MergeResult<T, NewT>>;
/**
* Adds the specified JSON string to the collection of data sources that will be used to build the
* configuration object.
* @param json The JSON string to parse into a JavaScript object, or a function that returns said string.
* @param jsonParser Optional JSON parser. If not specified, the built-in JSON object will be used.
* @param reviver Optional reviver function. For more information see the JSON.parse() documentation.
*/
addJson<NewT extends Record<string, any>>(json: string | (() => Promise<string>), jsonParser?: IJsonParser<NewT>, reviver?: (this: any, key: string, value: any) => any): IBuilder<MergeResult<T, NewT>>;
/**
* Adds a single value to the collection of data sources that will be used to build the configuration object.
* @param path Key comprised of names that determine the hierarchy of the value.
* @param value Value of the property.
* @param hierarchySeparator Optional hierarchy separator. If not specified, colon (:) is assumed.
*/
addSingleValue<TKey extends string, TValue extends ConfigurationValue, TSep extends string = ':'>(path: TKey, value?: TValue, hierarchySeparator?: TSep): IBuilder<MergeResult<T, InflateKey<TKey, TValue, TSep>>>;
/**
* Adds a single value to the collection of data sources that will be used to build the configuration object.
* @param dataFn Function that returns the [key, value] tuple that needs to be added.
* @param hierarchySeparator Optional hierarchy separator. If not specified, colon (:) is assumed.
*/
addSingleValue<TKey extends string, TValue extends ConfigurationValue, TSep extends string = ':'>(dataFn: () => Promise<readonly [TKey, TValue]>, hierarchySeparator?: TSep): IBuilder<MergeResult<T, InflateKey<TKey, TValue, TSep>>>;
/**
* Sets the data source name of the last data source added to the builder.
* @param name Name for the data source.
*/
name(name: string): IBuilder<T>;
/**
* Makes the last-added data source conditionally inclusive.
* @param predicate Predicate function that is run whenever the build function runs. If the predicate returns
* true, then the data source will be included; if it returns false, then the data source is skipped.
* @param dataSourceName Optional data source name. Provided to simplify the build chain and is merely a
* shortcut to include a call to the name() function. Equivalent to when().name().
*/
when(predicate: () => boolean, dataSourceName?: string): IBuilder<T>;
/**
* Adds the provided environment object as a property of the final configuration object.
* @param env Previously created environment object.
* @param propertyName Optional property name for the environment object.
*/
includeEnvironment<TEnvironments extends string, TEnvironmentKey extends string = "environment">(
env: IEnvironment<TEnvironments>,
propertyName?: TEnvironmentKey
): IEnvAwareBuilder<TEnvironments, Omit<T, TEnvironmentKey> & IncludeEnvironment<TEnvironments, TEnvironmentKey>>;
/**
* Creates URL functions in the final configuration object for URL's defined according to the wj-config standard.
* @param wsPropertyNames Optional list of property names whose values are expected to be objects that contain
* host, port, scheme or root path data at some point in their child hierarchy. If not provided, then the default
* list will be used. It can also be a single string, which is the same as a 1-element array.
* @param routeValueRegExp Optional regular expression used to identify replaceable route values. If this is not
* provided, then the default regular expression will match route values of the form {<route value name>}, such as
* {code} or {id}.
*/
createUrlFunctions<TUrl extends keyof T>(wsPropertyNames: TUrl | TUrl[], routeValueRegExp?: RegExp): IBuilder<Omit<T, TUrl> & UrlBuilderSectionWithCheck<T, TUrl>>;
/**
* Asynchronously builds the final configuration object.
*/
build(traceValueSources?: boolean): Promise<T>;
/**
* Defines a post-merge operation. Post-merge operations are run after all the data sources have been merged and
* after all URL functions have been created, and allow the developer to perform additional processing on the final
* configuration object.
*
* Any number of post-merge operations can be defined; these are run in the order they are provided, each being fed
* the configuration object that the previous post-merge operation returns.
*
* This is useful for things like creating composite values. The use-case that inspired this feature is the
* creation of scopes for Azure SSO.
*
* @example
*
* A configuration JSON file containing SSO configuration might look like this:
*
* ```json
* {
* "sso": {
* "auth": {
* "clientId": "12345678-1234-1234-1234-123456789012",
* "redirectUri": "/",
* "authority": "https://login.microsoftonline.com/common",
* "clientCapabilities": [ "CP1" ]
* },
* "scopes": [ "some.api.access" ]
* }
* }
* ```
*
* Scopes are usually in the form `"api://{clientId}/{scope}"`, so the post-merge function can be used to replace
* the scopes in the config JSON file with scopes of the above form. The advantage over just typing them already
* in this format? You don't have to repeat the client ID on every scope. We are all about DRY configuration.
*
* ```ts
* export default await wjConfig()
* ...
* .postMerge(c => c.sso.scopes = c.sso.scopes.map(s => `api://${c.sso.auth.clientId}/${s}`))
* .build();
* ```
*
* @param fn Function that receives the (otherwise) final configuration object and allows the developer to perform
* additional processing on it. The function must return the modified object, which is passed to the next post
* merge function.
*/
postMerge<U extends Record<string, any> = T>(fn: (config: T) => U | Promise<U>): IBuilder<U>;
}
export interface IEnvAwareBuilder<TEnvironments extends string, T extends Record<string, any> = {}> {
/**
* Adds the provided data source to the collection of data sources that will be used to build the
* configuration object.
* @param dataSource The data source to include.
*/
add<NewT extends Record<string, any>>(dataSource: IDataSource<NewT>): IEnvAwareBuilder<TEnvironments, MergeResult<T, NewT>>;
/**
* Adds the specified object to the collection of data sources that will be used to build the configuration object.
* @param obj Data object to include as part of the final configuration data, or a function that returns said
* object.
*/
addObject<NewT extends Record<string, any>>(obj: NewT | (() => Promise<NewT>)): IEnvAwareBuilder<TEnvironments, MergeResult<T, NewT>>;
/**
* Adds the specified dictionary to the collection of data sources that will be used to build the configuration
* object.
* @param dictionary Dictionary object to include (after processing) as part of the final configuration data,
* or a function that returns said object.
* @param hierarchySeparator Optional hierarchy separator. If none is specified, a colon (:) is assumed.
* @param prefix Optional prefix. Only properties that start with the specified prefix are included, and the
* prefix is always removed after the dictionary is processed. If no prefix is provided, then all dictionary
* entries will contribute to the configuration data.
*/
addDictionary<TDic extends Record<string, ConfigurationValue>, TSep extends string = ':'>(dictionary: Record<string, ConfigurationValue> | (() => Promise<Record<string, ConfigurationValue>>), hierarchySeparator?: string, prefix?: string): IEnvAwareBuilder<TEnvironments, MergeResult<T, InflateDictionary<TDic, TSep>>>;
/**
* Adds the specified dictionary to the collection of data sources that will be used to build the configuration
* object.
* @param dictionary Dictionary object to include (after processing) as part of the final configuration data,
* or a function that returns said object.
* @param hierarchySeparator Optional hierarchy separator. If none is specified, a colon (:) is assumed.
* @param predicate Optional predicate function that is called for every property in the dictionary. Only when
* the return value of the predicate is true the property is included in configuration.
*/
addDictionary<TDic extends Record<string, ConfigurationValue>, TSep extends string = ':'>(dictionary: Record<string, ConfigurationValue> | (() => Promise<Record<string, ConfigurationValue>>), hierarchySeparator?: string, predicate?: Predicate<string>): IEnvAwareBuilder<TEnvironments, MergeResult<T, InflateDictionary<TDic, TSep>>>;
/**
* Adds the qualifying environment variables to the collection of data sources that will be used to build the
* configuration object.
* @param env Environment object containing the environment variables to include in the configuration, or a
* function that returns said object.
* @param prefix Optional prefix. Only properties that start with the specified prefix are included, and the
* prefix is always removed after processing. To avoid exposing non-application data as configuration, a prefix
* is always used. If none is specified, the default prefix is OPT_.
*/
addEnvironment<TDic extends Record<string, ConfigurationValue>, TPrefix extends string = 'OPT_'>(env: TDic | (() => Promise<TDic>), prefix?: TPrefix): IEnvAwareBuilder<TEnvironments, MergeResult<T, InflateDictionary<TDic, '__', TPrefix>>>;
/**
* Adds a fetch operation to the collection of data sources that will be used to build the configuration object.
* @param url URL to fetch.
* @param required Optional Boolean value indicating if the fetch must produce an object.
* @param init Optional fetch init data. Refer to the fecth() documentation for information.
* @param processFn Optional processing function that must return the configuration data as an object.
*/
addFetched<NewT extends Record<string, any>>(url: URL | (() => Promise<URL>), required?: boolean, init?: RequestInit, processFn?: ProcessFetchResponse<NewT>): IEnvAwareBuilder<TEnvironments, MergeResult<T, NewT>>;
/**
* Adds a fetch operation to the collection of data sources that will be used to build the configuration
* object.
* @param request Request object to use when fetching. Refer to the fetch() documentation for information.
* @param required Optional Boolean value indicating if the fetch must produce an object.
* @param init Optional fetch init data. Refer to the fecth() documentation for information.
* @param processFn Optional processing function that must return the configuration data as an object.
*/
addFetched<NewT extends Record<string, any>>(request: RequestInfo | (() => Promise<RequestInfo>), required?: boolean, init?: RequestInit, processFn?: ProcessFetchResponse<NewT>): IEnvAwareBuilder<TEnvironments, MergeResult<T, NewT>>;
/**
* Adds the specified JSON string to the collection of data sources that will be used to build the
* configuration object.
* @param json The JSON string to parse into a JavaScript object, or a function that returns said string.
* @param jsonParser Optional JSON parser. If not specified, the built-in JSON object will be used.
* @param reviver Optional reviver function. For more information see the JSON.parse() documentation.
*/
addJson<NewT extends Record<string, any>>(json: string | (() => Promise<string>), jsonParser?: IJsonParser<NewT>, reviver?: (this: any, key: string, value: any) => any): IEnvAwareBuilder<TEnvironments, MergeResult<T, NewT>>;
/**
* Adds a single value to the collection of data sources that will be used to build the configuration object.
* @param path Key comprised of names that determine the hierarchy of the value.
* @param value Value of the property.
* @param hierarchySeparator Optional hierarchy separator. If not specified, colon (:) is assumed.
*/
addSingleValue<TKey extends string, TValue extends ConfigurationValue, TSep extends string = ':'>(path: string, value?: ConfigurationValue, hierarchySeparator?: string): IEnvAwareBuilder<TEnvironments, MergeResult<T, InflateKey<TKey, TValue, TSep>>>;
/**
* Adds a single value to the collection of data sources that will be used to build the configuration object.
* @param dataFn Function that returns the [key, value] tuple that needs to be added.
* @param hierarchySeparator Optional hierarchy separator. If not specified, colon (:) is assumed.
*/
addSingleValue<TKey extends string, TValue extends ConfigurationValue, TSep extends string = ':'>(dataFn: () => Promise<[string, ConfigurationValue]>, hierarchySeparator?: string): IEnvAwareBuilder<TEnvironments, MergeResult<T, InflateKey<TKey, TValue, TSep>>>;
/**
* Sets the data source name of the last data source added to the builder.
* @param name Name for the data source.
*/
name(name: string): IEnvAwareBuilder<TEnvironments, T>;
/**
* Special function that allows the developer the opportunity to add one data source per defined environment.
*
* The function iterates through all possible environments and calls the addDs function for each one. It is
* assumed that the addDs function will add zero or one data source. To signal no data source was added,
* addDs must return the boolean value "false".
* @param addDs Function that is meant to add a single data source of any type that is associated to the
* provided environment name.
*/
addPerEnvironment<NewT extends Record<string, any> = {}>(addDs: (builder: IEnvAwareBuilder<TEnvironments, T>, envName: TEnvironments) => boolean | string): IEnvAwareBuilder<TEnvironments, MergeResult<T, NewT>>;
/**
* Makes the last-added data source conditionally inclusive.
* @param predicate Predicate function that is run whenever the build function runs. If the predicate returns
* true, then the data source will be included; if it returns false, then the data source is skipped.
* @param dataSourceName Optional data source name. Provided to simplify the build chain and is merely a
* shortcut to include a call to the name() function. Equivalent to when().name().
*/
when(predicate: Predicate<IEnvironment<TEnvironments> | undefined>, dataSourceName?: string): IEnvAwareBuilder<TEnvironments, T>;
/**
* Makes the last-added data source conditionally included only if the current environment possesses all of
* the listed traits.
* @param traits The traits the current environment must have for the data source to be added.
* @param dataSourceName Optional data source name. Provided to simplify the build chain and is merely a
* shortcut to include a call to the name() function. Equivalent to whenAllTraits().name().
*/
whenAllTraits(traits: Traits, dataSourceName?: string): IEnvAwareBuilder<TEnvironments, T>;
/**
* Makes the last-added data source conditionally included if the current environment possesses any of the
* listed traits. Only one coincidence is necessary.
* @param traits The list of possible traits the current environment may have in order for the data source to
* be included.
* @param dataSourceName Optional data source name. Provided to simplify the build chain and is merely a
* shortcut to include a call to the name() function. Equivalent to whenAnyTrait().name().
*/
whenAnyTrait(traits: Traits, dataSourceName?: string): IEnvAwareBuilder<TEnvironments, T>;
/**
* Makes the last-added data source conditionally included if the current environment's name is equal to the
* provided environment name.
* @param envName The environment name to use to conditionally include the last-added data source.
* @param dataSourceName Optional data source name. Provided to simplify the build chain and is
* merely a shortcut to include a call to the name() function.
*/
forEnvironment(envName: TEnvironments, dataSourceName?: string): IEnvAwareBuilder<TEnvironments, T>;
/**
* Creates URL functions in the final configuration object for URL's defined according to the wj-config standard.
* @param wsPropertyNames Optional list of property names whose values are expected to be objects that contain
* host, port, scheme or root path data at some point in their child hierarchy. If not provided, then the default
* list will be used. It can also be a single string, which is the same as a 1-element array.
* @param routeValueRegExp Optional regular expression used to identify replaceable route values. If this is not
* provided, then the default regular expression will match route values of the form {<route value name>}, such as
* {code} or {id}.
*/
createUrlFunctions<TUrl extends keyof T>(wsPropertyNames: TUrl | TUrl[], routeValueRegExp?: RegExp): IEnvAwareBuilder<TEnvironments, Omit<T, TUrl> & UrlBuilderSectionWithCheck<T, TUrl>>;
/**
* Asynchronously builds the final configuration object.
*/
build(traceValueSources?: boolean, enforcePerEnvironmentCoverage?: boolean): Promise<T>;
/**
* Defines a post-merge operation. Post-merge operations are run after all the data sources have been merged and
* after all URL functions have been created, and allow the developer to perform additional processing on the final
* configuration object.
*
* Any number of post-merge operations can be defined; these are run in the order they are provided, each being fed
* the configuration object that the previous post-merge operation returns.
*
* This is useful for things like creating composite values. The use-case that inspired this feature is the
* creation of scopes for Azure SSO.
*
* @example
*
* A configuration JSON file containing SSO configuration might look like this:
*
* ```json
* {
* "sso": {
* "auth": {
* "clientId": "12345678-1234-1234-1234-123456789012",
* "redirectUri": "/",
* "authority": "https://login.microsoftonline.com/common",
* "clientCapabilities": [ "CP1" ]
* },
* "scopes": [ "some.api.access" ]
* }
* }
* ```
*
* Scopes are usually in the form `"api://{clientId}/{scope}"`, so the post-merge function can be used to replace
* the scopes in the config JSON file with scopes of the above form. The advantage over just typing them already
* in this format? You don't have to repeat the client ID on every scope. We are all about DRY configuration.
*
* ```ts
* export default await wjConfig()
* ...
* .postMerge(c => c.sso.scopes = c.sso.scopes.map(s => `api://${c.sso.auth.clientId}/${s}`))
* .build();
* ```
*
* @param fn Function that receives the (otherwise) final configuration object and allows the developer to perform
* additional processing on it. The function must return the modified object, which is passed to the next post
* merge function.
*/
postMerge<U extends Record<string, any> = T>(fn: (config: T) => U | Promise<U>): IEnvAwareBuilder<TEnvironments, U>;
}
/**
* Function type that allows environment objects to declare testing functions, such as isProduction().
*/
export type EnvironmentTestFn = () => boolean;
/**
* Defines the capabilities required from environment objects.
*/
export type IEnvironment<TEnvironments extends string> = {
[K in TEnvironments as `is${Capitalize<K>}`]: EnvironmentTestFn;
} & {
/**
* The current environment (represented by an environment definition).
*/
readonly current: IEnvironmentDefinition<Exclude<TEnvironments, undefined>>;
/**
* The list of known environments (represented by a list of environment definitions).
*/
readonly all: TEnvironments[];
/**
* Tests the current environment definition for the presence of the specified traits. It will return true
* only if all specified traits are present; othewise it will return false.
* @param traits The environment traits expected to be found in the current environment definition.
*/
hasTraits(traits: Traits): boolean;
/**
* Tests the current environment definition for the presence of any of the specified traits. It will return
* true if any of the specified traits is present. If none of the specified traits are present, then the
* return value will be false.
* @param traits The environments traits of which at least on of them is expected to be found in the current
* environment definition.
*/
hasAnyTrait(traits: Traits): boolean;
}
/**
* Type used to determine if a configuration node is an appropriate URL root node.
*/
export type HasHost = {
host: string;
}
/**
* Type used to determine if a configuration node holds path information.
*/
export type HasRootPath = {
rootPath: string;
}
/**
* Defines the reserved property names for the URL-building feature of the builders.
*/
export type UrlSectionReserved = {
host?: string;
port?: number;
scheme?: string;
rootPath?: string;
};
/**
* Type of function used as route values when calling a URL-building function.
*/
export type RouteValuesFn = (name: string) => any;
/**
* Defines the acceptable route value replacement types.
*/
export type RouteReplacementArg = Record<string, any> | any[] | RouteValuesFn;
/**
* Defines the acceptable query string argument types.
*/
export type QueryStringArg = Record<string, any> | string | (() => string | Record<string, any>);
/**
* Defines the shape of the dynamically-created URL-building functions.
*/
export type UrlBuilderFn = (routeValues?: RouteReplacementArg, queryString?: QueryStringArg) => string;
/**
* Defines the signature of the `buildUrl` functions that get created in the configuration object when using the
* builder's `createUrlFunctions()` method.
* @param path The extra path to add to the built URL.
* @param routeValues Optional argument used to perform route value replacement.
* @param queryString Optional argument used to append query string data to the generated URL.
*/
export type BuildUrlFn = (path: string, routeValues?: RouteReplacementArg, queryString?: QueryStringArg) => string;
/**
* Defines the shape of nodes in the configuration object that have been successfully processed and converted to
* provide URL-building functions.
*/
export type UrlNode = Partial<HasRootPath> & {
buildUrl: BuildUrlFn;
/**
* Calculates the accumulated root path for this node.
* @returns The accumulated root path for this node as a string.
*/
_rootPath: () => string;
} & {
[x: string]: UrlBuilderFn;
};
export type UrlRoot = UrlSectionReserved & UrlNode;
/**
* Defines how url-building sections of the configuration are transformed by `createUrlFunctions()`.
*/
export type UrlBuilderSection<T, TUrl extends keyof T> = {
buildUrl: (extraPath: string, routeValues?: RouteReplacementArg, queryString?: QueryStringArg) => string;
} & {
[K in TUrl]: K extends keyof UrlSectionReserved ? UrlSectionReserved[K] :
K extends `_${string}` ? T[K] :
T[K] extends string ? UrlBuilderFn :
T[K] extends Record<string, any> ? UrlBuilderSection<T[K], keyof T[K]> :
T[K]
};
/**
* Defines how url-building sections of the configuration are transformed by `createUrlFunctions()` while traversing the
* configuration tree in the looks for `rootPath` or `host`.
*/
export type UrlBuilderSectionWithCheck<T, TUrl extends keyof T> = T extends HasHost ?
UrlBuilderSection<T, TUrl> :
T extends HasRootPath ?
UrlBuilderSection<T, TUrl> :
T extends Record<string, any> ?
{
[K in TUrl]: UrlBuilderSectionWithCheck<T[K], keyof T[K]>
}
: T;
/**
* Type that defines the acceptable trait types for a single trait. It is encouraged to use number-based traits.
*/
export type Trait = number | string;
/**
* Type that defines the acceptable trait types for multiple traits. It is encouraged to use number-based traits.
*/
export type Traits = number | string[];
/**
* Defines the capabilities required from objects used as environment definitions.
*/
export interface IEnvironmentDefinition<TEnvironments extends string> {
/**
* Gets the environment's name.
*/
readonly name: TEnvironments,
/**
* Gets the environment's traits.
*/
readonly traits: Traits
}
/**
* Utility type that extracts the environment names from a constant list of possible names.
*
* @example
* ```ts
* const envs = ['Development', 'Staging', 'Production'] as const;
* type EnvName = EnvironmentName<typeof envs>; // 'Development' | 'Staging' | 'Production'
*
* const env = buildEnvironment(process.env.NODE_ENV as EnvName, envs);
* env.isDevelopment(); // true if the current environment is 'Development'
* env.isStaging(); // true if the current environment is 'Staging'
* env.isProduction(); // true if the current environment is 'Production'
* ```
*
* Without the type casting (`as EnvName`), the `isXXX()` functions would not show up in Intellisense, as the type of
* `env` would be`IEnvironment<string>`.
*/
export type EnvironmentName<T extends readonly string[]> = T[number];