rouzer 1.0.0-beta.2 → 1.0.0-beta.20

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/dist/auth.d.ts ADDED
@@ -0,0 +1 @@
1
+ export {};
package/dist/auth.js ADDED
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,64 @@
1
+ import { Route } from '../route.js';
2
+ import type { InferRouteResponse, Promisable, RouteArgs, RouteRequest, RouteSchema } from '../types.js';
3
+ export type RouzerClient<TRoutes extends Record<string, Route> = Record<string, never>> = ReturnType<typeof createClient<TRoutes>>;
4
+ export declare function createClient<TRoutes extends Record<string, Route> = Record<string, never>>(config: {
5
+ /**
6
+ * Base URL to use for all requests.
7
+ */
8
+ baseURL: string;
9
+ /**
10
+ * Default headers to send with every request.
11
+ */
12
+ headers?: Record<string, string>;
13
+ /**
14
+ * Pass in routes to attach them as methods on the client.
15
+ * @example
16
+ * ```ts
17
+ * const client = createClient({ baseURL: '/api/', routes: { helloRoute } })
18
+ * client.helloRoute.GET({ path: { name: 'world' } })
19
+ * ```
20
+ */
21
+ routes?: TRoutes;
22
+ /**
23
+ * Custom handler for non-200 response to a `.json()` request. By default, the
24
+ * response is always parsed as JSON, regardless of the HTTP status code.
25
+ */
26
+ onJsonError?: (response: Response) => Promisable<Response>;
27
+ }): { [K in keyof TRoutes]: TRoutes[K]["methods"] extends infer TMethods ? { [M in keyof TMethods]: RouteFunction<Extract<TMethods[M], RouteSchema>, TRoutes[K]["path"]["source"]>; } : never; } & {
28
+ config: {
29
+ /**
30
+ * Base URL to use for all requests.
31
+ */
32
+ baseURL: string;
33
+ /**
34
+ * Default headers to send with every request.
35
+ */
36
+ headers?: Record<string, string> | undefined;
37
+ /**
38
+ * Pass in routes to attach them as methods on the client.
39
+ * @example
40
+ * ```ts
41
+ * const client = createClient({ baseURL: '/api/', routes: { helloRoute } })
42
+ * client.helloRoute.GET({ path: { name: 'world' } })
43
+ * ```
44
+ */
45
+ routes?: TRoutes | undefined;
46
+ /**
47
+ * Custom handler for non-200 response to a `.json()` request. By default, the
48
+ * response is always parsed as JSON, regardless of the HTTP status code.
49
+ */
50
+ onJsonError?: ((response: Response) => Promisable<Response>) | undefined;
51
+ };
52
+ request: <T extends RouteRequest>({ path: pathBuilder, method, args: { path, query, body, headers }, schema, }: T) => Promise<Response & {
53
+ json(): Promise<T["$result"]>;
54
+ }>;
55
+ json: <T extends RouteRequest>(props: T) => Promise<T["$result"]>;
56
+ };
57
+ /**
58
+ * This function sends a request to a route of the same name. Such a function is
59
+ * accessible by setting the `routes` option when creating a Rouzer client,
60
+ * where it will exist as a method on the client.
61
+ */
62
+ 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 {
63
+ response: any;
64
+ } ? InferRouteResponse<T> : Response>;
@@ -0,0 +1,69 @@
1
+ import { mapValues, shake } from '../common.js';
2
+ export function createClient(config) {
3
+ const baseURL = config.baseURL.replace(/\/$/, '');
4
+ async function request({ path: pathBuilder, method, args: { path, query, body, headers }, schema, }) {
5
+ if (schema.path) {
6
+ path = schema.path.parse(path);
7
+ }
8
+ let url;
9
+ const href = pathBuilder.href(path);
10
+ if (href[0] === '/') {
11
+ url = new URL(baseURL);
12
+ url.pathname += pathBuilder.href(path);
13
+ }
14
+ else {
15
+ url = new URL(href);
16
+ }
17
+ if (schema.query) {
18
+ query = schema.query.parse(query ?? {});
19
+ url.search = new URLSearchParams(query).toString();
20
+ }
21
+ else if (query) {
22
+ throw new Error('Unexpected query parameters');
23
+ }
24
+ if (schema.body) {
25
+ body = schema.body.parse(body !== undefined ? body : {});
26
+ }
27
+ else if (body !== undefined) {
28
+ throw new Error('Unexpected body');
29
+ }
30
+ if (config.headers || headers) {
31
+ headers = {
32
+ ...config.headers,
33
+ ...(headers && shake(headers)),
34
+ };
35
+ }
36
+ if (schema.headers) {
37
+ headers = schema.headers.parse(headers);
38
+ }
39
+ return fetch(url, {
40
+ method,
41
+ body: body !== undefined ? JSON.stringify(body) : undefined,
42
+ headers: headers,
43
+ });
44
+ }
45
+ async function json(props) {
46
+ const response = await request(props);
47
+ if (!response.ok && config.onJsonError) {
48
+ return config.onJsonError(response);
49
+ }
50
+ return response.json();
51
+ }
52
+ return {
53
+ ...(config.routes
54
+ ? mapValues(config.routes, route => connectRoute(route, request, json))
55
+ : null),
56
+ config,
57
+ request,
58
+ json,
59
+ };
60
+ }
61
+ function connectRoute(route, request, json) {
62
+ return {
63
+ ...route,
64
+ ...mapValues(route.methods, (schema, key) => {
65
+ const fetch = schema.response ? json : request;
66
+ return (args) => fetch(route[key](args));
67
+ }),
68
+ };
69
+ }
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Map over all the keys to create a new object.
3
+ *
4
+ * @see https://radashi.js.org/reference/object/mapEntries
5
+ * @example
6
+ * ```ts
7
+ * const a = { a: 1, b: 2, c: 3 }
8
+ * mapEntries(a, (key, value) => [value, key])
9
+ * // => { 1: 'a', 2: 'b', 3: 'c' }
10
+ * ```
11
+ * @version 12.1.0
12
+ */
13
+ export declare function mapEntries<TKey extends string | number | symbol, TValue, TNewKey extends string | number | symbol, TNewValue>(obj: Record<TKey, TValue>, toEntry: (key: TKey, value: TValue) => [TNewKey, TNewValue]): Record<TNewKey, TNewValue>;
14
+ /**
15
+ * Removes (shakes out) undefined entries from an object. Optional
16
+ * second argument shakes out values by custom evaluation.
17
+ *
18
+ * Note that non-enumerable keys are never shaken out.
19
+ *
20
+ * @see https://radashi.js.org/reference/object/shake
21
+ * @example
22
+ * ```ts
23
+ * const a = { a: 1, b: undefined, c: 3 }
24
+ * shake(a)
25
+ * // => { a: 1, c: 3 }
26
+ * ```
27
+ * @version 12.1.0
28
+ */
29
+ export declare function shake<T extends object>(obj: T): {
30
+ [K in keyof T]: Exclude<T[K], undefined>;
31
+ };
32
+ export declare function shake<T extends object>(obj: T, filter: ((value: unknown) => boolean) | undefined): T;
33
+ /**
34
+ * Map over all the keys to create a new object.
35
+ *
36
+ * @see https://radashi.js.org/reference/object/mapValues
37
+ * @example
38
+ * ```ts
39
+ * const a = { a: 1, b: 2, c: 3 }
40
+ * mapValues(a, (value, key) => value * 2)
41
+ * // => { a: 2, b: 4, c: 6 }
42
+ * ```
43
+ * @version 12.1.0
44
+ */
45
+ export declare function mapValues<T extends object, U>(obj: T, mapFunc: (value: Required<T>[keyof T], key: keyof T) => U): {
46
+ [K in keyof T]: U;
47
+ };
package/dist/common.js ADDED
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Map over all the keys to create a new object.
3
+ *
4
+ * @see https://radashi.js.org/reference/object/mapEntries
5
+ * @example
6
+ * ```ts
7
+ * const a = { a: 1, b: 2, c: 3 }
8
+ * mapEntries(a, (key, value) => [value, key])
9
+ * // => { 1: 'a', 2: 'b', 3: 'c' }
10
+ * ```
11
+ * @version 12.1.0
12
+ */
13
+ export function mapEntries(obj, toEntry) {
14
+ if (!obj) {
15
+ return {};
16
+ }
17
+ return Object.entries(obj).reduce((acc, [key, value]) => {
18
+ const [newKey, newValue] = toEntry(key, value);
19
+ acc[newKey] = newValue;
20
+ return acc;
21
+ }, {});
22
+ }
23
+ export function shake(obj, filter = value => value === undefined) {
24
+ if (!obj) {
25
+ return {};
26
+ }
27
+ return Object.keys(obj).reduce((acc, key) => {
28
+ if (!filter(obj[key])) {
29
+ acc[key] = obj[key];
30
+ }
31
+ return acc;
32
+ }, {});
33
+ }
34
+ /**
35
+ * Map over all the keys to create a new object.
36
+ *
37
+ * @see https://radashi.js.org/reference/object/mapValues
38
+ * @example
39
+ * ```ts
40
+ * const a = { a: 1, b: 2, c: 3 }
41
+ * mapValues(a, (value, key) => value * 2)
42
+ * // => { a: 2, b: 4, c: 6 }
43
+ * ```
44
+ * @version 12.1.0
45
+ */
46
+ export function mapValues(obj, mapFunc) {
47
+ return Object.keys(obj).reduce((acc, key) => {
48
+ acc[key] = mapFunc(obj[key], key);
49
+ return acc;
50
+ }, {});
51
+ }
@@ -0,0 +1,5 @@
1
+ export * from './route.js';
2
+ export * from './server/router.js';
3
+ export * from './client/index.js';
4
+ export type * from './types.js';
5
+ export * from 'alien-middleware';
package/dist/index.js ADDED
@@ -0,0 +1,4 @@
1
+ export * from './route.js';
2
+ export * from './server/router.js';
3
+ export * from './client/index.js';
4
+ export * from 'alien-middleware';
@@ -0,0 +1,10 @@
1
+ import { RoutePattern } from '@remix-run/route-pattern';
2
+ import type { RouteRequestFactory, RouteSchema, RouteSchemaMap, Unchecked } from './types.js';
3
+ export declare function $type<T>(): Unchecked<T>;
4
+ export type Route<P extends string = string, T extends RouteSchemaMap = RouteSchemaMap> = {
5
+ path: RoutePattern<P>;
6
+ methods: T;
7
+ } & {
8
+ [K in keyof T]: RouteRequestFactory<Extract<T[K], RouteSchema>, P>;
9
+ };
10
+ export declare function route<P extends string, T extends RouteSchemaMap>(pattern: P, methods: T): Route<P, T>;
package/dist/route.js ADDED
@@ -0,0 +1,21 @@
1
+ import { RoutePattern } from '@remix-run/route-pattern';
2
+ import { mapEntries } from './common.js';
3
+ export function $type() {
4
+ return null;
5
+ }
6
+ export function route(pattern, methods) {
7
+ const path = new RoutePattern(pattern);
8
+ const createFetch = (method, schema) => (args = {}) => {
9
+ return {
10
+ schema,
11
+ path,
12
+ method,
13
+ args,
14
+ $result: undefined,
15
+ };
16
+ };
17
+ return Object.assign({ path, methods }, mapEntries(methods, (method, schema) => [
18
+ method,
19
+ createFetch(method, schema),
20
+ ]));
21
+ }
@@ -0,0 +1,119 @@
1
+ import { type Params } from '@remix-run/route-pattern';
2
+ import { chain, MiddlewareChain, type HattipContext, type MiddlewareContext } from 'alien-middleware';
3
+ import * as z from 'zod/mini';
4
+ import type { InferRouteResponse, MutationRouteSchema, Promisable, QueryRouteSchema, Routes } from '../types.js';
5
+ export { chain };
6
+ type EmptyMiddlewareChain<TPlatform = unknown> = MiddlewareChain<{
7
+ initial: {
8
+ env: {};
9
+ properties: {};
10
+ };
11
+ current: {
12
+ env: {};
13
+ properties: {};
14
+ };
15
+ platform: TPlatform;
16
+ }>;
17
+ export type RouterConfig = {
18
+ /**
19
+ * Base path to prepend to all routes.
20
+ * @example
21
+ * ```ts
22
+ * basePath: 'api/',
23
+ * ```
24
+ */
25
+ basePath?: string;
26
+ /**
27
+ * Routes to match.
28
+ * @example
29
+ * ```ts
30
+ * // This namespace contains your `route()` declarations.
31
+ * // Pass it to the `createRouter` function.
32
+ * import * as routes from './routes'
33
+ *
34
+ * createRouter({ routes })({
35
+ * // your route handlers...
36
+ * })
37
+ * ```
38
+ */
39
+ routes: Routes;
40
+ /**
41
+ * Middleware to apply to all routes.
42
+ * @see https://github.com/alien-rpc/alien-middleware#quick-start
43
+ * @example
44
+ * ```ts
45
+ * middlewares: chain().use(ctx => {
46
+ * return {
47
+ * db: postgres(ctx.env('POSTGRES_URL')),
48
+ * }
49
+ * }),
50
+ * ```
51
+ */
52
+ middlewares?: MiddlewareChain;
53
+ /**
54
+ * Enable debugging features.
55
+ * - When a handler throws an error, include its message in the response body.
56
+ * - Throw an error if a handler is not found for a route.
57
+ * @example
58
+ * ```ts
59
+ * debug: process.env.NODE_ENV !== 'production',
60
+ * ```
61
+ */
62
+ debug?: boolean;
63
+ /**
64
+ * CORS configuration.
65
+ */
66
+ cors?: {
67
+ /**
68
+ * If defined, requests must have an `Origin` header that is in this list.
69
+ *
70
+ * Origins may contain wildcards for protocol and subdomain. The protocol is
71
+ * optional and defaults to `https`.
72
+ *
73
+ * @example
74
+ * ```ts
75
+ * allowOrigins: ['example.net', 'https://*.example.com', '*://localhost:3000']
76
+ * ```
77
+ */
78
+ allowOrigins?: string[];
79
+ };
80
+ };
81
+ interface CreateRouterConfig<TRoutes extends Routes, TMiddleware extends MiddlewareChain> extends RouterConfig {
82
+ routes: TRoutes;
83
+ middlewares?: TMiddleware;
84
+ }
85
+ export type Router<TRoutes extends Routes, TMiddleware extends MiddlewareChain = EmptyMiddlewareChain> = ReturnType<typeof createRouter<TRoutes, TMiddleware>>;
86
+ export declare function createRouter<TRoutes extends Routes, TMiddleware extends MiddlewareChain = EmptyMiddlewareChain>(config: CreateRouterConfig<TRoutes, TMiddleware>): (handlers: { [K in keyof TRoutes]: { [M in keyof TRoutes[K]["methods"]]: TRoutes[K]["methods"][M] extends infer T ? T extends TRoutes[K]["methods"][M] ? T extends QueryRouteSchema ? (context: MiddlewareContext<TMiddleware> & {
87
+ path: T extends {
88
+ path: any;
89
+ } ? z.infer<T["path"]> : Params<TRoutes[K]["path"]["source"]>;
90
+ query: T extends {
91
+ query: any;
92
+ } ? z.infer<T["query"]> : undefined;
93
+ headers: T extends {
94
+ headers: any;
95
+ } ? z.infer<T["headers"]> : undefined;
96
+ }) => Promisable<Response | InferRouteResponse<T>> : T extends MutationRouteSchema ? (context: MiddlewareContext<TMiddleware> & {
97
+ path: T extends {
98
+ path: any;
99
+ } ? z.infer<T["path"]> : Params<TRoutes[K]["path"]["source"]>;
100
+ body: T extends {
101
+ body: any;
102
+ } ? z.infer<T["body"]> : undefined;
103
+ headers: T extends {
104
+ headers: any;
105
+ } ? z.infer<T["headers"]> : undefined;
106
+ }) => Promisable<Response | InferRouteResponse<T>> : never : never : never; }; }) => import("alien-middleware").ApplyMiddleware<TMiddleware, (context: HattipContext<TMiddleware extends MiddlewareChain<infer T extends {
107
+ initial: {
108
+ env: object;
109
+ properties: object;
110
+ };
111
+ current: {
112
+ env: object;
113
+ properties: object;
114
+ };
115
+ platform: unknown;
116
+ }> ? T["platform"] : never, any> & {
117
+ url?: URL | undefined;
118
+ path?: {} | undefined;
119
+ }) => Promise<Response | undefined>>;
@@ -0,0 +1,179 @@
1
+ import { RoutePattern } from '@remix-run/route-pattern';
2
+ import { chain, } from 'alien-middleware';
3
+ import * as z from 'zod/mini';
4
+ import { mapValues } from '../common.js';
5
+ export { chain };
6
+ export function createRouter(config) {
7
+ const keys = Object.keys(config.routes);
8
+ const middlewares = config.middlewares ?? chain();
9
+ const basePath = config.basePath?.replace(/\/?$/, '/');
10
+ const patterns = mapValues(config.routes, ({ path }) => basePath ? new RoutePattern(path.source.replace(/^\/?/, basePath)) : path);
11
+ const allowOrigins = config.cors?.allowOrigins?.map(origin => {
12
+ if (!origin.includes('//')) {
13
+ origin = `https://${origin}`;
14
+ }
15
+ if (origin.includes('*')) {
16
+ return new RegExp(`^${origin
17
+ .replace(/\./g, '\\.')
18
+ .replace(/\*:/g, '[^:]+:') // Wildcard protocol
19
+ .replace(/\*\\\./g, '([^/]+\\.)?') // Wildcard subdomain
20
+ }$`);
21
+ }
22
+ return new ExactPattern(origin);
23
+ });
24
+ return (handlers) => middlewares.use(async function (context) {
25
+ const request = context.request;
26
+ const origin = request.headers.get('Origin');
27
+ if (origin &&
28
+ allowOrigins &&
29
+ !allowOrigins.some(pattern => pattern.test(origin))) {
30
+ return new Response(null, { status: 403 });
31
+ }
32
+ const url = (context.url ??= new URL(request.url));
33
+ let method = request.method.toUpperCase();
34
+ let isPreflight = false;
35
+ if (method === 'OPTIONS') {
36
+ method = request.headers.get('Access-Control-Request-Method') ?? 'GET';
37
+ isPreflight = true;
38
+ }
39
+ for (let i = 0; i < keys.length; i++) {
40
+ const route = config.routes[keys[i]].methods[method];
41
+ if (!route) {
42
+ continue;
43
+ }
44
+ const match = patterns[keys[i]].match(url);
45
+ if (!match) {
46
+ continue;
47
+ }
48
+ const handler = handlers[keys[i]][method];
49
+ if (!handler) {
50
+ if (config.debug) {
51
+ throw new Error(`Handler not found for route: ${keys[i]} ${method}`);
52
+ }
53
+ continue;
54
+ }
55
+ if (isPreflight) {
56
+ return new Response(null, {
57
+ headers: {
58
+ 'Access-Control-Allow-Origin': origin ?? '*',
59
+ 'Access-Control-Allow-Methods': method,
60
+ 'Access-Control-Allow-Headers': request.headers.get('Access-Control-Request-Headers') ?? '',
61
+ },
62
+ });
63
+ }
64
+ if (origin) {
65
+ context.setHeader('Access-Control-Allow-Origin', origin);
66
+ }
67
+ if (route.path) {
68
+ const error = parsePathParams(context, enableStringParsing(route.path), match.params);
69
+ if (error) {
70
+ return httpClientError(error, 'Invalid path parameter', config);
71
+ }
72
+ }
73
+ else {
74
+ context.path = match.params;
75
+ }
76
+ if (route.headers) {
77
+ const error = parseHeaders(context, enableStringParsing(route.headers));
78
+ if (error) {
79
+ return httpClientError(error, 'Invalid request headers', config);
80
+ }
81
+ }
82
+ if (route.query) {
83
+ const error = parseQueryString(context, enableStringParsing(route.query));
84
+ if (error) {
85
+ return httpClientError(error, 'Invalid query string', config);
86
+ }
87
+ }
88
+ if (route.body) {
89
+ const error = await parseRequestBody(context, route.body);
90
+ if (error) {
91
+ return httpClientError(error, 'Invalid request body', config);
92
+ }
93
+ }
94
+ const result = await handler(context);
95
+ if (result instanceof Response) {
96
+ return result;
97
+ }
98
+ return Response.json(result);
99
+ }
100
+ });
101
+ }
102
+ function httpClientError(error, message, config) {
103
+ return Response.json({
104
+ ...error,
105
+ message: config.debug ? `${message}: ${error.message}` : message,
106
+ }, { status: 400 });
107
+ }
108
+ function parsePathParams(context, schema, params) {
109
+ const result = schema.safeParse(params);
110
+ if (!result.success) {
111
+ return result.error;
112
+ }
113
+ context.path = result.data;
114
+ return null;
115
+ }
116
+ function parseHeaders(context, schema) {
117
+ const headers = Object.fromEntries(context.request.headers);
118
+ const result = schema.safeParse(headers);
119
+ if (!result.success) {
120
+ return result.error;
121
+ }
122
+ context.headers = result.data;
123
+ return null;
124
+ }
125
+ function parseQueryString(context, schema) {
126
+ const result = schema.safeParse(Object.fromEntries(context.url.searchParams));
127
+ if (!result.success) {
128
+ return result.error;
129
+ }
130
+ context.query = result.data;
131
+ return null;
132
+ }
133
+ async function parseRequestBody(context, schema) {
134
+ const result = await context.request.json().then(body => schema.safeParse(body), error => ({ success: false, error }));
135
+ if (!result.success) {
136
+ return result.error;
137
+ }
138
+ context.body = result.data;
139
+ return null;
140
+ }
141
+ const seen = new WeakMap();
142
+ /**
143
+ * Traverse object and array schemas, finding schemas that expect a number or
144
+ * boolean, and replace those schemas with a new schema that parses the input
145
+ * value as a number or boolean.
146
+ */
147
+ function enableStringParsing(schema) {
148
+ if (schema.type === 'number') {
149
+ return z.pipe(z.transform(Number), schema);
150
+ }
151
+ if (schema.type === 'boolean') {
152
+ return z.pipe(z.transform(toBooleanStrict), schema);
153
+ }
154
+ if (schema.type === 'object') {
155
+ const cached = seen.get(schema);
156
+ if (cached) {
157
+ return cached;
158
+ }
159
+ const modified = z.object(mapValues(schema.def.shape, enableStringParsing));
160
+ seen.set(schema, modified);
161
+ return modified;
162
+ }
163
+ if (schema.type === 'array') {
164
+ return z.array(enableStringParsing(schema.def.element));
165
+ }
166
+ return schema;
167
+ }
168
+ function toBooleanStrict(value) {
169
+ return value === 'true' || (value === 'false' ? false : value);
170
+ }
171
+ class ExactPattern {
172
+ value;
173
+ constructor(value) {
174
+ this.value = value;
175
+ }
176
+ test(input) {
177
+ return input === this.value;
178
+ }
179
+ }
@@ -0,0 +1,91 @@
1
+ import { Params, RoutePattern } from '@remix-run/route-pattern';
2
+ import * as z from 'zod/mini';
3
+ export type Promisable<T> = T | Promise<T>;
4
+ export type Unchecked<T> = {
5
+ __unchecked__: T;
6
+ };
7
+ export type QueryRouteSchema = {
8
+ path?: z.ZodMiniObject<any>;
9
+ query?: z.ZodMiniObject<any>;
10
+ body?: never;
11
+ headers?: z.ZodMiniObject<any>;
12
+ response?: Unchecked<any>;
13
+ };
14
+ export type MutationRouteSchema = {
15
+ path?: z.ZodMiniObject<any>;
16
+ query?: never;
17
+ body?: z.ZodMiniType<any, any>;
18
+ headers?: z.ZodMiniObject<any>;
19
+ response?: Unchecked<any>;
20
+ };
21
+ export type RouteSchemaMap = {
22
+ GET?: QueryRouteSchema;
23
+ POST?: MutationRouteSchema;
24
+ PUT?: MutationRouteSchema;
25
+ PATCH?: MutationRouteSchema;
26
+ DELETE?: MutationRouteSchema;
27
+ };
28
+ export type Method = string & keyof RouteSchemaMap;
29
+ export type RouteSchema = QueryRouteSchema | MutationRouteSchema;
30
+ export type Routes = {
31
+ [key: string]: {
32
+ path: RoutePattern;
33
+ methods: RouteSchemaMap;
34
+ };
35
+ };
36
+ declare class Any {
37
+ private isAny;
38
+ }
39
+ type PathArgs<T, P extends string> = T extends {
40
+ path: infer TPath;
41
+ } ? {} extends z.infer<TPath> ? {
42
+ path?: z.infer<TPath>;
43
+ } : {
44
+ path: z.infer<TPath>;
45
+ } : Params<P> extends infer TParams ? {} extends TParams ? {
46
+ path?: TParams;
47
+ } : {
48
+ path: TParams;
49
+ } : unknown;
50
+ type QueryArgs<T> = T extends QueryRouteSchema & {
51
+ query: infer TQuery;
52
+ } ? {} extends z.infer<TQuery> ? {
53
+ query?: z.infer<TQuery>;
54
+ } : {
55
+ query: z.infer<TQuery>;
56
+ } : unknown;
57
+ type MutationArgs<T> = T extends MutationRouteSchema ? T extends {
58
+ body: infer TBody;
59
+ } ? {} extends z.infer<TBody> ? {
60
+ body?: z.infer<TBody>;
61
+ } : {
62
+ body: z.infer<TBody>;
63
+ } : {
64
+ body?: unknown;
65
+ } : unknown;
66
+ export type RouteArgs<T extends RouteSchema = any, P extends string = string> = ([T] extends [Any] ? {
67
+ query?: any;
68
+ body?: any;
69
+ path?: any;
70
+ } : QueryArgs<T> & MutationArgs<T> & PathArgs<T, P>) & Omit<RequestInit, 'method' | 'body' | 'headers'> & {
71
+ headers?: Record<string, string | undefined>;
72
+ };
73
+ export type RouteRequest<TResult = any> = {
74
+ schema: RouteSchema;
75
+ path: RoutePattern;
76
+ method: string;
77
+ args: RouteArgs;
78
+ $result: TResult;
79
+ };
80
+ export type RouteResponse<TResult = any> = Response & {
81
+ json(): Promise<TResult>;
82
+ };
83
+ export type InferRouteResponse<T extends RouteSchema> = T extends {
84
+ response: Unchecked<infer TResponse>;
85
+ } ? TResponse : void;
86
+ export type RouteRequestFactory<T extends RouteSchema, P extends string> = {
87
+ (...p: RouteArgs<T, P> extends infer TArgs ? {} extends TArgs ? [args?: TArgs] : [args: TArgs] : never): RouteRequest<InferRouteResponse<T>>;
88
+ $args: RouteArgs<T, P>;
89
+ $response: InferRouteResponse<T>;
90
+ };
91
+ export {};
package/dist/types.js ADDED
@@ -0,0 +1 @@
1
+ export {};