UNPKG

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
/** * 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];