rouzer 1.0.0-beta.7 → 1.0.0-beta.9

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.
@@ -22,14 +22,14 @@ export declare function createClient(config: {
22
22
  /**
23
23
  * Default headers to send with every request.
24
24
  */
25
- headers?: Record<string, string>;
25
+ headers?: Record<string, string> | undefined;
26
26
  /**
27
27
  * Custom handler for non-200 response to a `.json()` request. By default, the
28
28
  * response is always parsed as JSON, regardless of the HTTP status code.
29
29
  */
30
- onJsonError?: (response: Response) => Promisable<Response>;
30
+ onJsonError?: ((response: Response) => Promisable<Response>) | undefined;
31
31
  };
32
- request<T extends RouteRequest>({ pathPattern, method, args: { path, query, body, headers }, route, }: T): Promise<Response & {
32
+ request<T extends RouteRequest>({ path: pathBuilder, method, args: { path, query, body, headers }, route, }: T): Promise<Response & {
33
33
  json(): Promise<T["$result"]>;
34
34
  }>;
35
35
  json<T extends RouteRequest>(request: T): Promise<T["$result"]>;
@@ -3,12 +3,19 @@ export function createClient(config) {
3
3
  const baseURL = config.baseURL.replace(/\/$/, '');
4
4
  return {
5
5
  config,
6
- request({ pathPattern, method, args: { path, query, body, headers }, route, }) {
6
+ request({ path: pathBuilder, method, args: { path, query, body, headers }, route, }) {
7
7
  if (route.path) {
8
8
  path = route.path.parse(path);
9
9
  }
10
- const url = new URL(baseURL);
11
- url.pathname += pathPattern.href(path);
10
+ let url;
11
+ const href = pathBuilder.href(path);
12
+ if (href[0] === '/') {
13
+ url = new URL(baseURL);
14
+ url.pathname += pathBuilder.href(path);
15
+ }
16
+ else {
17
+ url = new URL(href);
18
+ }
12
19
  if (route.query) {
13
20
  query = route.query.parse(query ?? {});
14
21
  url.search = new URLSearchParams(query).toString();
@@ -34,7 +41,7 @@ export function createClient(config) {
34
41
  return fetch(url, {
35
42
  method,
36
43
  body: body !== undefined ? JSON.stringify(body) : undefined,
37
- headers,
44
+ headers: headers,
38
45
  });
39
46
  },
40
47
  async json(request) {
package/dist/route.d.ts CHANGED
@@ -1,8 +1,7 @@
1
1
  import { RoutePattern } from '@remix-run/route-pattern';
2
2
  import type { MutationMethod, QueryMethod, RouteFunction, RouteMethods, Unchecked } from './types.js';
3
3
  export declare function $type<T>(): Unchecked<T>;
4
- export declare function route<P extends string, T extends RouteMethods>(path: P, methods: T): {
5
- path: P;
6
- pathPattern: RoutePattern<string>;
4
+ export declare function route<P extends string, T extends RouteMethods>(pattern: P, methods: T): {
5
+ path: RoutePattern<P>;
7
6
  methods: T;
8
7
  } & { [K in keyof T]: RouteFunction<Extract<T[K], MutationMethod | QueryMethod>>; };
package/dist/route.js CHANGED
@@ -3,16 +3,16 @@ import { mapEntries } from './common.js';
3
3
  export function $type() {
4
4
  return null;
5
5
  }
6
- export function route(path, methods) {
7
- const pathPattern = new RoutePattern(path);
6
+ export function route(pattern, methods) {
7
+ const path = new RoutePattern(pattern);
8
8
  const createFetch = (method, route) => (args) => {
9
9
  return {
10
10
  route,
11
- pathPattern,
11
+ path,
12
12
  method,
13
13
  args,
14
14
  $result: undefined,
15
15
  };
16
16
  };
17
- return Object.assign({ path, pathPattern, methods }, mapEntries(methods, (method, route) => [method, createFetch(method, route)]));
17
+ return Object.assign({ path, methods }, mapEntries(methods, (method, route) => [method, createFetch(method, route)]));
18
18
  }
@@ -2,7 +2,7 @@ import type { AdapterRequestContext } from '@hattip/core';
2
2
  import { type Params } from '@remix-run/route-pattern';
3
3
  import { chain, MiddlewareChain, type MiddlewareContext } from 'alien-middleware';
4
4
  import * as z from 'zod/mini';
5
- import type { InferRouteResponse, MutationMethod, Promisable, QueryMethod, RouteMethods } from '../types.js';
5
+ import type { InferRouteResponse, MutationMethod, Promisable, QueryMethod, Routes } from '../types.js';
6
6
  export { chain };
7
7
  type EmptyMiddlewareChain<TPlatform = unknown> = MiddlewareChain<{
8
8
  initial: {
@@ -37,10 +37,7 @@ export type RouterConfig = {
37
37
  * })
38
38
  * ```
39
39
  */
40
- routes: Record<string, {
41
- path: string;
42
- methods: RouteMethods;
43
- }>;
40
+ routes: Routes;
44
41
  /**
45
42
  * Middleware to apply to all routes.
46
43
  * @see https://github.com/alien-rpc/alien-middleware#quick-start
@@ -64,23 +61,30 @@ export type RouterConfig = {
64
61
  * ```
65
62
  */
66
63
  debug?: boolean;
64
+ /**
65
+ * CORS configuration.
66
+ */
67
+ cors?: {
68
+ /**
69
+ * If defined, requests must have an `Origin` header that is in this list.
70
+ */
71
+ allowOrigins?: string[];
72
+ };
67
73
  };
68
- export declare function createRouter<TRoutes extends Record<string, {
69
- path: string;
70
- methods: RouteMethods;
71
- }>, TMiddleware extends MiddlewareChain = EmptyMiddlewareChain>(config: RouterConfig & {
74
+ interface CreateRouterConfig<TRoutes extends Routes, TMiddleware extends MiddlewareChain> extends RouterConfig {
72
75
  routes: TRoutes;
73
76
  middlewares?: TMiddleware;
74
- }): (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 QueryMethod ? (context: MiddlewareContext<TMiddleware> & {
77
+ }
78
+ 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 QueryMethod ? (context: MiddlewareContext<TMiddleware> & {
75
79
  path: T extends {
76
80
  path: any;
77
- } ? z.infer<T["path"]> : Params<TRoutes[K]["path"]>;
81
+ } ? z.infer<T["path"]> : Params<TRoutes[K]["path"]["source"]>;
78
82
  query: z.infer<T["query"]>;
79
83
  headers: z.infer<T["headers"]>;
80
84
  }) => Promisable<Response | InferRouteResponse<T>> : T extends MutationMethod ? (context: MiddlewareContext<TMiddleware> & {
81
85
  path: T extends {
82
86
  path: any;
83
- } ? z.infer<T["path"]> : Params<TRoutes[K]["path"]>;
87
+ } ? z.infer<T["path"]> : Params<TRoutes[K]["path"]["source"]>;
84
88
  body: z.infer<T["body"]>;
85
89
  headers: z.infer<T["headers"]>;
86
90
  }) => Promisable<Response | InferRouteResponse<T>> : never : never : never; }; }) => import("alien-middleware").ApplyMiddleware<TMiddleware, (context: AdapterRequestContext<TMiddleware extends MiddlewareChain<infer T extends {
@@ -94,6 +98,6 @@ export declare function createRouter<TRoutes extends Record<string, {
94
98
  };
95
99
  platform: unknown;
96
100
  }> ? T["platform"] : never> & {
97
- url?: URL;
98
- path?: {};
99
- }) => Promise<Response>>;
101
+ url?: URL | undefined;
102
+ path?: {} | undefined;
103
+ }) => Promise<Response | undefined>>;
@@ -6,14 +6,17 @@ export { chain };
6
6
  export function createRouter(config) {
7
7
  const keys = Object.keys(config.routes);
8
8
  const middlewares = config.middlewares ?? chain();
9
- const basePath = config.basePath?.replace(/(^\/)|(\/$)/, '');
10
- const patterns = mapValues(config.routes, basePath
11
- ? ({ path }) => new RoutePattern(`${basePath}/${path}`)
12
- : ({ path }) => new RoutePattern(path));
9
+ const basePath = config.basePath?.replace(/\/?$/, '/');
10
+ const patterns = mapValues(config.routes, ({ path }) => basePath ? new RoutePattern(path.source.replace(/^\/?/, basePath)) : path);
13
11
  return (handlers) => middlewares.use(async function (context) {
14
12
  const request = context.request;
15
- const method = request.method.toUpperCase();
16
13
  const url = (context.url ??= new URL(request.url));
14
+ let method = request.method.toUpperCase();
15
+ let isPreflight = false;
16
+ if (method === 'OPTIONS') {
17
+ method = request.headers.get('Access-Control-Request-Method') ?? 'GET';
18
+ isPreflight = true;
19
+ }
17
20
  for (let i = 0; i < keys.length; i++) {
18
21
  const route = config.routes[keys[i]].methods[method];
19
22
  if (!route) {
@@ -30,6 +33,20 @@ export function createRouter(config) {
30
33
  }
31
34
  continue;
32
35
  }
36
+ if (isPreflight) {
37
+ const origin = request.headers.get('Origin');
38
+ const allowed = config.cors?.allowOrigins;
39
+ if (allowed && !(origin && allowed.includes(origin))) {
40
+ return new Response(null, { status: 403 });
41
+ }
42
+ return new Response(null, {
43
+ headers: {
44
+ 'Access-Control-Allow-Origin': origin ?? '*',
45
+ 'Access-Control-Allow-Methods': method,
46
+ 'Access-Control-Allow-Headers': request.headers.get('Access-Control-Request-Headers') ?? '',
47
+ },
48
+ });
49
+ }
33
50
  if (route.path) {
34
51
  const error = parsePathParams(context, enableStringParsing(route.path), match.params);
35
52
  if (error) {
package/dist/types.d.ts CHANGED
@@ -25,6 +25,12 @@ export type RouteMethods = {
25
25
  PATCH?: MutationMethod;
26
26
  DELETE?: MutationMethod;
27
27
  };
28
+ export type Routes = {
29
+ [key: string]: {
30
+ path: RoutePattern;
31
+ methods: RouteMethods;
32
+ };
33
+ };
28
34
  declare class Any {
29
35
  private isAny;
30
36
  }
@@ -60,7 +66,7 @@ export type RouteArgs<T extends QueryMethod | MutationMethod = any> = ([
60
66
  };
61
67
  export type RouteRequest<TResult = any> = {
62
68
  route: QueryMethod | MutationMethod;
63
- pathPattern: RoutePattern;
69
+ path: RoutePattern;
64
70
  method: string;
65
71
  args: RouteArgs;
66
72
  $result: TResult;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rouzer",
3
- "version": "1.0.0-beta.7",
3
+ "version": "1.0.0-beta.9",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {
@@ -15,6 +15,8 @@
15
15
  "@alloc/prettier-config": "^1.0.0",
16
16
  "@typescript/native-preview": "7.0.0-dev.20251208.1",
17
17
  "prettier": "^3.7.4",
18
+ "tsc-lint": "^0.1.9",
19
+ "typescript": "^5.9.3",
18
20
  "zod": "^4.1.13"
19
21
  },
20
22
  "dependencies": {