@taserjs/router 0.1.8 → 0.1.10

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 (55) hide show
  1. package/dist/cjs/builder/middleware-unit.cjs +50 -50
  2. package/dist/cjs/builder/middleware-unit.cjs.map +1 -1
  3. package/dist/cjs/builder/middleware-unit.d.cts +34 -1
  4. package/dist/cjs/builder/middleware.cjs +13 -12
  5. package/dist/cjs/builder/middleware.cjs.map +1 -1
  6. package/dist/cjs/builder/normalize.cjs +10 -0
  7. package/dist/cjs/builder/normalize.cjs.map +1 -0
  8. package/dist/cjs/builder/normalize.d.cts +2 -0
  9. package/dist/cjs/builder/route.cjs +63 -67
  10. package/dist/cjs/builder/route.cjs.map +1 -1
  11. package/dist/cjs/builder/standalone.cjs +18 -34
  12. package/dist/cjs/builder/standalone.cjs.map +1 -1
  13. package/dist/cjs/builder/standalone.d.cts +7 -14
  14. package/dist/cjs/define/middleware.cjs.map +1 -1
  15. package/dist/cjs/define/middleware.d.cts +5 -5
  16. package/dist/cjs/index.cjs +0 -11
  17. package/dist/cjs/index.d.cts +3 -3
  18. package/dist/cjs/middleware/hono-mw.cjs.map +1 -1
  19. package/dist/cjs/types/index.d.cts +4 -3
  20. package/dist/esm/builder/middleware-unit.d.ts +34 -1
  21. package/dist/esm/builder/middleware-unit.js +50 -50
  22. package/dist/esm/builder/middleware-unit.js.map +1 -1
  23. package/dist/esm/builder/middleware.js +13 -12
  24. package/dist/esm/builder/middleware.js.map +1 -1
  25. package/dist/esm/builder/normalize.d.ts +2 -0
  26. package/dist/esm/builder/normalize.js +10 -0
  27. package/dist/esm/builder/normalize.js.map +1 -0
  28. package/dist/esm/builder/route.js +63 -67
  29. package/dist/esm/builder/route.js.map +1 -1
  30. package/dist/esm/builder/standalone.d.ts +7 -14
  31. package/dist/esm/builder/standalone.js +19 -24
  32. package/dist/esm/builder/standalone.js.map +1 -1
  33. package/dist/esm/define/middleware.d.ts +5 -5
  34. package/dist/esm/define/middleware.js.map +1 -1
  35. package/dist/esm/index.d.ts +3 -3
  36. package/dist/esm/index.js +2 -2
  37. package/dist/esm/middleware/hono-mw.js.map +1 -1
  38. package/dist/esm/types/index.d.ts +4 -3
  39. package/package.json +3 -3
  40. package/src/builder/middleware-unit.ts +56 -51
  41. package/src/builder/middleware.ts +16 -18
  42. package/src/builder/normalize.ts +13 -0
  43. package/src/builder/route.ts +81 -82
  44. package/src/builder/standalone.ts +56 -52
  45. package/src/define/middleware.ts +5 -4
  46. package/src/index.ts +3 -17
  47. package/src/middleware/hono-mw.ts +4 -1
  48. package/src/types/index.ts +16 -8
  49. package/dist/cjs/builder/factories.cjs +0 -31
  50. package/dist/cjs/builder/factories.cjs.map +0 -1
  51. package/dist/cjs/builder/factories.d.cts +0 -16
  52. package/dist/esm/builder/factories.d.ts +0 -16
  53. package/dist/esm/builder/factories.js +0 -22
  54. package/dist/esm/builder/factories.js.map +0 -1
  55. package/src/builder/factories.ts +0 -59
@@ -1 +1 @@
1
- {"version":3,"file":"middleware.js","names":[],"sources":["../../../src/define/middleware.ts"],"sourcesContent":["import { createMiddlewareUnitBuilder } from \"../builder/middleware-unit.js\";\nimport type {\n DefineMiddlewareResult,\n ExtractState,\n IsUnknown,\n MiddlewareRequirements,\n MiddlewareUnitBuilder,\n NextFn,\n StandaloneMiddlewareContext,\n} from \"../types/units.js\";\nimport type {\n AppContext,\n LayoutId,\n ResolveLayoutMiddlewaresState,\n ResolveLayoutsState,\n} from \"../types/index.js\";\n\nexport interface MiddlewareFn<TAppContext extends Record<string, unknown> = AppContext> {\n /**\n * Defines a standalone, unscoped middleware using a short function signature.\n *\n * @example\n * ```ts\n * const auth = middleware((ctx, next) => {\n * return next({ user: \"alice\" });\n * });\n * ```\n */\n <TState = unknown, TRequires extends MiddlewareRequirements = {}, R = unknown>(\n fn: (\n ctx: StandaloneMiddlewareContext<unknown, unknown, unknown, TAppContext, {}, TRequires>,\n next: NextFn<NoInfer<TState>>,\n ) => R,\n ): DefineMiddlewareResult<\n unknown,\n unknown,\n unknown,\n IsUnknown<TState> extends true ? ExtractState<R> : TState,\n R,\n unknown,\n unknown,\n unknown,\n {},\n null,\n TRequires\n >;\n\n /**\n * Constructs a fluent unscoped middleware builder, with optional explicit State generic.\n *\n * @example\n * ```ts\n * const auth = middleware<{ user: string }>()\n * .query(z.object({ token: z.string() }))\n * .body(\"form\", z.object({ file: z.instanceof(File) }))\n * .returns({ 401: z.string() })\n * .handler(async (ctx, next) => next({ user: \"alice\" }));\n * ```\n */\n <TState = unknown>(): MiddlewareUnitBuilder<\n unknown,\n unknown,\n unknown,\n {},\n null,\n {},\n TAppContext,\n {},\n unknown,\n unknown,\n unknown,\n TState\n >;\n\n /**\n * Defines a standalone middleware scoped to a single layout branch using a short function signature.\n */\n <\n const Layout extends LayoutId,\n TState = unknown,\n TRequires extends MiddlewareRequirements = {},\n R = unknown,\n >(\n layout: Layout,\n fn: (\n ctx: StandaloneMiddlewareContext<\n unknown,\n unknown,\n unknown,\n TAppContext,\n ResolveLayoutMiddlewaresState<Layout>,\n TRequires\n >,\n next: NextFn<NoInfer<TState>>,\n ) => R,\n ): DefineMiddlewareResult<\n unknown,\n unknown,\n unknown,\n IsUnknown<TState> extends true ? ExtractState<R> : TState,\n R,\n unknown,\n unknown,\n unknown,\n {},\n Layout,\n TRequires\n >;\n\n /**\n * Defines a standalone middleware scoped to multiple layout branches using a short function signature.\n */\n <\n const Layouts extends readonly [LayoutId, ...LayoutId[]],\n TState = unknown,\n TRequires extends MiddlewareRequirements = {},\n R = unknown,\n >(\n layouts: Layouts,\n fn: (\n ctx: StandaloneMiddlewareContext<\n unknown,\n unknown,\n unknown,\n TAppContext,\n ResolveLayoutsState<Layouts>,\n TRequires\n >,\n next: NextFn<NoInfer<TState>>,\n ) => R,\n ): DefineMiddlewareResult<\n unknown,\n unknown,\n unknown,\n IsUnknown<TState> extends true ? ExtractState<R> : TState,\n R,\n unknown,\n unknown,\n unknown,\n {},\n Layouts,\n TRequires\n >;\n\n /**\n * Constructs a fluent middleware builder scoped to a single layout branch, with optional explicit State generic.\n */\n <const Layout extends LayoutId, TState = unknown>(\n layout: Layout,\n ): MiddlewareUnitBuilder<\n unknown,\n unknown,\n unknown,\n {},\n Layout,\n {},\n TAppContext,\n ResolveLayoutMiddlewaresState<Layout>,\n unknown,\n unknown,\n unknown,\n TState\n >;\n\n /**\n * Constructs a fluent middleware builder scoped to multiple layout branches (branch union), with optional explicit State generic.\n */\n <const Layouts extends readonly [LayoutId, ...LayoutId[]], TState = unknown>(\n layouts: Layouts,\n ): MiddlewareUnitBuilder<\n unknown,\n unknown,\n unknown,\n {},\n Layouts,\n {},\n TAppContext,\n ResolveLayoutsState<Layouts>,\n unknown,\n unknown,\n unknown,\n TState\n >;\n}\n\nexport const middleware: MiddlewareFn<AppContext> = function middleware(\n first?: any,\n second?: any,\n): any {\n if (first === undefined) {\n return createMiddlewareUnitBuilder();\n }\n\n if (typeof first === \"string\" || Array.isArray(first)) {\n if (typeof second === \"function\") {\n return {\n handler: second,\n __middlewareAcc: undefined as unknown,\n __requiredLayouts: first,\n };\n }\n return createMiddlewareUnitBuilder(first);\n }\n\n if (typeof first === \"function\") {\n return {\n handler: first,\n __middlewareAcc: undefined as unknown,\n };\n }\n\n return createMiddlewareUnitBuilder();\n};\n"],"mappings":";;AAyLA,IAAa,aAAuC,SAAS,WAC3D,OACA,QACK;CACL,IAAI,UAAU,KAAA,GACZ,OAAO,4BAA4B;CAGrC,IAAI,OAAO,UAAU,YAAY,MAAM,QAAQ,KAAK,GAAG;EACrD,IAAI,OAAO,WAAW,YACpB,OAAO;GACL,SAAS;GACT,iBAAiB,KAAA;GACjB,mBAAmB;EACrB;EAEF,OAAO,4BAA4B,KAAK;CAC1C;CAEA,IAAI,OAAO,UAAU,YACnB,OAAO;EACL,SAAS;EACT,iBAAiB,KAAA;CACnB;CAGF,OAAO,4BAA4B;AACrC"}
1
+ {"version":3,"file":"middleware.js","names":[],"sources":["../../../src/define/middleware.ts"],"sourcesContent":["import { createMiddlewareUnitBuilder } from \"../builder/middleware-unit.js\";\nimport type {\n DefineMiddlewareResult,\n ExtractState,\n IsUnknown,\n MiddlewareRequirements,\n MiddlewareUnitBuilder,\n NextFn,\n StandaloneMiddlewareContext,\n} from \"../types/units.js\";\nimport type {\n AppContext,\n LayoutId,\n LayoutParams,\n ResolveLayoutMiddlewaresState,\n ResolveLayoutsState,\n} from \"../types/index.js\";\n\nexport interface MiddlewareFn<TAppContext extends Record<string, unknown> = AppContext> {\n /**\n * Defines a standalone, unscoped middleware using a short function signature.\n *\n * @example\n * ```ts\n * const auth = middleware((ctx, next) => {\n * return next({ user: \"alice\" });\n * });\n * ```\n */\n <TState = unknown, TRequires extends MiddlewareRequirements = {}, R = unknown>(\n fn: (\n ctx: StandaloneMiddlewareContext<unknown, unknown, unknown, TAppContext, {}, TRequires>,\n next: NextFn<NoInfer<TState>>,\n ) => R,\n ): DefineMiddlewareResult<\n unknown,\n unknown,\n unknown,\n IsUnknown<TState> extends true ? ExtractState<R> : TState,\n R,\n unknown,\n unknown,\n unknown,\n {},\n null,\n TRequires\n >;\n\n /**\n * Constructs a fluent unscoped middleware builder, with optional explicit State generic.\n *\n * @example\n * ```ts\n * const auth = middleware<{ user: string }>()\n * .query(z.object({ token: z.string() }))\n * .body(\"form\", z.object({ file: z.instanceof(File) }))\n * .returns({ 401: z.string() })\n * .handler(async (ctx, next) => next({ user: \"alice\" }));\n * ```\n */\n <TState = unknown>(): MiddlewareUnitBuilder<\n unknown,\n unknown,\n unknown,\n {},\n null,\n {},\n TAppContext,\n {},\n unknown,\n unknown,\n unknown,\n TState\n >;\n\n /**\n * Defines a standalone middleware scoped to a single layout branch using a short function signature.\n */\n <\n const Layout extends LayoutId,\n TState = unknown,\n TRequires extends MiddlewareRequirements = {},\n R = unknown,\n >(\n layout: Layout,\n fn: (\n ctx: StandaloneMiddlewareContext<\n unknown,\n LayoutParams<Layout>,\n unknown,\n TAppContext,\n ResolveLayoutMiddlewaresState<Layout>,\n TRequires\n >,\n next: NextFn<NoInfer<TState>>,\n ) => R,\n ): DefineMiddlewareResult<\n unknown,\n unknown,\n unknown,\n IsUnknown<TState> extends true ? ExtractState<R> : TState,\n R,\n unknown,\n unknown,\n unknown,\n {},\n Layout,\n TRequires\n >;\n\n /**\n * Defines a standalone middleware scoped to multiple layout branches using a short function signature.\n */\n <\n const Layouts extends readonly [LayoutId, ...LayoutId[]],\n TState = unknown,\n TRequires extends MiddlewareRequirements = {},\n R = unknown,\n >(\n layouts: Layouts,\n fn: (\n ctx: StandaloneMiddlewareContext<\n unknown,\n LayoutParams<Layouts[number]>,\n unknown,\n TAppContext,\n ResolveLayoutsState<Layouts>,\n TRequires\n >,\n next: NextFn<NoInfer<TState>>,\n ) => R,\n ): DefineMiddlewareResult<\n unknown,\n unknown,\n unknown,\n IsUnknown<TState> extends true ? ExtractState<R> : TState,\n R,\n unknown,\n unknown,\n unknown,\n {},\n Layouts,\n TRequires\n >;\n\n /**\n * Constructs a fluent middleware builder scoped to a single layout branch, with optional explicit State generic.\n */\n <const Layout extends LayoutId, TState = unknown>(\n layout: Layout,\n ): MiddlewareUnitBuilder<\n unknown,\n LayoutParams<Layout>,\n unknown,\n {},\n Layout,\n {},\n TAppContext,\n ResolveLayoutMiddlewaresState<Layout>,\n unknown,\n unknown,\n unknown,\n TState\n >;\n\n /**\n * Constructs a fluent middleware builder scoped to multiple layout branches (branch union), with optional explicit State generic.\n */\n <const Layouts extends readonly [LayoutId, ...LayoutId[]], TState = unknown>(\n layouts: Layouts,\n ): MiddlewareUnitBuilder<\n unknown,\n LayoutParams<Layouts[number]>,\n unknown,\n {},\n Layouts,\n {},\n TAppContext,\n ResolveLayoutsState<Layouts>,\n unknown,\n unknown,\n unknown,\n TState\n >;\n}\n\nexport const middleware: MiddlewareFn<AppContext> = function middleware(\n first?: any,\n second?: any,\n): any {\n if (first === undefined) {\n return createMiddlewareUnitBuilder();\n }\n\n if (typeof first === \"string\" || Array.isArray(first)) {\n if (typeof second === \"function\") {\n return {\n handler: second,\n __middlewareAcc: undefined as unknown,\n __requiredLayouts: first,\n };\n }\n return createMiddlewareUnitBuilder(first);\n }\n\n if (typeof first === \"function\") {\n return {\n handler: first,\n __middlewareAcc: undefined as unknown,\n };\n }\n\n return createMiddlewareUnitBuilder();\n};\n"],"mappings":";;AA0LA,IAAa,aAAuC,SAAS,WAC3D,OACA,QACK;CACL,IAAI,UAAU,KAAA,GACZ,OAAO,4BAA4B;CAGrC,IAAI,OAAO,UAAU,YAAY,MAAM,QAAQ,KAAK,GAAG;EACrD,IAAI,OAAO,WAAW,YACpB,OAAO;GACL,SAAS;GACT,iBAAiB,KAAA;GACjB,mBAAmB;EACrB;EAEF,OAAO,4BAA4B,KAAK;CAC1C;CAEA,IAAI,OAAO,UAAU,YACnB,OAAO;EACL,SAAS;EACT,iBAAiB,KAAA;CACnB;CAGF,OAAO,4BAA4B;AACrC"}
@@ -4,14 +4,14 @@ export interface RouterMiddlewaresRegister {
4
4
  }
5
5
  export interface RouterRoutesRegister {
6
6
  }
7
- export type { AppContext, EmptyAppContext, HttpMethod, InferInput, InferOutput, InferRouteContext, InferRouteInput, InferRouteInputFromPath, InferRouteOutput, LayoutBuilder, LayoutId, LayoutMiddlewaresMap, LayoutTree, Method, MiddlewareBuilder, MiddlewareDefinition, MiddlewareUnit, MiddlewareUnitBuilder, PathParams, ReturnsMap, RouteByPathMethod, RouteDefinition, RouteExport, RoutePath, Schema, Simplify, StatusCode, } from './types/index.js';
7
+ export type { AppContext, EmptyAppContext, HttpMethod, InferInput, InferOutput, InferRouteContext, InferRouteInput, InferRouteInputFromPath, InferRouteOutput, LayoutBuilder, LayoutId, LayoutMiddlewaresMap, LayoutParams, LayoutTree, Method, MiddlewareBuilder, MiddlewareDefinition, MiddlewareUnit, MiddlewareUnitBuilder, PathParams, ReturnsMap, RouteByPathMethod, RouteDefinition, RouteExport, RoutePath, Schema, Simplify, StatusCode, } from './types/index.js';
8
8
  export type { ContextDefinition, CreateTaserAppOptions, InferAppContext, InferAppManifest, OnErrorOptions, RouteManifestShape, } from './types/app.js';
9
9
  export { ValidationError, validationErrorSchema } from '@taserjs/router-utils';
10
10
  export type { ResponseValidationFailureHandler, SuccessStatusCode } from '@taserjs/router-utils';
11
- export { all, any, del, get, head, layout, middleware, options, patch, post, put, query, t, type TaserNamespace, } from './builder/standalone.js';
11
+ export { middleware, t, type TaserNamespace } from './builder/standalone.js';
12
12
  export { createTaserApp, TaserRouter } from './builder/router.js';
13
13
  export { TaserApp } from './builder/app.js';
14
14
  export { createContext } from './context/create-context.js';
15
15
  export { honoMw } from './middleware/hono-mw.js';
16
16
  export type { MiddlewareFn } from './define/middleware.js';
17
- export type { TaserCookieJar, TaserHeaders } from '@taserjs/router-core';
17
+ export type { TaserCookieJar, TaserCookieOptions, TaserHeaders } from '@taserjs/router-core';
package/dist/esm/index.js CHANGED
@@ -1,7 +1,7 @@
1
- import { all, any, del, get, head, layout, middleware, options, patch, post, put, query, t } from "./builder/standalone.js";
1
+ import { middleware, t } from "./builder/standalone.js";
2
2
  import { TaserApp } from "./builder/app.js";
3
3
  import { TaserRouter, createTaserApp } from "./builder/router.js";
4
4
  import { createContext } from "./context/create-context.js";
5
5
  import { honoMw } from "./middleware/hono-mw.js";
6
6
  import { ValidationError, validationErrorSchema } from "@taserjs/router-utils";
7
- export { TaserApp, TaserRouter, ValidationError, all, any, createContext, createTaserApp, del, get, head, honoMw, layout, middleware, options, patch, post, put, query, t, validationErrorSchema };
7
+ export { TaserApp, TaserRouter, ValidationError, createContext, createTaserApp, honoMw, middleware, t, validationErrorSchema };
@@ -1 +1 @@
1
- {"version":3,"file":"hono-mw.js","names":[],"sources":["../../../src/middleware/hono-mw.ts"],"sourcesContent":["import { createTaserCompatHandler } from \"@taserjs/router-core\";\nimport type { MiddlewareHandler } from \"hono\";\n\nimport type { NextFn } from \"../types/units.js\";\n\n/**\n * Adapts a standard Web / Hono-compatible middleware function `(c, next) => ...`\n * into a Taser-compatible middleware handler function `(ctx, next) => Promise<Response>`.\n *\n * @example\n * ```ts\n * import { honoMw } from \"@taserjs/router\";\n * import { cors } from \"hono/cors\";\n *\n * const app = t.get(\"/hello\").use(honoMw(cors()));\n * // or\n * const customCors = middleware(honoMw(cors()));\n * ```\n */\nexport function honoMw<TState extends Record<string, unknown> = {}>(\n middleware: MiddlewareHandler,\n): (ctx: unknown, next: NextFn<TState>) => Promise<Response> {\n return createTaserCompatHandler(middleware) as (ctx: unknown, next: NextFn<TState>) => Promise<Response>;\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAmBA,SAAgB,OACd,YAC2D;CAC3D,OAAO,yBAAyB,UAAU;AAC5C"}
1
+ {"version":3,"file":"hono-mw.js","names":[],"sources":["../../../src/middleware/hono-mw.ts"],"sourcesContent":["import { createTaserCompatHandler } from \"@taserjs/router-core\";\nimport type { MiddlewareHandler } from \"hono\";\n\nimport type { NextFn } from \"../types/units.js\";\n\n/**\n * Adapts a standard Web / Hono-compatible middleware function `(c, next) => ...`\n * into a Taser-compatible middleware handler function `(ctx, next) => Promise<Response>`.\n *\n * @example\n * ```ts\n * import { honoMw } from \"@taserjs/router\";\n * import { cors } from \"hono/cors\";\n *\n * const app = t.get(\"/hello\").use(honoMw(cors()));\n * // or\n * const customCors = middleware(honoMw(cors()));\n * ```\n */\nexport function honoMw<TState extends Record<string, unknown> = {}>(\n middleware: MiddlewareHandler,\n): (ctx: unknown, next: NextFn<TState>) => Promise<Response> {\n return createTaserCompatHandler(middleware) as (\n ctx: unknown,\n next: NextFn<TState>,\n ) => Promise<Response>;\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAmBA,SAAgB,OACd,YAC2D;CAC3D,OAAO,yBAAyB,UAAU;AAI5C"}
@@ -38,7 +38,8 @@ type ParseParam<Segment extends string> = Segment extends "*" ? {
38
38
  [K in Name]: string;
39
39
  } : {};
40
40
  type ParseParamsFromSegments<S extends string> = S extends `${infer Segment}/${infer Rest}` ? ParseParam<Segment> & ParseParamsFromSegments<Rest> : ParseParam<S>;
41
- export type PathParams<Path extends string> = Path extends `/${infer Rest}` ? ParseParamsFromSegments<Rest> : {};
41
+ export type PathParams<Path extends string> = Path extends `/${infer Rest}` ? ParseParamsFromSegments<Rest> : ParseParamsFromSegments<Path>;
42
+ export type LayoutParams<Layout extends string> = PathParams<Layout>;
42
43
  export type ResolveParams<TPathParams, TSchemaParams> = TSchemaParams extends Record<string, unknown> ? [keyof TSchemaParams] extends [never] ? TPathParams : Simplify<Omit<TPathParams, keyof TSchemaParams> & TSchemaParams> : TPathParams;
43
44
  type LayoutParent<Layout extends LayoutId> = Layout extends keyof LayoutTree ? LayoutTree[Layout] extends {
44
45
  parent: infer P extends LayoutId;
@@ -177,7 +178,7 @@ export type MiddlewareChainContext<Layout extends LayoutId, Acc extends readonly
177
178
  readonly headers: TaserHeaders;
178
179
  readonly cookies: TaserCookieJar;
179
180
  query: Simplify<MergePart<TQuery, MiddlewareChainField<Layout, Acc, "query">>>;
180
- params: Simplify<MergePart<TParams, MiddlewareChainField<Layout, Acc, "params">>>;
181
+ params: Simplify<ResolveParams<LayoutParams<Layout>, Simplify<MergePart<TParams, MiddlewareChainField<Layout, Acc, "params">>>>>;
181
182
  body: Simplify<MergePart<TBody, MiddlewareChainField<Layout, Acc, "body">>>;
182
183
  state: Simplify<MiddlewareChainField<Layout, Acc, "state">>;
183
184
  }>;
@@ -201,7 +202,7 @@ export type MiddlewareBuilder<Layout extends LayoutId, Acc extends readonly unkn
201
202
  readonly headers: TaserHeaders;
202
203
  readonly cookies: TaserCookieJar;
203
204
  query: Simplify<MergePart<unknown, MiddlewareChainField<Layout, Acc, "query">>>;
204
- params: Simplify<MergePart<unknown, MiddlewareChainField<Layout, Acc, "params">>>;
205
+ params: Simplify<ResolveParams<LayoutParams<Layout>, Simplify<MergePart<unknown, MiddlewareChainField<Layout, Acc, "params">>>>>;
205
206
  body: Simplify<MergePart<unknown, MiddlewareChainField<Layout, Acc, "body">>>;
206
207
  state: Simplify<MiddlewareChainField<Layout, Acc, "state">>;
207
208
  }>, next: NextFn) => Awaitable<R>): MiddlewareBuilder<Layout, readonly [...Acc, MiddlewareReturnFromParts<unknown, unknown, unknown, ExtractState<R>>], TAppContext>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@taserjs/router",
3
- "version": "0.1.8",
3
+ "version": "0.1.10",
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.1.8",
151
- "@taserjs/router-utils": "0.1.8"
150
+ "@taserjs/router-core": "0.1.10",
151
+ "@taserjs/router-utils": "0.1.10"
152
152
  },
153
153
  "devDependencies": {
154
154
  "@tanstack/vite-config": "^0.6.0",
@@ -4,63 +4,68 @@ import type { ReturnsMap } from "../types/returns.js";
4
4
  import type { Schema } from "../types/schema.js";
5
5
  import { pickDefinedSchemas, type SchemaValidators } from "../define/validators.js";
6
6
 
7
- export function createMiddlewareUnitBuilder(requiredLayouts?: string | readonly string[]) {
8
- const validators: SchemaValidators = {};
9
- let bodyMode: BodyMode | undefined;
10
- let returnsMap: ReturnsMap | undefined;
7
+ class MiddlewareUnitBuilderImpl {
8
+ private readonly validators: SchemaValidators = {};
9
+ private bodyMode: BodyMode | undefined;
10
+ private returnsMap: ReturnsMap | undefined;
11
+ readonly __requiredLayouts: string | readonly string[] | undefined;
12
+ readonly __middlewareAcc: unknown = undefined;
13
+
14
+ constructor(requiredLayouts?: string | readonly string[]) {
15
+ this.__requiredLayouts = requiredLayouts;
16
+ }
17
+
18
+ query(schema: Schema<unknown>): this {
19
+ this.validators.query = schema;
20
+ return this;
21
+ }
22
+
23
+ params(schema: Schema<unknown>): this {
24
+ this.validators.params = schema;
25
+ return this;
26
+ }
27
+
28
+ body(modeOrSchema: BodyMode | Schema<unknown>, schema?: Schema<unknown>): this {
29
+ if (typeof modeOrSchema === "string") {
30
+ this.bodyMode = modeOrSchema as BodyMode;
31
+ if (schema !== undefined) {
32
+ this.validators.body = schema;
33
+ }
34
+ } else {
35
+ this.bodyMode = "json";
36
+ this.validators.body = modeOrSchema;
37
+ }
38
+ return this;
39
+ }
40
+
41
+ returns(map: ReturnsMap): this {
42
+ this.returnsMap = this.returnsMap ? { ...this.returnsMap, ...map } : { ...map };
43
+ return this;
44
+ }
45
+
46
+ requires(): this {
47
+ return this;
48
+ }
11
49
 
12
- function toUnit(fn?: (ctx: any, next: any) => any) {
13
- const schemas = pickDefinedSchemas(validators);
50
+ handler(fn: (ctx: any, next: any) => any) {
51
+ return this.toUnit(fn);
52
+ }
53
+
54
+ toUnit(fn?: (ctx: any, next: any) => any) {
55
+ const schemas = pickDefinedSchemas(this.validators);
14
56
  return {
15
57
  ...(fn ? { handler: fn } : {}),
16
58
  ...schemas,
17
- ...(bodyMode ? { bodyMode } : {}),
18
- ...(returnsMap ? { returns: returnsMap, __returns: returnsMap } : {}),
19
- ...(requiredLayouts !== undefined ? { __requiredLayouts: requiredLayouts } : {}),
59
+ ...(this.bodyMode ? { bodyMode: this.bodyMode } : {}),
60
+ ...(this.returnsMap ? { returns: this.returnsMap, __returns: this.returnsMap } : {}),
61
+ ...(this.__requiredLayouts !== undefined
62
+ ? { __requiredLayouts: this.__requiredLayouts }
63
+ : {}),
20
64
  __middlewareAcc: undefined as unknown,
21
65
  };
22
66
  }
67
+ }
23
68
 
24
- const builder: any = {
25
- query(schema: Schema<unknown>) {
26
- validators.query = schema;
27
- return builder;
28
- },
29
- params(schema: Schema<unknown>) {
30
- validators.params = schema;
31
- return builder;
32
- },
33
- body(modeOrSchema: BodyMode | Schema<unknown>, schema?: Schema<unknown>) {
34
- if (typeof modeOrSchema === "string") {
35
- bodyMode = modeOrSchema as BodyMode;
36
- if (schema !== undefined) {
37
- validators.body = schema;
38
- }
39
- } else {
40
- bodyMode = "json";
41
- validators.body = modeOrSchema;
42
- }
43
- return builder;
44
- },
45
- returns(map: ReturnsMap) {
46
- returnsMap = { ...returnsMap, ...map };
47
- return builder;
48
- },
49
- requires() {
50
- return builder;
51
- },
52
- handler(fn: (ctx: any, next: any) => any) {
53
- return toUnit(fn);
54
- },
55
- toUnit() {
56
- return toUnit();
57
- },
58
- __middlewareAcc: undefined as unknown,
59
- };
60
-
61
- if (requiredLayouts !== undefined) {
62
- builder.__requiredLayouts = requiredLayouts;
63
- }
64
-
65
- return builder;
69
+ export function createMiddlewareUnitBuilder(requiredLayouts?: string | readonly string[]) {
70
+ return new MiddlewareUnitBuilderImpl(requiredLayouts);
66
71
  }
@@ -1,25 +1,23 @@
1
1
  import type { LayoutId, MiddlewareBuilder } from "../types/index.js";
2
2
  import type { MiddlewareDefinition } from "../types/units.js";
3
+ import { normalizeMiddlewareDefinition } from "./normalize.js";
4
+
5
+ class MiddlewareBuilderImpl<Layout extends LayoutId> {
6
+ readonly layout: Layout;
7
+ readonly middlewares: MiddlewareDefinition[] = [];
8
+
9
+ constructor(layout: Layout) {
10
+ this.layout = layout;
11
+ }
12
+
13
+ use(definition: MiddlewareDefinition | ((ctx: any, next: any) => any)): this {
14
+ this.middlewares.push(normalizeMiddlewareDefinition(definition));
15
+ return this;
16
+ }
17
+ }
3
18
 
4
19
  export function createMiddleware<const Layout extends LayoutId>(
5
20
  layout: Layout,
6
21
  ): MiddlewareBuilder<Layout, readonly []> {
7
- const entries: MiddlewareDefinition[] = [];
8
-
9
- const chain = {
10
- layout,
11
- middlewares: entries,
12
- use(definition: MiddlewareDefinition | ((ctx: any, next: any) => any)) {
13
- if (typeof definition === "function") {
14
- entries.push({ handler: definition as any });
15
- } else if (typeof (definition as any)?.toUnit === "function") {
16
- entries.push((definition as any).toUnit());
17
- } else {
18
- entries.push(definition);
19
- }
20
- return chain;
21
- },
22
- };
23
-
24
- return chain as unknown as MiddlewareBuilder<Layout, readonly []>;
22
+ return new MiddlewareBuilderImpl(layout) as unknown as MiddlewareBuilder<Layout, readonly []>;
25
23
  }
@@ -0,0 +1,13 @@
1
+ import type { MiddlewareDefinition } from "../types/units.js";
2
+
3
+ export function normalizeMiddlewareDefinition(
4
+ definition: MiddlewareDefinition | ((ctx: any, next: any) => any),
5
+ ): MiddlewareDefinition {
6
+ if (typeof definition === "function") {
7
+ return { handler: definition as any };
8
+ }
9
+ if (typeof (definition as any)?.toUnit === "function") {
10
+ return (definition as any).toUnit();
11
+ }
12
+ return definition;
13
+ }
@@ -17,21 +17,9 @@ import type {
17
17
  } from "../types/index.js";
18
18
  import type { HttpMethod, MiddlewareDefinition } from "../types/units.js";
19
19
  import { pickDefinedSchemas, type SchemaValidators } from "../define/validators.js";
20
+ import { normalizeMiddlewareDefinition } from "./normalize.js";
20
21
 
21
- function toUtilsMap(map: ReturnsMap | undefined): Record<number, StandardSchemaV1> {
22
- if (!map) {
23
- return {};
24
- }
25
- const out: Record<number, StandardSchemaV1> = {};
26
- for (const [key, schema] of Object.entries(map)) {
27
- if (schema !== undefined) {
28
- out[Number(key)] = schema;
29
- }
30
- }
31
- return out;
32
- }
33
-
34
- function buildEffectiveReturns(args: {
22
+ function buildRouteReturnsWith422(args: {
35
23
  middlewareReturns: ReturnsMap;
36
24
  routeReturns: ReturnsMap;
37
25
  schemas: {
@@ -42,8 +30,8 @@ function buildEffectiveReturns(args: {
42
30
  };
43
31
  }): Record<number, StandardSchemaV1> | undefined {
44
32
  const merged = mergeReturnsMaps(
45
- toUtilsMap(args.middlewareReturns),
46
- toUtilsMap(args.routeReturns),
33
+ args.middlewareReturns as Record<number, StandardSchemaV1>,
34
+ args.routeReturns as Record<number, StandardSchemaV1>,
47
35
  );
48
36
  const with422 = withAuto422(merged, hasInputSchemas(args.schemas));
49
37
  return Object.keys(with422).length > 0 ? with422 : undefined;
@@ -63,76 +51,87 @@ function buildRouteBase(
63
51
  };
64
52
  }
65
53
 
54
+ class RouteBuilderImpl {
55
+ readonly path: string;
56
+ readonly method: HttpMethod | "ANY" | "ALL";
57
+ readonly methods: readonly HttpMethod[] | undefined;
58
+ private readonly middlewares: MiddlewareDefinition[] = [];
59
+ private readonly routeReturns: Record<number, StandardSchemaV1> = {};
60
+ private readonly validators: SchemaValidators = {};
61
+ private bodyMode: BodyMode | undefined;
62
+
63
+ constructor(path: string, method: HttpMethod | "ANY" | "ALL", methods?: readonly HttpMethod[]) {
64
+ this.path = path;
65
+ this.method = method;
66
+ this.methods = methods;
67
+ }
68
+
69
+ query(schema: Schema<unknown>): this {
70
+ this.validators.query = schema;
71
+ return this;
72
+ }
73
+
74
+ params(schema: Schema<unknown>): this {
75
+ this.validators.params = schema;
76
+ return this;
77
+ }
78
+
79
+ body(modeOrSchema: BodyMode | Schema<unknown>, schema?: Schema<unknown>): this {
80
+ if (typeof modeOrSchema === "string") {
81
+ this.bodyMode = modeOrSchema as BodyMode;
82
+ if (schema !== undefined) {
83
+ this.validators.body = schema;
84
+ }
85
+ } else {
86
+ this.bodyMode = "json";
87
+ this.validators.body = modeOrSchema;
88
+ }
89
+ return this;
90
+ }
91
+
92
+ returns(map: ReturnsMap): this {
93
+ Object.assign(this.routeReturns, map);
94
+ return this;
95
+ }
96
+
97
+ use(definition: MiddlewareDefinition | ((ctx: any, next: any) => any)): this {
98
+ this.middlewares.push(normalizeMiddlewareDefinition(definition));
99
+ return this;
100
+ }
101
+
102
+ handler(fn: (ctx: unknown) => Response | Promise<Response>) {
103
+ const routeSchemas = pickDefinedSchemas(this.validators);
104
+ const base = buildRouteBase(this.path, this.method, this.methods, this.middlewares);
105
+
106
+ const returns = buildRouteReturnsWith422({
107
+ middlewareReturns: collectReturnsFromDefinitions(this.middlewares),
108
+ routeReturns: this.routeReturns,
109
+ schemas: {
110
+ ...this.validators,
111
+ middlewares: this.middlewares,
112
+ },
113
+ });
114
+
115
+ return {
116
+ ...base,
117
+ handler: fn,
118
+ ...routeSchemas,
119
+ ...(this.bodyMode ? { bodyMode: this.bodyMode } : {}),
120
+ ...(returns ? { returns } : {}),
121
+ };
122
+ }
123
+ }
124
+
66
125
  /** Shared internal builder — not part of the public `@taserjs/router` API. */
67
126
  export function createRouteBuilder(
68
127
  path: string,
69
128
  method: HttpMethod | "ANY" | "ALL",
70
129
  methods?: readonly HttpMethod[],
71
130
  ): RouteBuilder<RoutePath, Method, readonly [], ValidatorParts> {
72
- const middlewares: MiddlewareDefinition[] = [];
73
- let routeReturns: Record<number, StandardSchemaV1> = {};
74
- const validators: SchemaValidators = {};
75
- let bodyMode: BodyMode | undefined;
76
-
77
- const builder = {
78
- path,
79
- method,
80
- query(schema: Schema<unknown>) {
81
- validators.query = schema;
82
- return builder;
83
- },
84
- params(schema: Schema<unknown>) {
85
- validators.params = schema;
86
- return builder;
87
- },
88
- body(modeOrSchema: BodyMode | Schema<unknown>, schema?: Schema<unknown>) {
89
- if (typeof modeOrSchema === "string") {
90
- bodyMode = modeOrSchema as BodyMode;
91
- if (schema !== undefined) {
92
- validators.body = schema;
93
- }
94
- } else {
95
- bodyMode = "json";
96
- validators.body = modeOrSchema;
97
- }
98
- return builder;
99
- },
100
- returns(map: ReturnsMap) {
101
- routeReturns = { ...routeReturns, ...toUtilsMap(map) };
102
- return builder;
103
- },
104
- use(definition: MiddlewareDefinition | ((ctx: any, next: any) => any)) {
105
- if (typeof definition === "function") {
106
- middlewares.push({ handler: definition as any });
107
- } else if (typeof (definition as any)?.toUnit === "function") {
108
- middlewares.push((definition as any).toUnit());
109
- } else {
110
- middlewares.push(definition);
111
- }
112
- return builder;
113
- },
114
- handler(fn: (ctx: unknown) => Response | Promise<Response>) {
115
- const routeSchemas = pickDefinedSchemas(validators);
116
- const base = buildRouteBase(path, method, methods, middlewares);
117
-
118
- const returns = buildEffectiveReturns({
119
- middlewareReturns: collectReturnsFromDefinitions(middlewares),
120
- routeReturns,
121
- schemas: {
122
- ...validators,
123
- middlewares,
124
- },
125
- });
126
-
127
- return {
128
- ...base,
129
- handler: fn,
130
- ...routeSchemas,
131
- ...(bodyMode ? { bodyMode } : {}),
132
- ...(returns ? { returns } : {}),
133
- };
134
- },
135
- };
136
-
137
- return builder as unknown as RouteBuilder<RoutePath, Method, readonly [], ValidatorParts>;
131
+ return new RouteBuilderImpl(path, method, methods) as unknown as RouteBuilder<
132
+ RoutePath,
133
+ Method,
134
+ readonly [],
135
+ ValidatorParts
136
+ >;
138
137
  }
@@ -1,48 +1,49 @@
1
- import type { AppContext, LayoutBuilder } from "../types/index.js";
2
- import {
3
- createAllRoute,
4
- createAnyRoute,
5
- createDeleteRoute,
6
- createGetRoute,
7
- createHeadRoute,
8
- createOptionsRoute,
9
- createPatchRoute,
10
- createPostRoute,
11
- createPutRoute,
12
- createQueryRoute,
13
- type CreateAllRoute,
14
- type CreateAnyRoute,
15
- type CreateWithBodyRoute,
16
- type CreateWithoutBodyRoute,
17
- } from "./factories.js";
1
+ import type { AppContext, LayoutBuilder, RouteBuilder, RoutePath } from "../types/index.js";
2
+ import type { HttpMethod } from "../types/units.js";
3
+ import { createRouteBuilder } from "./route.js";
18
4
  import { createMiddleware } from "./middleware.js";
19
5
  import { middleware as createMiddlewareFn, type MiddlewareFn } from "../define/middleware.js";
20
6
 
21
- export const get: CreateWithoutBodyRoute<"GET", AppContext> =
22
- createGetRoute as CreateWithoutBodyRoute<"GET", AppContext>;
23
- export const post: CreateWithBodyRoute<"POST", AppContext> = createPostRoute as CreateWithBodyRoute<
24
- "POST",
25
- AppContext
26
- >;
27
- export const put: CreateWithBodyRoute<"PUT", AppContext> = createPutRoute as CreateWithBodyRoute<
28
- "PUT",
29
- AppContext
30
- >;
31
- export const patch: CreateWithBodyRoute<"PATCH", AppContext> =
32
- createPatchRoute as CreateWithBodyRoute<"PATCH", AppContext>;
33
- export const del: CreateWithoutBodyRoute<"DELETE", AppContext> =
34
- createDeleteRoute as CreateWithoutBodyRoute<"DELETE", AppContext>;
35
- export const options: CreateWithoutBodyRoute<"OPTIONS", AppContext> =
36
- createOptionsRoute as CreateWithoutBodyRoute<"OPTIONS", AppContext>;
37
- export const head: CreateWithoutBodyRoute<"HEAD", AppContext> =
38
- createHeadRoute as CreateWithoutBodyRoute<"HEAD", AppContext>;
39
- export const query: CreateWithBodyRoute<"QUERY", AppContext> =
40
- createQueryRoute as CreateWithBodyRoute<"QUERY", AppContext>;
41
- export const any: CreateAnyRoute<AppContext> = createAnyRoute as CreateAnyRoute<AppContext>;
42
- export const all: CreateAllRoute<AppContext> = createAllRoute as CreateAllRoute<AppContext>;
7
+ export type CreateWithoutBodyRoute<
8
+ M extends "GET" | "DELETE" | "OPTIONS" | "HEAD",
9
+ TAppContext extends Record<string, unknown> = AppContext,
10
+ > = <const Path extends RoutePath>(
11
+ path: Path,
12
+ ) => RouteBuilder<Path, M, readonly [], {}, {}, TAppContext>;
43
13
 
44
- export const layout: LayoutBuilder<AppContext> = createMiddleware as LayoutBuilder<AppContext>;
45
- export const middleware: MiddlewareFn<AppContext> = createMiddlewareFn as MiddlewareFn<AppContext>;
14
+ export type CreateWithBodyRoute<
15
+ M extends "POST" | "PUT" | "PATCH" | "QUERY",
16
+ TAppContext extends Record<string, unknown> = AppContext,
17
+ > = <const Path extends RoutePath>(
18
+ path: Path,
19
+ ) => RouteBuilder<Path, M, readonly [], {}, {}, TAppContext>;
20
+
21
+ export type CreateAnyRoute<TAppContext extends Record<string, unknown> = AppContext> = <
22
+ const Path extends RoutePath,
23
+ const Methods extends readonly HttpMethod[],
24
+ >(
25
+ path: Path,
26
+ methods: Methods,
27
+ ) => RouteBuilder<Path, Methods[number], readonly [], {}, {}, TAppContext>;
28
+
29
+ export type CreateAllRoute<TAppContext extends Record<string, unknown> = AppContext> = <
30
+ const Path extends RoutePath,
31
+ >(
32
+ path: Path,
33
+ ) => RouteBuilder<Path, HttpMethod, readonly [], {}, {}, TAppContext>;
34
+
35
+ function createWithoutBodyRoute<M extends "GET" | "DELETE" | "OPTIONS" | "HEAD">(
36
+ method: M,
37
+ ): CreateWithoutBodyRoute<M> {
38
+ return ((path: string) =>
39
+ createRouteBuilder(path, method)) as unknown as CreateWithoutBodyRoute<M>;
40
+ }
41
+
42
+ function createWithBodyRoute<M extends "POST" | "PUT" | "PATCH" | "QUERY">(
43
+ method: M,
44
+ ): CreateWithBodyRoute<M> {
45
+ return ((path: string) => createRouteBuilder(path, method)) as unknown as CreateWithBodyRoute<M>;
46
+ }
46
47
 
47
48
  export type TaserNamespace<TAppContext extends Record<string, unknown> = AppContext> = {
48
49
  readonly get: CreateWithoutBodyRoute<"GET", TAppContext>;
@@ -59,17 +60,20 @@ export type TaserNamespace<TAppContext extends Record<string, unknown> = AppCont
59
60
  readonly middleware: MiddlewareFn<TAppContext>;
60
61
  };
61
62
 
63
+ export const middleware: MiddlewareFn<AppContext> = createMiddlewareFn as MiddlewareFn<AppContext>;
64
+
62
65
  export const t: TaserNamespace<AppContext> = {
63
- get,
64
- post,
65
- put,
66
- patch,
67
- delete: del,
68
- options,
69
- head,
70
- query,
71
- any,
72
- all,
73
- layout,
66
+ get: createWithoutBodyRoute("GET"),
67
+ post: createWithBodyRoute("POST"),
68
+ put: createWithBodyRoute("PUT"),
69
+ patch: createWithBodyRoute("PATCH"),
70
+ delete: createWithoutBodyRoute("DELETE"),
71
+ options: createWithoutBodyRoute("OPTIONS"),
72
+ head: createWithoutBodyRoute("HEAD"),
73
+ query: createWithBodyRoute("QUERY"),
74
+ any: ((path: string, methods: readonly HttpMethod[]) =>
75
+ createRouteBuilder(path, "ANY", methods)) as unknown as CreateAnyRoute<AppContext>,
76
+ all: ((path: string) => createRouteBuilder(path, "ALL")) as unknown as CreateAllRoute<AppContext>,
77
+ layout: createMiddleware as LayoutBuilder<AppContext>,
74
78
  middleware,
75
79
  };
@@ -11,6 +11,7 @@ import type {
11
11
  import type {
12
12
  AppContext,
13
13
  LayoutId,
14
+ LayoutParams,
14
15
  ResolveLayoutMiddlewaresState,
15
16
  ResolveLayoutsState,
16
17
  } from "../types/index.js";
@@ -85,7 +86,7 @@ export interface MiddlewareFn<TAppContext extends Record<string, unknown> = AppC
85
86
  fn: (
86
87
  ctx: StandaloneMiddlewareContext<
87
88
  unknown,
88
- unknown,
89
+ LayoutParams<Layout>,
89
90
  unknown,
90
91
  TAppContext,
91
92
  ResolveLayoutMiddlewaresState<Layout>,
@@ -120,7 +121,7 @@ export interface MiddlewareFn<TAppContext extends Record<string, unknown> = AppC
120
121
  fn: (
121
122
  ctx: StandaloneMiddlewareContext<
122
123
  unknown,
123
- unknown,
124
+ LayoutParams<Layouts[number]>,
124
125
  unknown,
125
126
  TAppContext,
126
127
  ResolveLayoutsState<Layouts>,
@@ -149,7 +150,7 @@ export interface MiddlewareFn<TAppContext extends Record<string, unknown> = AppC
149
150
  layout: Layout,
150
151
  ): MiddlewareUnitBuilder<
151
152
  unknown,
152
- unknown,
153
+ LayoutParams<Layout>,
153
154
  unknown,
154
155
  {},
155
156
  Layout,
@@ -169,7 +170,7 @@ export interface MiddlewareFn<TAppContext extends Record<string, unknown> = AppC
169
170
  layouts: Layouts,
170
171
  ): MiddlewareUnitBuilder<
171
172
  unknown,
172
- unknown,
173
+ LayoutParams<Layouts[number]>,
173
174
  unknown,
174
175
  {},
175
176
  Layouts,