@taserjs/router 0.0.3 → 0.0.4

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.
@@ -1,10 +1,20 @@
1
1
  let _taserjs_router_core = require("@taserjs/router-core");
2
2
  //#region src/define/middleware.ts
3
- function defineMiddleware(options) {
4
- if (typeof options === "function") {
5
- const honoMiddleware = options;
3
+ function defineMiddleware(first, second) {
4
+ if (typeof first === "string" || Array.isArray(first)) {
5
+ const options = second;
6
+ return {
7
+ ...options,
8
+ __middlewareAcc: void 0,
9
+ ...options?.returns ? { __returns: options.returns } : {},
10
+ __requiredLayouts: first
11
+ };
12
+ }
13
+ if (typeof first === "function") {
14
+ const honoMiddleware = first;
6
15
  return defineMiddleware({ handler: (ctx, next) => (0, _taserjs_router_core.createTaserCompatHandler)(honoMiddleware)(ctx, next) });
7
16
  }
17
+ const options = first;
8
18
  return {
9
19
  ...options,
10
20
  __middlewareAcc: void 0,
@@ -1 +1 @@
1
- {"version":3,"file":"middleware.cjs","names":[],"sources":["../../../src/define/middleware.ts"],"sourcesContent":["import { createTaserCompatHandler, type Awaitable } from \"@taserjs/router-core\";\n\nimport type { ReturnsMap } from \"../types/returns.js\";\nimport type {\n AppContext,\n ExtractState,\n IsUnknown,\n MiddlewareReturnFromParts,\n MiddlewareUnit,\n NextFn,\n StandaloneMiddlewareContext,\n} from \"../types/units.js\";\nimport type { Schema } from \"../types/schema.js\";\n\ntype HonoMiddlewareHandler = Parameters<typeof createTaserCompatHandler>[0];\n\ntype HonoMiddlewareUnit = MiddlewareUnit<MiddlewareReturnFromParts<unknown, unknown, unknown, {}>>;\n\nexport type DefineMiddlewareOptions<\n TState = unknown,\n TQuery = unknown,\n TParams = unknown,\n TBody = unknown,\n TReturns extends ReturnsMap = {},\n TQueryIn = unknown,\n TParamsIn = unknown,\n TBodyIn = unknown,\n TAppContext extends Record<string, unknown> = AppContext,\n R = unknown,\n> = {\n query?: Schema<TQuery, TQueryIn>;\n params?: Schema<TParams, TParamsIn>;\n body?: Schema<TBody, TBodyIn>;\n returns?: TReturns;\n handler: (\n ctx: StandaloneMiddlewareContext<TQuery, TParams, TBody, TAppContext>,\n next: NextFn<NoInfer<TState>>,\n ) => Awaitable<R>;\n};\n\nexport function defineMiddleware(middleware: HonoMiddlewareHandler): HonoMiddlewareUnit;\n\nexport function defineMiddleware<\n TState = unknown,\n TAppContext extends Record<string, unknown> = AppContext,\n TQuery = unknown,\n TParams = unknown,\n TBody = unknown,\n TReturns extends ReturnsMap = {},\n TQueryIn = unknown,\n TParamsIn = unknown,\n TBodyIn = unknown,\n R = unknown,\n>(\n options: DefineMiddlewareOptions<\n TState,\n TQuery,\n TParams,\n TBody,\n TReturns,\n TQueryIn,\n TParamsIn,\n TBodyIn,\n TAppContext,\n R\n >,\n): MiddlewareUnit<\n MiddlewareReturnFromParts<\n TQuery,\n TParams,\n TBody,\n IsUnknown<TState> extends true ? ExtractState<R> : TState,\n TQueryIn,\n TParamsIn,\n TBodyIn\n >,\n TReturns\n>;\n\nexport function defineMiddleware(\n options:\n | HonoMiddlewareHandler\n | DefineMiddlewareOptions<any, any, any, any, any, any, any, any, any, any>,\n): unknown {\n if (typeof options === \"function\") {\n const honoMiddleware = options as HonoMiddlewareHandler;\n return defineMiddleware({\n handler: (ctx, next) => createTaserCompatHandler(honoMiddleware)(ctx, next),\n });\n }\n\n const unit = {\n ...options,\n __middlewareAcc: undefined as unknown,\n ...(options.returns ? { __returns: options.returns } : {}),\n };\n\n return unit;\n}\n"],"mappings":";;AA+EA,SAAgB,iBACd,SAGS;CACT,IAAI,OAAO,YAAY,YAAY;EACjC,MAAM,iBAAiB;EACvB,OAAO,iBAAiB,EACtB,UAAU,KAAK,UAAA,GAAA,qBAAA,yBAAA,CAAkC,cAAc,CAAC,CAAC,KAAK,IAAI,EAC5E,CAAC;CACH;CAQA,OAAO;EALL,GAAG;EACH,iBAAiB,KAAA;EACjB,GAAI,QAAQ,UAAU,EAAE,WAAW,QAAQ,QAAQ,IAAI,CAAC;CAGnD;AACT"}
1
+ {"version":3,"file":"middleware.cjs","names":[],"sources":["../../../src/define/middleware.ts"],"sourcesContent":["import { createTaserCompatHandler, type Awaitable } from \"@taserjs/router-core\";\n\nimport type { ReturnsMap } from \"../types/returns.js\";\nimport type {\n AppContext,\n ExtractState,\n IsUnknown,\n MiddlewareReturnFromParts,\n MiddlewareUnit,\n NextFn,\n StandaloneMiddlewareContext,\n} from \"../types/units.js\";\nimport type {\n LayoutId,\n ResolveLayoutMiddlewaresState,\n ResolveLayoutsState,\n} from \"../types/index.js\";\nimport type { Schema } from \"../types/schema.js\";\n\ntype HonoMiddlewareHandler = Parameters<typeof createTaserCompatHandler>[0];\n\ntype HonoMiddlewareUnit = MiddlewareUnit<\n MiddlewareReturnFromParts<unknown, unknown, unknown, {}>,\n {},\n null,\n {}\n>;\n\nexport type DefineMiddlewareOptions<\n TState = unknown,\n TRequires = {},\n TQuery = unknown,\n TParams = unknown,\n TBody = unknown,\n TReturns extends ReturnsMap = {},\n TQueryIn = unknown,\n TParamsIn = unknown,\n TBodyIn = unknown,\n TAppContext extends Record<string, unknown> = AppContext,\n R = unknown,\n> = {\n query?: Schema<TQuery, TQueryIn>;\n params?: Schema<TParams, TParamsIn>;\n body?: Schema<TBody, TBodyIn>;\n returns?: TReturns;\n handler: (\n ctx: StandaloneMiddlewareContext<TQuery, TParams, TBody, TAppContext, TRequires>,\n next: NextFn<NoInfer<TState>>,\n ) => Awaitable<R>;\n};\n\nexport type ScopedMiddlewareOptions<\n Layout extends LayoutId,\n TState = unknown,\n TRequires = {},\n TQuery = unknown,\n TParams = unknown,\n TBody = unknown,\n TReturns extends ReturnsMap = {},\n TQueryIn = unknown,\n TParamsIn = unknown,\n TBodyIn = unknown,\n TAppContext extends Record<string, unknown> = AppContext,\n R = unknown,\n> = {\n query?: Schema<TQuery, TQueryIn>;\n params?: Schema<TParams, TParamsIn>;\n body?: Schema<TBody, TBodyIn>;\n returns?: TReturns;\n handler: (\n ctx: StandaloneMiddlewareContext<\n TQuery,\n TParams,\n TBody,\n TAppContext,\n ResolveLayoutMiddlewaresState<Layout> & TRequires\n >,\n next: NextFn<NoInfer<TState>>,\n ) => Awaitable<R>;\n};\n\nexport type MultiScopedMiddlewareOptions<\n Layouts extends readonly LayoutId[],\n TState = unknown,\n TRequires = {},\n TQuery = unknown,\n TParams = unknown,\n TBody = unknown,\n TReturns extends ReturnsMap = {},\n TQueryIn = unknown,\n TParamsIn = unknown,\n TBodyIn = unknown,\n TAppContext extends Record<string, unknown> = AppContext,\n R = unknown,\n> = {\n query?: Schema<TQuery, TQueryIn>;\n params?: Schema<TParams, TParamsIn>;\n body?: Schema<TBody, TBodyIn>;\n returns?: TReturns;\n handler: (\n ctx: StandaloneMiddlewareContext<\n TQuery,\n TParams,\n TBody,\n TAppContext,\n ResolveLayoutsState<Layouts> & TRequires\n >,\n next: NextFn<NoInfer<TState>>,\n ) => Awaitable<R>;\n};\n\nexport function defineMiddleware(middleware: HonoMiddlewareHandler): HonoMiddlewareUnit;\n\nexport function defineMiddleware<\n const Layout extends LayoutId,\n TState = unknown,\n TRequires = {},\n TAppContext extends Record<string, unknown> = AppContext,\n TQuery = unknown,\n TParams = unknown,\n TBody = unknown,\n TReturns extends ReturnsMap = {},\n TQueryIn = unknown,\n TParamsIn = unknown,\n TBodyIn = unknown,\n R = unknown,\n>(\n layout: Layout,\n options: ScopedMiddlewareOptions<\n Layout,\n TState,\n TRequires,\n TQuery,\n TParams,\n TBody,\n TReturns,\n TQueryIn,\n TParamsIn,\n TBodyIn,\n TAppContext,\n R\n >,\n): MiddlewareUnit<\n MiddlewareReturnFromParts<\n TQuery,\n TParams,\n TBody,\n IsUnknown<TState> extends true ? ExtractState<R> : TState,\n TQueryIn,\n TParamsIn,\n TBodyIn\n >,\n TReturns,\n Layout,\n TRequires\n>;\n\nexport function defineMiddleware<\n const Layouts extends readonly [LayoutId, ...LayoutId[]],\n TState = unknown,\n TRequires = {},\n TAppContext extends Record<string, unknown> = AppContext,\n TQuery = unknown,\n TParams = unknown,\n TBody = unknown,\n TReturns extends ReturnsMap = {},\n TQueryIn = unknown,\n TParamsIn = unknown,\n TBodyIn = unknown,\n R = unknown,\n>(\n layouts: Layouts,\n options: MultiScopedMiddlewareOptions<\n Layouts,\n TState,\n TRequires,\n TQuery,\n TParams,\n TBody,\n TReturns,\n TQueryIn,\n TParamsIn,\n TBodyIn,\n TAppContext,\n R\n >,\n): MiddlewareUnit<\n MiddlewareReturnFromParts<\n TQuery,\n TParams,\n TBody,\n IsUnknown<TState> extends true ? ExtractState<R> : TState,\n TQueryIn,\n TParamsIn,\n TBodyIn\n >,\n TReturns,\n Layouts,\n TRequires\n>;\n\nexport function defineMiddleware<\n TState = unknown,\n TRequires = {},\n TAppContext extends Record<string, unknown> = AppContext,\n TQuery = unknown,\n TParams = unknown,\n TBody = unknown,\n TReturns extends ReturnsMap = {},\n TQueryIn = unknown,\n TParamsIn = unknown,\n TBodyIn = unknown,\n R = unknown,\n>(\n options: DefineMiddlewareOptions<\n TState,\n TRequires,\n TQuery,\n TParams,\n TBody,\n TReturns,\n TQueryIn,\n TParamsIn,\n TBodyIn,\n TAppContext,\n R\n >,\n): MiddlewareUnit<\n MiddlewareReturnFromParts<\n TQuery,\n TParams,\n TBody,\n IsUnknown<TState> extends true ? ExtractState<R> : TState,\n TQueryIn,\n TParamsIn,\n TBodyIn\n >,\n TReturns,\n null,\n TRequires\n>;\n\nexport function defineMiddleware(\n first:\n | string\n | readonly string[]\n | HonoMiddlewareHandler\n | DefineMiddlewareOptions<any, any, any, any, any, any, any, any, any, any, any>,\n second?: DefineMiddlewareOptions<any, any, any, any, any, any, any, any, any, any, any>,\n): unknown {\n if (typeof first === \"string\" || Array.isArray(first)) {\n const options = second!;\n const unit = {\n ...options,\n __middlewareAcc: undefined as unknown,\n ...(options?.returns ? { __returns: options.returns } : {}),\n __requiredLayouts: first,\n };\n return unit;\n }\n\n if (typeof first === \"function\") {\n const honoMiddleware = first as HonoMiddlewareHandler;\n return defineMiddleware({\n handler: (ctx, next) => createTaserCompatHandler(honoMiddleware)(ctx, next),\n });\n }\n\n const options = first as DefineMiddlewareOptions<\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any\n >;\n const unit = {\n ...options,\n __middlewareAcc: undefined as unknown,\n ...(options.returns ? { __returns: options.returns } : {}),\n };\n\n return unit;\n}\n"],"mappings":";;AAkPA,SAAgB,iBACd,OAKA,QACS;CACT,IAAI,OAAO,UAAU,YAAY,MAAM,QAAQ,KAAK,GAAG;EACrD,MAAM,UAAU;EAOhB,OAAO;GALL,GAAG;GACH,iBAAiB,KAAA;GACjB,GAAI,SAAS,UAAU,EAAE,WAAW,QAAQ,QAAQ,IAAI,CAAC;GACzD,mBAAmB;EAEd;CACT;CAEA,IAAI,OAAO,UAAU,YAAY;EAC/B,MAAM,iBAAiB;EACvB,OAAO,iBAAiB,EACtB,UAAU,KAAK,UAAA,GAAA,qBAAA,yBAAA,CAAkC,cAAc,CAAC,CAAC,KAAK,IAAI,EAC5E,CAAC;CACH;CAEA,MAAM,UAAU;CAmBhB,OAAO;EALL,GAAG;EACH,iBAAiB,KAAA;EACjB,GAAI,QAAQ,UAAU,EAAE,WAAW,QAAQ,QAAQ,IAAI,CAAC;CAGnD;AACT"}
@@ -1,16 +1,33 @@
1
1
  import { createTaserCompatHandler, Awaitable } from '@taserjs/router-core';
2
2
  import { ReturnsMap } from '../types/returns.js';
3
3
  import { AppContext, ExtractState, IsUnknown, MiddlewareReturnFromParts, MiddlewareUnit, NextFn, StandaloneMiddlewareContext } from '../types/units.js';
4
+ import { LayoutId, ResolveLayoutMiddlewaresState, ResolveLayoutsState } from '../types/index.js';
4
5
  import { Schema } from '../types/schema.js';
5
6
  type HonoMiddlewareHandler = Parameters<typeof createTaserCompatHandler>[0];
6
- type HonoMiddlewareUnit = MiddlewareUnit<MiddlewareReturnFromParts<unknown, unknown, unknown, {}>>;
7
- export type DefineMiddlewareOptions<TState = unknown, TQuery = unknown, TParams = unknown, TBody = unknown, TReturns extends ReturnsMap = {}, TQueryIn = unknown, TParamsIn = unknown, TBodyIn = unknown, TAppContext extends Record<string, unknown> = AppContext, R = unknown> = {
7
+ type HonoMiddlewareUnit = MiddlewareUnit<MiddlewareReturnFromParts<unknown, unknown, unknown, {}>, {}, null, {}>;
8
+ export type DefineMiddlewareOptions<TState = unknown, TRequires = {}, TQuery = unknown, TParams = unknown, TBody = unknown, TReturns extends ReturnsMap = {}, TQueryIn = unknown, TParamsIn = unknown, TBodyIn = unknown, TAppContext extends Record<string, unknown> = AppContext, R = unknown> = {
8
9
  query?: Schema<TQuery, TQueryIn>;
9
10
  params?: Schema<TParams, TParamsIn>;
10
11
  body?: Schema<TBody, TBodyIn>;
11
12
  returns?: TReturns;
12
- handler: (ctx: StandaloneMiddlewareContext<TQuery, TParams, TBody, TAppContext>, next: NextFn<NoInfer<TState>>) => Awaitable<R>;
13
+ handler: (ctx: StandaloneMiddlewareContext<TQuery, TParams, TBody, TAppContext, TRequires>, next: NextFn<NoInfer<TState>>) => Awaitable<R>;
14
+ };
15
+ export type ScopedMiddlewareOptions<Layout extends LayoutId, TState = unknown, TRequires = {}, TQuery = unknown, TParams = unknown, TBody = unknown, TReturns extends ReturnsMap = {}, TQueryIn = unknown, TParamsIn = unknown, TBodyIn = unknown, TAppContext extends Record<string, unknown> = AppContext, R = unknown> = {
16
+ query?: Schema<TQuery, TQueryIn>;
17
+ params?: Schema<TParams, TParamsIn>;
18
+ body?: Schema<TBody, TBodyIn>;
19
+ returns?: TReturns;
20
+ handler: (ctx: StandaloneMiddlewareContext<TQuery, TParams, TBody, TAppContext, ResolveLayoutMiddlewaresState<Layout> & TRequires>, next: NextFn<NoInfer<TState>>) => Awaitable<R>;
21
+ };
22
+ export type MultiScopedMiddlewareOptions<Layouts extends readonly LayoutId[], TState = unknown, TRequires = {}, TQuery = unknown, TParams = unknown, TBody = unknown, TReturns extends ReturnsMap = {}, TQueryIn = unknown, TParamsIn = unknown, TBodyIn = unknown, TAppContext extends Record<string, unknown> = AppContext, R = unknown> = {
23
+ query?: Schema<TQuery, TQueryIn>;
24
+ params?: Schema<TParams, TParamsIn>;
25
+ body?: Schema<TBody, TBodyIn>;
26
+ returns?: TReturns;
27
+ handler: (ctx: StandaloneMiddlewareContext<TQuery, TParams, TBody, TAppContext, ResolveLayoutsState<Layouts> & TRequires>, next: NextFn<NoInfer<TState>>) => Awaitable<R>;
13
28
  };
14
29
  export declare function defineMiddleware(middleware: HonoMiddlewareHandler): HonoMiddlewareUnit;
15
- export declare function defineMiddleware<TState = unknown, TAppContext extends Record<string, unknown> = AppContext, TQuery = unknown, TParams = unknown, TBody = unknown, TReturns extends ReturnsMap = {}, TQueryIn = unknown, TParamsIn = unknown, TBodyIn = unknown, R = unknown>(options: DefineMiddlewareOptions<TState, TQuery, TParams, TBody, TReturns, TQueryIn, TParamsIn, TBodyIn, TAppContext, R>): MiddlewareUnit<MiddlewareReturnFromParts<TQuery, TParams, TBody, IsUnknown<TState> extends true ? ExtractState<R> : TState, TQueryIn, TParamsIn, TBodyIn>, TReturns>;
30
+ export declare function defineMiddleware<const Layout extends LayoutId, TState = unknown, TRequires = {}, TAppContext extends Record<string, unknown> = AppContext, TQuery = unknown, TParams = unknown, TBody = unknown, TReturns extends ReturnsMap = {}, TQueryIn = unknown, TParamsIn = unknown, TBodyIn = unknown, R = unknown>(layout: Layout, options: ScopedMiddlewareOptions<Layout, TState, TRequires, TQuery, TParams, TBody, TReturns, TQueryIn, TParamsIn, TBodyIn, TAppContext, R>): MiddlewareUnit<MiddlewareReturnFromParts<TQuery, TParams, TBody, IsUnknown<TState> extends true ? ExtractState<R> : TState, TQueryIn, TParamsIn, TBodyIn>, TReturns, Layout, TRequires>;
31
+ export declare function defineMiddleware<const Layouts extends readonly [LayoutId, ...LayoutId[]], TState = unknown, TRequires = {}, TAppContext extends Record<string, unknown> = AppContext, TQuery = unknown, TParams = unknown, TBody = unknown, TReturns extends ReturnsMap = {}, TQueryIn = unknown, TParamsIn = unknown, TBodyIn = unknown, R = unknown>(layouts: Layouts, options: MultiScopedMiddlewareOptions<Layouts, TState, TRequires, TQuery, TParams, TBody, TReturns, TQueryIn, TParamsIn, TBodyIn, TAppContext, R>): MiddlewareUnit<MiddlewareReturnFromParts<TQuery, TParams, TBody, IsUnknown<TState> extends true ? ExtractState<R> : TState, TQueryIn, TParamsIn, TBodyIn>, TReturns, Layouts, TRequires>;
32
+ export declare function defineMiddleware<TState = unknown, TRequires = {}, TAppContext extends Record<string, unknown> = AppContext, TQuery = unknown, TParams = unknown, TBody = unknown, TReturns extends ReturnsMap = {}, TQueryIn = unknown, TParamsIn = unknown, TBodyIn = unknown, R = unknown>(options: DefineMiddlewareOptions<TState, TRequires, TQuery, TParams, TBody, TReturns, TQueryIn, TParamsIn, TBodyIn, TAppContext, R>): MiddlewareUnit<MiddlewareReturnFromParts<TQuery, TParams, TBody, IsUnknown<TState> extends true ? ExtractState<R> : TState, TQueryIn, TParamsIn, TBodyIn>, TReturns, null, TRequires>;
16
33
  export {};
@@ -1,6 +1,6 @@
1
1
  import { Awaitable, TaserCookieJar, TaserHeaders } from '@taserjs/router-core';
2
2
  import { RouterRegister } from '../register.js';
3
- import { MergeMiddlewareField, MergeMiddlewareInputField, MergePart, RequestShape, RuntimeContextFields, Simplify } from './type-utils.js';
3
+ import { MergeMiddlewareField, MergeMiddlewareInputField, MergePart, RequestShape, RuntimeContextFields, Simplify, UnionToIntersection } from './type-utils.js';
4
4
  import { ReturnsMap, ValidHandlerReply } from './returns.js';
5
5
  import { Schema } from './schema.js';
6
6
  import { AppContext, ExtractState, HandlerContext, HandlerUnit, InlineMiddlewareOptions, Method, MiddlewareDefinition, MiddlewareReturnFromParts, MiddlewareUnit, NativeContext, ValidatorParts } from './units.js';
@@ -36,6 +36,9 @@ type ResolveLayoutMiddlewares<Layout extends LayoutId | null> = Layout extends n
36
36
  ...ResolveLayoutMiddlewares<LayoutParent<Layout>>,
37
37
  ...LayoutTree[Layout]["middlewares"]
38
38
  ] : readonly [];
39
+ export type ResolveLayoutIdChain<L extends LayoutId | null> = L extends null ? readonly [] : L extends LayoutId ? readonly [...ResolveLayoutIdChain<LayoutParent<L>>, L] : readonly [];
40
+ export type ResolveLayoutMiddlewaresState<Layout extends LayoutId | null> = Layout extends LayoutId ? MergeMiddlewareField<ResolveLayoutMiddlewares<Layout>, "state"> : {};
41
+ export type ResolveLayoutsState<Layouts extends LayoutId | readonly LayoutId[]> = Layouts extends LayoutId ? ResolveLayoutMiddlewaresState<Layouts> : Layouts extends readonly LayoutId[] ? UnionToIntersection<Layouts[number] extends infer L extends LayoutId ? ResolveLayoutMiddlewaresState<L> : never> : {};
39
42
  /** Fold layout middleware Acc from a route's layoutChain (shallow → deep). */
40
43
  type ResolveLayoutChainMiddlewares<Chain extends readonly LayoutId[]> = Chain extends readonly [
41
44
  infer Head extends LayoutId,
@@ -104,6 +107,10 @@ export type RouteHandleContextWithoutBody<Path extends RoutePath, TMethod extend
104
107
  type RouteResolvedInputField<Path extends RoutePath, TMethod extends Method, Acc extends readonly unknown[], Field extends "query" | "params" | "body"> = MergeMiddlewareInputField<ResolveLayoutChainMiddlewares<RouteLayoutChain<Path, TMethod>>, Field> & MergeMiddlewareInputField<Acc, Field>;
105
108
  type IsNever<T> = [T] extends [never] ? true : false;
106
109
  type IsEmptyObject<T> = [keyof T] extends [never] ? true : false;
110
+ export type IsLayoutAllowed<TRequiredLayouts, TChain extends readonly LayoutId[]> = [
111
+ TRequiredLayouts
112
+ ] extends [never] ? true : [unknown] extends [TRequiredLayouts] ? true : [null] extends [TRequiredLayouts] ? true : [undefined] extends [TRequiredLayouts] ? true : TRequiredLayouts extends LayoutId ? TRequiredLayouts extends TChain[number] ? true : false : TRequiredLayouts extends readonly LayoutId[] ? [TRequiredLayouts[number] & TChain[number]] extends [never] ? false : true : true;
113
+ export type IsStateSatisfied<TActualState, TRequiredState> = [TRequiredState] extends [never] ? true : [unknown] extends [TRequiredState] ? true : [null] extends [TRequiredState] ? true : [undefined] extends [TRequiredState] ? true : [IsEmptyObject<TRequiredState>] extends [true] ? true : [TActualState] extends [TRequiredState] ? true : false;
107
114
  type InputFacet<T, K extends string> = IsNever<T> extends true ? {} : IsEmptyObject<T> extends true ? {} : {
108
115
  [Key in K]: T;
109
116
  };
@@ -146,7 +153,18 @@ export type MiddlewareBuilder<Layout extends LayoutId, Acc extends readonly unkn
146
153
  readonly $Infer: {
147
154
  Context: MiddlewareChainContext<Layout, Acc, unknown, unknown, unknown, TAppContext>;
148
155
  };
149
- use<TAcc, TReturns extends ReturnsMap = {}>(unit: MiddlewareUnit<TAcc, TReturns>): MiddlewareBuilder<Layout, readonly [...Acc, TAcc], TAppContext>;
156
+ use<TAcc, TReturns extends ReturnsMap = {}, TRequiredLayouts = unknown, TRequiredState = unknown>(unit: MiddlewareUnit<TAcc, TReturns, TRequiredLayouts, TRequiredState>, ..._assert: [
157
+ IsLayoutAllowed<TRequiredLayouts, ResolveLayoutIdChain<Layout>>,
158
+ IsStateSatisfied<MiddlewareChainField<Layout, Acc, "state">, TRequiredState>
159
+ ] extends [true, true] ? [] : [
160
+ {
161
+ error: "Middleware cannot be attached to this layout";
162
+ requiredLayouts: TRequiredLayouts;
163
+ layoutChain: ResolveLayoutIdChain<Layout>;
164
+ requiredState: TRequiredState;
165
+ actualState: MiddlewareChainField<Layout, Acc, "state">;
166
+ }
167
+ ]): MiddlewareBuilder<Layout, readonly [...Acc, TAcc], TAppContext>;
150
168
  use<TQuery = unknown, TParams = unknown, TBody = unknown, TReturns extends ReturnsMap = {}, TQueryIn = unknown, TParamsIn = unknown, TBodyIn = unknown, R = unknown>(options: InlineMiddlewareOptions<MiddlewareChainContext<Layout, Acc, TQuery, TParams, TBody, TAppContext>, TQuery, TParams, TBody, TReturns, TQueryIn, TParamsIn, TBodyIn, R>): MiddlewareBuilder<Layout, readonly [
151
169
  ...Acc,
152
170
  MiddlewareReturnFromParts<TQuery, TParams, TBody, ExtractState<R>, TQueryIn, TParamsIn, TBodyIn>
@@ -186,7 +204,18 @@ export type RouteBuilder<Path extends RoutePath, TMethod extends Method, Acc ext
186
204
  Context: TMethod extends "GET" | "DELETE" | "HEAD" | "OPTIONS" ? RouteHandleContextWithoutBody<Path, TMethod, Acc, Validators, TAppContext> : RouteHandleContext<Path, TMethod, Acc, Validators, TAppContext>;
187
205
  };
188
206
  returns<const M extends ReturnsMap>(map: M): RouteBuilder<Path, TMethod, Acc, Validators, Omit<TReturns, keyof M> & M, TAppContext>;
189
- use<TAcc, UReturns extends ReturnsMap = {}>(unit: MiddlewareUnit<TAcc, UReturns>): RouteBuilder<Path, TMethod, readonly [...Acc, TAcc], Validators, Omit<TReturns, keyof UReturns> & UReturns, TAppContext>;
207
+ use<TAcc, UReturns extends ReturnsMap = {}, TRequiredLayouts = unknown, TRequiredState = unknown>(unit: MiddlewareUnit<TAcc, UReturns, TRequiredLayouts, TRequiredState>, ..._assert: [
208
+ IsLayoutAllowed<TRequiredLayouts, RouteLayoutChain<Path, TMethod>>,
209
+ IsStateSatisfied<RouteResolvedField<Path, TMethod, Acc, "state">, TRequiredState>
210
+ ] extends [true, true] ? [] : [
211
+ {
212
+ error: "Middleware cannot be attached to this route";
213
+ requiredLayouts: TRequiredLayouts;
214
+ routeLayoutChain: RouteLayoutChain<Path, TMethod>;
215
+ requiredState: TRequiredState;
216
+ actualState: RouteResolvedField<Path, TMethod, Acc, "state">;
217
+ }
218
+ ]): RouteBuilder<Path, TMethod, readonly [...Acc, TAcc], Validators, Omit<TReturns, keyof UReturns> & UReturns, TAppContext>;
190
219
  use<TQuery = unknown, TParams = unknown, TBody = unknown, UReturns extends ReturnsMap = {}, TQueryIn = unknown, TParamsIn = unknown, TBodyIn = unknown, R = unknown>(options: InlineMiddlewareOptions<RouteChainContext<Path, TMethod, Acc, TQuery, TParams, TBody, TAppContext>, TQuery, TParams, TBody, UReturns, TQueryIn, TParamsIn, TBodyIn, R>): RouteBuilder<Path, TMethod, readonly [
191
220
  ...Acc,
192
221
  MiddlewareReturnFromParts<TQuery, TParams, TBody, ExtractState<R>, TQueryIn, TParamsIn, TBodyIn>
@@ -201,7 +230,15 @@ export type RouteBuilder<Path extends RoutePath, TMethod extends Method, Acc ext
201
230
  };
202
231
  export type HandlerBuilder<Acc extends readonly unknown[] = readonly [], Validators extends ValidatorParts = {}, TReturns extends ReturnsMap = {}, TAppContext extends Record<string, unknown> = AppContext> = {
203
232
  returns<const M extends ReturnsMap>(map: M): HandlerBuilder<Acc, Validators, Omit<TReturns, keyof M> & M, TAppContext>;
204
- use<TAcc, UReturns extends ReturnsMap = {}>(unit: MiddlewareUnit<TAcc, UReturns>): HandlerBuilder<readonly [...Acc, TAcc], Validators, Omit<TReturns, keyof UReturns> & UReturns, TAppContext>;
233
+ use<TAcc, UReturns extends ReturnsMap = {}, TRequiredLayouts = unknown, TRequiredState = unknown>(unit: MiddlewareUnit<TAcc, UReturns, TRequiredLayouts, TRequiredState>, ..._assert: [IsStateSatisfied<MergeMiddlewareField<Acc, "state">, TRequiredState>] extends [
234
+ true
235
+ ] ? [] : [
236
+ {
237
+ error: "Middleware cannot be attached to this handler";
238
+ requiredState: TRequiredState;
239
+ actualState: MergeMiddlewareField<Acc, "state">;
240
+ }
241
+ ]): HandlerBuilder<readonly [...Acc, TAcc], Validators, Omit<TReturns, keyof UReturns> & UReturns, TAppContext>;
205
242
  use<TQuery = unknown, TParams = unknown, TBody = unknown, UReturns extends ReturnsMap = {}, TQueryIn = unknown, TParamsIn = unknown, TBodyIn = unknown, R = unknown>(options: InlineMiddlewareOptions<HandlerContext<Acc, {
206
243
  query: TQuery;
207
244
  params: TParams;
@@ -1 +1 @@
1
- {"version":3,"file":"units.cjs","names":[],"sources":["../../../src/types/units.ts"],"sourcesContent":["import type { Awaitable, TaserCookieJar, TaserHeaders } from \"@taserjs/router-core\";\nimport type { ReplyResult } from \"@taserjs/router-utils\";\n\nimport type { RouterRegister } from \"../register.js\";\nimport type {\n MergeMiddlewareField,\n MergePart,\n RequestShape,\n RuntimeContextFields,\n Simplify,\n UnwrapPart,\n} from \"./type-utils.js\";\nimport type { ReturnsMap } from \"./returns.js\";\nimport type { Schema } from \"./schema.js\";\n\nexport type { HttpMethod, Method } from \"./type-utils.js\";\nexport type {\n EmptyReturns,\n MergeReturns,\n ReturnsMap,\n HandlerReply,\n HasReturns,\n StatusCode,\n ValidHandlerReply,\n EnforceHandlerReply,\n} from \"./returns.js\";\n\n/** Default empty app context for standalone units without a bound router instance. */\nexport type AppContext = Record<never, never>;\n\nexport type NativeContext = RouterRegister extends { NativeContext: infer N } ? N : unknown;\n\nexport type ValidatorParts = {\n query?: unknown;\n params?: unknown;\n body?: unknown;\n queryIn?: unknown;\n paramsIn?: unknown;\n bodyIn?: unknown;\n};\n\nexport declare const StateBrand: unique symbol;\n\nexport type NextResult<TState = unknown> = ReplyResult & {\n readonly [StateBrand]?: TState;\n};\n\nexport type ExtractState<T> = [Extract<Awaited<T>, { [StateBrand]?: unknown }>] extends [never]\n ? {}\n : Extract<Awaited<T>, { [StateBrand]?: unknown }> extends { [StateBrand]?: infer S }\n ? [S] extends [never]\n ? {}\n : S\n : {};\n\nexport type IsUnknown<T> = [unknown] extends [T] ? true : false;\n\nexport type NextFn<TExpectedState = unknown> =\n IsUnknown<TExpectedState> extends true\n ? <S extends Record<string, unknown> = {}>(state?: S) => Promise<NextResult<S>>\n : (state: TExpectedState) => Promise<NextResult<TExpectedState>>;\n\nexport type MiddlewareNext = NextFn;\n\nexport type MiddlewareDefinition = {\n query?: unknown;\n params?: unknown;\n body?: unknown;\n returns?: ReturnsMap;\n handler: (ctx: unknown, next: MiddlewareNext) => Awaitable<ReplyResult | Response | unknown>;\n};\n\nexport type MiddlewareReturnFromParts<\n TQuery,\n TParams,\n TBody,\n TState,\n TQueryIn = TQuery,\n TParamsIn = TParams,\n TBodyIn = TBody,\n> = {\n query: Simplify<UnwrapPart<TQuery>>;\n params: Simplify<UnwrapPart<TParams>>;\n body: Simplify<UnwrapPart<TBody>>;\n state: Simplify<UnwrapPart<TState>>;\n /** Pre-parse shapes merged into route `$Infer.Input`. */\n input: {\n query: RequestShape<TQueryIn, TQuery>;\n params: RequestShape<TParamsIn, TParams>;\n body: RequestShape<TBodyIn, TBody>;\n };\n};\n\ntype UnitRuntimeContext = Omit<RuntimeContextFields<NativeContext>, \"var\">;\n\nexport type StandaloneMiddlewareContext<\n TQuery = unknown,\n TParams = unknown,\n TBody = unknown,\n TAppContext extends Record<string, unknown> = AppContext,\n> = Simplify<\n TAppContext &\n UnitRuntimeContext & {\n query: Simplify<UnwrapPart<TQuery>>;\n params: Simplify<UnwrapPart<TParams>>;\n body: Simplify<UnwrapPart<TBody>>;\n state: {};\n headers: TaserHeaders;\n cookies: TaserCookieJar;\n }\n>;\n\nexport type HandlerContext<\n Acc extends readonly unknown[],\n Validators extends ValidatorParts,\n TAppContext extends Record<string, unknown> = AppContext,\n> = Simplify<\n TAppContext &\n UnitRuntimeContext & {\n query: Simplify<\n MergePart<\n Validators extends { query?: infer Q } ? Q : unknown,\n MergeMiddlewareField<Acc, \"query\">\n >\n >;\n params: Simplify<\n MergePart<\n Validators extends { params?: infer P } ? P : unknown,\n MergeMiddlewareField<Acc, \"params\">\n >\n >;\n body: Simplify<\n MergePart<\n Validators extends { body?: infer B } ? B : unknown,\n MergeMiddlewareField<Acc, \"body\">\n >\n >;\n state: Simplify<MergeMiddlewareField<Acc, \"state\">>;\n headers: TaserHeaders;\n cookies: TaserCookieJar;\n }\n>;\n\nexport type MiddlewareUnit<\n TAcc = unknown,\n TReturns extends ReturnsMap = {},\n> = MiddlewareDefinition & {\n readonly __middlewareAcc: TAcc;\n readonly __returns?: TReturns;\n};\n\nexport type HandlerUnit<\n _Acc extends readonly unknown[],\n _Validators extends ValidatorParts,\n TReturns extends ReturnsMap = {},\n TOutput = Response,\n> = {\n readonly __returns?: TReturns;\n readonly $Infer: {\n Output: TOutput;\n };\n middlewares: readonly MiddlewareDefinition[];\n handler: (ctx: unknown) => Awaitable<ReplyResult | Response>;\n returns?: ReturnsMap;\n query?: Schema<unknown>;\n params?: Schema<unknown>;\n body?: Schema<unknown>;\n};\n\nexport type InlineMiddlewareOptions<\n Ctx = unknown,\n TQuery = unknown,\n TParams = unknown,\n TBody = unknown,\n TReturns extends ReturnsMap = {},\n TQueryIn = unknown,\n TParamsIn = unknown,\n TBodyIn = unknown,\n R = unknown,\n> = {\n query?: Schema<TQuery, TQueryIn>;\n params?: Schema<TParams, TParamsIn>;\n body?: Schema<TBody, TBodyIn>;\n returns?: TReturns;\n handler: (ctx: Ctx, next: NextFn) => Awaitable<R>;\n};\n\nexport function isHandlerUnit(\n value: unknown,\n): value is HandlerUnit<readonly unknown[], ValidatorParts> {\n return (\n typeof value === \"object\" &&\n value !== null &&\n \"handler\" in value &&\n \"middlewares\" in value &&\n Array.isArray((value as HandlerUnit<readonly unknown[], ValidatorParts>).middlewares)\n );\n}\n"],"mappings":";AA2LA,SAAgB,cACd,OAC0D;CAC1D,OACE,OAAO,UAAU,YACjB,UAAU,QACV,aAAa,SACb,iBAAiB,SACjB,MAAM,QAAS,MAA0D,WAAW;AAExF"}
1
+ {"version":3,"file":"units.cjs","names":[],"sources":["../../../src/types/units.ts"],"sourcesContent":["import type { Awaitable, TaserCookieJar, TaserHeaders } from \"@taserjs/router-core\";\nimport type { ReplyResult } from \"@taserjs/router-utils\";\n\nimport type { RouterRegister } from \"../register.js\";\nimport type {\n MergeMiddlewareField,\n MergePart,\n RequestShape,\n RuntimeContextFields,\n Simplify,\n UnwrapPart,\n} from \"./type-utils.js\";\nimport type { ReturnsMap } from \"./returns.js\";\nimport type { Schema } from \"./schema.js\";\n\nexport type { HttpMethod, Method } from \"./type-utils.js\";\nexport type {\n EmptyReturns,\n MergeReturns,\n ReturnsMap,\n HandlerReply,\n HasReturns,\n StatusCode,\n ValidHandlerReply,\n EnforceHandlerReply,\n} from \"./returns.js\";\n\n/** Default empty app context for standalone units without a bound router instance. */\nexport type AppContext = Record<never, never>;\n\nexport type NativeContext = RouterRegister extends { NativeContext: infer N } ? N : unknown;\n\nexport type ValidatorParts = {\n query?: unknown;\n params?: unknown;\n body?: unknown;\n queryIn?: unknown;\n paramsIn?: unknown;\n bodyIn?: unknown;\n};\n\nexport declare const StateBrand: unique symbol;\n\nexport type NextResult<TState = unknown> = ReplyResult & {\n readonly [StateBrand]?: TState;\n};\n\nexport type ExtractState<T> = [Extract<Awaited<T>, { [StateBrand]?: unknown }>] extends [never]\n ? {}\n : Extract<Awaited<T>, { [StateBrand]?: unknown }> extends { [StateBrand]?: infer S }\n ? [S] extends [never]\n ? {}\n : S\n : {};\n\nexport type IsUnknown<T> = [unknown] extends [T] ? true : false;\n\nexport type NextFn<TExpectedState = unknown> =\n IsUnknown<TExpectedState> extends true\n ? <S extends Record<string, unknown> = {}>(state?: S) => Promise<NextResult<S>>\n : (state: TExpectedState) => Promise<NextResult<TExpectedState>>;\n\nexport type MiddlewareNext = NextFn;\n\nexport type MiddlewareDefinition = {\n query?: unknown;\n params?: unknown;\n body?: unknown;\n returns?: ReturnsMap;\n handler: (ctx: unknown, next: MiddlewareNext) => Awaitable<ReplyResult | Response | unknown>;\n};\n\nexport type MiddlewareReturnFromParts<\n TQuery,\n TParams,\n TBody,\n TState,\n TQueryIn = TQuery,\n TParamsIn = TParams,\n TBodyIn = TBody,\n> = {\n query: Simplify<UnwrapPart<TQuery>>;\n params: Simplify<UnwrapPart<TParams>>;\n body: Simplify<UnwrapPart<TBody>>;\n state: Simplify<UnwrapPart<TState>>;\n /** Pre-parse shapes merged into route `$Infer.Input`. */\n input: {\n query: RequestShape<TQueryIn, TQuery>;\n params: RequestShape<TParamsIn, TParams>;\n body: RequestShape<TBodyIn, TBody>;\n };\n};\n\ntype UnitRuntimeContext = Omit<RuntimeContextFields<NativeContext>, \"var\">;\n\nexport type StandaloneMiddlewareContext<\n TQuery = unknown,\n TParams = unknown,\n TBody = unknown,\n TAppContext extends Record<string, unknown> = AppContext,\n TState = {},\n> = Simplify<\n TAppContext &\n UnitRuntimeContext & {\n query: Simplify<UnwrapPart<TQuery>>;\n params: Simplify<UnwrapPart<TParams>>;\n body: Simplify<UnwrapPart<TBody>>;\n state: Simplify<UnwrapPart<TState>>;\n headers: TaserHeaders;\n cookies: TaserCookieJar;\n }\n>;\n\nexport type HandlerContext<\n Acc extends readonly unknown[],\n Validators extends ValidatorParts,\n TAppContext extends Record<string, unknown> = AppContext,\n> = Simplify<\n TAppContext &\n UnitRuntimeContext & {\n query: Simplify<\n MergePart<\n Validators extends { query?: infer Q } ? Q : unknown,\n MergeMiddlewareField<Acc, \"query\">\n >\n >;\n params: Simplify<\n MergePart<\n Validators extends { params?: infer P } ? P : unknown,\n MergeMiddlewareField<Acc, \"params\">\n >\n >;\n body: Simplify<\n MergePart<\n Validators extends { body?: infer B } ? B : unknown,\n MergeMiddlewareField<Acc, \"body\">\n >\n >;\n state: Simplify<MergeMiddlewareField<Acc, \"state\">>;\n headers: TaserHeaders;\n cookies: TaserCookieJar;\n }\n>;\n\nexport type MiddlewareUnit<\n TAcc = unknown,\n TReturns extends ReturnsMap = {},\n TRequiredLayouts = unknown,\n TRequiredState = unknown,\n> = MiddlewareDefinition & {\n readonly __middlewareAcc: TAcc;\n readonly __returns?: TReturns;\n readonly __requiredLayouts?: TRequiredLayouts;\n readonly __requiredState?: TRequiredState;\n};\n\nexport type HandlerUnit<\n _Acc extends readonly unknown[],\n _Validators extends ValidatorParts,\n TReturns extends ReturnsMap = {},\n TOutput = Response,\n> = {\n readonly __returns?: TReturns;\n readonly $Infer: {\n Output: TOutput;\n };\n middlewares: readonly MiddlewareDefinition[];\n handler: (ctx: unknown) => Awaitable<ReplyResult | Response>;\n returns?: ReturnsMap;\n query?: Schema<unknown>;\n params?: Schema<unknown>;\n body?: Schema<unknown>;\n};\n\nexport type InlineMiddlewareOptions<\n Ctx = unknown,\n TQuery = unknown,\n TParams = unknown,\n TBody = unknown,\n TReturns extends ReturnsMap = {},\n TQueryIn = unknown,\n TParamsIn = unknown,\n TBodyIn = unknown,\n R = unknown,\n> = {\n query?: Schema<TQuery, TQueryIn>;\n params?: Schema<TParams, TParamsIn>;\n body?: Schema<TBody, TBodyIn>;\n returns?: TReturns;\n handler: (ctx: Ctx, next: NextFn) => Awaitable<R>;\n};\n\nexport function isHandlerUnit(\n value: unknown,\n): value is HandlerUnit<readonly unknown[], ValidatorParts> {\n return (\n typeof value === \"object\" &&\n value !== null &&\n \"handler\" in value &&\n \"middlewares\" in value &&\n Array.isArray((value as HandlerUnit<readonly unknown[], ValidatorParts>).middlewares)\n );\n}\n"],"mappings":";AAgMA,SAAgB,cACd,OAC0D;CAC1D,OACE,OAAO,UAAU,YACjB,UAAU,QACV,aAAa,SACb,iBAAiB,SACjB,MAAM,QAAS,MAA0D,WAAW;AAExF"}
@@ -53,11 +53,11 @@ export type MiddlewareReturnFromParts<TQuery, TParams, TBody, TState, TQueryIn =
53
53
  };
54
54
  };
55
55
  type UnitRuntimeContext = Omit<RuntimeContextFields<NativeContext>, "var">;
56
- export type StandaloneMiddlewareContext<TQuery = unknown, TParams = unknown, TBody = unknown, TAppContext extends Record<string, unknown> = AppContext> = Simplify<TAppContext & UnitRuntimeContext & {
56
+ export type StandaloneMiddlewareContext<TQuery = unknown, TParams = unknown, TBody = unknown, TAppContext extends Record<string, unknown> = AppContext, TState = {}> = Simplify<TAppContext & UnitRuntimeContext & {
57
57
  query: Simplify<UnwrapPart<TQuery>>;
58
58
  params: Simplify<UnwrapPart<TParams>>;
59
59
  body: Simplify<UnwrapPart<TBody>>;
60
- state: {};
60
+ state: Simplify<UnwrapPart<TState>>;
61
61
  headers: TaserHeaders;
62
62
  cookies: TaserCookieJar;
63
63
  }>;
@@ -75,9 +75,11 @@ export type HandlerContext<Acc extends readonly unknown[], Validators extends Va
75
75
  headers: TaserHeaders;
76
76
  cookies: TaserCookieJar;
77
77
  }>;
78
- export type MiddlewareUnit<TAcc = unknown, TReturns extends ReturnsMap = {}> = MiddlewareDefinition & {
78
+ export type MiddlewareUnit<TAcc = unknown, TReturns extends ReturnsMap = {}, TRequiredLayouts = unknown, TRequiredState = unknown> = MiddlewareDefinition & {
79
79
  readonly __middlewareAcc: TAcc;
80
80
  readonly __returns?: TReturns;
81
+ readonly __requiredLayouts?: TRequiredLayouts;
82
+ readonly __requiredState?: TRequiredState;
81
83
  };
82
84
  export type HandlerUnit<_Acc extends readonly unknown[], _Validators extends ValidatorParts, TReturns extends ReturnsMap = {}, TOutput = Response> = {
83
85
  readonly __returns?: TReturns;
@@ -1,16 +1,33 @@
1
1
  import { createTaserCompatHandler, Awaitable } from '@taserjs/router-core';
2
2
  import { ReturnsMap } from '../types/returns.js';
3
3
  import { AppContext, ExtractState, IsUnknown, MiddlewareReturnFromParts, MiddlewareUnit, NextFn, StandaloneMiddlewareContext } from '../types/units.js';
4
+ import { LayoutId, ResolveLayoutMiddlewaresState, ResolveLayoutsState } from '../types/index.js';
4
5
  import { Schema } from '../types/schema.js';
5
6
  type HonoMiddlewareHandler = Parameters<typeof createTaserCompatHandler>[0];
6
- type HonoMiddlewareUnit = MiddlewareUnit<MiddlewareReturnFromParts<unknown, unknown, unknown, {}>>;
7
- export type DefineMiddlewareOptions<TState = unknown, TQuery = unknown, TParams = unknown, TBody = unknown, TReturns extends ReturnsMap = {}, TQueryIn = unknown, TParamsIn = unknown, TBodyIn = unknown, TAppContext extends Record<string, unknown> = AppContext, R = unknown> = {
7
+ type HonoMiddlewareUnit = MiddlewareUnit<MiddlewareReturnFromParts<unknown, unknown, unknown, {}>, {}, null, {}>;
8
+ export type DefineMiddlewareOptions<TState = unknown, TRequires = {}, TQuery = unknown, TParams = unknown, TBody = unknown, TReturns extends ReturnsMap = {}, TQueryIn = unknown, TParamsIn = unknown, TBodyIn = unknown, TAppContext extends Record<string, unknown> = AppContext, R = unknown> = {
8
9
  query?: Schema<TQuery, TQueryIn>;
9
10
  params?: Schema<TParams, TParamsIn>;
10
11
  body?: Schema<TBody, TBodyIn>;
11
12
  returns?: TReturns;
12
- handler: (ctx: StandaloneMiddlewareContext<TQuery, TParams, TBody, TAppContext>, next: NextFn<NoInfer<TState>>) => Awaitable<R>;
13
+ handler: (ctx: StandaloneMiddlewareContext<TQuery, TParams, TBody, TAppContext, TRequires>, next: NextFn<NoInfer<TState>>) => Awaitable<R>;
14
+ };
15
+ export type ScopedMiddlewareOptions<Layout extends LayoutId, TState = unknown, TRequires = {}, TQuery = unknown, TParams = unknown, TBody = unknown, TReturns extends ReturnsMap = {}, TQueryIn = unknown, TParamsIn = unknown, TBodyIn = unknown, TAppContext extends Record<string, unknown> = AppContext, R = unknown> = {
16
+ query?: Schema<TQuery, TQueryIn>;
17
+ params?: Schema<TParams, TParamsIn>;
18
+ body?: Schema<TBody, TBodyIn>;
19
+ returns?: TReturns;
20
+ handler: (ctx: StandaloneMiddlewareContext<TQuery, TParams, TBody, TAppContext, ResolveLayoutMiddlewaresState<Layout> & TRequires>, next: NextFn<NoInfer<TState>>) => Awaitable<R>;
21
+ };
22
+ export type MultiScopedMiddlewareOptions<Layouts extends readonly LayoutId[], TState = unknown, TRequires = {}, TQuery = unknown, TParams = unknown, TBody = unknown, TReturns extends ReturnsMap = {}, TQueryIn = unknown, TParamsIn = unknown, TBodyIn = unknown, TAppContext extends Record<string, unknown> = AppContext, R = unknown> = {
23
+ query?: Schema<TQuery, TQueryIn>;
24
+ params?: Schema<TParams, TParamsIn>;
25
+ body?: Schema<TBody, TBodyIn>;
26
+ returns?: TReturns;
27
+ handler: (ctx: StandaloneMiddlewareContext<TQuery, TParams, TBody, TAppContext, ResolveLayoutsState<Layouts> & TRequires>, next: NextFn<NoInfer<TState>>) => Awaitable<R>;
13
28
  };
14
29
  export declare function defineMiddleware(middleware: HonoMiddlewareHandler): HonoMiddlewareUnit;
15
- export declare function defineMiddleware<TState = unknown, TAppContext extends Record<string, unknown> = AppContext, TQuery = unknown, TParams = unknown, TBody = unknown, TReturns extends ReturnsMap = {}, TQueryIn = unknown, TParamsIn = unknown, TBodyIn = unknown, R = unknown>(options: DefineMiddlewareOptions<TState, TQuery, TParams, TBody, TReturns, TQueryIn, TParamsIn, TBodyIn, TAppContext, R>): MiddlewareUnit<MiddlewareReturnFromParts<TQuery, TParams, TBody, IsUnknown<TState> extends true ? ExtractState<R> : TState, TQueryIn, TParamsIn, TBodyIn>, TReturns>;
30
+ export declare function defineMiddleware<const Layout extends LayoutId, TState = unknown, TRequires = {}, TAppContext extends Record<string, unknown> = AppContext, TQuery = unknown, TParams = unknown, TBody = unknown, TReturns extends ReturnsMap = {}, TQueryIn = unknown, TParamsIn = unknown, TBodyIn = unknown, R = unknown>(layout: Layout, options: ScopedMiddlewareOptions<Layout, TState, TRequires, TQuery, TParams, TBody, TReturns, TQueryIn, TParamsIn, TBodyIn, TAppContext, R>): MiddlewareUnit<MiddlewareReturnFromParts<TQuery, TParams, TBody, IsUnknown<TState> extends true ? ExtractState<R> : TState, TQueryIn, TParamsIn, TBodyIn>, TReturns, Layout, TRequires>;
31
+ export declare function defineMiddleware<const Layouts extends readonly [LayoutId, ...LayoutId[]], TState = unknown, TRequires = {}, TAppContext extends Record<string, unknown> = AppContext, TQuery = unknown, TParams = unknown, TBody = unknown, TReturns extends ReturnsMap = {}, TQueryIn = unknown, TParamsIn = unknown, TBodyIn = unknown, R = unknown>(layouts: Layouts, options: MultiScopedMiddlewareOptions<Layouts, TState, TRequires, TQuery, TParams, TBody, TReturns, TQueryIn, TParamsIn, TBodyIn, TAppContext, R>): MiddlewareUnit<MiddlewareReturnFromParts<TQuery, TParams, TBody, IsUnknown<TState> extends true ? ExtractState<R> : TState, TQueryIn, TParamsIn, TBodyIn>, TReturns, Layouts, TRequires>;
32
+ export declare function defineMiddleware<TState = unknown, TRequires = {}, TAppContext extends Record<string, unknown> = AppContext, TQuery = unknown, TParams = unknown, TBody = unknown, TReturns extends ReturnsMap = {}, TQueryIn = unknown, TParamsIn = unknown, TBodyIn = unknown, R = unknown>(options: DefineMiddlewareOptions<TState, TRequires, TQuery, TParams, TBody, TReturns, TQueryIn, TParamsIn, TBodyIn, TAppContext, R>): MiddlewareUnit<MiddlewareReturnFromParts<TQuery, TParams, TBody, IsUnknown<TState> extends true ? ExtractState<R> : TState, TQueryIn, TParamsIn, TBodyIn>, TReturns, null, TRequires>;
16
33
  export {};
@@ -1,10 +1,20 @@
1
1
  import { createTaserCompatHandler } from "@taserjs/router-core";
2
2
  //#region src/define/middleware.ts
3
- function defineMiddleware(options) {
4
- if (typeof options === "function") {
5
- const honoMiddleware = options;
3
+ function defineMiddleware(first, second) {
4
+ if (typeof first === "string" || Array.isArray(first)) {
5
+ const options = second;
6
+ return {
7
+ ...options,
8
+ __middlewareAcc: void 0,
9
+ ...options?.returns ? { __returns: options.returns } : {},
10
+ __requiredLayouts: first
11
+ };
12
+ }
13
+ if (typeof first === "function") {
14
+ const honoMiddleware = first;
6
15
  return defineMiddleware({ handler: (ctx, next) => createTaserCompatHandler(honoMiddleware)(ctx, next) });
7
16
  }
17
+ const options = first;
8
18
  return {
9
19
  ...options,
10
20
  __middlewareAcc: void 0,
@@ -1 +1 @@
1
- {"version":3,"file":"middleware.js","names":[],"sources":["../../../src/define/middleware.ts"],"sourcesContent":["import { createTaserCompatHandler, type Awaitable } from \"@taserjs/router-core\";\n\nimport type { ReturnsMap } from \"../types/returns.js\";\nimport type {\n AppContext,\n ExtractState,\n IsUnknown,\n MiddlewareReturnFromParts,\n MiddlewareUnit,\n NextFn,\n StandaloneMiddlewareContext,\n} from \"../types/units.js\";\nimport type { Schema } from \"../types/schema.js\";\n\ntype HonoMiddlewareHandler = Parameters<typeof createTaserCompatHandler>[0];\n\ntype HonoMiddlewareUnit = MiddlewareUnit<MiddlewareReturnFromParts<unknown, unknown, unknown, {}>>;\n\nexport type DefineMiddlewareOptions<\n TState = unknown,\n TQuery = unknown,\n TParams = unknown,\n TBody = unknown,\n TReturns extends ReturnsMap = {},\n TQueryIn = unknown,\n TParamsIn = unknown,\n TBodyIn = unknown,\n TAppContext extends Record<string, unknown> = AppContext,\n R = unknown,\n> = {\n query?: Schema<TQuery, TQueryIn>;\n params?: Schema<TParams, TParamsIn>;\n body?: Schema<TBody, TBodyIn>;\n returns?: TReturns;\n handler: (\n ctx: StandaloneMiddlewareContext<TQuery, TParams, TBody, TAppContext>,\n next: NextFn<NoInfer<TState>>,\n ) => Awaitable<R>;\n};\n\nexport function defineMiddleware(middleware: HonoMiddlewareHandler): HonoMiddlewareUnit;\n\nexport function defineMiddleware<\n TState = unknown,\n TAppContext extends Record<string, unknown> = AppContext,\n TQuery = unknown,\n TParams = unknown,\n TBody = unknown,\n TReturns extends ReturnsMap = {},\n TQueryIn = unknown,\n TParamsIn = unknown,\n TBodyIn = unknown,\n R = unknown,\n>(\n options: DefineMiddlewareOptions<\n TState,\n TQuery,\n TParams,\n TBody,\n TReturns,\n TQueryIn,\n TParamsIn,\n TBodyIn,\n TAppContext,\n R\n >,\n): MiddlewareUnit<\n MiddlewareReturnFromParts<\n TQuery,\n TParams,\n TBody,\n IsUnknown<TState> extends true ? ExtractState<R> : TState,\n TQueryIn,\n TParamsIn,\n TBodyIn\n >,\n TReturns\n>;\n\nexport function defineMiddleware(\n options:\n | HonoMiddlewareHandler\n | DefineMiddlewareOptions<any, any, any, any, any, any, any, any, any, any>,\n): unknown {\n if (typeof options === \"function\") {\n const honoMiddleware = options as HonoMiddlewareHandler;\n return defineMiddleware({\n handler: (ctx, next) => createTaserCompatHandler(honoMiddleware)(ctx, next),\n });\n }\n\n const unit = {\n ...options,\n __middlewareAcc: undefined as unknown,\n ...(options.returns ? { __returns: options.returns } : {}),\n };\n\n return unit;\n}\n"],"mappings":";;AA+EA,SAAgB,iBACd,SAGS;CACT,IAAI,OAAO,YAAY,YAAY;EACjC,MAAM,iBAAiB;EACvB,OAAO,iBAAiB,EACtB,UAAU,KAAK,SAAS,yBAAyB,cAAc,CAAC,CAAC,KAAK,IAAI,EAC5E,CAAC;CACH;CAQA,OAAO;EALL,GAAG;EACH,iBAAiB,KAAA;EACjB,GAAI,QAAQ,UAAU,EAAE,WAAW,QAAQ,QAAQ,IAAI,CAAC;CAGnD;AACT"}
1
+ {"version":3,"file":"middleware.js","names":[],"sources":["../../../src/define/middleware.ts"],"sourcesContent":["import { createTaserCompatHandler, type Awaitable } from \"@taserjs/router-core\";\n\nimport type { ReturnsMap } from \"../types/returns.js\";\nimport type {\n AppContext,\n ExtractState,\n IsUnknown,\n MiddlewareReturnFromParts,\n MiddlewareUnit,\n NextFn,\n StandaloneMiddlewareContext,\n} from \"../types/units.js\";\nimport type {\n LayoutId,\n ResolveLayoutMiddlewaresState,\n ResolveLayoutsState,\n} from \"../types/index.js\";\nimport type { Schema } from \"../types/schema.js\";\n\ntype HonoMiddlewareHandler = Parameters<typeof createTaserCompatHandler>[0];\n\ntype HonoMiddlewareUnit = MiddlewareUnit<\n MiddlewareReturnFromParts<unknown, unknown, unknown, {}>,\n {},\n null,\n {}\n>;\n\nexport type DefineMiddlewareOptions<\n TState = unknown,\n TRequires = {},\n TQuery = unknown,\n TParams = unknown,\n TBody = unknown,\n TReturns extends ReturnsMap = {},\n TQueryIn = unknown,\n TParamsIn = unknown,\n TBodyIn = unknown,\n TAppContext extends Record<string, unknown> = AppContext,\n R = unknown,\n> = {\n query?: Schema<TQuery, TQueryIn>;\n params?: Schema<TParams, TParamsIn>;\n body?: Schema<TBody, TBodyIn>;\n returns?: TReturns;\n handler: (\n ctx: StandaloneMiddlewareContext<TQuery, TParams, TBody, TAppContext, TRequires>,\n next: NextFn<NoInfer<TState>>,\n ) => Awaitable<R>;\n};\n\nexport type ScopedMiddlewareOptions<\n Layout extends LayoutId,\n TState = unknown,\n TRequires = {},\n TQuery = unknown,\n TParams = unknown,\n TBody = unknown,\n TReturns extends ReturnsMap = {},\n TQueryIn = unknown,\n TParamsIn = unknown,\n TBodyIn = unknown,\n TAppContext extends Record<string, unknown> = AppContext,\n R = unknown,\n> = {\n query?: Schema<TQuery, TQueryIn>;\n params?: Schema<TParams, TParamsIn>;\n body?: Schema<TBody, TBodyIn>;\n returns?: TReturns;\n handler: (\n ctx: StandaloneMiddlewareContext<\n TQuery,\n TParams,\n TBody,\n TAppContext,\n ResolveLayoutMiddlewaresState<Layout> & TRequires\n >,\n next: NextFn<NoInfer<TState>>,\n ) => Awaitable<R>;\n};\n\nexport type MultiScopedMiddlewareOptions<\n Layouts extends readonly LayoutId[],\n TState = unknown,\n TRequires = {},\n TQuery = unknown,\n TParams = unknown,\n TBody = unknown,\n TReturns extends ReturnsMap = {},\n TQueryIn = unknown,\n TParamsIn = unknown,\n TBodyIn = unknown,\n TAppContext extends Record<string, unknown> = AppContext,\n R = unknown,\n> = {\n query?: Schema<TQuery, TQueryIn>;\n params?: Schema<TParams, TParamsIn>;\n body?: Schema<TBody, TBodyIn>;\n returns?: TReturns;\n handler: (\n ctx: StandaloneMiddlewareContext<\n TQuery,\n TParams,\n TBody,\n TAppContext,\n ResolveLayoutsState<Layouts> & TRequires\n >,\n next: NextFn<NoInfer<TState>>,\n ) => Awaitable<R>;\n};\n\nexport function defineMiddleware(middleware: HonoMiddlewareHandler): HonoMiddlewareUnit;\n\nexport function defineMiddleware<\n const Layout extends LayoutId,\n TState = unknown,\n TRequires = {},\n TAppContext extends Record<string, unknown> = AppContext,\n TQuery = unknown,\n TParams = unknown,\n TBody = unknown,\n TReturns extends ReturnsMap = {},\n TQueryIn = unknown,\n TParamsIn = unknown,\n TBodyIn = unknown,\n R = unknown,\n>(\n layout: Layout,\n options: ScopedMiddlewareOptions<\n Layout,\n TState,\n TRequires,\n TQuery,\n TParams,\n TBody,\n TReturns,\n TQueryIn,\n TParamsIn,\n TBodyIn,\n TAppContext,\n R\n >,\n): MiddlewareUnit<\n MiddlewareReturnFromParts<\n TQuery,\n TParams,\n TBody,\n IsUnknown<TState> extends true ? ExtractState<R> : TState,\n TQueryIn,\n TParamsIn,\n TBodyIn\n >,\n TReturns,\n Layout,\n TRequires\n>;\n\nexport function defineMiddleware<\n const Layouts extends readonly [LayoutId, ...LayoutId[]],\n TState = unknown,\n TRequires = {},\n TAppContext extends Record<string, unknown> = AppContext,\n TQuery = unknown,\n TParams = unknown,\n TBody = unknown,\n TReturns extends ReturnsMap = {},\n TQueryIn = unknown,\n TParamsIn = unknown,\n TBodyIn = unknown,\n R = unknown,\n>(\n layouts: Layouts,\n options: MultiScopedMiddlewareOptions<\n Layouts,\n TState,\n TRequires,\n TQuery,\n TParams,\n TBody,\n TReturns,\n TQueryIn,\n TParamsIn,\n TBodyIn,\n TAppContext,\n R\n >,\n): MiddlewareUnit<\n MiddlewareReturnFromParts<\n TQuery,\n TParams,\n TBody,\n IsUnknown<TState> extends true ? ExtractState<R> : TState,\n TQueryIn,\n TParamsIn,\n TBodyIn\n >,\n TReturns,\n Layouts,\n TRequires\n>;\n\nexport function defineMiddleware<\n TState = unknown,\n TRequires = {},\n TAppContext extends Record<string, unknown> = AppContext,\n TQuery = unknown,\n TParams = unknown,\n TBody = unknown,\n TReturns extends ReturnsMap = {},\n TQueryIn = unknown,\n TParamsIn = unknown,\n TBodyIn = unknown,\n R = unknown,\n>(\n options: DefineMiddlewareOptions<\n TState,\n TRequires,\n TQuery,\n TParams,\n TBody,\n TReturns,\n TQueryIn,\n TParamsIn,\n TBodyIn,\n TAppContext,\n R\n >,\n): MiddlewareUnit<\n MiddlewareReturnFromParts<\n TQuery,\n TParams,\n TBody,\n IsUnknown<TState> extends true ? ExtractState<R> : TState,\n TQueryIn,\n TParamsIn,\n TBodyIn\n >,\n TReturns,\n null,\n TRequires\n>;\n\nexport function defineMiddleware(\n first:\n | string\n | readonly string[]\n | HonoMiddlewareHandler\n | DefineMiddlewareOptions<any, any, any, any, any, any, any, any, any, any, any>,\n second?: DefineMiddlewareOptions<any, any, any, any, any, any, any, any, any, any, any>,\n): unknown {\n if (typeof first === \"string\" || Array.isArray(first)) {\n const options = second!;\n const unit = {\n ...options,\n __middlewareAcc: undefined as unknown,\n ...(options?.returns ? { __returns: options.returns } : {}),\n __requiredLayouts: first,\n };\n return unit;\n }\n\n if (typeof first === \"function\") {\n const honoMiddleware = first as HonoMiddlewareHandler;\n return defineMiddleware({\n handler: (ctx, next) => createTaserCompatHandler(honoMiddleware)(ctx, next),\n });\n }\n\n const options = first as DefineMiddlewareOptions<\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any\n >;\n const unit = {\n ...options,\n __middlewareAcc: undefined as unknown,\n ...(options.returns ? { __returns: options.returns } : {}),\n };\n\n return unit;\n}\n"],"mappings":";;AAkPA,SAAgB,iBACd,OAKA,QACS;CACT,IAAI,OAAO,UAAU,YAAY,MAAM,QAAQ,KAAK,GAAG;EACrD,MAAM,UAAU;EAOhB,OAAO;GALL,GAAG;GACH,iBAAiB,KAAA;GACjB,GAAI,SAAS,UAAU,EAAE,WAAW,QAAQ,QAAQ,IAAI,CAAC;GACzD,mBAAmB;EAEd;CACT;CAEA,IAAI,OAAO,UAAU,YAAY;EAC/B,MAAM,iBAAiB;EACvB,OAAO,iBAAiB,EACtB,UAAU,KAAK,SAAS,yBAAyB,cAAc,CAAC,CAAC,KAAK,IAAI,EAC5E,CAAC;CACH;CAEA,MAAM,UAAU;CAmBhB,OAAO;EALL,GAAG;EACH,iBAAiB,KAAA;EACjB,GAAI,QAAQ,UAAU,EAAE,WAAW,QAAQ,QAAQ,IAAI,CAAC;CAGnD;AACT"}
@@ -1,6 +1,6 @@
1
1
  import { Awaitable, TaserCookieJar, TaserHeaders } from '@taserjs/router-core';
2
2
  import { RouterRegister } from '../register.js';
3
- import { MergeMiddlewareField, MergeMiddlewareInputField, MergePart, RequestShape, RuntimeContextFields, Simplify } from './type-utils.js';
3
+ import { MergeMiddlewareField, MergeMiddlewareInputField, MergePart, RequestShape, RuntimeContextFields, Simplify, UnionToIntersection } from './type-utils.js';
4
4
  import { ReturnsMap, ValidHandlerReply } from './returns.js';
5
5
  import { Schema } from './schema.js';
6
6
  import { AppContext, ExtractState, HandlerContext, HandlerUnit, InlineMiddlewareOptions, Method, MiddlewareDefinition, MiddlewareReturnFromParts, MiddlewareUnit, NativeContext, ValidatorParts } from './units.js';
@@ -36,6 +36,9 @@ type ResolveLayoutMiddlewares<Layout extends LayoutId | null> = Layout extends n
36
36
  ...ResolveLayoutMiddlewares<LayoutParent<Layout>>,
37
37
  ...LayoutTree[Layout]["middlewares"]
38
38
  ] : readonly [];
39
+ export type ResolveLayoutIdChain<L extends LayoutId | null> = L extends null ? readonly [] : L extends LayoutId ? readonly [...ResolveLayoutIdChain<LayoutParent<L>>, L] : readonly [];
40
+ export type ResolveLayoutMiddlewaresState<Layout extends LayoutId | null> = Layout extends LayoutId ? MergeMiddlewareField<ResolveLayoutMiddlewares<Layout>, "state"> : {};
41
+ export type ResolveLayoutsState<Layouts extends LayoutId | readonly LayoutId[]> = Layouts extends LayoutId ? ResolveLayoutMiddlewaresState<Layouts> : Layouts extends readonly LayoutId[] ? UnionToIntersection<Layouts[number] extends infer L extends LayoutId ? ResolveLayoutMiddlewaresState<L> : never> : {};
39
42
  /** Fold layout middleware Acc from a route's layoutChain (shallow → deep). */
40
43
  type ResolveLayoutChainMiddlewares<Chain extends readonly LayoutId[]> = Chain extends readonly [
41
44
  infer Head extends LayoutId,
@@ -104,6 +107,10 @@ export type RouteHandleContextWithoutBody<Path extends RoutePath, TMethod extend
104
107
  type RouteResolvedInputField<Path extends RoutePath, TMethod extends Method, Acc extends readonly unknown[], Field extends "query" | "params" | "body"> = MergeMiddlewareInputField<ResolveLayoutChainMiddlewares<RouteLayoutChain<Path, TMethod>>, Field> & MergeMiddlewareInputField<Acc, Field>;
105
108
  type IsNever<T> = [T] extends [never] ? true : false;
106
109
  type IsEmptyObject<T> = [keyof T] extends [never] ? true : false;
110
+ export type IsLayoutAllowed<TRequiredLayouts, TChain extends readonly LayoutId[]> = [
111
+ TRequiredLayouts
112
+ ] extends [never] ? true : [unknown] extends [TRequiredLayouts] ? true : [null] extends [TRequiredLayouts] ? true : [undefined] extends [TRequiredLayouts] ? true : TRequiredLayouts extends LayoutId ? TRequiredLayouts extends TChain[number] ? true : false : TRequiredLayouts extends readonly LayoutId[] ? [TRequiredLayouts[number] & TChain[number]] extends [never] ? false : true : true;
113
+ export type IsStateSatisfied<TActualState, TRequiredState> = [TRequiredState] extends [never] ? true : [unknown] extends [TRequiredState] ? true : [null] extends [TRequiredState] ? true : [undefined] extends [TRequiredState] ? true : [IsEmptyObject<TRequiredState>] extends [true] ? true : [TActualState] extends [TRequiredState] ? true : false;
107
114
  type InputFacet<T, K extends string> = IsNever<T> extends true ? {} : IsEmptyObject<T> extends true ? {} : {
108
115
  [Key in K]: T;
109
116
  };
@@ -146,7 +153,18 @@ export type MiddlewareBuilder<Layout extends LayoutId, Acc extends readonly unkn
146
153
  readonly $Infer: {
147
154
  Context: MiddlewareChainContext<Layout, Acc, unknown, unknown, unknown, TAppContext>;
148
155
  };
149
- use<TAcc, TReturns extends ReturnsMap = {}>(unit: MiddlewareUnit<TAcc, TReturns>): MiddlewareBuilder<Layout, readonly [...Acc, TAcc], TAppContext>;
156
+ use<TAcc, TReturns extends ReturnsMap = {}, TRequiredLayouts = unknown, TRequiredState = unknown>(unit: MiddlewareUnit<TAcc, TReturns, TRequiredLayouts, TRequiredState>, ..._assert: [
157
+ IsLayoutAllowed<TRequiredLayouts, ResolveLayoutIdChain<Layout>>,
158
+ IsStateSatisfied<MiddlewareChainField<Layout, Acc, "state">, TRequiredState>
159
+ ] extends [true, true] ? [] : [
160
+ {
161
+ error: "Middleware cannot be attached to this layout";
162
+ requiredLayouts: TRequiredLayouts;
163
+ layoutChain: ResolveLayoutIdChain<Layout>;
164
+ requiredState: TRequiredState;
165
+ actualState: MiddlewareChainField<Layout, Acc, "state">;
166
+ }
167
+ ]): MiddlewareBuilder<Layout, readonly [...Acc, TAcc], TAppContext>;
150
168
  use<TQuery = unknown, TParams = unknown, TBody = unknown, TReturns extends ReturnsMap = {}, TQueryIn = unknown, TParamsIn = unknown, TBodyIn = unknown, R = unknown>(options: InlineMiddlewareOptions<MiddlewareChainContext<Layout, Acc, TQuery, TParams, TBody, TAppContext>, TQuery, TParams, TBody, TReturns, TQueryIn, TParamsIn, TBodyIn, R>): MiddlewareBuilder<Layout, readonly [
151
169
  ...Acc,
152
170
  MiddlewareReturnFromParts<TQuery, TParams, TBody, ExtractState<R>, TQueryIn, TParamsIn, TBodyIn>
@@ -186,7 +204,18 @@ export type RouteBuilder<Path extends RoutePath, TMethod extends Method, Acc ext
186
204
  Context: TMethod extends "GET" | "DELETE" | "HEAD" | "OPTIONS" ? RouteHandleContextWithoutBody<Path, TMethod, Acc, Validators, TAppContext> : RouteHandleContext<Path, TMethod, Acc, Validators, TAppContext>;
187
205
  };
188
206
  returns<const M extends ReturnsMap>(map: M): RouteBuilder<Path, TMethod, Acc, Validators, Omit<TReturns, keyof M> & M, TAppContext>;
189
- use<TAcc, UReturns extends ReturnsMap = {}>(unit: MiddlewareUnit<TAcc, UReturns>): RouteBuilder<Path, TMethod, readonly [...Acc, TAcc], Validators, Omit<TReturns, keyof UReturns> & UReturns, TAppContext>;
207
+ use<TAcc, UReturns extends ReturnsMap = {}, TRequiredLayouts = unknown, TRequiredState = unknown>(unit: MiddlewareUnit<TAcc, UReturns, TRequiredLayouts, TRequiredState>, ..._assert: [
208
+ IsLayoutAllowed<TRequiredLayouts, RouteLayoutChain<Path, TMethod>>,
209
+ IsStateSatisfied<RouteResolvedField<Path, TMethod, Acc, "state">, TRequiredState>
210
+ ] extends [true, true] ? [] : [
211
+ {
212
+ error: "Middleware cannot be attached to this route";
213
+ requiredLayouts: TRequiredLayouts;
214
+ routeLayoutChain: RouteLayoutChain<Path, TMethod>;
215
+ requiredState: TRequiredState;
216
+ actualState: RouteResolvedField<Path, TMethod, Acc, "state">;
217
+ }
218
+ ]): RouteBuilder<Path, TMethod, readonly [...Acc, TAcc], Validators, Omit<TReturns, keyof UReturns> & UReturns, TAppContext>;
190
219
  use<TQuery = unknown, TParams = unknown, TBody = unknown, UReturns extends ReturnsMap = {}, TQueryIn = unknown, TParamsIn = unknown, TBodyIn = unknown, R = unknown>(options: InlineMiddlewareOptions<RouteChainContext<Path, TMethod, Acc, TQuery, TParams, TBody, TAppContext>, TQuery, TParams, TBody, UReturns, TQueryIn, TParamsIn, TBodyIn, R>): RouteBuilder<Path, TMethod, readonly [
191
220
  ...Acc,
192
221
  MiddlewareReturnFromParts<TQuery, TParams, TBody, ExtractState<R>, TQueryIn, TParamsIn, TBodyIn>
@@ -201,7 +230,15 @@ export type RouteBuilder<Path extends RoutePath, TMethod extends Method, Acc ext
201
230
  };
202
231
  export type HandlerBuilder<Acc extends readonly unknown[] = readonly [], Validators extends ValidatorParts = {}, TReturns extends ReturnsMap = {}, TAppContext extends Record<string, unknown> = AppContext> = {
203
232
  returns<const M extends ReturnsMap>(map: M): HandlerBuilder<Acc, Validators, Omit<TReturns, keyof M> & M, TAppContext>;
204
- use<TAcc, UReturns extends ReturnsMap = {}>(unit: MiddlewareUnit<TAcc, UReturns>): HandlerBuilder<readonly [...Acc, TAcc], Validators, Omit<TReturns, keyof UReturns> & UReturns, TAppContext>;
233
+ use<TAcc, UReturns extends ReturnsMap = {}, TRequiredLayouts = unknown, TRequiredState = unknown>(unit: MiddlewareUnit<TAcc, UReturns, TRequiredLayouts, TRequiredState>, ..._assert: [IsStateSatisfied<MergeMiddlewareField<Acc, "state">, TRequiredState>] extends [
234
+ true
235
+ ] ? [] : [
236
+ {
237
+ error: "Middleware cannot be attached to this handler";
238
+ requiredState: TRequiredState;
239
+ actualState: MergeMiddlewareField<Acc, "state">;
240
+ }
241
+ ]): HandlerBuilder<readonly [...Acc, TAcc], Validators, Omit<TReturns, keyof UReturns> & UReturns, TAppContext>;
205
242
  use<TQuery = unknown, TParams = unknown, TBody = unknown, UReturns extends ReturnsMap = {}, TQueryIn = unknown, TParamsIn = unknown, TBodyIn = unknown, R = unknown>(options: InlineMiddlewareOptions<HandlerContext<Acc, {
206
243
  query: TQuery;
207
244
  params: TParams;
@@ -53,11 +53,11 @@ export type MiddlewareReturnFromParts<TQuery, TParams, TBody, TState, TQueryIn =
53
53
  };
54
54
  };
55
55
  type UnitRuntimeContext = Omit<RuntimeContextFields<NativeContext>, "var">;
56
- export type StandaloneMiddlewareContext<TQuery = unknown, TParams = unknown, TBody = unknown, TAppContext extends Record<string, unknown> = AppContext> = Simplify<TAppContext & UnitRuntimeContext & {
56
+ export type StandaloneMiddlewareContext<TQuery = unknown, TParams = unknown, TBody = unknown, TAppContext extends Record<string, unknown> = AppContext, TState = {}> = Simplify<TAppContext & UnitRuntimeContext & {
57
57
  query: Simplify<UnwrapPart<TQuery>>;
58
58
  params: Simplify<UnwrapPart<TParams>>;
59
59
  body: Simplify<UnwrapPart<TBody>>;
60
- state: {};
60
+ state: Simplify<UnwrapPart<TState>>;
61
61
  headers: TaserHeaders;
62
62
  cookies: TaserCookieJar;
63
63
  }>;
@@ -75,9 +75,11 @@ export type HandlerContext<Acc extends readonly unknown[], Validators extends Va
75
75
  headers: TaserHeaders;
76
76
  cookies: TaserCookieJar;
77
77
  }>;
78
- export type MiddlewareUnit<TAcc = unknown, TReturns extends ReturnsMap = {}> = MiddlewareDefinition & {
78
+ export type MiddlewareUnit<TAcc = unknown, TReturns extends ReturnsMap = {}, TRequiredLayouts = unknown, TRequiredState = unknown> = MiddlewareDefinition & {
79
79
  readonly __middlewareAcc: TAcc;
80
80
  readonly __returns?: TReturns;
81
+ readonly __requiredLayouts?: TRequiredLayouts;
82
+ readonly __requiredState?: TRequiredState;
81
83
  };
82
84
  export type HandlerUnit<_Acc extends readonly unknown[], _Validators extends ValidatorParts, TReturns extends ReturnsMap = {}, TOutput = Response> = {
83
85
  readonly __returns?: TReturns;
@@ -1 +1 @@
1
- {"version":3,"file":"units.js","names":[],"sources":["../../../src/types/units.ts"],"sourcesContent":["import type { Awaitable, TaserCookieJar, TaserHeaders } from \"@taserjs/router-core\";\nimport type { ReplyResult } from \"@taserjs/router-utils\";\n\nimport type { RouterRegister } from \"../register.js\";\nimport type {\n MergeMiddlewareField,\n MergePart,\n RequestShape,\n RuntimeContextFields,\n Simplify,\n UnwrapPart,\n} from \"./type-utils.js\";\nimport type { ReturnsMap } from \"./returns.js\";\nimport type { Schema } from \"./schema.js\";\n\nexport type { HttpMethod, Method } from \"./type-utils.js\";\nexport type {\n EmptyReturns,\n MergeReturns,\n ReturnsMap,\n HandlerReply,\n HasReturns,\n StatusCode,\n ValidHandlerReply,\n EnforceHandlerReply,\n} from \"./returns.js\";\n\n/** Default empty app context for standalone units without a bound router instance. */\nexport type AppContext = Record<never, never>;\n\nexport type NativeContext = RouterRegister extends { NativeContext: infer N } ? N : unknown;\n\nexport type ValidatorParts = {\n query?: unknown;\n params?: unknown;\n body?: unknown;\n queryIn?: unknown;\n paramsIn?: unknown;\n bodyIn?: unknown;\n};\n\nexport declare const StateBrand: unique symbol;\n\nexport type NextResult<TState = unknown> = ReplyResult & {\n readonly [StateBrand]?: TState;\n};\n\nexport type ExtractState<T> = [Extract<Awaited<T>, { [StateBrand]?: unknown }>] extends [never]\n ? {}\n : Extract<Awaited<T>, { [StateBrand]?: unknown }> extends { [StateBrand]?: infer S }\n ? [S] extends [never]\n ? {}\n : S\n : {};\n\nexport type IsUnknown<T> = [unknown] extends [T] ? true : false;\n\nexport type NextFn<TExpectedState = unknown> =\n IsUnknown<TExpectedState> extends true\n ? <S extends Record<string, unknown> = {}>(state?: S) => Promise<NextResult<S>>\n : (state: TExpectedState) => Promise<NextResult<TExpectedState>>;\n\nexport type MiddlewareNext = NextFn;\n\nexport type MiddlewareDefinition = {\n query?: unknown;\n params?: unknown;\n body?: unknown;\n returns?: ReturnsMap;\n handler: (ctx: unknown, next: MiddlewareNext) => Awaitable<ReplyResult | Response | unknown>;\n};\n\nexport type MiddlewareReturnFromParts<\n TQuery,\n TParams,\n TBody,\n TState,\n TQueryIn = TQuery,\n TParamsIn = TParams,\n TBodyIn = TBody,\n> = {\n query: Simplify<UnwrapPart<TQuery>>;\n params: Simplify<UnwrapPart<TParams>>;\n body: Simplify<UnwrapPart<TBody>>;\n state: Simplify<UnwrapPart<TState>>;\n /** Pre-parse shapes merged into route `$Infer.Input`. */\n input: {\n query: RequestShape<TQueryIn, TQuery>;\n params: RequestShape<TParamsIn, TParams>;\n body: RequestShape<TBodyIn, TBody>;\n };\n};\n\ntype UnitRuntimeContext = Omit<RuntimeContextFields<NativeContext>, \"var\">;\n\nexport type StandaloneMiddlewareContext<\n TQuery = unknown,\n TParams = unknown,\n TBody = unknown,\n TAppContext extends Record<string, unknown> = AppContext,\n> = Simplify<\n TAppContext &\n UnitRuntimeContext & {\n query: Simplify<UnwrapPart<TQuery>>;\n params: Simplify<UnwrapPart<TParams>>;\n body: Simplify<UnwrapPart<TBody>>;\n state: {};\n headers: TaserHeaders;\n cookies: TaserCookieJar;\n }\n>;\n\nexport type HandlerContext<\n Acc extends readonly unknown[],\n Validators extends ValidatorParts,\n TAppContext extends Record<string, unknown> = AppContext,\n> = Simplify<\n TAppContext &\n UnitRuntimeContext & {\n query: Simplify<\n MergePart<\n Validators extends { query?: infer Q } ? Q : unknown,\n MergeMiddlewareField<Acc, \"query\">\n >\n >;\n params: Simplify<\n MergePart<\n Validators extends { params?: infer P } ? P : unknown,\n MergeMiddlewareField<Acc, \"params\">\n >\n >;\n body: Simplify<\n MergePart<\n Validators extends { body?: infer B } ? B : unknown,\n MergeMiddlewareField<Acc, \"body\">\n >\n >;\n state: Simplify<MergeMiddlewareField<Acc, \"state\">>;\n headers: TaserHeaders;\n cookies: TaserCookieJar;\n }\n>;\n\nexport type MiddlewareUnit<\n TAcc = unknown,\n TReturns extends ReturnsMap = {},\n> = MiddlewareDefinition & {\n readonly __middlewareAcc: TAcc;\n readonly __returns?: TReturns;\n};\n\nexport type HandlerUnit<\n _Acc extends readonly unknown[],\n _Validators extends ValidatorParts,\n TReturns extends ReturnsMap = {},\n TOutput = Response,\n> = {\n readonly __returns?: TReturns;\n readonly $Infer: {\n Output: TOutput;\n };\n middlewares: readonly MiddlewareDefinition[];\n handler: (ctx: unknown) => Awaitable<ReplyResult | Response>;\n returns?: ReturnsMap;\n query?: Schema<unknown>;\n params?: Schema<unknown>;\n body?: Schema<unknown>;\n};\n\nexport type InlineMiddlewareOptions<\n Ctx = unknown,\n TQuery = unknown,\n TParams = unknown,\n TBody = unknown,\n TReturns extends ReturnsMap = {},\n TQueryIn = unknown,\n TParamsIn = unknown,\n TBodyIn = unknown,\n R = unknown,\n> = {\n query?: Schema<TQuery, TQueryIn>;\n params?: Schema<TParams, TParamsIn>;\n body?: Schema<TBody, TBodyIn>;\n returns?: TReturns;\n handler: (ctx: Ctx, next: NextFn) => Awaitable<R>;\n};\n\nexport function isHandlerUnit(\n value: unknown,\n): value is HandlerUnit<readonly unknown[], ValidatorParts> {\n return (\n typeof value === \"object\" &&\n value !== null &&\n \"handler\" in value &&\n \"middlewares\" in value &&\n Array.isArray((value as HandlerUnit<readonly unknown[], ValidatorParts>).middlewares)\n );\n}\n"],"mappings":";AA2LA,SAAgB,cACd,OAC0D;CAC1D,OACE,OAAO,UAAU,YACjB,UAAU,QACV,aAAa,SACb,iBAAiB,SACjB,MAAM,QAAS,MAA0D,WAAW;AAExF"}
1
+ {"version":3,"file":"units.js","names":[],"sources":["../../../src/types/units.ts"],"sourcesContent":["import type { Awaitable, TaserCookieJar, TaserHeaders } from \"@taserjs/router-core\";\nimport type { ReplyResult } from \"@taserjs/router-utils\";\n\nimport type { RouterRegister } from \"../register.js\";\nimport type {\n MergeMiddlewareField,\n MergePart,\n RequestShape,\n RuntimeContextFields,\n Simplify,\n UnwrapPart,\n} from \"./type-utils.js\";\nimport type { ReturnsMap } from \"./returns.js\";\nimport type { Schema } from \"./schema.js\";\n\nexport type { HttpMethod, Method } from \"./type-utils.js\";\nexport type {\n EmptyReturns,\n MergeReturns,\n ReturnsMap,\n HandlerReply,\n HasReturns,\n StatusCode,\n ValidHandlerReply,\n EnforceHandlerReply,\n} from \"./returns.js\";\n\n/** Default empty app context for standalone units without a bound router instance. */\nexport type AppContext = Record<never, never>;\n\nexport type NativeContext = RouterRegister extends { NativeContext: infer N } ? N : unknown;\n\nexport type ValidatorParts = {\n query?: unknown;\n params?: unknown;\n body?: unknown;\n queryIn?: unknown;\n paramsIn?: unknown;\n bodyIn?: unknown;\n};\n\nexport declare const StateBrand: unique symbol;\n\nexport type NextResult<TState = unknown> = ReplyResult & {\n readonly [StateBrand]?: TState;\n};\n\nexport type ExtractState<T> = [Extract<Awaited<T>, { [StateBrand]?: unknown }>] extends [never]\n ? {}\n : Extract<Awaited<T>, { [StateBrand]?: unknown }> extends { [StateBrand]?: infer S }\n ? [S] extends [never]\n ? {}\n : S\n : {};\n\nexport type IsUnknown<T> = [unknown] extends [T] ? true : false;\n\nexport type NextFn<TExpectedState = unknown> =\n IsUnknown<TExpectedState> extends true\n ? <S extends Record<string, unknown> = {}>(state?: S) => Promise<NextResult<S>>\n : (state: TExpectedState) => Promise<NextResult<TExpectedState>>;\n\nexport type MiddlewareNext = NextFn;\n\nexport type MiddlewareDefinition = {\n query?: unknown;\n params?: unknown;\n body?: unknown;\n returns?: ReturnsMap;\n handler: (ctx: unknown, next: MiddlewareNext) => Awaitable<ReplyResult | Response | unknown>;\n};\n\nexport type MiddlewareReturnFromParts<\n TQuery,\n TParams,\n TBody,\n TState,\n TQueryIn = TQuery,\n TParamsIn = TParams,\n TBodyIn = TBody,\n> = {\n query: Simplify<UnwrapPart<TQuery>>;\n params: Simplify<UnwrapPart<TParams>>;\n body: Simplify<UnwrapPart<TBody>>;\n state: Simplify<UnwrapPart<TState>>;\n /** Pre-parse shapes merged into route `$Infer.Input`. */\n input: {\n query: RequestShape<TQueryIn, TQuery>;\n params: RequestShape<TParamsIn, TParams>;\n body: RequestShape<TBodyIn, TBody>;\n };\n};\n\ntype UnitRuntimeContext = Omit<RuntimeContextFields<NativeContext>, \"var\">;\n\nexport type StandaloneMiddlewareContext<\n TQuery = unknown,\n TParams = unknown,\n TBody = unknown,\n TAppContext extends Record<string, unknown> = AppContext,\n TState = {},\n> = Simplify<\n TAppContext &\n UnitRuntimeContext & {\n query: Simplify<UnwrapPart<TQuery>>;\n params: Simplify<UnwrapPart<TParams>>;\n body: Simplify<UnwrapPart<TBody>>;\n state: Simplify<UnwrapPart<TState>>;\n headers: TaserHeaders;\n cookies: TaserCookieJar;\n }\n>;\n\nexport type HandlerContext<\n Acc extends readonly unknown[],\n Validators extends ValidatorParts,\n TAppContext extends Record<string, unknown> = AppContext,\n> = Simplify<\n TAppContext &\n UnitRuntimeContext & {\n query: Simplify<\n MergePart<\n Validators extends { query?: infer Q } ? Q : unknown,\n MergeMiddlewareField<Acc, \"query\">\n >\n >;\n params: Simplify<\n MergePart<\n Validators extends { params?: infer P } ? P : unknown,\n MergeMiddlewareField<Acc, \"params\">\n >\n >;\n body: Simplify<\n MergePart<\n Validators extends { body?: infer B } ? B : unknown,\n MergeMiddlewareField<Acc, \"body\">\n >\n >;\n state: Simplify<MergeMiddlewareField<Acc, \"state\">>;\n headers: TaserHeaders;\n cookies: TaserCookieJar;\n }\n>;\n\nexport type MiddlewareUnit<\n TAcc = unknown,\n TReturns extends ReturnsMap = {},\n TRequiredLayouts = unknown,\n TRequiredState = unknown,\n> = MiddlewareDefinition & {\n readonly __middlewareAcc: TAcc;\n readonly __returns?: TReturns;\n readonly __requiredLayouts?: TRequiredLayouts;\n readonly __requiredState?: TRequiredState;\n};\n\nexport type HandlerUnit<\n _Acc extends readonly unknown[],\n _Validators extends ValidatorParts,\n TReturns extends ReturnsMap = {},\n TOutput = Response,\n> = {\n readonly __returns?: TReturns;\n readonly $Infer: {\n Output: TOutput;\n };\n middlewares: readonly MiddlewareDefinition[];\n handler: (ctx: unknown) => Awaitable<ReplyResult | Response>;\n returns?: ReturnsMap;\n query?: Schema<unknown>;\n params?: Schema<unknown>;\n body?: Schema<unknown>;\n};\n\nexport type InlineMiddlewareOptions<\n Ctx = unknown,\n TQuery = unknown,\n TParams = unknown,\n TBody = unknown,\n TReturns extends ReturnsMap = {},\n TQueryIn = unknown,\n TParamsIn = unknown,\n TBodyIn = unknown,\n R = unknown,\n> = {\n query?: Schema<TQuery, TQueryIn>;\n params?: Schema<TParams, TParamsIn>;\n body?: Schema<TBody, TBodyIn>;\n returns?: TReturns;\n handler: (ctx: Ctx, next: NextFn) => Awaitable<R>;\n};\n\nexport function isHandlerUnit(\n value: unknown,\n): value is HandlerUnit<readonly unknown[], ValidatorParts> {\n return (\n typeof value === \"object\" &&\n value !== null &&\n \"handler\" in value &&\n \"middlewares\" in value &&\n Array.isArray((value as HandlerUnit<readonly unknown[], ValidatorParts>).middlewares)\n );\n}\n"],"mappings":";AAgMA,SAAgB,cACd,OAC0D;CAC1D,OACE,OAAO,UAAU,YACjB,UAAU,QACV,aAAa,SACb,iBAAiB,SACjB,MAAM,QAAS,MAA0D,WAAW;AAExF"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@taserjs/router",
3
- "version": "0.0.3",
3
+ "version": "0.0.4",
4
4
  "description": "Type-safe Node.js router with runtime validation and middleware",
5
5
  "license": "ISC",
6
6
  "repository": {
@@ -147,8 +147,8 @@
147
147
  "dependencies": {
148
148
  "@standard-schema/spec": "^1.1.0",
149
149
  "hono": "^4.10.3",
150
- "@taserjs/router-core": "0.0.3",
151
- "@taserjs/router-utils": "0.0.3"
150
+ "@taserjs/router-core": "0.0.4",
151
+ "@taserjs/router-utils": "0.0.4"
152
152
  },
153
153
  "devDependencies": {
154
154
  "@tanstack/vite-config": "^0.6.0",
@@ -10,14 +10,25 @@ import type {
10
10
  NextFn,
11
11
  StandaloneMiddlewareContext,
12
12
  } from "../types/units.js";
13
+ import type {
14
+ LayoutId,
15
+ ResolveLayoutMiddlewaresState,
16
+ ResolveLayoutsState,
17
+ } from "../types/index.js";
13
18
  import type { Schema } from "../types/schema.js";
14
19
 
15
20
  type HonoMiddlewareHandler = Parameters<typeof createTaserCompatHandler>[0];
16
21
 
17
- type HonoMiddlewareUnit = MiddlewareUnit<MiddlewareReturnFromParts<unknown, unknown, unknown, {}>>;
22
+ type HonoMiddlewareUnit = MiddlewareUnit<
23
+ MiddlewareReturnFromParts<unknown, unknown, unknown, {}>,
24
+ {},
25
+ null,
26
+ {}
27
+ >;
18
28
 
19
29
  export type DefineMiddlewareOptions<
20
30
  TState = unknown,
31
+ TRequires = {},
21
32
  TQuery = unknown,
22
33
  TParams = unknown,
23
34
  TBody = unknown,
@@ -33,7 +44,67 @@ export type DefineMiddlewareOptions<
33
44
  body?: Schema<TBody, TBodyIn>;
34
45
  returns?: TReturns;
35
46
  handler: (
36
- ctx: StandaloneMiddlewareContext<TQuery, TParams, TBody, TAppContext>,
47
+ ctx: StandaloneMiddlewareContext<TQuery, TParams, TBody, TAppContext, TRequires>,
48
+ next: NextFn<NoInfer<TState>>,
49
+ ) => Awaitable<R>;
50
+ };
51
+
52
+ export type ScopedMiddlewareOptions<
53
+ Layout extends LayoutId,
54
+ TState = unknown,
55
+ TRequires = {},
56
+ TQuery = unknown,
57
+ TParams = unknown,
58
+ TBody = unknown,
59
+ TReturns extends ReturnsMap = {},
60
+ TQueryIn = unknown,
61
+ TParamsIn = unknown,
62
+ TBodyIn = unknown,
63
+ TAppContext extends Record<string, unknown> = AppContext,
64
+ R = unknown,
65
+ > = {
66
+ query?: Schema<TQuery, TQueryIn>;
67
+ params?: Schema<TParams, TParamsIn>;
68
+ body?: Schema<TBody, TBodyIn>;
69
+ returns?: TReturns;
70
+ handler: (
71
+ ctx: StandaloneMiddlewareContext<
72
+ TQuery,
73
+ TParams,
74
+ TBody,
75
+ TAppContext,
76
+ ResolveLayoutMiddlewaresState<Layout> & TRequires
77
+ >,
78
+ next: NextFn<NoInfer<TState>>,
79
+ ) => Awaitable<R>;
80
+ };
81
+
82
+ export type MultiScopedMiddlewareOptions<
83
+ Layouts extends readonly LayoutId[],
84
+ TState = unknown,
85
+ TRequires = {},
86
+ TQuery = unknown,
87
+ TParams = unknown,
88
+ TBody = unknown,
89
+ TReturns extends ReturnsMap = {},
90
+ TQueryIn = unknown,
91
+ TParamsIn = unknown,
92
+ TBodyIn = unknown,
93
+ TAppContext extends Record<string, unknown> = AppContext,
94
+ R = unknown,
95
+ > = {
96
+ query?: Schema<TQuery, TQueryIn>;
97
+ params?: Schema<TParams, TParamsIn>;
98
+ body?: Schema<TBody, TBodyIn>;
99
+ returns?: TReturns;
100
+ handler: (
101
+ ctx: StandaloneMiddlewareContext<
102
+ TQuery,
103
+ TParams,
104
+ TBody,
105
+ TAppContext,
106
+ ResolveLayoutsState<Layouts> & TRequires
107
+ >,
37
108
  next: NextFn<NoInfer<TState>>,
38
109
  ) => Awaitable<R>;
39
110
  };
@@ -41,7 +112,96 @@ export type DefineMiddlewareOptions<
41
112
  export function defineMiddleware(middleware: HonoMiddlewareHandler): HonoMiddlewareUnit;
42
113
 
43
114
  export function defineMiddleware<
115
+ const Layout extends LayoutId,
116
+ TState = unknown,
117
+ TRequires = {},
118
+ TAppContext extends Record<string, unknown> = AppContext,
119
+ TQuery = unknown,
120
+ TParams = unknown,
121
+ TBody = unknown,
122
+ TReturns extends ReturnsMap = {},
123
+ TQueryIn = unknown,
124
+ TParamsIn = unknown,
125
+ TBodyIn = unknown,
126
+ R = unknown,
127
+ >(
128
+ layout: Layout,
129
+ options: ScopedMiddlewareOptions<
130
+ Layout,
131
+ TState,
132
+ TRequires,
133
+ TQuery,
134
+ TParams,
135
+ TBody,
136
+ TReturns,
137
+ TQueryIn,
138
+ TParamsIn,
139
+ TBodyIn,
140
+ TAppContext,
141
+ R
142
+ >,
143
+ ): MiddlewareUnit<
144
+ MiddlewareReturnFromParts<
145
+ TQuery,
146
+ TParams,
147
+ TBody,
148
+ IsUnknown<TState> extends true ? ExtractState<R> : TState,
149
+ TQueryIn,
150
+ TParamsIn,
151
+ TBodyIn
152
+ >,
153
+ TReturns,
154
+ Layout,
155
+ TRequires
156
+ >;
157
+
158
+ export function defineMiddleware<
159
+ const Layouts extends readonly [LayoutId, ...LayoutId[]],
44
160
  TState = unknown,
161
+ TRequires = {},
162
+ TAppContext extends Record<string, unknown> = AppContext,
163
+ TQuery = unknown,
164
+ TParams = unknown,
165
+ TBody = unknown,
166
+ TReturns extends ReturnsMap = {},
167
+ TQueryIn = unknown,
168
+ TParamsIn = unknown,
169
+ TBodyIn = unknown,
170
+ R = unknown,
171
+ >(
172
+ layouts: Layouts,
173
+ options: MultiScopedMiddlewareOptions<
174
+ Layouts,
175
+ TState,
176
+ TRequires,
177
+ TQuery,
178
+ TParams,
179
+ TBody,
180
+ TReturns,
181
+ TQueryIn,
182
+ TParamsIn,
183
+ TBodyIn,
184
+ TAppContext,
185
+ R
186
+ >,
187
+ ): MiddlewareUnit<
188
+ MiddlewareReturnFromParts<
189
+ TQuery,
190
+ TParams,
191
+ TBody,
192
+ IsUnknown<TState> extends true ? ExtractState<R> : TState,
193
+ TQueryIn,
194
+ TParamsIn,
195
+ TBodyIn
196
+ >,
197
+ TReturns,
198
+ Layouts,
199
+ TRequires
200
+ >;
201
+
202
+ export function defineMiddleware<
203
+ TState = unknown,
204
+ TRequires = {},
45
205
  TAppContext extends Record<string, unknown> = AppContext,
46
206
  TQuery = unknown,
47
207
  TParams = unknown,
@@ -54,6 +214,7 @@ export function defineMiddleware<
54
214
  >(
55
215
  options: DefineMiddlewareOptions<
56
216
  TState,
217
+ TRequires,
57
218
  TQuery,
58
219
  TParams,
59
220
  TBody,
@@ -74,21 +235,50 @@ export function defineMiddleware<
74
235
  TParamsIn,
75
236
  TBodyIn
76
237
  >,
77
- TReturns
238
+ TReturns,
239
+ null,
240
+ TRequires
78
241
  >;
79
242
 
80
243
  export function defineMiddleware(
81
- options:
244
+ first:
245
+ | string
246
+ | readonly string[]
82
247
  | HonoMiddlewareHandler
83
- | DefineMiddlewareOptions<any, any, any, any, any, any, any, any, any, any>,
248
+ | DefineMiddlewareOptions<any, any, any, any, any, any, any, any, any, any, any>,
249
+ second?: DefineMiddlewareOptions<any, any, any, any, any, any, any, any, any, any, any>,
84
250
  ): unknown {
85
- if (typeof options === "function") {
86
- const honoMiddleware = options as HonoMiddlewareHandler;
251
+ if (typeof first === "string" || Array.isArray(first)) {
252
+ const options = second!;
253
+ const unit = {
254
+ ...options,
255
+ __middlewareAcc: undefined as unknown,
256
+ ...(options?.returns ? { __returns: options.returns } : {}),
257
+ __requiredLayouts: first,
258
+ };
259
+ return unit;
260
+ }
261
+
262
+ if (typeof first === "function") {
263
+ const honoMiddleware = first as HonoMiddlewareHandler;
87
264
  return defineMiddleware({
88
265
  handler: (ctx, next) => createTaserCompatHandler(honoMiddleware)(ctx, next),
89
266
  });
90
267
  }
91
268
 
269
+ const options = first as DefineMiddlewareOptions<
270
+ any,
271
+ any,
272
+ any,
273
+ any,
274
+ any,
275
+ any,
276
+ any,
277
+ any,
278
+ any,
279
+ any,
280
+ any
281
+ >;
92
282
  const unit = {
93
283
  ...options,
94
284
  __middlewareAcc: undefined as unknown,
@@ -7,6 +7,7 @@ import type {
7
7
  RequestShape,
8
8
  RuntimeContextFields,
9
9
  Simplify,
10
+ UnionToIntersection,
10
11
  } from "./type-utils.js";
11
12
  import type { ReturnsMap, ValidHandlerReply } from "./returns.js";
12
13
  import type { Schema } from "./schema.js";
@@ -111,6 +112,27 @@ type ResolveLayoutMiddlewares<Layout extends LayoutId | null> = Layout extends n
111
112
  ]
112
113
  : readonly [];
113
114
 
115
+ export type ResolveLayoutIdChain<L extends LayoutId | null> = L extends null
116
+ ? readonly []
117
+ : L extends LayoutId
118
+ ? readonly [...ResolveLayoutIdChain<LayoutParent<L>>, L]
119
+ : readonly [];
120
+
121
+ export type ResolveLayoutMiddlewaresState<Layout extends LayoutId | null> = Layout extends LayoutId
122
+ ? MergeMiddlewareField<ResolveLayoutMiddlewares<Layout>, "state">
123
+ : {};
124
+
125
+ export type ResolveLayoutsState<Layouts extends LayoutId | readonly LayoutId[]> =
126
+ Layouts extends LayoutId
127
+ ? ResolveLayoutMiddlewaresState<Layouts>
128
+ : Layouts extends readonly LayoutId[]
129
+ ? UnionToIntersection<
130
+ Layouts[number] extends infer L extends LayoutId
131
+ ? ResolveLayoutMiddlewaresState<L>
132
+ : never
133
+ >
134
+ : {};
135
+
114
136
  /** Fold layout middleware Acc from a route's layoutChain (shallow → deep). */
115
137
  type ResolveLayoutChainMiddlewares<Chain extends readonly LayoutId[]> = Chain extends readonly [
116
138
  infer Head extends LayoutId,
@@ -268,6 +290,40 @@ type IsNever<T> = [T] extends [never] ? true : false;
268
290
 
269
291
  type IsEmptyObject<T> = [keyof T] extends [never] ? true : false;
270
292
 
293
+ export type IsLayoutAllowed<TRequiredLayouts, TChain extends readonly LayoutId[]> = [
294
+ TRequiredLayouts,
295
+ ] extends [never]
296
+ ? true
297
+ : [unknown] extends [TRequiredLayouts]
298
+ ? true
299
+ : [null] extends [TRequiredLayouts]
300
+ ? true
301
+ : [undefined] extends [TRequiredLayouts]
302
+ ? true
303
+ : TRequiredLayouts extends LayoutId
304
+ ? TRequiredLayouts extends TChain[number]
305
+ ? true
306
+ : false
307
+ : TRequiredLayouts extends readonly LayoutId[]
308
+ ? [TRequiredLayouts[number] & TChain[number]] extends [never]
309
+ ? false
310
+ : true
311
+ : true;
312
+
313
+ export type IsStateSatisfied<TActualState, TRequiredState> = [TRequiredState] extends [never]
314
+ ? true
315
+ : [unknown] extends [TRequiredState]
316
+ ? true
317
+ : [null] extends [TRequiredState]
318
+ ? true
319
+ : [undefined] extends [TRequiredState]
320
+ ? true
321
+ : [IsEmptyObject<TRequiredState>] extends [true]
322
+ ? true
323
+ : [TActualState] extends [TRequiredState]
324
+ ? true
325
+ : false;
326
+
271
327
  type InputFacet<T, K extends string> =
272
328
  IsNever<T> extends true ? {} : IsEmptyObject<T> extends true ? {} : { [Key in K]: T };
273
329
 
@@ -380,8 +436,22 @@ export type MiddlewareBuilder<
380
436
  readonly $Infer: {
381
437
  Context: MiddlewareChainContext<Layout, Acc, unknown, unknown, unknown, TAppContext>;
382
438
  };
383
- use<TAcc, TReturns extends ReturnsMap = {}>(
384
- unit: MiddlewareUnit<TAcc, TReturns>,
439
+ use<TAcc, TReturns extends ReturnsMap = {}, TRequiredLayouts = unknown, TRequiredState = unknown>(
440
+ unit: MiddlewareUnit<TAcc, TReturns, TRequiredLayouts, TRequiredState>,
441
+ ..._assert: [
442
+ IsLayoutAllowed<TRequiredLayouts, ResolveLayoutIdChain<Layout>>,
443
+ IsStateSatisfied<MiddlewareChainField<Layout, Acc, "state">, TRequiredState>,
444
+ ] extends [true, true]
445
+ ? []
446
+ : [
447
+ {
448
+ error: "Middleware cannot be attached to this layout";
449
+ requiredLayouts: TRequiredLayouts;
450
+ layoutChain: ResolveLayoutIdChain<Layout>;
451
+ requiredState: TRequiredState;
452
+ actualState: MiddlewareChainField<Layout, Acc, "state">;
453
+ },
454
+ ]
385
455
  ): MiddlewareBuilder<Layout, readonly [...Acc, TAcc], TAppContext>;
386
456
  use<
387
457
  TQuery = unknown,
@@ -489,8 +559,22 @@ export type RouteBuilder<
489
559
  returns<const M extends ReturnsMap>(
490
560
  map: M,
491
561
  ): RouteBuilder<Path, TMethod, Acc, Validators, Omit<TReturns, keyof M> & M, TAppContext>;
492
- use<TAcc, UReturns extends ReturnsMap = {}>(
493
- unit: MiddlewareUnit<TAcc, UReturns>,
562
+ use<TAcc, UReturns extends ReturnsMap = {}, TRequiredLayouts = unknown, TRequiredState = unknown>(
563
+ unit: MiddlewareUnit<TAcc, UReturns, TRequiredLayouts, TRequiredState>,
564
+ ..._assert: [
565
+ IsLayoutAllowed<TRequiredLayouts, RouteLayoutChain<Path, TMethod>>,
566
+ IsStateSatisfied<RouteResolvedField<Path, TMethod, Acc, "state">, TRequiredState>,
567
+ ] extends [true, true]
568
+ ? []
569
+ : [
570
+ {
571
+ error: "Middleware cannot be attached to this route";
572
+ requiredLayouts: TRequiredLayouts;
573
+ routeLayoutChain: RouteLayoutChain<Path, TMethod>;
574
+ requiredState: TRequiredState;
575
+ actualState: RouteResolvedField<Path, TMethod, Acc, "state">;
576
+ },
577
+ ]
494
578
  ): RouteBuilder<
495
579
  Path,
496
580
  TMethod,
@@ -581,8 +665,19 @@ export type HandlerBuilder<
581
665
  returns<const M extends ReturnsMap>(
582
666
  map: M,
583
667
  ): HandlerBuilder<Acc, Validators, Omit<TReturns, keyof M> & M, TAppContext>;
584
- use<TAcc, UReturns extends ReturnsMap = {}>(
585
- unit: MiddlewareUnit<TAcc, UReturns>,
668
+ use<TAcc, UReturns extends ReturnsMap = {}, TRequiredLayouts = unknown, TRequiredState = unknown>(
669
+ unit: MiddlewareUnit<TAcc, UReturns, TRequiredLayouts, TRequiredState>,
670
+ ..._assert: [IsStateSatisfied<MergeMiddlewareField<Acc, "state">, TRequiredState>] extends [
671
+ true,
672
+ ]
673
+ ? []
674
+ : [
675
+ {
676
+ error: "Middleware cannot be attached to this handler";
677
+ requiredState: TRequiredState;
678
+ actualState: MergeMiddlewareField<Acc, "state">;
679
+ },
680
+ ]
586
681
  ): HandlerBuilder<
587
682
  readonly [...Acc, TAcc],
588
683
  Validators,
@@ -98,13 +98,14 @@ export type StandaloneMiddlewareContext<
98
98
  TParams = unknown,
99
99
  TBody = unknown,
100
100
  TAppContext extends Record<string, unknown> = AppContext,
101
+ TState = {},
101
102
  > = Simplify<
102
103
  TAppContext &
103
104
  UnitRuntimeContext & {
104
105
  query: Simplify<UnwrapPart<TQuery>>;
105
106
  params: Simplify<UnwrapPart<TParams>>;
106
107
  body: Simplify<UnwrapPart<TBody>>;
107
- state: {};
108
+ state: Simplify<UnwrapPart<TState>>;
108
109
  headers: TaserHeaders;
109
110
  cookies: TaserCookieJar;
110
111
  }
@@ -144,9 +145,13 @@ export type HandlerContext<
144
145
  export type MiddlewareUnit<
145
146
  TAcc = unknown,
146
147
  TReturns extends ReturnsMap = {},
148
+ TRequiredLayouts = unknown,
149
+ TRequiredState = unknown,
147
150
  > = MiddlewareDefinition & {
148
151
  readonly __middlewareAcc: TAcc;
149
152
  readonly __returns?: TReturns;
153
+ readonly __requiredLayouts?: TRequiredLayouts;
154
+ readonly __requiredState?: TRequiredState;
150
155
  };
151
156
 
152
157
  export type HandlerUnit<