rouzer 2.0.1 → 3.0.1

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,4 +1,5 @@
1
1
  import { RoutePattern } from '@remix-run/route-pattern';
2
+ import { createMatcher } from '@remix-run/route-pattern/match';
2
3
  import { chain, MiddlewareChain, } from 'alien-middleware';
3
4
  import * as z from 'zod';
4
5
  import { mapValues } from '../common.js';
@@ -13,14 +14,14 @@ class RouterObject extends MiddlewareChain {
13
14
  this.basePath = config.basePath?.replace(/\/?$/, '/');
14
15
  const allowOrigins = config.cors?.allowOrigins?.map(createOriginPattern);
15
16
  if (allowOrigins) {
16
- super.use((ctx) => {
17
+ super.use(((ctx) => {
17
18
  const origin = ctx.request.headers.get('Origin');
18
19
  if (origin &&
19
20
  allowOrigins &&
20
21
  !allowOrigins.some(pattern => pattern.test(origin))) {
21
22
  return new Response(null, { status: 403 });
22
23
  }
23
- });
24
+ }));
24
25
  }
25
26
  }
26
27
  use(...args) {
@@ -31,28 +32,13 @@ class RouterObject extends MiddlewareChain {
31
32
  /** @internal */
32
33
  useRoutes(routeSchemas, handlers) {
33
34
  const { config, basePath } = this;
34
- const routes = Object.entries(routeSchemas).map(([name, route]) => ({
35
- name,
36
- path: basePath
37
- ? new RoutePattern(route.path.source.replace(/^\/?/, basePath))
38
- : route.path,
39
- methods: mapValues(route.methods, (schema, method) => {
40
- const handler = handlers[name][method];
41
- if (!handler && config.debug) {
42
- console.error(`Handler missing for route: ${method} ${name}`);
43
- }
44
- return {
45
- schema,
46
- handler,
47
- };
48
- }),
49
- }));
35
+ const routes = flattenRoutes(routeSchemas, handlers, basePath ?? '', config.debug);
50
36
  const addDebugHeaders = config.debug
51
37
  ? (context, route) => {
52
38
  context.setHeader('X-Route-Name', route.name);
53
39
  }
54
40
  : null;
55
- return super.use(async function (context) {
41
+ return super.use((async function (context) {
56
42
  const request = context.request;
57
43
  const origin = request.headers.get('Origin');
58
44
  const url = (context.url ??= new URL(request.url));
@@ -65,28 +51,18 @@ class RouterObject extends MiddlewareChain {
65
51
  'GET';
66
52
  }
67
53
  for (const route of routes) {
68
- const props = route.methods.hasOwnProperty(method)
69
- ? route.methods[method]
70
- : route.methods.ALL;
71
- if (!props) {
54
+ if (route.method !== method) {
72
55
  continue;
73
56
  }
74
- const { schema, handler } = props;
57
+ const { schema, handler } = route;
75
58
  if (!handler) {
76
59
  continue;
77
60
  }
78
- const match = route.path.match(url);
61
+ const match = route.matcher.match(url);
79
62
  if (!match) {
80
63
  continue;
81
64
  }
82
65
  if (isPreflight) {
83
- const optionsHandler = handlers[route.name].OPTIONS;
84
- if (optionsHandler) {
85
- const response = await optionsHandler(context);
86
- if (response) {
87
- return response;
88
- }
89
- }
90
66
  return new Response(null, {
91
67
  headers: {
92
68
  'Access-Control-Allow-Origin': origin ?? '',
@@ -136,7 +112,7 @@ class RouterObject extends MiddlewareChain {
136
112
  }
137
113
  return Response.json(result);
138
114
  }
139
- });
115
+ }));
140
116
  }
141
117
  }
142
118
  /**
@@ -153,6 +129,32 @@ export function createRouter(config = {}) {
153
129
  Object.setPrototypeOf(handler, router);
154
130
  return handler;
155
131
  }
132
+ function flattenRoutes(tree, handlers, prefix, debug) {
133
+ const routes = [];
134
+ for (const [name, node] of Object.entries(tree)) {
135
+ if (node.kind === 'resource') {
136
+ routes.push(...flattenRoutes(node.children, handlers[name], joinPaths(prefix, node.path.source), debug));
137
+ }
138
+ else {
139
+ const handler = handlers[name];
140
+ if (!handler && debug) {
141
+ console.error(`Handler missing for route: ${node.method} ${name}`);
142
+ }
143
+ routes.push({
144
+ name,
145
+ path: RoutePattern.parse(joinPaths(prefix, node.path?.source ?? '')),
146
+ matcher: createMatcher(joinPaths(prefix, node.path?.source ?? '')),
147
+ method: node.method,
148
+ schema: node.schema,
149
+ handler,
150
+ });
151
+ }
152
+ }
153
+ return routes;
154
+ }
155
+ function joinPaths(left, right) {
156
+ return [left, right].filter(Boolean).join('/').replace(/\/+/g, '/');
157
+ }
156
158
  function httpClientError(error, message, config) {
157
159
  return Response.json({
158
160
  ...error,
@@ -1,46 +1,42 @@
1
- import type { Params } from '@remix-run/route-pattern';
1
+ import type { MatchParams } from '@remix-run/route-pattern/match';
2
2
  import type { AnyMiddlewareChain, MiddlewareChain, MiddlewareContext } from 'alien-middleware';
3
3
  import type * as z from 'zod';
4
4
  import { Promisable } from '../common.js';
5
- import type { InferRouteResponse, Routes, RouteSchema } from '../types.js';
5
+ import type { HttpAction, HttpResource, HttpRouteTree } from '../http.js';
6
+ import type { InferRouteResponse, RouteSchema } from '../types.js';
6
7
  type RequestContext<TMiddleware extends AnyMiddlewareChain> = MiddlewareContext<TMiddleware>;
7
8
  type RouteRequestHandler<TMiddleware extends AnyMiddlewareChain, TArgs extends object, TResult> = (context: RequestContext<TMiddleware> & TArgs) => Promisable<TResult | Response>;
8
- type InferRouteRequestHandler<TMiddleware extends AnyMiddlewareChain, TSchema extends RouteSchema, TMethod extends string, TPath extends string> = TMethod extends 'GET' ? RouteRequestHandler<TMiddleware, {
9
- path: TSchema extends {
9
+ type InferActionHandler<TMiddleware extends AnyMiddlewareChain, TAction extends HttpAction, TPath extends string> = TAction['method'] extends 'GET' ? RouteRequestHandler<TMiddleware, {
10
+ path: TAction['schema'] extends {
10
11
  path: any;
11
- } ? z.infer<TSchema['path']> : Params<TPath>;
12
- query: TSchema extends {
12
+ } ? z.infer<TAction['schema']['path']> : MatchParams<TPath>;
13
+ query: TAction['schema'] extends {
13
14
  query: any;
14
- } ? z.infer<TSchema['query']> : undefined;
15
- headers: TSchema extends {
15
+ } ? z.infer<TAction['schema']['query']> : undefined;
16
+ headers: TAction['schema'] extends {
16
17
  headers: any;
17
- } ? z.infer<TSchema['headers']> : undefined;
18
- }, InferRouteResponse<TSchema>> : RouteRequestHandler<TMiddleware, {
19
- path: TSchema extends {
18
+ } ? z.infer<TAction['schema']['headers']> : undefined;
19
+ }, InferRouteResponse<Extract<TAction['schema'], RouteSchema>>> : RouteRequestHandler<TMiddleware, {
20
+ path: TAction['schema'] extends {
20
21
  path: any;
21
- } ? z.infer<TSchema['path']> : Params<TPath>;
22
- body: TSchema extends {
22
+ } ? z.infer<TAction['schema']['path']> : MatchParams<TPath>;
23
+ body: TAction['schema'] extends {
23
24
  body: any;
24
- } ? z.infer<TSchema['body']> : undefined;
25
- headers: TSchema extends {
25
+ } ? z.infer<TAction['schema']['body']> : undefined;
26
+ headers: TAction['schema'] extends {
26
27
  headers: any;
27
- } ? z.infer<TSchema['headers']> : undefined;
28
- }, InferRouteResponse<TSchema>>;
28
+ } ? z.infer<TAction['schema']['headers']> : undefined;
29
+ }, InferRouteResponse<Extract<TAction['schema'], RouteSchema>>>;
30
+ type Join<A extends string, B extends string> = A extends '' ? B : B extends '' ? A : `${A}/${B}`;
29
31
  /**
30
32
  * Handler map shape required by `createRouter().use(routes, handlers)`.
31
33
  *
32
- * @remarks Each route key must provide handlers for the methods declared by its
33
- * route schema. Handler context is inferred from middleware plus the route's
34
- * path, query/body, and header schemas. An optional `OPTIONS` handler can
35
- * customize CORS preflight responses for a route.
34
+ * @remarks The handler object mirrors the HTTP route tree. Resource nodes become
35
+ * nested handler objects, while action nodes become direct handler functions.
36
+ * Handler context is inferred from middleware plus accumulated path params,
37
+ * query/body schemas, and header schemas.
36
38
  */
37
- export type RouteRequestHandlerMap<TRoutes extends Routes = Routes, TMiddleware extends AnyMiddlewareChain = MiddlewareChain> = {
38
- [K in keyof TRoutes]: {
39
- [TMethod in keyof TRoutes[K]['methods']]: InferRouteRequestHandler<TMiddleware, Extract<TRoutes[K]['methods'][TMethod], RouteSchema>, Extract<TMethod, string>, TRoutes[K]['path']['source']>;
40
- } & {
41
- OPTIONS?: RouteRequestHandler<TMiddleware, {
42
- path: Params<TRoutes[K]['path']['source']>;
43
- }, void>;
44
- };
39
+ export type RouteRequestHandlerMap<TRoutes extends HttpRouteTree = HttpRouteTree, TMiddleware extends AnyMiddlewareChain = MiddlewareChain, TPrefix extends string = ''> = {
40
+ [K in keyof TRoutes]: TRoutes[K] extends HttpResource<infer P, infer C> ? RouteRequestHandlerMap<C, TMiddleware, Join<TPrefix, P>> : TRoutes[K] extends HttpAction<infer P, any, any> ? InferActionHandler<TMiddleware, TRoutes[K], Join<TPrefix, P>> : never;
45
41
  };
46
42
  export {};
package/dist/type.d.ts ADDED
@@ -0,0 +1,22 @@
1
+ import { Unchecked } from './common.js';
2
+ /**
3
+ * Create a compile-time-only marker for an action's JSON response payload type.
4
+ *
5
+ * @remarks `$type<T>()` does not perform runtime validation. It lets Rouzer type
6
+ * server handler return values and client action functions for HTTP actions
7
+ * whose responses are expected to be JSON.
8
+ *
9
+ * @example
10
+ * ```ts
11
+ * import { $type } from 'rouzer'
12
+ * import * as http from 'rouzer/http'
13
+ *
14
+ * const hello = http.get('hello/:name', {
15
+ * response: $type<{ message: string }>(),
16
+ * })
17
+ * ```
18
+ */
19
+ export declare function $type<T>(): Unchecked<T>;
20
+ export declare namespace $type {
21
+ var symbol: symbol;
22
+ }
package/dist/type.js ADDED
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Create a compile-time-only marker for an action's JSON response payload type.
3
+ *
4
+ * @remarks `$type<T>()` does not perform runtime validation. It lets Rouzer type
5
+ * server handler return values and client action functions for HTTP actions
6
+ * whose responses are expected to be JSON.
7
+ *
8
+ * @example
9
+ * ```ts
10
+ * import { $type } from 'rouzer'
11
+ * import * as http from 'rouzer/http'
12
+ *
13
+ * const hello = http.get('hello/:name', {
14
+ * response: $type<{ message: string }>(),
15
+ * })
16
+ * ```
17
+ */
18
+ export function $type() {
19
+ return $type.symbol;
20
+ }
21
+ $type.symbol = Symbol();
@@ -0,0 +1,51 @@
1
+ import type { MatchParams } from '@remix-run/route-pattern/match';
2
+ import type * as z from 'zod';
3
+ import type { MutationRouteSchema, QueryRouteSchema, RouteSchema } from './schema.js';
4
+ declare class Any {
5
+ private isAny;
6
+ }
7
+ type PathArgs<T, P extends string> = T extends {
8
+ path: infer TPath;
9
+ } ? {} extends z.infer<TPath> ? {
10
+ [K in keyof T as 'path']?: z.infer<TPath>;
11
+ } : {
12
+ [K in keyof T as 'path']: z.infer<TPath>;
13
+ } : MatchParams<P> extends infer TParams ? {} extends TParams ? {
14
+ [K in keyof T as 'path']?: TParams;
15
+ } : {
16
+ [K in keyof T as 'path']: TParams;
17
+ } : unknown;
18
+ type QueryArgs<T> = T extends QueryRouteSchema & {
19
+ query: infer TQuery;
20
+ } ? {} extends z.infer<TQuery> ? {
21
+ [K in keyof T as 'query']?: z.infer<TQuery>;
22
+ } : {
23
+ [K in keyof T as 'query']: z.infer<TQuery>;
24
+ } : unknown;
25
+ type MutationArgs<T> = T extends MutationRouteSchema ? T extends {
26
+ body: infer TBody;
27
+ } ? {} extends z.infer<TBody> ? {
28
+ [K in keyof T as 'body']?: z.infer<TBody>;
29
+ } : {
30
+ [K in keyof T as 'body']: z.infer<TBody>;
31
+ } : {
32
+ body?: unknown;
33
+ } : unknown;
34
+ /**
35
+ * Arguments accepted by a client action function or low-level request factory.
36
+ *
37
+ * @remarks The type is derived from an action schema and route pattern. `path`,
38
+ * `query`, `body`, and `headers` are validated by the client before `fetch` when
39
+ * a matching schema exists. Other `RequestInit` fields are forwarded to `fetch`,
40
+ * except `method`, `body`, and `headers`, which Rouzer derives from the action
41
+ * schema and call arguments.
42
+ */
43
+ export type RouteArgs<T extends RouteSchema = any, P extends string = string> = ([T] extends [Any] ? {
44
+ query?: any;
45
+ body?: any;
46
+ path?: any;
47
+ } : QueryArgs<T> & MutationArgs<T> & PathArgs<T, P>) & Omit<RequestInit, 'method' | 'body' | 'headers'> & {
48
+ /** Headers for this request. Undefined values are removed before `fetch`. */
49
+ headers?: Record<string, string | undefined>;
50
+ };
51
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,31 @@
1
+ import type { MatchParams } from '@remix-run/route-pattern/match';
2
+ import type { AnyMiddlewareChain, MiddlewareContext } from 'alien-middleware';
3
+ import type * as z from 'zod';
4
+ import { Promisable } from '../common.js';
5
+ import type { HttpAction } from '../http.js';
6
+ import type { InferRouteResponse } from './response.js';
7
+ import type { RouteSchema } from './schema.js';
8
+ type RequestContext<TMiddleware extends AnyMiddlewareChain> = MiddlewareContext<TMiddleware>;
9
+ export type RouteRequestHandler<TMiddleware extends AnyMiddlewareChain, TArgs extends object, TResult> = (context: RequestContext<TMiddleware> & TArgs) => Promisable<TResult | Response>;
10
+ export type InferActionHandler<TMiddleware extends AnyMiddlewareChain, TAction extends HttpAction, TPath extends string> = TAction['method'] extends 'GET' ? RouteRequestHandler<TMiddleware, {
11
+ path: TAction['schema'] extends {
12
+ path: any;
13
+ } ? z.infer<TAction['schema']['path']> : MatchParams<TPath>;
14
+ query: TAction['schema'] extends {
15
+ query: any;
16
+ } ? z.infer<TAction['schema']['query']> : undefined;
17
+ headers: TAction['schema'] extends {
18
+ headers: any;
19
+ } ? z.infer<TAction['schema']['headers']> : undefined;
20
+ }, InferRouteResponse<Extract<TAction['schema'], RouteSchema>>> : RouteRequestHandler<TMiddleware, {
21
+ path: TAction['schema'] extends {
22
+ path: any;
23
+ } ? z.infer<TAction['schema']['path']> : MatchParams<TPath>;
24
+ body: TAction['schema'] extends {
25
+ body: any;
26
+ } ? z.infer<TAction['schema']['body']> : undefined;
27
+ headers: TAction['schema'] extends {
28
+ headers: any;
29
+ } ? z.infer<TAction['schema']['headers']> : undefined;
30
+ }, InferRouteResponse<Extract<TAction['schema'], RouteSchema>>>;
31
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,7 @@
1
+ export type * from './args.js';
2
+ export type * from './handler.js';
3
+ export type * from './infer.js';
4
+ export type * from './request.js';
5
+ export type * from './response.js';
6
+ export type * from './schema.js';
7
+ export type * from './server.js';
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,19 @@
1
+ import type * as z from 'zod';
2
+ import type { MutationRouteSchema, RouteSchema } from './schema.js';
3
+ import type { RouteRequestFactory } from './request.js';
4
+ type InferRouteSchemaBody<TSchema> = TSchema extends MutationRouteSchema ? TSchema extends {
5
+ body: infer TBody;
6
+ } ? z.infer<TBody> : unknown : never;
7
+ type InferRouteArgsBody<TArgs> = TArgs extends {
8
+ body?: infer TBody;
9
+ } ? TBody : never;
10
+ /**
11
+ * Infer the request body type from an action schema or request factory.
12
+ *
13
+ * @remarks HTTP action schemas can be inspected with
14
+ * `InferRouteBody<typeof action.schema>`. Request factories for mutation actions
15
+ * infer their `body` argument type. Schemas without a body schema infer
16
+ * `unknown`.
17
+ */
18
+ export type InferRouteBody<T> = T extends RouteRequestFactory<any, any> ? InferRouteArgsBody<T['$args']> : T extends RouteSchema ? InferRouteSchemaBody<T> : never;
19
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export type Join<A extends string, B extends string> = A extends '' ? B : B extends '' ? A : `${A}/${B}`;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,35 @@
1
+ import type { RoutePattern } from '@remix-run/route-pattern';
2
+ import type { RouteArgs } from './args.js';
3
+ import type { InferRouteResponse } from './response.js';
4
+ import type { RouteSchema } from './schema.js';
5
+ /**
6
+ * Request descriptor produced by an HTTP action request factory.
7
+ *
8
+ * @remarks Pass this object to `client.request(...)` for a raw `Response` or
9
+ * `client.json(...)` for parsed JSON handling.
10
+ */
11
+ export type RouteRequest<TResult = any> = {
12
+ /** Method schema used for client-side validation. */
13
+ schema: RouteSchema;
14
+ /** Parsed route pattern used to generate the request URL. */
15
+ path: RoutePattern;
16
+ /** HTTP method to send. */
17
+ method: string;
18
+ /** Validated route arguments and request options. */
19
+ args: RouteArgs;
20
+ /** Phantom result type consumed by `client.json(...)`. */
21
+ $result: TResult;
22
+ };
23
+ /**
24
+ * Callable factory attached to an HTTP action.
25
+ *
26
+ * @remarks Calling a factory validates no data by itself; it creates a typed
27
+ * `RouteRequest` descriptor for `createClient` to validate and send.
28
+ */
29
+ export type RouteRequestFactory<T extends RouteSchema, P extends string> = {
30
+ (...p: RouteArgs<T, P> extends infer TArgs ? {} extends TArgs ? [args?: TArgs] : [args: TArgs] : never): RouteRequest<InferRouteResponse<T>>;
31
+ /** Inferred argument type for this request factory. */
32
+ $args: RouteArgs<T, P>;
33
+ /** Inferred JSON response type for this request factory. */
34
+ $response: InferRouteResponse<T>;
35
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,9 @@
1
+ import type { Unchecked, RouteSchema } from './schema.js';
2
+ /** `Response` whose `.json()` method resolves to a known payload type. */
3
+ export type RouteResponse<TResult = any> = Response & {
4
+ json(): Promise<TResult>;
5
+ };
6
+ /** Infer the JSON response payload type from an action schema. */
7
+ export type InferRouteResponse<T extends RouteSchema> = T extends {
8
+ response: Unchecked<infer TResponse>;
9
+ } ? TResponse : void;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,37 @@
1
+ import * as z from 'zod';
2
+ import { Unchecked } from '../common.js';
3
+ /**
4
+ * Compile-time-only marker used by `$type<T>()` for unchecked response types.
5
+ *
6
+ * @remarks Application code should usually call `$type<T>()` instead of naming
7
+ * this marker directly.
8
+ */
9
+ export type { Unchecked };
10
+ /** Schema shape for `GET` route methods. */
11
+ export type QueryRouteSchema = {
12
+ /** Optional Zod object used to validate path params. */
13
+ path?: z.ZodObject<any>;
14
+ /** Optional Zod object used to validate URL query params. */
15
+ query?: z.ZodObject<any>;
16
+ /** `GET` routes do not accept request bodies. */
17
+ body?: never;
18
+ /** Optional Zod object used to validate request headers. */
19
+ headers?: z.ZodObject<any>;
20
+ /** Optional compile-time-only JSON response type marker. */
21
+ response?: Unchecked<any>;
22
+ };
23
+ /** Schema shape for mutation route methods. */
24
+ export type MutationRouteSchema = {
25
+ /** Optional Zod object used to validate path params. */
26
+ path?: z.ZodObject<any>;
27
+ /** Mutation routes do not accept query schemas. */
28
+ query?: never;
29
+ /** Optional Zod schema used to validate the JSON request body. */
30
+ body?: z.ZodType<any, any>;
31
+ /** Optional Zod object used to validate request headers. */
32
+ headers?: z.ZodObject<any>;
33
+ /** Optional compile-time-only JSON response type marker. */
34
+ response?: Unchecked<any>;
35
+ };
36
+ /** Any HTTP action schema Rouzer can execute. */
37
+ export type RouteSchema = QueryRouteSchema | MutationRouteSchema;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,15 @@
1
+ import type { AnyMiddlewareChain, MiddlewareChain } from 'alien-middleware';
2
+ import type { HttpAction, HttpResource, HttpRouteTree } from '../http.js';
3
+ import type { InferActionHandler } from './handler.js';
4
+ import type { Join } from './path.js';
5
+ /**
6
+ * Handler map shape required by `createRouter().use(routes, handlers)`.
7
+ *
8
+ * @remarks The handler object mirrors the HTTP route tree. Resource nodes become
9
+ * nested handler objects, while action nodes become direct handler functions.
10
+ * Handler context is inferred from middleware plus accumulated path params,
11
+ * query/body schemas, and header schemas.
12
+ */
13
+ export type RouteRequestHandlerMap<TRoutes extends HttpRouteTree = HttpRouteTree, TMiddleware extends AnyMiddlewareChain = MiddlewareChain, TPrefix extends string = ''> = {
14
+ [K in keyof TRoutes]: TRoutes[K] extends HttpResource<infer P, infer C> ? RouteRequestHandlerMap<C, TMiddleware, Join<TPrefix, P>> : TRoutes[K] extends HttpAction<infer P, any, any> ? InferActionHandler<TMiddleware, TRoutes[K], Join<TPrefix, P>> : never;
15
+ };
@@ -0,0 +1 @@
1
+ export {};
package/dist/types.d.ts CHANGED
@@ -1,4 +1,5 @@
1
- import { Params, RoutePattern } from '@remix-run/route-pattern';
1
+ import type { MatchParams } from '@remix-run/route-pattern/match';
2
+ import { RoutePattern } from '@remix-run/route-pattern';
2
3
  import * as z from 'zod';
3
4
  import { Unchecked } from './common.js';
4
5
  /**
@@ -34,41 +35,8 @@ export type MutationRouteSchema = {
34
35
  /** Optional compile-time-only JSON response type marker. */
35
36
  response?: Unchecked<any>;
36
37
  };
37
- /**
38
- * Method schema map accepted by `route(...)`.
39
- *
40
- * @remarks `GET` validates query input, mutation methods validate JSON body
41
- * input, and `ALL` acts as a fallback for methods that are not declared
42
- * explicitly.
43
- */
44
- export type RouteSchemaMap = {
45
- GET?: QueryRouteSchema;
46
- POST?: MutationRouteSchema;
47
- PUT?: MutationRouteSchema;
48
- PATCH?: MutationRouteSchema;
49
- DELETE?: MutationRouteSchema;
50
- ALL?: {
51
- /** Optional Zod object used to validate path params. */
52
- path?: z.ZodObject<any>;
53
- /** Optional Zod object used to validate URL query params. */
54
- query?: z.ZodObject<any>;
55
- /** `ALL` fallback routes do not accept request bodies. */
56
- body?: never;
57
- /** Optional Zod object used to validate request headers. */
58
- headers?: z.ZodObject<any>;
59
- /** `ALL` fallback routes do not define typed JSON responses. */
60
- response?: never;
61
- };
62
- };
63
- /** Any route method schema Rouzer can execute. */
38
+ /** Any HTTP action schema Rouzer can execute. */
64
39
  export type RouteSchema = QueryRouteSchema | MutationRouteSchema;
65
- /** Route map accepted by `createRouter().use(...)` and `createClient(...)`. */
66
- export type Routes = {
67
- [key: string]: {
68
- path: RoutePattern;
69
- methods: RouteSchemaMap;
70
- };
71
- };
72
40
  declare class Any {
73
41
  private isAny;
74
42
  }
@@ -78,7 +46,7 @@ type PathArgs<T, P extends string> = T extends {
78
46
  [K in keyof T as 'path']?: z.infer<TPath>;
79
47
  } : {
80
48
  [K in keyof T as 'path']: z.infer<TPath>;
81
- } : Params<P> extends infer TParams ? {} extends TParams ? {
49
+ } : MatchParams<P> extends infer TParams ? {} extends TParams ? {
82
50
  [K in keyof T as 'path']?: TParams;
83
51
  } : {
84
52
  [K in keyof T as 'path']: TParams;
@@ -100,13 +68,13 @@ type MutationArgs<T> = T extends MutationRouteSchema ? T extends {
100
68
  body?: unknown;
101
69
  } : unknown;
102
70
  /**
103
- * Arguments accepted by a route request factory such as `route.GET(...)`.
71
+ * Arguments accepted by a client action function or low-level request factory.
104
72
  *
105
- * @remarks The type is derived from a method schema and route pattern. `path`,
73
+ * @remarks The type is derived from an action schema and route pattern. `path`,
106
74
  * `query`, `body`, and `headers` are validated by the client before `fetch` when
107
- * a matching schema exists. The client forwards the HTTP method, JSON body, and
108
- * headers; extra `RequestInit` fields are accepted by the type surface but are
109
- * not forwarded.
75
+ * a matching schema exists. Other `RequestInit` fields are forwarded to `fetch`,
76
+ * except `method`, `body`, and `headers`, which Rouzer derives from the action
77
+ * schema and call arguments.
110
78
  */
111
79
  export type RouteArgs<T extends RouteSchema = any, P extends string = string> = ([T] extends [Any] ? {
112
80
  query?: any;
@@ -117,7 +85,7 @@ export type RouteArgs<T extends RouteSchema = any, P extends string = string> =
117
85
  headers?: Record<string, string | undefined>;
118
86
  };
119
87
  /**
120
- * Request descriptor produced by a route request factory.
88
+ * Request descriptor produced by an HTTP action request factory.
121
89
  *
122
90
  * @remarks Pass this object to `client.request(...)` for a raw `Response` or
123
91
  * `client.json(...)` for parsed JSON handling.
@@ -138,7 +106,7 @@ export type RouteRequest<TResult = any> = {
138
106
  export type RouteResponse<TResult = any> = Response & {
139
107
  json(): Promise<TResult>;
140
108
  };
141
- /** Infer the JSON response payload type from a method schema. */
109
+ /** Infer the JSON response payload type from an action schema. */
142
110
  export type InferRouteResponse<T extends RouteSchema> = T extends {
143
111
  response: Unchecked<infer TResponse>;
144
112
  } ? TResponse : void;
@@ -149,23 +117,16 @@ type InferRouteArgsBody<TArgs> = TArgs extends {
149
117
  body?: infer TBody;
150
118
  } ? TBody : never;
151
119
  /**
152
- * Infer the request body type from a mutation schema or route request factory.
120
+ * Infer the request body type from an action schema or request factory.
153
121
  *
154
- * @remarks Route request factories for mutation methods infer their `body`
155
- * argument type. Mutation schemas without a body schema infer `unknown`.
122
+ * @remarks HTTP action schemas can be inspected with
123
+ * `InferRouteBody<typeof action.schema>`. Request factories for mutation actions
124
+ * infer their `body` argument type. Schemas without a body schema infer
125
+ * `unknown`.
156
126
  */
157
127
  export type InferRouteBody<T> = T extends RouteRequestFactory<any, any> ? InferRouteArgsBody<T['$args']> : T extends RouteSchema ? InferRouteSchemaBody<T> : never;
158
128
  /**
159
- * Infer the request body type for a named method on a `Route`.
160
- *
161
- * @remarks `GET` and `ALL` infer `never` because they do not accept request
162
- * bodies.
163
- */
164
- export type InferRouteMethodBody<TRoute extends {
165
- methods: RouteSchemaMap;
166
- }, TMethod extends keyof TRoute['methods']> = TMethod extends 'GET' | 'ALL' ? never : TMethod extends keyof TRoute ? InferRouteBody<TRoute[TMethod]> : InferRouteBody<Extract<TRoute['methods'][TMethod], RouteSchema>>;
167
- /**
168
- * Callable factory attached to a `Route` for each declared method.
129
+ * Callable factory attached to an HTTP action.
169
130
  *
170
131
  * @remarks Calling a factory validates no data by itself; it creates a typed
171
132
  * `RouteRequest` descriptor for `createClient` to validate and send.