@taserjs/router 0.1.6 → 0.1.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/dist/cjs/builder/factories.cjs +2 -0
  2. package/dist/cjs/builder/factories.cjs.map +1 -1
  3. package/dist/cjs/builder/factories.d.cts +2 -1
  4. package/dist/cjs/builder/standalone.cjs +3 -0
  5. package/dist/cjs/builder/standalone.cjs.map +1 -1
  6. package/dist/cjs/builder/standalone.d.cts +2 -0
  7. package/dist/cjs/define/middleware.cjs.map +1 -1
  8. package/dist/cjs/define/middleware.d.cts +20 -20
  9. package/dist/cjs/index.cjs +1 -0
  10. package/dist/cjs/index.d.cts +6 -2
  11. package/dist/cjs/middleware/hono-mw.cjs.map +1 -1
  12. package/dist/cjs/middleware/hono-mw.d.cts +2 -1
  13. package/dist/cjs/types/index.d.cts +90 -64
  14. package/dist/cjs/types/type-utils.d.cts +3 -3
  15. package/dist/cjs/types/units.d.cts +26 -20
  16. package/dist/esm/builder/factories.d.ts +2 -1
  17. package/dist/esm/builder/factories.js +2 -1
  18. package/dist/esm/builder/factories.js.map +1 -1
  19. package/dist/esm/builder/standalone.d.ts +2 -0
  20. package/dist/esm/builder/standalone.js +4 -2
  21. package/dist/esm/builder/standalone.js.map +1 -1
  22. package/dist/esm/define/middleware.d.ts +20 -20
  23. package/dist/esm/define/middleware.js.map +1 -1
  24. package/dist/esm/index.d.ts +6 -2
  25. package/dist/esm/index.js +2 -2
  26. package/dist/esm/middleware/hono-mw.d.ts +2 -1
  27. package/dist/esm/middleware/hono-mw.js.map +1 -1
  28. package/dist/esm/types/index.d.ts +90 -64
  29. package/dist/esm/types/type-utils.d.ts +3 -3
  30. package/dist/esm/types/units.d.ts +26 -20
  31. package/package.json +3 -3
  32. package/src/builder/factories.ts +3 -2
  33. package/src/builder/standalone.ts +5 -0
  34. package/src/define/middleware.ts +68 -60
  35. package/src/index.ts +4 -0
  36. package/src/middleware/hono-mw.ts +5 -3
  37. package/src/types/index.ts +215 -130
  38. package/src/types/type-utils.ts +1 -1
  39. package/src/types/units.ts +47 -22
package/src/index.ts CHANGED
@@ -1,4 +1,6 @@
1
1
  export interface RouterRegister {}
2
+ export interface RouterMiddlewaresRegister {}
3
+ export interface RouterRoutesRegister {}
2
4
  export type {
3
5
  AppContext,
4
6
  EmptyAppContext,
@@ -11,6 +13,7 @@ export type {
11
13
  InferRouteOutput,
12
14
  LayoutBuilder,
13
15
  LayoutId,
16
+ LayoutMiddlewaresMap,
14
17
  LayoutTree,
15
18
  Method,
16
19
  MiddlewareBuilder,
@@ -51,6 +54,7 @@ export {
51
54
  patch,
52
55
  post,
53
56
  put,
57
+ query,
54
58
  t,
55
59
  type TaserNamespace,
56
60
  } from "./builder/standalone.js";
@@ -1,6 +1,8 @@
1
1
  import { createTaserCompatHandler } from "@taserjs/router-core";
2
2
  import type { MiddlewareHandler } from "hono";
3
3
 
4
+ import type { NextFn } from "../types/units.js";
5
+
4
6
  /**
5
7
  * Adapts a standard Web / Hono-compatible middleware function `(c, next) => ...`
6
8
  * into a Taser-compatible middleware handler function `(ctx, next) => Promise<Response>`.
@@ -15,8 +17,8 @@ import type { MiddlewareHandler } from "hono";
15
17
  * const customCors = middleware(honoMw(cors()));
16
18
  * ```
17
19
  */
18
- export function honoMw(
20
+ export function honoMw<TState extends Record<string, unknown> = {}>(
19
21
  middleware: MiddlewareHandler,
20
- ): (ctx: unknown, next: any) => Promise<Response> {
21
- return createTaserCompatHandler(middleware) as (ctx: unknown, next: any) => Promise<Response>;
22
+ ): (ctx: unknown, next: NextFn<TState>) => Promise<Response> {
23
+ return createTaserCompatHandler(middleware) as (ctx: unknown, next: NextFn<TState>) => Promise<Response>;
22
24
  }
@@ -1,5 +1,9 @@
1
1
  import type { Awaitable, TaserCookieJar, TaserHeaders } from "@taserjs/router-core";
2
- import type { RouterRegister } from "../index.js";
2
+ import type {
3
+ RouterMiddlewaresRegister,
4
+ RouterRegister,
5
+ RouterRoutesRegister,
6
+ } from "../index.js";
3
7
  import type {
4
8
  MergeMiddlewareField,
5
9
  MergeMiddlewareInputField,
@@ -16,6 +20,7 @@ import type {
16
20
  ExtractState,
17
21
  Method,
18
22
  MiddlewareDefinition,
23
+ MiddlewareRequirements,
19
24
  MiddlewareReturnFromParts,
20
25
  MiddlewareUnit,
21
26
  NextFn,
@@ -57,6 +62,7 @@ export type {
57
62
  IsUnknown,
58
63
  Method,
59
64
  MiddlewareDefinition,
65
+ MiddlewareRequirements,
60
66
  MiddlewareReturnFromParts,
61
67
  MiddlewareUnit,
62
68
  MiddlewareUnitBuilder,
@@ -72,9 +78,18 @@ export type AppContext = RouterRegister extends { AppContext: infer C } ? C : Em
72
78
  export type RoutePath = RouterRegister extends { RoutePath: infer P } ? P : never;
73
79
  export type LayoutId = RouterRegister extends { LayoutId: infer L } ? L : never;
74
80
  export type LayoutTree = RouterRegister extends { LayoutTree: infer T } ? T : Record<never, never>;
75
- export type RouteByPathMethod = RouterRegister extends { RouteByPathMethod: infer R }
81
+ export type LayoutMiddlewaresMap = RouterMiddlewaresRegister extends {
82
+ LayoutMiddlewares: infer M;
83
+ }
84
+ ? M
85
+ : RouterRegister extends { LayoutMiddlewares: infer M }
86
+ ? M
87
+ : Record<never, never>;
88
+ export type RouteByPathMethod = RouterRoutesRegister extends { RouteByPathMethod: infer R }
76
89
  ? R
77
- : Record<never, never>;
90
+ : RouterRegister extends { RouteByPathMethod: infer R }
91
+ ? R
92
+ : Record<never, never>;
78
93
 
79
94
  export type LayoutBuilder<TAppContext extends Record<string, unknown> = AppContext> = <
80
95
  const Layout extends LayoutId,
@@ -105,40 +120,32 @@ export type ResolveParams<TPathParams, TSchemaParams> =
105
120
  : Simplify<Omit<TPathParams, keyof TSchemaParams> & TSchemaParams>
106
121
  : TPathParams;
107
122
 
108
- type LayoutParentsMap = RouterRegister extends {
109
- LayoutParents: infer LP extends Record<string, unknown>;
110
- }
111
- ? LP
112
- : {};
113
-
114
- type LayoutParent<Layout extends LayoutId> = Layout extends keyof LayoutParentsMap
115
- ? LayoutParentsMap[Layout] extends LayoutId
116
- ? LayoutParentsMap[Layout]
123
+ type LayoutParent<Layout extends LayoutId> = Layout extends keyof LayoutTree
124
+ ? LayoutTree[Layout] extends { parent: infer P extends LayoutId }
125
+ ? P
117
126
  : null
118
- : LayoutTree[Layout] extends {
119
- parent: infer Parent extends LayoutId | null;
120
- }
121
- ? Parent
122
- : null;
123
-
124
- type LayoutMiddlewares<Layout extends LayoutId> = LayoutTree[Layout]["middlewares"] extends {
125
- readonly __acc?: infer Acc extends readonly unknown[];
126
- }
127
- ? Acc
128
- : LayoutTree[Layout]["middlewares"] extends infer Acc extends readonly unknown[]
127
+ : null;
128
+
129
+ type LayoutMiddlewares<Layout extends LayoutId> = [Layout] extends [keyof LayoutMiddlewaresMap]
130
+ ? LayoutMiddlewaresMap[Layout] extends { readonly __acc?: infer Acc extends readonly unknown[] }
129
131
  ? Acc
130
- : readonly [];
132
+ : LayoutMiddlewaresMap[Layout] extends readonly unknown[]
133
+ ? LayoutMiddlewaresMap[Layout]
134
+ : readonly []
135
+ : readonly [];
131
136
 
132
137
  type ResolveLayoutMiddlewares<
133
138
  Layout extends LayoutId | null,
134
139
  Depth extends readonly unknown[] = [],
135
140
  > = Depth["length"] extends 10
136
141
  ? readonly []
137
- : Layout extends LayoutId
138
- ? readonly [
139
- ...ResolveLayoutMiddlewares<LayoutParent<Layout>, [...Depth, unknown]>,
140
- ...LayoutMiddlewares<Layout>,
141
- ]
142
+ : [Layout] extends [LayoutId]
143
+ ? LayoutParent<Layout> extends infer Parent extends LayoutId
144
+ ? readonly [
145
+ ...ResolveLayoutMiddlewares<Parent, [...Depth, unknown]>,
146
+ ...LayoutMiddlewares<Layout>,
147
+ ]
148
+ : readonly [...LayoutMiddlewares<Layout>]
142
149
  : readonly [];
143
150
 
144
151
  export type ResolveLayoutIdChain<
@@ -182,17 +189,10 @@ type RouteEntry<Path extends RoutePath, TMethod extends Method> = RouteByPathMet
182
189
  ? Entry
183
190
  : never;
184
191
 
185
- type RouteParent<Path extends RoutePath, TMethod extends Method> =
186
- RouteEntry<Path, TMethod> extends { parent: infer Parent extends LayoutId | null }
187
- ? Parent
188
- : null;
189
-
190
192
  type RouteLayoutChain<Path extends RoutePath, TMethod extends Method> =
191
- RouteEntry<Path, TMethod> extends { layoutChain: infer Chain extends readonly LayoutId[] }
193
+ RouteEntry<Path, TMethod> extends { layouts: infer Chain extends readonly LayoutId[] }
192
194
  ? Chain
193
- : RouteParent<Path, TMethod> extends LayoutId
194
- ? readonly [RouteParent<Path, TMethod>]
195
- : readonly [];
195
+ : readonly [];
196
196
 
197
197
  export type RouteLayoutMiddlewares<
198
198
  Path extends RoutePath,
@@ -229,15 +229,13 @@ export type RouteChainContext<
229
229
  > = Simplify<
230
230
  TAppContext &
231
231
  UnitRuntimeContext & {
232
- query: Simplify<MergePart<TQuery, RouteResolvedField<Path, TMethod, Acc, "query">>>;
233
- params: Simplify<
234
- ResolveParams<
235
- PathParams<Path>,
236
- MergePart<TParams, RouteResolvedField<Path, TMethod, Acc, "params">>
237
- >
232
+ query: MergePart<TQuery, RouteResolvedField<Path, TMethod, Acc, "query">>;
233
+ params: ResolveParams<
234
+ PathParams<Path>,
235
+ MergePart<TParams, RouteResolvedField<Path, TMethod, Acc, "params">>
238
236
  >;
239
- body: Simplify<MergePart<TBody, RouteResolvedField<Path, TMethod, Acc, "body">>>;
240
- state: Simplify<RouteResolvedField<Path, TMethod, Acc, "state">>;
237
+ body: MergePart<TBody, RouteResolvedField<Path, TMethod, Acc, "body">>;
238
+ state: RouteResolvedField<Path, TMethod, Acc, "state">;
241
239
  headers: TaserHeaders;
242
240
  cookies: TaserCookieJar;
243
241
  }
@@ -352,19 +350,66 @@ export type IsLayoutAllowed<TRequiredLayouts, TChain extends readonly LayoutId[]
352
350
  : true
353
351
  : true;
354
352
 
355
- export type IsStateSatisfied<TActualState, TRequiredState> = [TRequiredState] extends [never]
353
+ export type IsRequirementsSatisfied<
354
+ TActual extends {
355
+ query?: unknown;
356
+ params?: unknown;
357
+ body?: unknown;
358
+ state?: unknown;
359
+ },
360
+ TRequires extends MiddlewareRequirements,
361
+ > = [TRequires] extends [never]
356
362
  ? true
357
- : [unknown] extends [TRequiredState]
363
+ : [unknown] extends [TRequires]
358
364
  ? true
359
- : [null] extends [TRequiredState]
365
+ : [null] extends [TRequires]
360
366
  ? true
361
- : [undefined] extends [TRequiredState]
367
+ : [undefined] extends [TRequires]
362
368
  ? true
363
- : [IsEmptyObject<TRequiredState>] extends [true]
369
+ : [IsEmptyObject<TRequires>] extends [true]
364
370
  ? true
365
- : [TActualState] extends [TRequiredState]
366
- ? true
367
- : false;
371
+ : (
372
+ TRequires extends { state: infer S extends Record<string, unknown> }
373
+ ? [TActual["state"]] extends [S]
374
+ ? true
375
+ : false
376
+ : true
377
+ ) extends false
378
+ ? false
379
+ : (
380
+ TRequires extends { query: infer Q extends Record<string, unknown> }
381
+ ? [TActual["query"]] extends [Q]
382
+ ? true
383
+ : false
384
+ : true
385
+ ) extends false
386
+ ? false
387
+ : (
388
+ TRequires extends { params: infer P extends Record<string, unknown> }
389
+ ? [TActual["params"]] extends [P]
390
+ ? true
391
+ : false
392
+ : true
393
+ ) extends false
394
+ ? false
395
+ : (
396
+ TRequires extends { body: infer B }
397
+ ? unknown extends B
398
+ ? true
399
+ : [TRequires["body"]] extends [undefined]
400
+ ? true
401
+ : [TActual["body"]] extends [B]
402
+ ? true
403
+ : false
404
+ : true
405
+ ) extends false
406
+ ? false
407
+ : true;
408
+
409
+ export type IsStateSatisfied<TActualState, TRequiredState> = IsRequirementsSatisfied<
410
+ { state: TActualState },
411
+ { state: TRequiredState extends Record<string, unknown> ? TRequiredState : {} }
412
+ >;
368
413
 
369
414
  type InputFacet<T, K extends string> =
370
415
  IsNever<T> extends true ? {} : IsEmptyObject<T> extends true ? {} : { [Key in K]: T };
@@ -447,7 +492,9 @@ export type InferRouteInputFromPath<Path extends RoutePath, TMethod extends Meth
447
492
  export type MiddlewareLayoutField<
448
493
  Layout extends LayoutId,
449
494
  Field extends "query" | "params" | "body" | "state",
450
- > = MergeMiddlewareField<ResolveLayoutMiddlewares<LayoutParent<Layout>>, Field>;
495
+ > = [LayoutParent<Layout>] extends [infer Parent extends LayoutId]
496
+ ? MergeMiddlewareField<ResolveLayoutMiddlewares<Parent>, Field>
497
+ : {};
451
498
 
452
499
  type MiddlewareChainField<
453
500
  Layout extends LayoutId,
@@ -458,20 +505,22 @@ type MiddlewareChainField<
458
505
  export type MiddlewareChainContext<
459
506
  Layout extends LayoutId,
460
507
  Acc extends readonly unknown[],
461
- TQuery,
462
- TParams,
463
- TBody,
508
+ TQuery = unknown,
509
+ TParams = unknown,
510
+ TBody = unknown,
464
511
  TAppContext extends Record<string, unknown> = AppContext,
465
512
  > = Simplify<
466
- TAppContext &
467
- UnitRuntimeContext & {
468
- query: Simplify<MergePart<TQuery, MiddlewareChainField<Layout, Acc, "query">>>;
469
- params: Simplify<MergePart<TParams, MiddlewareChainField<Layout, Acc, "params">>>;
470
- body: Simplify<MergePart<TBody, MiddlewareChainField<Layout, Acc, "body">>>;
471
- state: Simplify<MiddlewareChainField<Layout, Acc, "state">>;
472
- headers: TaserHeaders;
473
- cookies: TaserCookieJar;
474
- }
513
+ TAppContext & {
514
+ readonly request: Request;
515
+ readonly method: Method;
516
+ readonly url: URL;
517
+ readonly headers: TaserHeaders;
518
+ readonly cookies: TaserCookieJar;
519
+ query: Simplify<MergePart<TQuery, MiddlewareChainField<Layout, Acc, "query">>>;
520
+ params: Simplify<MergePart<TParams, MiddlewareChainField<Layout, Acc, "params">>>;
521
+ body: Simplify<MergePart<TBody, MiddlewareChainField<Layout, Acc, "body">>>;
522
+ state: Simplify<MiddlewareChainField<Layout, Acc, "state">>;
523
+ }
475
524
  >;
476
525
 
477
526
  export type MiddlewareBuilder<
@@ -482,29 +531,42 @@ export type MiddlewareBuilder<
482
531
  readonly layout: Layout;
483
532
  readonly middlewares: readonly MiddlewareDefinition[];
484
533
  readonly __acc?: Acc;
485
- readonly $Infer: {
486
- Context: MiddlewareChainContext<Layout, Acc, unknown, unknown, unknown, TAppContext>;
487
- };
488
- use<TAcc, TReturns extends ReturnsMap = {}, TRequiredLayouts = unknown, TRequiredState = unknown>(
489
- unit: MiddlewareUnit<TAcc, TReturns, TRequiredLayouts, TRequiredState>,
490
- ..._assert: [
534
+ use<
535
+ TAcc,
536
+ TReturns extends ReturnsMap = {},
537
+ TRequiredLayouts = unknown,
538
+ TRequires extends MiddlewareRequirements = {},
539
+ >(
540
+ unit: [
491
541
  IsLayoutAllowed<TRequiredLayouts, ResolveLayoutIdChain<Layout>>,
492
- IsStateSatisfied<MiddlewareChainField<Layout, Acc, "state">, TRequiredState>,
542
+ IsRequirementsSatisfied<
543
+ {
544
+ query: MiddlewareChainField<Layout, Acc, "query">;
545
+ params: MiddlewareChainField<Layout, Acc, "params">;
546
+ body: MiddlewareChainField<Layout, Acc, "body">;
547
+ state: MiddlewareChainField<Layout, Acc, "state">;
548
+ },
549
+ TRequires
550
+ >,
493
551
  ] extends [true, true]
494
- ? []
495
- : [
496
- {
497
- error: "Middleware cannot be attached to this layout";
498
- requiredLayouts: TRequiredLayouts;
499
- layoutChain: ResolveLayoutIdChain<Layout>;
500
- requiredState: TRequiredState;
501
- actualState: MiddlewareChainField<Layout, Acc, "state">;
502
- },
503
- ]
552
+ ? MiddlewareUnit<TAcc, TReturns, TRequiredLayouts, TRequires>
553
+ : never,
504
554
  ): MiddlewareBuilder<Layout, readonly [...Acc, TAcc], TAppContext>;
505
555
  use<R = unknown>(
506
556
  fn: (
507
- ctx: MiddlewareChainContext<Layout, Acc, unknown, unknown, unknown, TAppContext>,
557
+ ctx: Simplify<
558
+ TAppContext & {
559
+ readonly request: Request;
560
+ readonly method: Method;
561
+ readonly url: URL;
562
+ readonly headers: TaserHeaders;
563
+ readonly cookies: TaserCookieJar;
564
+ query: Simplify<MergePart<unknown, MiddlewareChainField<Layout, Acc, "query">>>;
565
+ params: Simplify<MergePart<unknown, MiddlewareChainField<Layout, Acc, "params">>>;
566
+ body: Simplify<MergePart<unknown, MiddlewareChainField<Layout, Acc, "body">>>;
567
+ state: Simplify<MiddlewareChainField<Layout, Acc, "state">>;
568
+ }
569
+ >,
508
570
  next: NextFn,
509
571
  ) => Awaitable<R>,
510
572
  ): MiddlewareBuilder<
@@ -563,7 +625,7 @@ type RouteHandleResult<
563
625
  };
564
626
  };
565
627
 
566
- export type RouteBuilderBase<
628
+ export type RouteContractBuilderBase<
567
629
  Path extends RoutePath,
568
630
  TMethod extends Method,
569
631
  Acc extends readonly unknown[] = readonly [],
@@ -584,7 +646,7 @@ export type RouteBuilderBase<
584
646
  };
585
647
  query<TQuery, TQueryIn = unknown>(
586
648
  schema: Schema<TQuery, TQueryIn>,
587
- ): RouteBuilder<
649
+ ): RouteContractBuilder<
588
650
  Path,
589
651
  TMethod,
590
652
  Acc,
@@ -594,7 +656,7 @@ export type RouteBuilderBase<
594
656
  >;
595
657
  params<TParams, TParamsIn = unknown>(
596
658
  schema: Schema<TParams, TParamsIn>,
597
- ): RouteBuilder<
659
+ ): RouteContractBuilder<
598
660
  Path,
599
661
  TMethod,
600
662
  Acc,
@@ -604,44 +666,7 @@ export type RouteBuilderBase<
604
666
  >;
605
667
  returns<const M extends ReturnsMap>(
606
668
  map: M,
607
- ): RouteBuilder<Path, TMethod, Acc, Validators, Omit<TReturns, keyof M> & M, TAppContext>;
608
- use<TAcc, UReturns extends ReturnsMap = {}, TRequiredLayouts = unknown, TRequiredState = unknown>(
609
- unit: MiddlewareUnit<TAcc, UReturns, TRequiredLayouts, TRequiredState>,
610
- ..._assert: [
611
- IsLayoutAllowed<TRequiredLayouts, RouteLayoutChain<Path, TMethod>>,
612
- IsStateSatisfied<RouteResolvedField<Path, TMethod, Acc, "state">, TRequiredState>,
613
- ] extends [true, true]
614
- ? []
615
- : [
616
- {
617
- error: "Middleware cannot be attached to this route";
618
- requiredLayouts: TRequiredLayouts;
619
- routeLayoutChain: RouteLayoutChain<Path, TMethod>;
620
- requiredState: TRequiredState;
621
- actualState: RouteResolvedField<Path, TMethod, Acc, "state">;
622
- },
623
- ]
624
- ): RouteBuilder<
625
- Path,
626
- TMethod,
627
- readonly [...Acc, TAcc],
628
- Validators,
629
- Omit<TReturns, keyof UReturns> & UReturns,
630
- TAppContext
631
- >;
632
- use<R = unknown>(
633
- fn: (
634
- ctx: RouteChainContext<Path, TMethod, Acc, unknown, unknown, unknown, TAppContext>,
635
- next: NextFn,
636
- ) => Awaitable<R>,
637
- ): RouteBuilder<
638
- Path,
639
- TMethod,
640
- readonly [...Acc, MiddlewareReturnFromParts<unknown, unknown, unknown, ExtractState<R>>],
641
- Validators,
642
- TReturns,
643
- TAppContext
644
- >;
669
+ ): RouteContractBuilder<Path, TMethod, Acc, Validators, Omit<TReturns, keyof M> & M, TAppContext>;
645
670
  handler<R extends Response = Response>(
646
671
  fn: (
647
672
  ctx: TMethod extends "GET" | "DELETE" | "HEAD" | "OPTIONS"
@@ -659,20 +684,20 @@ export type RouteBuilderBase<
659
684
  ): RouteHandleResult<Path, TMethod, Acc, Validators, TReturns, R, TAppContext>;
660
685
  };
661
686
 
662
- export type RouteBuilder<
687
+ export type RouteContractBuilder<
663
688
  Path extends RoutePath,
664
689
  TMethod extends Method,
665
690
  Acc extends readonly unknown[] = readonly [],
666
691
  Validators extends ValidatorParts = {},
667
692
  TReturns extends ReturnsMap = {},
668
693
  TAppContext extends Record<string, unknown> = AppContext,
669
- > = RouteBuilderBase<Path, TMethod, Acc, Validators, TReturns, TAppContext> &
694
+ > = RouteContractBuilderBase<Path, TMethod, Acc, Validators, TReturns, TAppContext> &
670
695
  (TMethod extends "GET" | "DELETE" | "HEAD" | "OPTIONS"
671
696
  ? {}
672
697
  : {
673
698
  body<TBody, TBodyIn = unknown>(
674
699
  schema: Schema<TBody, TBodyIn>,
675
- ): RouteBuilder<
700
+ ): RouteContractBuilder<
676
701
  Path,
677
702
  TMethod,
678
703
  Acc,
@@ -683,7 +708,7 @@ export type RouteBuilder<
683
708
  body<Mode extends "json" | "form" | "urlencoded", TBody, TBodyIn = unknown>(
684
709
  mode: Mode,
685
710
  schema: Schema<TBody, TBodyIn>,
686
- ): RouteBuilder<
711
+ ): RouteContractBuilder<
687
712
  Path,
688
713
  TMethod,
689
714
  Acc,
@@ -693,6 +718,66 @@ export type RouteBuilder<
693
718
  >;
694
719
  });
695
720
 
721
+ export type RouteMiddlewareBuilder<
722
+ Path extends RoutePath,
723
+ TMethod extends Method,
724
+ Acc extends readonly unknown[] = readonly [],
725
+ Validators extends ValidatorParts = {},
726
+ TReturns extends ReturnsMap = {},
727
+ TAppContext extends Record<string, unknown> = AppContext,
728
+ > = RouteContractBuilder<Path, TMethod, Acc, Validators, TReturns, TAppContext> & {
729
+ use<
730
+ TAcc,
731
+ UReturns extends ReturnsMap = {},
732
+ TRequiredLayouts = unknown,
733
+ TRequires extends MiddlewareRequirements = {},
734
+ >(
735
+ unit: [
736
+ IsLayoutAllowed<TRequiredLayouts, RouteLayoutChain<Path, TMethod>>,
737
+ IsRequirementsSatisfied<
738
+ {
739
+ query: RouteResolvedField<Path, TMethod, Acc, "query">;
740
+ params: ResolveParams<PathParams<Path>, RouteResolvedField<Path, TMethod, Acc, "params">>;
741
+ body: RouteResolvedField<Path, TMethod, Acc, "body">;
742
+ state: RouteResolvedField<Path, TMethod, Acc, "state">;
743
+ },
744
+ TRequires
745
+ >,
746
+ ] extends [true, true]
747
+ ? MiddlewareUnit<TAcc, UReturns, TRequiredLayouts, TRequires>
748
+ : never,
749
+ ): RouteMiddlewareBuilder<
750
+ Path,
751
+ TMethod,
752
+ readonly [...Acc, TAcc],
753
+ Validators,
754
+ Omit<TReturns, keyof UReturns> & UReturns,
755
+ TAppContext
756
+ >;
757
+ use<R = unknown>(
758
+ fn: (
759
+ ctx: RouteChainContext<Path, TMethod, Acc, unknown, unknown, unknown, TAppContext>,
760
+ next: NextFn,
761
+ ) => Awaitable<R>,
762
+ ): RouteMiddlewareBuilder<
763
+ Path,
764
+ TMethod,
765
+ readonly [...Acc, MiddlewareReturnFromParts<unknown, unknown, unknown, ExtractState<R>>],
766
+ Validators,
767
+ TReturns,
768
+ TAppContext
769
+ >;
770
+ };
771
+
772
+ export type RouteBuilder<
773
+ Path extends RoutePath,
774
+ TMethod extends Method,
775
+ Acc extends readonly unknown[] = readonly [],
776
+ Validators extends ValidatorParts = {},
777
+ TReturns extends ReturnsMap = {},
778
+ TAppContext extends Record<string, unknown> = AppContext,
779
+ > = RouteMiddlewareBuilder<Path, TMethod, Acc, Validators, TReturns, TAppContext>;
780
+
696
781
  export type RouteDefinition<
697
782
  Path extends RoutePath = RoutePath,
698
783
  TMethod extends Method = Method,
@@ -13,7 +13,7 @@ export type UnwrapPart<T> = unknown extends T ? {} : T;
13
13
  export type MergePart<T, Base> = unknown extends T ? Base : Base & T;
14
14
 
15
15
  /** Collapse intersections so `Omit`/`keyof` treat the result as one object type. */
16
- export type Simplify<T> = { [K in keyof T]: T[K] } & {};
16
+ export type Simplify<T> = [T] extends [infer U] ? { [K in keyof U]: U[K] } : never;
17
17
 
18
18
  /**
19
19
  * Request prop: when schema input is `unknown` (e.g. zod coerce),