1 |
|
2 |
|
3 | import { Middleware, MiddlewareObj } from './middleware'
|
4 | import Composer from './composer'
|
5 | import Context from './context'
|
6 |
|
7 | type NonemptyReadonlyArray<T> = readonly [T, ...T[]]
|
8 |
|
9 | type RouteFn<TContext extends Context> = (
|
10 | ctx: TContext
|
11 | ) => {
|
12 | route: string
|
13 | context?: Partial<TContext>
|
14 | state?: Partial<TContext['state']>
|
15 | } | null
|
16 |
|
17 | export class Router<C extends Context> implements MiddlewareObj<C> {
|
18 | private otherwiseHandler: Middleware<C> = Composer.passThru()
|
19 |
|
20 | constructor(
|
21 | private readonly routeFn: RouteFn<C>,
|
22 | public handlers = new Map<string, Middleware<C>>()
|
23 | ) {
|
24 | if (typeof routeFn !== 'function') {
|
25 | throw new Error('Missing routing function')
|
26 | }
|
27 | }
|
28 |
|
29 | on(route: string, ...fns: NonemptyReadonlyArray<Middleware<C>>) {
|
30 | if (fns.length === 0) {
|
31 | throw new TypeError('At least one handler must be provided')
|
32 | }
|
33 | this.handlers.set(route, Composer.compose(fns))
|
34 | return this
|
35 | }
|
36 |
|
37 | otherwise(...fns: NonemptyReadonlyArray<Middleware<C>>) {
|
38 | if (fns.length === 0) {
|
39 | throw new TypeError('At least one otherwise handler must be provided')
|
40 | }
|
41 | this.otherwiseHandler = Composer.compose(fns)
|
42 | return this
|
43 | }
|
44 |
|
45 | middleware() {
|
46 | return Composer.lazy<C>((ctx) => {
|
47 | const result = this.routeFn(ctx)
|
48 | if (result == null) {
|
49 | return this.otherwiseHandler
|
50 | }
|
51 | Object.assign(ctx, result.context)
|
52 | Object.assign(ctx.state, result.state)
|
53 | return this.handlers.get(result.route) ?? this.otherwiseHandler
|
54 | })
|
55 | }
|
56 | }
|
57 |
|
\ | No newline at end of file |