import type { EmptyObject, Expand, IsNever, LengthArray, Nullable } from '../../types/misc.js'; import type { ArgValue, IsOptional, ReadonlyArrayNormalized, TemplateTransform, ToOptionalArray, ToRequired, ToRequiredArray, TransformMap } from './types.helpers.js'; import type { CombineOptions } from './utils/combineUrls.js'; export type ObjectBuilderArgs = string extends TArgs ? TFallback : ArgRecord; export type BuilderArgs = LengthArray | ArgRecord; export type ArgRecord = Expand<{ [K in T as true extends IsOptional ? never : K]: ArgValue; } & { [K in T as true extends IsOptional ? ToRequired : never]?: ArgValue | null; }>; /** Internal, Don't use it directly */ interface BaseBuilder { /** * Builds the path using provided args. * * Each arg is always a `ArgValue` (`string | number`), and args are provided as an array which should use the same order and length as the keys in the template. * * Also, args can be provided as an object with args as they were in the template and values as strings. */ build: (args?: B, options?: CombineOptions) => string; /** * Template builder that will build the path using args names but also can: * - keep args as they were in the template * - add a prefix for each key in case `transform` is provided as string * - format each key in case `transform` is provided as function * - append prefix/suffix/optional suffix for each key in case `transform` is provided as object * * For now it's recommended to cache the result, internally it doesn't care about it. */ template: (transform?: Nullable, options?: CombineOptions) => string; /** args as they were in the template */ readonly args: ReadonlyArrayNormalized>; /** * Types helper method to easily cast all kinds of builders to generic IBuilder or others. * It just always returns the passed object, so it's the implementation's responsibility to ensure the compatibility. * * Currently their signature is totally compatible with each other so this cast is safe. * * In future probably some extra conversion may be required. */ as(marker?: TOther): TOther extends readonly string[] ? SwitchBuilder : (TOther extends BaseBuilder ? TOther : never); /** Allows to specify default combine options for the current builder instance. */ withDefaults: (defaults: CombineOptions) => this; /** Allows to specify **build** transformation per key for the current builder instance. */ withBuildTransform: (transforms: TransformMap) => this; /** Allows to specify **template** transformation per key for the current builder instance. */ withTemplateTransform: (transforms: TransformMap) => this; } type EmptyBuilderArgs = [] | Record; export interface Builder extends BaseBuilder, BuilderArgs> { /** Marks input type for `build` to be `Partial`, so any/all arguments can be omitted. * * **Limitation**: will convert to optional ALL parameters if >=2 Builders combined via CombineBuilders */ asOptional(): Builder>; } export interface StaticBuilder extends BaseBuilder { } export interface IBuilder extends BaseBuilder { } export type StaticInput = string | readonly string[]; export type BaseInput = StaticInput | BaseBuilder; export type SwitchBuilder = IsNever)>; export type ExtractArgs = T extends Builder ? ToRequiredArray : readonly string[]; export type ExtractObjectArgs = T extends Builder ? ObjectBuilderArgs : F; /** Normalizes builder, makes sure dynamic args are inferrable. If input is not a builder (i.e. StaticInput), return StaticBuilder */ type Output = TInput extends StaticBuilder ? StaticBuilder : (TInput extends Builder ? Builder : (TInput extends StaticInput ? DynamicStringToBuilder : StaticBuilder)); /** Turns two inputs into one builder, merging static and dynamic args */ type CombineTwo = Output extends StaticBuilder ? Output : (Output extends StaticBuilder ? Output : (Output extends Builder ? (Output extends Builder ? Builder<[...Arr1, ...Arr2]> : never) : never)); export type CombineBuilders = T extends readonly [infer T1 extends BaseInput, infer T2 extends BaseInput, ...infer Rest] ? (CombineTwo extends infer C extends BaseBuilder & BaseInput ? (Rest extends readonly BaseInput[] ? CombineBuilders : never) : never) : (T extends readonly [infer T1] ? Output : StaticBuilder); type Splitter = T extends `${infer Head extends string}/${infer Rest}` ? [Head, ...Splitter] : [T]; type ExtractDynamic = T extends `:${infer Arg}` ? [Arg] : T; type SkipStaticParts = T extends readonly [infer Head extends string, ...infer Rest extends readonly string[]] ? ExtractDynamic extends [infer Arg] ? [Arg, ...SkipStaticParts] : SkipStaticParts : []; type ExtractDynamicParts = T extends readonly string[] ? SkipStaticParts : (T extends string ? SkipStaticParts> : []); type DynamicStringToBuilder = [] extends ExtractDynamicParts ? StaticBuilder : Builder>; export {};