rouzer 4.0.0 → 5.0.0

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
@@ -97,8 +97,8 @@ const client = createClient({
97
97
  })
98
98
 
99
99
  const { message } = await client.hello({
100
- path: { name: 'world' },
101
- query: { excited: true },
100
+ name: 'world',
101
+ excited: true,
102
102
  })
103
103
  ```
104
104
 
@@ -143,7 +143,7 @@ const client = createClient({
143
143
  routes,
144
144
  })
145
145
 
146
- const [error, user, status] = await client.getUser({ path: { id: '42' } })
146
+ const [error, user, status] = await client.getUser({ id: '42' })
147
147
  ```
148
148
 
149
149
  Success entries resolve as `[null, value, status]`; declared error entries
@@ -1,7 +1,7 @@
1
1
  import { Promisable } from '../common.js';
2
- import type { HttpAction, HttpResource, HttpRouteTree } from '../http.js';
2
+ import { type HttpAction, type HttpResource, type HttpRouteTree } from '../http.js';
3
3
  import { type ClientResponsePlugin } from '../response.js';
4
- import type { RouteArgs } from '../types/args.js';
4
+ import type { RouteInput, RouteOptions } from '../types/args.js';
5
5
  import type { InferRouteResponse } from '../types/response.js';
6
6
  import type { RouteSchema } from '../types/schema.js';
7
7
  /** Client type inferred from an HTTP route tree passed to `createClient`. */
@@ -106,7 +106,7 @@ export type ClientTree<T extends HttpRouteTree, TPrefix extends string = ''> = {
106
106
  * plugin's client result type. Actions without a response marker return the raw
107
107
  * `Response`.
108
108
  */
109
- 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 {
109
+ export type RouteFunction<T extends RouteSchema, P extends string> = (...p: RouteInput<T, P> extends infer TInput ? {} extends TInput ? [input?: TInput, options?: RouteOptions<T>] : [input: TInput, options?: RouteOptions<T>] : never) => Promise<T extends {
110
110
  response: any;
111
111
  } ? InferRouteResponse<T> : Response>;
112
112
  export {};
@@ -1,8 +1,9 @@
1
1
  import { RoutePattern } from '@remix-run/route-pattern';
2
2
  import { createHref } from '@remix-run/route-pattern/href';
3
3
  import { shake } from '../common.js';
4
- import { createResponsePluginMap, getResponsePluginMarkerId, } from '../response.js';
4
+ import { isRawBodySchema, } from '../http.js';
5
5
  import { getResponseMapPluginIds, isErrorMarker, isResponseMap, } from '../response-map.js';
6
+ import { createResponsePluginMap, getResponsePluginMarkerId, } from '../response.js';
6
7
  /**
7
8
  * Create a typed fetch client for an HTTP route tree.
8
9
  *
@@ -15,13 +16,12 @@ export function createClient(config) {
15
16
  const fetch = config.fetch ?? globalThis.fetch;
16
17
  const responsePlugins = createResponsePluginMap(config.plugins, 'client response');
17
18
  validateClientResponsePlugins(config.routes, responsePlugins);
18
- async function request({ path: pathBuilder, method, args, schema, }) {
19
- let { path, query, body, headers, ...init } = args;
20
- if (schema.path) {
21
- path = schema.path.parse(path);
22
- }
19
+ async function plainRequest({ path: pathPattern, method, input = {}, options: { body: rawBody, headers, ...init } = {}, schema, }) {
20
+ const path = schema.path
21
+ ? schema.path.parse(pickObjectSchemaFields(schema.path, input))
22
+ : input;
23
23
  let url;
24
- const href = createHref(pathBuilder, path);
24
+ const href = createHref(pathPattern, path);
25
25
  if (href[0] === '/') {
26
26
  url = new URL(baseURL);
27
27
  url.pathname += href.slice(1);
@@ -30,17 +30,14 @@ export function createClient(config) {
30
30
  url = new URL(href, baseURL);
31
31
  }
32
32
  if (schema.query) {
33
- query = schema.query.parse(query ?? {});
33
+ const query = schema.query.parse(pickObjectSchemaFields(schema.query, input));
34
34
  url.search = new URLSearchParams(shake(query)).toString();
35
35
  }
36
- else if (query) {
37
- throw new Error('Unexpected query parameters');
38
- }
36
+ let body;
39
37
  if (schema.body) {
40
- body = schema.body.parse(body !== undefined ? body : {});
41
- }
42
- else if (body !== undefined) {
43
- throw new Error('Unexpected body');
38
+ body = isRawBodySchema(schema.body)
39
+ ? rawBody
40
+ : schema.body.parse(pickObjectSchemaFields(schema.body, input));
44
41
  }
45
42
  if (headers) {
46
43
  headers = shake(headers);
@@ -54,20 +51,24 @@ export function createClient(config) {
54
51
  return fetch(url, {
55
52
  ...init,
56
53
  method,
57
- body: body !== undefined ? JSON.stringify(body) : undefined,
54
+ body: isRawBodySchema(schema.body)
55
+ ? body
56
+ : body !== undefined
57
+ ? JSON.stringify(body)
58
+ : undefined,
58
59
  headers: (headers ?? defaultHeaders),
59
60
  });
60
61
  }
61
- async function response(props) {
62
- const httpResponse = await request(props);
62
+ async function parsedRequest(props) {
63
+ const response = await plainRequest(props);
63
64
  const responseSchema = props.schema.response;
64
65
  // Handle status-keyed response maps
65
66
  if (isResponseMap(responseSchema)) {
66
- const status = httpResponse.status;
67
+ const status = response.status;
67
68
  if (status in responseSchema) {
68
69
  const marker = responseSchema[status];
69
70
  if (isErrorMarker(marker)) {
70
- return [await httpResponse.json(), null, status];
71
+ return [await response.json(), null, status];
71
72
  }
72
73
  const pluginId = getResponsePluginMarkerId(marker);
73
74
  if (pluginId) {
@@ -77,20 +78,20 @@ export function createClient(config) {
77
78
  }
78
79
  return [
79
80
  null,
80
- await plugin.decode(httpResponse, {
81
+ await plugin.decode(response, {
81
82
  marker: marker,
82
83
  request: props,
83
84
  }),
84
85
  status,
85
86
  ];
86
87
  }
87
- return [null, await httpResponse.json(), status];
88
+ return [null, await response.json(), status];
88
89
  }
89
90
  // Undeclared status — reject
90
- return handleResponseError(httpResponse, props);
91
+ return handleResponseError(response, props);
91
92
  }
92
- if (!httpResponse.ok) {
93
- return handleResponseError(httpResponse, props);
93
+ if (!response.ok) {
94
+ return handleResponseError(response, props);
94
95
  }
95
96
  const pluginId = getResponsePluginMarkerId(responseSchema);
96
97
  if (pluginId) {
@@ -98,18 +99,18 @@ export function createClient(config) {
98
99
  if (!plugin) {
99
100
  throw missingClientResponsePlugin(pluginId);
100
101
  }
101
- return plugin.decode(httpResponse, {
102
+ return plugin.decode(response, {
102
103
  marker: responseSchema,
103
104
  request: props,
104
105
  });
105
106
  }
106
- return httpResponse.json();
107
+ return response.json();
107
108
  }
108
109
  async function handleResponseError(response, props) {
109
110
  if (config.onJsonError) {
110
111
  return config.onJsonError(response);
111
112
  }
112
- const error = new Error(`Request to ${props.method} ${createHref(props.path, props.args.path)} failed with status ${response.status}`);
113
+ const error = new Error(`Request to ${props.method} ${createHref(props.path, props.input)} failed with status ${response.status}`);
113
114
  const contentType = response.headers.get('content-type');
114
115
  if (contentType?.includes('application/json')) {
115
116
  Object.assign(error, await response.json());
@@ -117,27 +118,28 @@ export function createClient(config) {
117
118
  throw error;
118
119
  }
119
120
  return {
120
- ...connectTree(config.routes, '', request, response),
121
+ ...connectTree(config.routes, '', plainRequest, parsedRequest),
121
122
  clientConfig: config,
122
123
  };
123
124
  }
124
- function connectTree(tree, prefix, request, response) {
125
+ function connectTree(tree, prefix, plainRequest, parsedRequest) {
125
126
  return Object.fromEntries(Object.entries(tree).map(([key, node]) => {
126
127
  if (node.kind === 'resource') {
127
128
  return [
128
129
  key,
129
- connectTree(node.children, joinPaths(prefix, node.path.source), request, response),
130
+ connectTree(node.children, joinPaths(prefix, node.path.source), plainRequest, parsedRequest),
130
131
  ];
131
132
  }
132
133
  const path = RoutePattern.parse(joinPaths(prefix, node.path?.source ?? ''));
133
- const fetch = node.schema.response ? response : request;
134
+ const fetch = node.schema.response ? parsedRequest : plainRequest;
134
135
  return [
135
136
  key,
136
- (args = {}) => fetch({
137
+ (input, options) => fetch({
137
138
  schema: node.schema,
138
139
  path,
139
140
  method: node.method,
140
- args,
141
+ input,
142
+ options,
141
143
  $result: undefined,
142
144
  }),
143
145
  ];
@@ -163,6 +165,14 @@ function validateClientResponsePlugins(tree, plugins) {
163
165
  function missingClientResponsePlugin(pluginId) {
164
166
  return new Error(`Missing client response plugin for ${pluginId}`);
165
167
  }
168
+ function pickObjectSchemaFields(schema, input) {
169
+ if (typeof input !== 'object' || input === null) {
170
+ return input;
171
+ }
172
+ return Object.fromEntries(Object.keys(schema.shape)
173
+ .filter(key => key in input)
174
+ .map(key => [key, input[key]]));
175
+ }
166
176
  function joinPaths(left, right) {
167
177
  return [left, right].filter(Boolean).join('/').replace(/\/+/g, '/');
168
178
  }
package/dist/http.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { RoutePattern } from '@remix-run/route-pattern';
2
- import type { RouteSchema } from './types/schema.js';
2
+ import type { RawBodySchema, RouteSchema } from './types/schema.js';
3
3
  /** HTTP methods supported by Rouzer action declarations. */
4
4
  export type HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
5
5
  /**
@@ -62,3 +62,6 @@ export declare function patch<const T extends RouteSchema>(schema: T): HttpActio
62
62
  declare function deleteAction<const P extends string, const T extends RouteSchema>(path: P, schema: T): HttpAction<P, T, 'DELETE'>;
63
63
  declare function deleteAction<const T extends RouteSchema>(schema: T): HttpAction<'', T, 'DELETE'>;
64
64
  export { deleteAction as delete };
65
+ /** Declare a request body that is passed through to `fetch` without JSON encoding. */
66
+ export declare function rawBody(): RawBodySchema;
67
+ export declare function isRawBodySchema(schema: unknown): schema is RawBodySchema;
package/dist/http.js CHANGED
@@ -29,6 +29,13 @@ function deleteAction(pathOrSchema, schema) {
29
29
  return action('DELETE', pathOrSchema, schema);
30
30
  }
31
31
  export { deleteAction as delete };
32
+ /** Declare a request body that is passed through to `fetch` without JSON encoding. */
33
+ export function rawBody() {
34
+ return { __rawBody__: Symbol('rouzer.rawBody') };
35
+ }
36
+ export function isRawBodySchema(schema) {
37
+ return Boolean(schema && typeof schema === 'object' && '__rawBody__' in schema);
38
+ }
32
39
  function action(method, pathOrSchema, schema) {
33
40
  const path = typeof pathOrSchema === 'string'
34
41
  ? RoutePattern.parse(pathOrSchema)
@@ -1,6 +1,6 @@
1
1
  import type { Promisable } from './common.js';
2
2
  import type { RoutePattern } from '@remix-run/route-pattern';
3
- import type { RouteArgs } from './types/args.js';
3
+ import type { RouteOptions } from './types/args.js';
4
4
  import type { RouteSchema } from './types/schema.js';
5
5
  /** Runtime key carried by response plugin markers. */
6
6
  export declare const responsePluginMarker: unique symbol;
@@ -34,7 +34,8 @@ export type ClientResponsePluginRequest = {
34
34
  schema: RouteSchema;
35
35
  path: RoutePattern;
36
36
  method: string;
37
- args: RouteArgs;
37
+ input?: unknown;
38
+ options?: RouteOptions;
38
39
  };
39
40
  /** Router-side response plugin used by `createRouter({ plugins })`. */
40
41
  export type RouterResponsePlugin = {
@@ -1,6 +1,6 @@
1
1
  import type { HattipHandler } from '@hattip/core';
2
2
  import { ApplyMiddleware, chain, ExtractMiddleware, MiddlewareChain, MiddlewareTypes } from 'alien-middleware';
3
- import type { HttpRouteTree } from '../http.js';
3
+ import { type HttpRouteTree } from '../http.js';
4
4
  import { type RouterResponsePlugin } from '../response.js';
5
5
  import type { RouteRequestHandlerMap } from '../types/server.js';
6
6
  export { chain };
@@ -3,6 +3,7 @@ import { createMatcher } from '@remix-run/route-pattern/match';
3
3
  import { chain, MiddlewareChain, } from 'alien-middleware';
4
4
  import * as z from 'zod';
5
5
  import { mapValues } from '../common.js';
6
+ import { isRawBodySchema } from '../http.js';
6
7
  import { createResponsePluginMap, getResponsePluginMarkerId, } from '../response.js';
7
8
  import { getDefaultSuccessStatus, getResponseMapPluginIds, isErrorMarker, isResponseMap, } from '../response-map.js';
8
9
  export { chain };
@@ -103,7 +104,7 @@ class RouterObject extends MiddlewareChain {
103
104
  return httpClientError(error, 'Invalid query string', config);
104
105
  }
105
106
  }
106
- if (schema.body) {
107
+ if (schema.body && !isRawBodySchema(schema.body)) {
107
108
  const error = await parseRequestBody(context, schema.body);
108
109
  if (error) {
109
110
  addDebugHeaders?.(context, route);
@@ -1,51 +1,42 @@
1
1
  import type { MatchParams } from '@remix-run/route-pattern/match';
2
2
  import type * as z from 'zod';
3
- import type { MutationRouteSchema, QueryRouteSchema, RouteSchema } from './schema.js';
3
+ import type { MutationRouteSchema, QueryRouteSchema, RawBodySchema, RouteSchema } from './schema.js';
4
4
  declare class Any {
5
5
  private isAny;
6
6
  }
7
- type PathArgs<T, P extends string> = T extends {
7
+ type PathInput<T, P extends string> = T extends {
8
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 & {
9
+ } ? z.infer<TPath> : MatchParams<P>;
10
+ type QueryInput<T> = T extends QueryRouteSchema & {
19
11
  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 {
12
+ } ? z.infer<TQuery> : unknown;
13
+ type BodyInput<T> = T extends MutationRouteSchema ? T extends {
26
14
  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;
15
+ } ? TBody extends RawBodySchema ? unknown : z.infer<TBody> : unknown : unknown;
16
+ type HeaderInput<T> = T extends {
17
+ headers: infer THeaders;
18
+ } ? Partial<z.infer<THeaders>> : Record<string, string | undefined>;
34
19
  /**
35
- * Arguments accepted by a generated client action function.
20
+ * Semantic input accepted by a generated client action function.
36
21
  *
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.
22
+ * @remarks Path params, query params, and JSON body fields are flattened into a
23
+ * single object. Avoid declaring duplicate keys across path/query/body schemas,
24
+ * since a flat input cannot distinguish their source.
42
25
  */
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'> & {
26
+ export type RouteInput<T extends RouteSchema = any, P extends string = string> = [T] extends [Any] ? any : PathInput<T, P> & QueryInput<T> & BodyInput<T>;
27
+ /**
28
+ * Fetch options accepted as the second argument to a generated client action.
29
+ *
30
+ * @remarks `headers` remains optional because required route headers may be
31
+ * supplied by `createClient({ headers })` defaults.
32
+ */
33
+ type RouteBodyOption<T> = T extends {
34
+ body: RawBodySchema;
35
+ } ? {
36
+ body: BodyInit | null;
37
+ } : {};
38
+ export type RouteOptions<T extends RouteSchema = any> = Omit<RequestInit, 'method' | 'body' | 'headers'> & RouteBodyOption<T> & {
48
39
  /** Headers for this request. Undefined values are removed before `fetch`. */
49
- headers?: Record<string, string | undefined>;
40
+ headers?: HeaderInput<T>;
50
41
  };
51
42
  export {};
@@ -4,7 +4,10 @@ import type * as z from 'zod';
4
4
  import { Promisable } from '../common.js';
5
5
  import type { HttpAction } from '../http.js';
6
6
  import type { InferRouteHandlerResult, InferResponseMapErrors, InferResponseMapSuccesses } from './response.js';
7
- import type { RouteResponseMap, RouteSchema } from './schema.js';
7
+ import type { RawBodySchema, RouteResponseMap, RouteSchema } from './schema.js';
8
+ type InferHandlerBody<T> = T extends {
9
+ body: infer TBody;
10
+ } ? TBody extends RawBodySchema ? undefined : z.infer<TBody> : undefined;
8
11
  type RequestContext<TMiddleware extends AnyMiddlewareChain> = MiddlewareContext<TMiddleware>;
9
12
  /**
10
13
  * Error response returned by `ctx.error(status, body)` in route handlers.
@@ -51,9 +54,7 @@ export type InferActionHandler<TMiddleware extends AnyMiddlewareChain, TAction e
51
54
  path: TAction['schema'] extends {
52
55
  path: any;
53
56
  } ? z.infer<TAction['schema']['path']> : MatchParams<TPath>;
54
- body: TAction['schema'] extends {
55
- body: any;
56
- } ? z.infer<TAction['schema']['body']> : undefined;
57
+ body: InferHandlerBody<TAction['schema']>;
57
58
  headers: TAction['schema'] extends {
58
59
  headers: any;
59
60
  } ? z.infer<TAction['schema']['headers']> : undefined;
@@ -71,9 +72,7 @@ export type InferActionHandler<TMiddleware extends AnyMiddlewareChain, TAction e
71
72
  path: TAction['schema'] extends {
72
73
  path: any;
73
74
  } ? z.infer<TAction['schema']['path']> : MatchParams<TPath>;
74
- body: TAction['schema'] extends {
75
- body: any;
76
- } ? z.infer<TAction['schema']['body']> : undefined;
75
+ body: InferHandlerBody<TAction['schema']>;
77
76
  headers: TAction['schema'] extends {
78
77
  headers: any;
79
78
  } ? z.infer<TAction['schema']['headers']> : undefined;
@@ -36,14 +36,18 @@ export type QueryRouteSchema = {
36
36
  /** Optional compile-time-only JSON or plugin response type marker. */
37
37
  response?: RouteResponseSchema;
38
38
  };
39
+ /** Marker for request bodies passed through to `fetch` without JSON encoding. */
40
+ export type RawBodySchema = {
41
+ readonly __rawBody__: unique symbol;
42
+ };
39
43
  /** Schema shape for mutation route methods. */
40
44
  export type MutationRouteSchema = {
41
45
  /** Optional Zod object used to validate path params. */
42
46
  path?: z.ZodObject<any>;
43
47
  /** Mutation routes do not accept query schemas. */
44
48
  query?: never;
45
- /** Optional Zod schema used to validate the JSON request body. */
46
- body?: z.ZodType<any, any>;
49
+ /** Optional Zod schema used to validate the JSON request body, or raw body marker. */
50
+ body?: z.ZodObject<any> | RawBodySchema;
47
51
  /** Optional Zod object used to validate request headers. */
48
52
  headers?: z.ZodObject<any>;
49
53
  /** Optional compile-time-only JSON or plugin response type marker. */
@@ -1,4 +1,4 @@
1
- import type { AnyMiddlewareChain, MiddlewareChain } from 'alien-middleware';
1
+ import type { AnyMiddlewareChain } from 'alien-middleware';
2
2
  import type { HttpAction, HttpResource, HttpRouteTree } from '../http.js';
3
3
  import type { InferActionHandler } from './handler.js';
4
4
  import type { Join } from './path.js';
@@ -10,6 +10,6 @@ import type { Join } from './path.js';
10
10
  * Handler context is inferred from middleware plus accumulated path params,
11
11
  * query/body schemas, and header schemas.
12
12
  */
13
- export type RouteRequestHandlerMap<TRoutes extends HttpRouteTree = HttpRouteTree, TMiddleware extends AnyMiddlewareChain = MiddlewareChain, TPrefix extends string = ''> = {
13
+ export type RouteRequestHandlerMap<TRoutes extends HttpRouteTree = HttpRouteTree, TMiddleware extends AnyMiddlewareChain = never, TPrefix extends string = ''> = {
14
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
15
  };
@@ -101,16 +101,9 @@ export async function runBasicUsageExample() {
101
101
  fetch: createLocalFetch(handler),
102
102
  })
103
103
 
104
- const fetched = await client.profiles.get({
105
- path: { id: '42' },
106
- query: { includePosts: false },
107
- headers: { 'x-request-id': 'docs' },
108
- })
104
+ const fetched = await client.profiles.get({ id: '42', includePosts: false }, { headers: { 'x-request-id': 'docs' } })
109
105
 
110
- const updated = await client.profiles.update({
111
- path: { id: '42' },
112
- body: { name: 'Grace' },
113
- })
106
+ const updated = await client.profiles.update({ id: '42', name: 'Grace' })
114
107
 
115
108
  return { fetched, updated }
116
109
  }
@@ -89,10 +89,10 @@ export async function runErrorResponsesExample() {
89
89
  fetch: createLocalFetch(handler),
90
90
  })
91
91
 
92
- const found = await client.getUser({ path: { id: '42' } })
93
- const created = await client.getUser({ path: { id: 'created' } })
94
- const missing = await client.getUser({ path: { id: 'missing' } })
95
- const unauthorized = await client.getUser({ path: { id: 'unauthorized' } })
92
+ const found = await client.getUser({ id: '42' })
93
+ const created = await client.getUser({ id: 'created' })
94
+ const missing = await client.getUser({ id: 'missing' })
95
+ const unauthorized = await client.getUser({ id: 'unauthorized' })
96
96
 
97
97
  return { found, created, missing, unauthorized }
98
98
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rouzer",
3
- "version": "4.0.0",
3
+ "version": "5.0.0",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {