11// deno-lint-ignore-file ban-types
22import {
33 brand ,
4- type Check ,
54 type Fold ,
65 type Materialize ,
76 type MethodElement ,
@@ -18,11 +17,11 @@ export type RouteZero<N extends string = string> = Route<
1817
1918export function route <
2019 const N extends string ,
21- const E extends readonly Unary [ ] ,
20+ const E extends readonly Input [ ] ,
2221> (
2322 start : Definition < N > ,
24- ...elements : E & Check < RouteZero < N > , E >
25- ) : Materialize < Fold < RouteZero < N > , E > > {
23+ ...elements : E & Validate < RouteZero < N > , E >
24+ ) : Materialize < Fold < RouteZero < N > , Normalize < E > > > {
2625 let zero : RouteZero < N > = {
2726 ...start ,
2827 methods : [ "help" ] ,
@@ -37,10 +36,10 @@ export function route<
3736 } ] ,
3837 } ;
3938
40- return elements . reduce < unknown > (
39+ return normalize < E > ( elements ) . reduce < unknown > (
4140 ( value , element ) => element ( value as never ) ,
4241 zero ,
43- ) as Materialize < Fold < RouteZero < N > , E > > ;
42+ ) as Materialize < Fold < RouteZero < N > , Normalize < E > > > ;
4443}
4544
4645export function version ( semver : string ) : MethodElement < "version" > {
@@ -58,7 +57,25 @@ export function executable(): MethodElement<"execute"> {
5857 } ) ) ;
5958}
6059
61- export function routes < const C extends readonly AnyRoute [ ] > (
60+ export type Input = Unary | AnyRoute ;
61+
62+ export type Normalize < E extends readonly Input [ ] > = {
63+ [ K in keyof E ] : Normalized < E [ K ] > ;
64+ } ;
65+
66+ // Walk invalid pipelines only, reporting errors on the original arguments.
67+ export type Validate < S , E extends readonly Input [ ] > =
68+ [ Fold < S , Normalize < E > > ] extends [ never ] ? Invalid < S , E > : unknown ;
69+
70+ export function normalize < const E extends readonly Input [ ] > (
71+ elements : E ,
72+ ) : Normalize < E > {
73+ return elements . map ( ( element ) =>
74+ typeof element === "function" ? element : routes ( element )
75+ ) as Normalize < E > ;
76+ }
77+
78+ function routes < const C extends readonly AnyRoute [ ] > (
6279 ...children : C
6380) : RoutesElement < C > {
6481 return brand < RoutesElement < C > > ( ( route : AnyRoute ) => {
@@ -75,3 +92,18 @@ export function routes<const C extends readonly AnyRoute[]>(
7592 } ;
7693 } ) ;
7794}
95+
96+ // Distribute over unions so Fold can retain its conservative union handling.
97+ type Normalized < E extends Input > = E extends Unary ? E
98+ : E extends AnyRoute ? RoutesElement < readonly [ E ] >
99+ : never ;
100+
101+ type Invalid < S , E extends readonly Input [ ] > = E extends readonly [
102+ infer Head extends Input ,
103+ ...infer Tail extends readonly Input [ ] ,
104+ ]
105+ ? Fold < S , readonly [ Normalized < Head > ] > extends infer Next
106+ ? [ Next ] extends [ never ] ? readonly [ never , ...Tail ]
107+ : readonly [ Head , ...Invalid < Next , Tail > ]
108+ : never
109+ : E ;
0 commit comments