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.
package/README.md CHANGED
@@ -1,17 +1,17 @@
1
1
  # Rouzer
2
2
 
3
- Rouzer lets you declare a route once and share its TypeScript types and Zod
4
- validation between a Hattip-compatible server and a typed fetch client.
3
+ Rouzer lets you declare an HTTP route tree once and share its TypeScript types
4
+ and Zod validation between a Hattip-compatible server and a typed fetch client.
5
5
 
6
6
  ## What it does
7
7
 
8
- A Rouzer route declaration defines a URL pattern, method schemas, and optional
9
- response type once, then reuses that contract to:
8
+ A Rouzer HTTP route tree defines URL patterns, named actions, method schemas, and
9
+ optional response types once, then reuses that contract to:
10
10
 
11
11
  - validate client arguments before `fetch`
12
12
  - match and validate server requests before handlers run
13
13
  - type handler context from path, query/body, headers, and middleware
14
- - attach typed client shorthand methods such as `client.helloRoute.GET(...)`
14
+ - attach typed client action functions such as `client.profiles.get(...)`
15
15
 
16
16
  Rouzer optimizes for shared TypeScript route modules over language-agnostic API
17
17
  schemas or generated SDKs.
@@ -20,10 +20,10 @@ schemas or generated SDKs.
20
20
 
21
21
  Use Rouzer if:
22
22
 
23
- - your server and client can import the same TypeScript route declarations
23
+ - your server and client can import the same TypeScript route tree
24
24
  - you want Zod request validation on both sides of an HTTP boundary
25
25
  - a Hattip-compatible handler fits your server runtime
26
- - you prefer a small routing/client contract over a full web framework
26
+ - you prefer named resource/action functions over a generated client class
27
27
 
28
28
  Consider something else if:
29
29
 
@@ -41,7 +41,7 @@ Consider something else if:
41
41
  - Zod v4 or newer
42
42
  - a Hattip adapter when using `createRouter(...)`
43
43
  - a Fetch API implementation when using `createClient(...)`
44
- - an absolute `baseURL` for pathname route patterns
44
+ - an absolute `baseURL` for generated client URLs
45
45
 
46
46
  ## Installation
47
47
 
@@ -49,41 +49,40 @@ Consider something else if:
49
49
  pnpm add rouzer zod
50
50
  ```
51
51
 
52
- Import the public API from the root package:
52
+ Import the primary API from the root package and declare routes through the HTTP
53
+ subpath:
53
54
 
54
55
  ```ts
55
- import { $type, chain, createClient, createRouter, route } from 'rouzer'
56
+ import { $type, chain, createClient, createRouter } from 'rouzer'
57
+ import * as http from 'rouzer/http'
56
58
  ```
57
59
 
58
60
  `chain` is re-exported from `alien-middleware` for typed server middleware.
59
61
 
60
62
  ## Quick example
61
63
 
62
- This example shows the core loop: one route contract defines validation, server
63
- handler types, and the typed client call.
64
+ This example shows the core loop: one HTTP action contract defines validation,
65
+ server handler types, and the typed client call.
64
66
 
65
67
  ```ts
66
68
  import * as z from 'zod'
67
- import { $type, createClient, createRouter, route } from 'rouzer'
68
-
69
- export const helloRoute = route('hello/:name', {
70
- GET: {
71
- query: z.object({
72
- excited: z.optional(z.boolean()),
73
- }),
74
- response: $type<{ message: string }>(),
75
- },
69
+ import { $type, createClient, createRouter } from 'rouzer'
70
+ import * as http from 'rouzer/http'
71
+
72
+ export const hello = http.get('hello/:name', {
73
+ query: z.object({
74
+ excited: z.optional(z.boolean()),
75
+ }),
76
+ response: $type<{ message: string }>(),
76
77
  })
77
78
 
78
- export const routes = { helloRoute }
79
+ export const routes = { hello }
79
80
 
80
81
  export const handler = createRouter({ basePath: 'api/' }).use(routes, {
81
- helloRoute: {
82
- GET(ctx) {
83
- return {
84
- message: `Hello, ${ctx.path.name}${ctx.query.excited ? '!' : '.'}`,
85
- }
86
- },
82
+ hello(ctx) {
83
+ return {
84
+ message: `Hello, ${ctx.path.name}${ctx.query.excited ? '!' : '.'}`,
85
+ }
87
86
  },
88
87
  })
89
88
 
@@ -92,20 +91,20 @@ const client = createClient({
92
91
  routes,
93
92
  })
94
93
 
95
- const { message } = await client.helloRoute.GET({
94
+ const { message } = await client.hello({
96
95
  path: { name: 'world' },
97
96
  query: { excited: true },
98
97
  })
99
98
  ```
100
99
 
101
- `handler` can be mounted with any Hattip adapter. Client calls validate route
102
- arguments before `fetch`; server handlers validate matched path, query, headers,
103
- and JSON bodies before your handler runs.
100
+ `handler` can be mounted with any Hattip adapter. Client action calls validate
101
+ route arguments before `fetch`; server handlers validate matched path, query,
102
+ headers, and JSON bodies before your handler runs.
104
103
 
105
104
  ## Documentation
106
105
 
107
- - [Concepts and API selection](docs/context.md)
106
+ - [Concepts, API selection, and v2.0.1 migration notes](docs/context.md)
108
107
  - [Runnable shared-route example](examples/basic-usage.ts)
109
108
  - Generated declarations in the published package provide the exact signatures
110
- for every public export.
109
+ for every public export, including the `rouzer/http` subpath.
111
110
  - Public TSDoc in `src/` owns symbol-level behavior and option details.
@@ -1,18 +1,21 @@
1
1
  import { Promisable } from '../common.js';
2
- import { Route } from '../route.js';
3
- import type { InferRouteResponse, RouteArgs, RouteRequest, RouteSchema } from '../types.js';
4
- /** Client type inferred from a route map passed to `createClient`. */
5
- export type RouzerClient<TRoutes extends Record<string, Route> = Record<string, never>> = ReturnType<typeof createClient<TRoutes>>;
2
+ import type { HttpAction, HttpResource, HttpRouteTree } from '../http.js';
3
+ import type { RouteArgs } from '../types/args.js';
4
+ import type { RouteRequest } from '../types/request.js';
5
+ import type { InferRouteResponse } from '../types/response.js';
6
+ import type { RouteSchema } from '../types/schema.js';
7
+ /** Client type inferred from an HTTP route tree passed to `createClient`. */
8
+ export type RouzerClient<TRoutes extends HttpRouteTree = Record<string, never>> = ReturnType<typeof createClient<TRoutes>>;
6
9
  /**
7
- * Create a typed fetch client for Rouzer route declarations.
10
+ * Create a typed fetch client for an HTTP route tree.
8
11
  *
9
12
  * @remarks The returned client always includes `request(...)` for raw responses
10
- * and `json(...)` for parsed JSON. Passing `routes` also attaches shorthand
11
- * methods such as `client.helloRoute.GET(...)`.
13
+ * and `json(...)` for parsed JSON. Passing `routes` also mirrors the resource
14
+ * tree and attaches direct action functions such as `client.users.list(...)`.
12
15
  */
13
- export declare function createClient<TRoutes extends Record<string, Route> = Record<string, never>>(config: {
16
+ export declare function createClient<TRoutes extends HttpRouteTree = Record<string, never>>(config: {
14
17
  /**
15
- * Absolute base URL used for pathname route patterns.
18
+ * Absolute base URL used for generated request URLs.
16
19
  *
17
20
  * @remarks A trailing slash is added when missing. In browsers, derive a
18
21
  * relative API path with `new URL('/api/', window.location.origin).href`.
@@ -26,12 +29,12 @@ export declare function createClient<TRoutes extends Record<string, Route> = Rec
26
29
  */
27
30
  headers?: Record<string, string>;
28
31
  /**
29
- * Route map to attach as shorthand methods on the client.
32
+ * HTTP route tree to attach as direct client action functions.
30
33
  *
31
34
  * @example
32
35
  * ```ts
33
36
  * const client = createClient({ baseURL: 'https://example.com/api/', routes })
34
- * await client.helloRoute.GET({ path: { name: 'world' } })
37
+ * await client.users.list({ query: { page: 1 } })
35
38
  * ```
36
39
  */
37
40
  routes?: TRoutes;
@@ -40,16 +43,14 @@ export declare function createClient<TRoutes extends Record<string, Route> = Rec
40
43
  *
41
44
  * @remarks When provided, the return value is returned from `.json()` as-is;
42
45
  * Rouzer does not automatically parse a `Response` returned by this hook.
43
- * Without this hook, `.json()` throws an `Error` and copies JSON error-body
44
- * properties onto it when the response has a JSON content type.
45
46
  */
46
47
  onJsonError?: (response: Response) => Promisable<Response>;
47
48
  /** Custom `fetch` implementation to use for requests. */
48
49
  fetch?: typeof globalThis.fetch;
49
- }): { [K in keyof TRoutes]: TRoutes[K]["methods"] extends infer TMethods ? { [M in keyof TMethods]: RouteFunction<Extract<TMethods[M], RouteSchema>, TRoutes[K]["path"]["source"]>; } : never; } & {
50
+ }): ClientTree<TRoutes, ""> & {
50
51
  config: {
51
52
  /**
52
- * Absolute base URL used for pathname route patterns.
53
+ * Absolute base URL used for generated request URLs.
53
54
  *
54
55
  * @remarks A trailing slash is added when missing. In browsers, derive a
55
56
  * relative API path with `new URL('/api/', window.location.origin).href`.
@@ -61,41 +62,44 @@ export declare function createClient<TRoutes extends Record<string, Route> = Rec
61
62
  * @remarks Per-request headers are merged on top of these values. Undefined
62
63
  * per-request headers are removed before `fetch`.
63
64
  */
64
- headers?: Record<string, string> | undefined;
65
+ headers?: Record<string, string>;
65
66
  /**
66
- * Route map to attach as shorthand methods on the client.
67
+ * HTTP route tree to attach as direct client action functions.
67
68
  *
68
69
  * @example
69
70
  * ```ts
70
71
  * const client = createClient({ baseURL: 'https://example.com/api/', routes })
71
- * await client.helloRoute.GET({ path: { name: 'world' } })
72
+ * await client.users.list({ query: { page: 1 } })
72
73
  * ```
73
74
  */
74
- routes?: TRoutes | undefined;
75
+ routes?: TRoutes;
75
76
  /**
76
77
  * Custom handler for non-2xx responses from `.json()`.
77
78
  *
78
79
  * @remarks When provided, the return value is returned from `.json()` as-is;
79
80
  * Rouzer does not automatically parse a `Response` returned by this hook.
80
- * Without this hook, `.json()` throws an `Error` and copies JSON error-body
81
- * properties onto it when the response has a JSON content type.
82
81
  */
83
- onJsonError?: ((response: Response) => Promisable<Response>) | undefined;
82
+ onJsonError?: (response: Response) => Promisable<Response>;
84
83
  /** Custom `fetch` implementation to use for requests. */
85
- fetch?: typeof globalThis.fetch | undefined;
84
+ fetch?: typeof globalThis.fetch;
86
85
  };
87
- request: <T extends RouteRequest>({ path: pathBuilder, method, args: { path, query, body, headers }, schema, }: T) => Promise<Response & {
88
- json(): Promise<T["$result"]>;
86
+ request: <T extends RouteRequest>({ path: pathBuilder, method, args, schema, }: T) => Promise<Response & {
87
+ json(): Promise<T['$result']>;
89
88
  }>;
90
- json: <T extends RouteRequest>(props: T) => Promise<T["$result"]>;
89
+ json: <T extends RouteRequest>(props: T) => Promise<T['$result']>;
90
+ };
91
+ type Join<A extends string, B extends string> = A extends '' ? B : B extends '' ? A : `${A}/${B}`;
92
+ /** Client object shape produced from an HTTP route tree. */
93
+ export type ClientTree<T extends HttpRouteTree, TPrefix extends string = ''> = {
94
+ [K in keyof T]: T[K] extends HttpResource<infer P, infer C> ? ClientTree<C, Join<TPrefix, P>> : T[K] extends HttpAction<infer P, infer S, any> ? RouteFunction<S, Join<TPrefix, P>> : never;
91
95
  };
92
96
  /**
93
- * Shorthand client method attached for each route method when `routes` is passed
94
- * to `createClient`.
97
+ * Client action function attached for each HTTP action leaf.
95
98
  *
96
- * @remarks Methods whose schema has `response: $type<T>()` return parsed JSON as
97
- * `T`. Methods without a response marker return the raw `Response`.
99
+ * @remarks Actions whose schema has `response: $type<T>()` return parsed JSON
100
+ * as `T`. Actions without a response marker return the raw `Response`.
98
101
  */
99
102
  export type RouteFunction<T extends RouteSchema, P extends string> = (...p: RouteArgs<T, P> extends infer TArgs ? {} extends TArgs ? [args?: TArgs] : [args: TArgs] : never) => Promise<T extends {
100
103
  response: any;
101
104
  } ? InferRouteResponse<T> : Response>;
105
+ export {};
@@ -1,27 +1,30 @@
1
- import { mapValues, shake } from '../common.js';
1
+ import { RoutePattern } from '@remix-run/route-pattern';
2
+ import { createHref } from '@remix-run/route-pattern/href';
3
+ import { shake } from '../common.js';
2
4
  /**
3
- * Create a typed fetch client for Rouzer route declarations.
5
+ * Create a typed fetch client for an HTTP route tree.
4
6
  *
5
7
  * @remarks The returned client always includes `request(...)` for raw responses
6
- * and `json(...)` for parsed JSON. Passing `routes` also attaches shorthand
7
- * methods such as `client.helloRoute.GET(...)`.
8
+ * and `json(...)` for parsed JSON. Passing `routes` also mirrors the resource
9
+ * tree and attaches direct action functions such as `client.users.list(...)`.
8
10
  */
9
11
  export function createClient(config) {
10
12
  const baseURL = config.baseURL.replace(/\/?$/, '/');
11
13
  const defaultHeaders = config.headers && shake(config.headers);
12
14
  const fetch = config.fetch ?? globalThis.fetch;
13
- async function request({ path: pathBuilder, method, args: { path, query, body, headers }, schema, }) {
15
+ async function request({ path: pathBuilder, method, args, schema, }) {
16
+ let { path, query, body, headers, ...init } = args;
14
17
  if (schema.path) {
15
18
  path = schema.path.parse(path);
16
19
  }
17
20
  let url;
18
- const href = pathBuilder.href(path);
21
+ const href = createHref(pathBuilder, path);
19
22
  if (href[0] === '/') {
20
23
  url = new URL(baseURL);
21
24
  url.pathname += href.slice(1);
22
25
  }
23
26
  else {
24
- url = new URL(href);
27
+ url = new URL(href, baseURL);
25
28
  }
26
29
  if (schema.query) {
27
30
  query = schema.query.parse(query ?? {});
@@ -46,6 +49,7 @@ export function createClient(config) {
46
49
  headers = schema.headers.parse(headers);
47
50
  }
48
51
  return fetch(url, {
52
+ ...init,
49
53
  method,
50
54
  body: body !== undefined ? JSON.stringify(body) : undefined,
51
55
  headers: (headers ?? defaultHeaders),
@@ -57,7 +61,7 @@ export function createClient(config) {
57
61
  if (config.onJsonError) {
58
62
  return config.onJsonError(response);
59
63
  }
60
- const error = new Error(`Request to ${props.method} ${props.path.href(props.args.path)} failed with status ${response.status}`);
64
+ const error = new Error(`Request to ${props.method} ${createHref(props.path, props.args.path)} failed with status ${response.status}`);
61
65
  const contentType = response.headers.get('content-type');
62
66
  if (contentType?.includes('application/json')) {
63
67
  Object.assign(error, await response.json());
@@ -68,19 +72,35 @@ export function createClient(config) {
68
72
  }
69
73
  return {
70
74
  ...(config.routes
71
- ? mapValues(config.routes, route => connectRoute(route, request, json))
75
+ ? connectTree(config.routes, '', request, json)
72
76
  : null),
73
77
  config,
74
78
  request,
75
79
  json,
76
80
  };
77
81
  }
78
- function connectRoute(route, request, json) {
79
- return {
80
- ...route,
81
- ...mapValues(route.methods, (schema, key) => {
82
- const fetch = schema.response ? json : request;
83
- return (args) => fetch(route[key](args));
84
- }),
85
- };
82
+ function connectTree(tree, prefix, request, json) {
83
+ return Object.fromEntries(Object.entries(tree).map(([key, node]) => {
84
+ if (node.kind === 'resource') {
85
+ return [
86
+ key,
87
+ connectTree(node.children, joinPaths(prefix, node.path.source), request, json),
88
+ ];
89
+ }
90
+ const path = RoutePattern.parse(joinPaths(prefix, node.path?.source ?? ''));
91
+ const fetch = node.schema.response ? json : request;
92
+ return [
93
+ key,
94
+ (args = {}) => fetch({
95
+ schema: node.schema,
96
+ path,
97
+ method: node.method,
98
+ args,
99
+ $result: undefined,
100
+ }),
101
+ ];
102
+ }));
103
+ }
104
+ function joinPaths(left, right) {
105
+ return [left, right].filter(Boolean).join('/').replace(/\/+/g, '/');
86
106
  }
package/dist/http.d.ts ADDED
@@ -0,0 +1,67 @@
1
+ import { RoutePattern } from '@remix-run/route-pattern';
2
+ import type { RouteRequestFactory } from './types/request.js';
3
+ import type { RouteSchema } from './types/schema.js';
4
+ /** HTTP methods supported by Rouzer action declarations. */
5
+ export type HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
6
+ /**
7
+ * Callable endpoint leaf in an HTTP route tree.
8
+ *
9
+ * @remarks Actions declare one HTTP operation. Their property name becomes the
10
+ * client/handler name, while their optional `path` contributes URL segments.
11
+ */
12
+ export type HttpAction<P extends string = string, T extends RouteSchema = RouteSchema, M extends HttpMethod = HttpMethod> = {
13
+ /** Discriminator used internally when traversing route trees. */
14
+ kind: 'action';
15
+ /** Optional action-local path segment appended after parent resources. */
16
+ path?: RoutePattern<P>;
17
+ /** HTTP method used when the client sends this action. */
18
+ method: M;
19
+ /** Request validation and optional response type schema. */
20
+ schema: T;
21
+ /** Low-level request descriptor factory for this action. */
22
+ request: RouteRequestFactory<T, P>;
23
+ };
24
+ /**
25
+ * Path-scoped namespace in an HTTP route tree.
26
+ *
27
+ * @remarks Resources contribute URL path segments and contain child resources or
28
+ * actions. They do not have handlers of their own.
29
+ */
30
+ export type HttpResource<P extends string = string, TChildren extends HttpRouteTree = HttpRouteTree> = {
31
+ /** Discriminator used internally when traversing route trees. */
32
+ kind: 'resource';
33
+ /** Path segment contributed by this resource. */
34
+ path: RoutePattern<P>;
35
+ /** Child resources and actions exposed below this resource. */
36
+ children: TChildren;
37
+ };
38
+ /** Node type accepted inside an HTTP route tree. */
39
+ export type HttpNode = HttpAction | HttpResource;
40
+ /** Route tree accepted by HTTP clients and routers. */
41
+ export type HttpRouteTree = {
42
+ [key: string]: HttpNode;
43
+ };
44
+ /**
45
+ * Declare an HTTP resource namespace.
46
+ *
47
+ * @remarks The resource `path` is joined with any parent resource path. Child
48
+ * property names are API names only; they do not affect the URL unless the child
49
+ * is another resource or an action with an explicit path.
50
+ */
51
+ export declare function resource<const P extends string, const TChildren extends HttpRouteTree>(path: P, children: TChildren): HttpResource<P, TChildren>;
52
+ /** Declare a GET action, optionally with an action-local path segment. */
53
+ export declare function get<const P extends string, const T extends RouteSchema>(path: P, schema: T): HttpAction<P, T, 'GET'>;
54
+ export declare function get<const T extends RouteSchema>(schema: T): HttpAction<'', T, 'GET'>;
55
+ /** Declare a POST action, optionally with an action-local path segment. */
56
+ export declare function post<const P extends string, const T extends RouteSchema>(path: P, schema: T): HttpAction<P, T, 'POST'>;
57
+ export declare function post<const T extends RouteSchema>(schema: T): HttpAction<'', T, 'POST'>;
58
+ /** Declare a PUT action, optionally with an action-local path segment. */
59
+ export declare function put<const P extends string, const T extends RouteSchema>(path: P, schema: T): HttpAction<P, T, 'PUT'>;
60
+ export declare function put<const T extends RouteSchema>(schema: T): HttpAction<'', T, 'PUT'>;
61
+ /** Declare a PATCH action, optionally with an action-local path segment. */
62
+ export declare function patch<const P extends string, const T extends RouteSchema>(path: P, schema: T): HttpAction<P, T, 'PATCH'>;
63
+ export declare function patch<const T extends RouteSchema>(schema: T): HttpAction<'', T, 'PATCH'>;
64
+ /** Declare a DELETE action, optionally with an action-local path segment. */
65
+ declare function deleteAction<const P extends string, const T extends RouteSchema>(path: P, schema: T): HttpAction<P, T, 'DELETE'>;
66
+ declare function deleteAction<const T extends RouteSchema>(schema: T): HttpAction<'', T, 'DELETE'>;
67
+ export { deleteAction as delete };
package/dist/http.js ADDED
@@ -0,0 +1,43 @@
1
+ import { RoutePattern } from '@remix-run/route-pattern';
2
+ /**
3
+ * Declare an HTTP resource namespace.
4
+ *
5
+ * @remarks The resource `path` is joined with any parent resource path. Child
6
+ * property names are API names only; they do not affect the URL unless the child
7
+ * is another resource or an action with an explicit path.
8
+ */
9
+ export function resource(path, children) {
10
+ return {
11
+ kind: 'resource',
12
+ path: RoutePattern.parse(path),
13
+ children,
14
+ };
15
+ }
16
+ export function get(pathOrSchema, schema) {
17
+ return action('GET', pathOrSchema, schema);
18
+ }
19
+ export function post(pathOrSchema, schema) {
20
+ return action('POST', pathOrSchema, schema);
21
+ }
22
+ export function put(pathOrSchema, schema) {
23
+ return action('PUT', pathOrSchema, schema);
24
+ }
25
+ export function patch(pathOrSchema, schema) {
26
+ return action('PATCH', pathOrSchema, schema);
27
+ }
28
+ function deleteAction(pathOrSchema, schema) {
29
+ return action('DELETE', pathOrSchema, schema);
30
+ }
31
+ export { deleteAction as delete };
32
+ function action(method, pathOrSchema, schema) {
33
+ const path = typeof pathOrSchema === 'string' ? RoutePattern.parse(pathOrSchema) : undefined;
34
+ schema ??= typeof pathOrSchema === 'string' ? {} : pathOrSchema;
35
+ const request = ((args = {}) => ({
36
+ schema,
37
+ path: path ?? RoutePattern.parse(''),
38
+ method,
39
+ args,
40
+ $result: undefined,
41
+ }));
42
+ return { kind: 'action', path, method, schema, request };
43
+ }
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  export * from './client/index.js';
2
- export * from './route.js';
2
+ export * from './type.js';
3
3
  export * from './server/router.js';
4
- export type * from './types.js';
4
+ export type * from './types/index.js';
5
5
  export * from 'alien-middleware';
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
1
  export * from './client/index.js';
2
- export * from './route.js';
2
+ export * from './type.js';
3
3
  export * from './server/router.js';
4
4
  export * from 'alien-middleware';
@@ -0,0 +1,17 @@
1
+ import type { RoutePattern } from '@remix-run/route-pattern';
2
+ import type { RouteRequestFactory, RouteSchema } from './types.js';
3
+ /** @internal */
4
+ export type RouteSchemaMap = {
5
+ GET?: RouteSchema;
6
+ POST?: RouteSchema;
7
+ PUT?: RouteSchema;
8
+ PATCH?: RouteSchema;
9
+ DELETE?: RouteSchema;
10
+ };
11
+ /** @internal */
12
+ export type Route<P extends string = string, T extends RouteSchemaMap = RouteSchemaMap> = {
13
+ path: RoutePattern<P>;
14
+ methods: T;
15
+ } & {
16
+ [K in keyof T]: RouteRequestFactory<Extract<T[K], RouteSchema>, P>;
17
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -1,7 +1,7 @@
1
1
  import type { HattipHandler } from '@hattip/core';
2
2
  import { ApplyMiddleware, chain, ExtractMiddleware, MiddlewareChain, MiddlewareTypes } from 'alien-middleware';
3
- import type { Routes } from '../types.js';
4
- import type { RouteRequestHandlerMap } from './types.js';
3
+ import type { HttpRouteTree } from '../http.js';
4
+ import type { RouteRequestHandlerMap } from '../types/server.js';
5
5
  export { chain };
6
6
  /** Configuration for `createRouter`. */
7
7
  export type RouterConfig = {
@@ -54,11 +54,15 @@ export interface Router<T extends MiddlewareTypes = any> extends HattipHandler<T
54
54
  */
55
55
  use<const TMiddleware extends ExtractMiddleware<this>>(middleware: TMiddleware | null): Router<ApplyMiddleware<this, TMiddleware>>;
56
56
  /**
57
- * Clone this router and add the given routes and handlers to the chain.
57
+ * Clone this router and add the given HTTP route tree and handlers to the
58
+ * chain.
59
+ *
60
+ * @remarks The handler object mirrors the resource tree. Resource nodes are
61
+ * nested objects, and action nodes are direct handler functions.
58
62
  *
59
63
  * @returns a new `Router` instance.
60
64
  */
61
- use<TRoutes extends Routes>(routes: TRoutes, handlers: RouteRequestHandlerMap<TRoutes, this>): Router<T>;
65
+ use<TRoutes extends HttpRouteTree>(routes: TRoutes, handlers: RouteRequestHandlerMap<TRoutes, this>): Router<T>;
62
66
  }
63
67
  /**
64
68
  * Create a Rouzer router that can be mounted by any Hattip adapter.