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 +33 -34
- package/dist/client/index.d.ts +34 -30
- package/dist/client/index.js +37 -17
- package/dist/http.d.ts +67 -0
- package/dist/http.js +43 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/internal.d.ts +17 -0
- package/dist/internal.js +1 -0
- package/dist/server/router.d.ts +8 -4
- package/dist/server/router.js +35 -33
- package/dist/server/types.d.ts +25 -29
- package/dist/type.d.ts +22 -0
- package/dist/type.js +21 -0
- package/dist/types/args.d.ts +51 -0
- package/dist/types/args.js +1 -0
- package/dist/types/handler.d.ts +31 -0
- package/dist/types/handler.js +1 -0
- package/dist/types/index.d.ts +7 -0
- package/dist/types/index.js +1 -0
- package/dist/types/infer.d.ts +19 -0
- package/dist/types/infer.js +1 -0
- package/dist/types/path.d.ts +1 -0
- package/dist/types/path.js +1 -0
- package/dist/types/request.d.ts +35 -0
- package/dist/types/request.js +1 -0
- package/dist/types/response.d.ts +9 -0
- package/dist/types/response.js +1 -0
- package/dist/types/schema.d.ts +37 -0
- package/dist/types/schema.js +1 -0
- package/dist/types/server.d.ts +15 -0
- package/dist/types/server.js +1 -0
- package/dist/types.d.ts +17 -56
- package/docs/context.md +205 -43
- package/examples/basic-usage.ts +17 -16
- package/package.json +14 -10
- package/dist/route.d.ts +0 -49
- package/dist/route.js +0 -47
package/README.md
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
# Rouzer
|
|
2
2
|
|
|
3
|
-
Rouzer lets you declare
|
|
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
|
|
9
|
-
response
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
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 = {
|
|
79
|
+
export const routes = { hello }
|
|
79
80
|
|
|
80
81
|
export const handler = createRouter({ basePath: 'api/' }).use(routes, {
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
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.
|
|
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
|
|
102
|
-
arguments before `fetch`; server handlers validate matched path, query,
|
|
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
|
|
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.
|
package/dist/client/index.d.ts
CHANGED
|
@@ -1,18 +1,21 @@
|
|
|
1
1
|
import { Promisable } from '../common.js';
|
|
2
|
-
import {
|
|
3
|
-
import type {
|
|
4
|
-
|
|
5
|
-
|
|
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
|
|
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
|
|
11
|
-
*
|
|
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
|
|
16
|
+
export declare function createClient<TRoutes extends HttpRouteTree = Record<string, never>>(config: {
|
|
14
17
|
/**
|
|
15
|
-
* Absolute base URL used for
|
|
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
|
-
*
|
|
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.
|
|
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
|
-
}):
|
|
50
|
+
}): ClientTree<TRoutes, ""> & {
|
|
50
51
|
config: {
|
|
51
52
|
/**
|
|
52
|
-
* Absolute base URL used for
|
|
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
|
|
65
|
+
headers?: Record<string, string>;
|
|
65
66
|
/**
|
|
66
|
-
*
|
|
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.
|
|
72
|
+
* await client.users.list({ query: { page: 1 } })
|
|
72
73
|
* ```
|
|
73
74
|
*/
|
|
74
|
-
routes?: TRoutes
|
|
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?: (
|
|
82
|
+
onJsonError?: (response: Response) => Promisable<Response>;
|
|
84
83
|
/** Custom `fetch` implementation to use for requests. */
|
|
85
|
-
fetch?: typeof globalThis.fetch
|
|
84
|
+
fetch?: typeof globalThis.fetch;
|
|
86
85
|
};
|
|
87
|
-
request: <T extends RouteRequest>({ path: pathBuilder, method, args
|
|
88
|
-
json(): Promise<T[
|
|
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[
|
|
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
|
-
*
|
|
94
|
-
* to `createClient`.
|
|
97
|
+
* Client action function attached for each HTTP action leaf.
|
|
95
98
|
*
|
|
96
|
-
* @remarks
|
|
97
|
-
* `T`.
|
|
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 {};
|
package/dist/client/index.js
CHANGED
|
@@ -1,27 +1,30 @@
|
|
|
1
|
-
import {
|
|
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
|
|
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
|
|
7
|
-
*
|
|
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
|
|
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
|
|
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
|
|
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
|
-
?
|
|
75
|
+
? connectTree(config.routes, '', request, json)
|
|
72
76
|
: null),
|
|
73
77
|
config,
|
|
74
78
|
request,
|
|
75
79
|
json,
|
|
76
80
|
};
|
|
77
81
|
}
|
|
78
|
-
function
|
|
79
|
-
return {
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
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
package/dist/index.js
CHANGED
|
@@ -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
|
+
};
|
package/dist/internal.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/server/router.d.ts
CHANGED
|
@@ -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 {
|
|
4
|
-
import type { RouteRequestHandlerMap } from '
|
|
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
|
|
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
|
|
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.
|