/** * This DSL represents all of the standard syntax for path-to-regexp (e.g. used in express.js and many other libs) * but does not cover the fancier regex validations that are techincally possible. * @since 0.13.0 */ import { pipe } from 'fp-ts/function' import * as O from 'fp-ts/Option' import ptr from 'path-to-regexp' import { A } from 'ts-toolbelt' import { Interpolate, optional, param, ParamsOf, pathJoin, prefix, queryParam, queryParams, QueryParamsOf, unnamed, } from './Path' import { altAll, ReaderOption } from './ReaderOption' /* End Region: Interpolations */ /* Start Region: Route */ /** * @category Model * @since 0.13.0 */ export interface Route

> { readonly path: P readonly match: ReaderOption readonly createPath: >(params: I) => Interpolate } /** * @category Type-level * @since 0.13.0 */ export type PathOf = [A] extends [Route] ? R : never /** * @category Type-level * @since 0.13.0 */ export type ValueOf = [A] extends [Route] ? ParamsOf : never /** * @category Constructor * @since 0.13.0 */ export function make

(path: P): Route

{ const parse = ptr.match(path) const createPath = ptr.compile(path) return { path, match: (path: string) => { const match = parse(path) return !match ? O.none : O.some(match.params as ParamsOf

) }, createPath: createPath as Route

['createPath'], } } /** * @category Combinator * @since 0.13.0 */ export function map(f: (value: A) => B) { return

(route: Route): Route => { return { ...route, match: (r) => pipe(r, route.match, O.map(f)), } } } /* End Region: Route */ /** * @category Combinator * @since 0.13.0 */ export function oneOf, ...Route[]]>( ...[first, ...rest]: Routes ): ReaderOption> { return (path: string) => { const f = first.match const rs = rest.map((r) => r.match) const all = pipe(rs, altAll(f)) return all(path) as O.Option> } } /** * @category Type-level * @since 0.13.0 */ export type OneOf, ...Route[]]> = ValueOf< Routes[number] > // ** Type-level Tests ** // Should always be dead-code eliminated const query = queryParams(queryParam('d', optional(param('foo'))), queryParam('e', unnamed)) const path = pathJoin('test', param('bar'), optional(prefix('~', param('baz'))), query) declare const check: <_ extends 1>() => true type Path_ = typeof path type Params_ = ParamsOf type QueryParams_ = QueryParamsOf check< A.Equals< Params_, { readonly bar: string readonly baz?: string readonly foo?: string readonly 0: string } > >() check< A.Equals< QueryParams_, { readonly d?: string readonly e: string } > >() check< A.Equals< Interpolate, '/test/bar~baz?d=foo&e=0' > >() check, '/test/bar?e=0'>>()