@mhbdev/next-safe-route 0.0.36 → 0.0.38

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/index.d.ts CHANGED
@@ -1,4 +1,6 @@
1
- import { S as Schema, I as Infer, V as ValidationIssue, a as ValidationAdapter, b as IfInstalled } from './types-UXG9BoMB.js';
1
+ import { S as Schema, a as Infer, b as ValidationIssue, V as ValidationAdapter, c as IfInstalled } from './types-DYbZEItT.js';
2
+ import { b as SafeActionBuilderConfig, c as SafeActionMiddleware, I as InferParsedActionInput, d as SafeActionHandler, e as SafeActionFn, f as InferActionInput, g as SafeActionClientOptions } from './safeActionTypes-ClmL2Zxu.js';
3
+ export { h as SafeActionMiddlewareNext, a as SafeActionResult, i as SafeActionServerErrorResult, j as SafeActionSuccessResult, k as SafeActionValidationErrorResult, S as SafeActionValidationErrors } from './safeActionTypes-ClmL2Zxu.js';
2
4
  import { z } from 'zod';
3
5
  export { valibotAdapter } from './valibot.js';
4
6
  export { yupAdapter } from './yup.js';
@@ -6,10 +8,34 @@ import '@sinclair/typebox';
6
8
  import 'valibot';
7
9
  import 'yup';
8
10
 
9
- type Awaitable<T> = T | Promise<T>;
11
+ type Awaitable$1<T> = T | Promise<T>;
12
+ type ValueCoercionMode = 'none' | 'primitive';
13
+ type ValueCoercionFn = (value: string, key: string) => unknown;
14
+ type ValueCoercion = ValueCoercionMode | ValueCoercionFn;
15
+ type QueryArrayStrategy = 'auto' | 'always' | 'never';
16
+ type QuerySingleValueStrategy = 'first' | 'last';
17
+ type QueryParserOptions = {
18
+ arrayStrategy?: QueryArrayStrategy;
19
+ singleValueStrategy?: QuerySingleValueStrategy;
20
+ coerce?: ValueCoercion;
21
+ };
22
+ type BodyFallbackStrategy = 'json-first' | 'text';
23
+ type BodyParserOptions = {
24
+ strictContentType?: boolean;
25
+ allowEmptyBody?: boolean;
26
+ emptyValue?: unknown;
27
+ coerce?: ValueCoercion;
28
+ fallbackStrategy?: BodyFallbackStrategy;
29
+ arrayStrategy?: QueryArrayStrategy;
30
+ singleValueStrategy?: QuerySingleValueStrategy;
31
+ };
32
+ type ParserOptions = {
33
+ query?: QueryParserOptions;
34
+ body?: BodyParserOptions;
35
+ };
10
36
  type InferMaybe<TSchema extends Schema | undefined> = TSchema extends Schema ? Infer<TSchema> : Record<string, unknown>;
11
37
  type RouteContext<TRawParams extends Record<string, unknown> = Record<string, string | string[]>> = {
12
- params: Awaitable<TRawParams>;
38
+ params: Awaitable$1<TRawParams>;
13
39
  };
14
40
  type HandlerFunction<TParams, TQuery, TBody, TContext> = (request: Request, context: {
15
41
  params: TParams;
@@ -21,7 +47,9 @@ type OriginalRouteHandler = (request: Request, context: RouteContext) => Respons
21
47
  type HandlerServerErrorFn = (error: Error) => Response;
22
48
  type ValidationErrorHandler = (issues: ValidationIssue[]) => Response;
23
49
 
24
- type Middleware<T = Record<string, unknown>> = (request: Request) => Promise<T | Response>;
50
+ type Awaitable<T> = T | Promise<T>;
51
+ type Middleware<TContext extends Record<string, unknown> = Record<string, unknown>, TReturn extends Record<string, unknown> = Record<string, unknown>> = (request: Request, data: TContext) => Awaitable<TReturn | Response>;
52
+ type AnyMiddleware = Middleware<Record<string, unknown>, Record<string, unknown>>;
25
53
  type BuilderConfig<TParams extends Schema | undefined, TQuery extends Schema | undefined, TBody extends Schema | undefined> = {
26
54
  paramsSchema: TParams;
27
55
  querySchema: TQuery;
@@ -34,22 +62,31 @@ declare class RouteHandlerBuilder<TParams extends Schema | undefined = undefined
34
62
  private validationErrorHandler?;
35
63
  private validationAdapter;
36
64
  private baseContext;
37
- constructor({ config, validationAdapter, middlewares, handleServerError, validationErrorHandler, baseContext, }: {
65
+ private parserOptions;
66
+ private parserOptionsInput?;
67
+ constructor({ config, validationAdapter, middlewares, handleServerError, validationErrorHandler, baseContext, parserOptions, }: {
38
68
  config?: BuilderConfig<TParams, TQuery, TBody>;
39
- middlewares?: Middleware[];
69
+ middlewares?: AnyMiddleware[];
40
70
  handleServerError?: HandlerServerErrorFn;
41
71
  validationErrorHandler?: ValidationErrorHandler;
42
72
  validationAdapter?: ValidationAdapter;
43
73
  baseContext?: TContext;
74
+ parserOptions?: ParserOptions;
44
75
  });
45
76
  params<T extends Schema>(schema: T): RouteHandlerBuilder<T, TQuery, TBody, TContext>;
46
77
  query<T extends Schema>(schema: T): RouteHandlerBuilder<TParams, T, TBody, TContext>;
47
78
  body<T extends Schema>(schema: T): RouteHandlerBuilder<TParams, TQuery, T, TContext>;
48
- use<TReturnType extends Record<string, unknown>>(middleware: Middleware<TReturnType>): RouteHandlerBuilder<TParams, TQuery, TBody, TContext & TReturnType>;
79
+ use<TReturnType extends Record<string, unknown>>(middleware: Middleware<TContext, TReturnType>): RouteHandlerBuilder<TParams, TQuery, TBody, TContext & TReturnType>;
49
80
  handler(handler: HandlerFunction<InferMaybe<TParams>, InferMaybe<TQuery>, InferMaybe<TBody>, TContext>): OriginalRouteHandler;
50
81
  private resolveParams;
51
82
  private validateInput;
52
83
  private getQueryParams;
84
+ private selectValues;
85
+ private pickSingleValue;
86
+ private coerceStringValue;
87
+ private coercePrimitiveValue;
88
+ private parseFormData;
89
+ private resolveEmptyBody;
53
90
  private parseRequestBody;
54
91
  private buildErrorResponse;
55
92
  }
@@ -59,9 +96,23 @@ type CreateSafeRouteParams<TContext extends Record<string, unknown>> = {
59
96
  validationErrorHandler?: ValidationErrorHandler;
60
97
  validationAdapter?: ValidationAdapter;
61
98
  baseContext?: TContext;
99
+ parserOptions?: ParserOptions;
62
100
  };
63
101
  declare function createSafeRoute<TContext extends Record<string, unknown> = Record<string, unknown>>(params?: CreateSafeRouteParams<TContext>): RouteHandlerBuilder<undefined, undefined, undefined, TContext>;
64
102
 
103
+ type DefaultObject = Record<string, never>;
104
+ declare class SafeActionBuilder<TInputSchema extends Schema | undefined = undefined, TOutputSchema extends Schema | undefined = undefined, TContext extends Record<string, unknown> = Record<string, unknown>, TMetadata extends Record<string, unknown> = DefaultObject> {
105
+ private config;
106
+ constructor(config: SafeActionBuilderConfig<TInputSchema, TOutputSchema, TContext, TMetadata>);
107
+ inputSchema<TSchema extends Schema>(schema: TSchema): SafeActionBuilder<TSchema, TOutputSchema, TContext, TMetadata>;
108
+ outputSchema<TSchema extends Schema>(schema: TSchema): SafeActionBuilder<TInputSchema, TSchema, TContext, TMetadata>;
109
+ metadata<TNextMetadata extends Record<string, unknown>>(metadata: TNextMetadata): SafeActionBuilder<TInputSchema, TOutputSchema, TContext, TNextMetadata>;
110
+ use<TContextPatch extends Record<string, unknown>>(middleware: SafeActionMiddleware<InferParsedActionInput<TInputSchema>, TContext, TMetadata, TContextPatch>): SafeActionBuilder<TInputSchema, TOutputSchema, TContext & TContextPatch, TMetadata>;
111
+ action<TData>(handler: SafeActionHandler<InferParsedActionInput<TInputSchema>, TContext, TMetadata, TData>): SafeActionFn<InferActionInput<TInputSchema>, TOutputSchema extends Schema ? Infer<TOutputSchema> : TData>;
112
+ }
113
+
114
+ declare function createSafeActionClient<TContext extends Record<string, unknown> = Record<string, unknown>>(options?: SafeActionClientOptions<TContext>): SafeActionBuilder<undefined, undefined, TContext, Record<string, never>>;
115
+
65
116
  declare class ZodAdapter implements ValidationAdapter {
66
117
  validate<S extends IfInstalled<z.ZodTypeAny>>(schema: S, data: unknown): Promise<{
67
118
  readonly success: true;
@@ -78,4 +129,4 @@ declare class ZodAdapter implements ValidationAdapter {
78
129
  }
79
130
  declare function zodAdapter(): ZodAdapter;
80
131
 
81
- export { type HandlerFunction, type HandlerServerErrorFn, IfInstalled, Infer, type InferMaybe, type OriginalRouteHandler, type RouteContext, RouteHandlerBuilder, Schema, ValidationAdapter, ValidationIssue, createSafeRoute, zodAdapter };
132
+ export { type BodyFallbackStrategy, type BodyParserOptions, type HandlerFunction, type HandlerServerErrorFn, IfInstalled, Infer, InferActionInput, type InferMaybe, InferParsedActionInput, type OriginalRouteHandler, type ParserOptions, type QueryArrayStrategy, type QueryParserOptions, type QuerySingleValueStrategy, type RouteContext, RouteHandlerBuilder, SafeActionBuilder, SafeActionBuilderConfig, SafeActionClientOptions, SafeActionFn, SafeActionHandler, SafeActionMiddleware, Schema, ValidationAdapter, ValidationIssue, type ValueCoercion, type ValueCoercionFn, type ValueCoercionMode, createSafeActionClient, createSafeRoute, zodAdapter };
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
- "use strict";var C=Object.create;var c=Object.defineProperty;var R=Object.getOwnPropertyDescriptor;var A=Object.getOwnPropertyNames;var H=Object.getPrototypeOf,P=Object.prototype.hasOwnProperty;var V=(t,e)=>{for(var a in e)c(t,a,{get:e[a],enumerable:!0})},S=(t,e,a,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let r of A(e))!P.call(t,r)&&r!==a&&c(t,r,{get:()=>e[r],enumerable:!(n=R(e,r))||n.enumerable});return t};var x=(t,e,a)=>(a=t!=null?C(H(t)):{},S(e||!t||!t.__esModule?c(a,"default",{value:t,enumerable:!0}):a,t)),k=t=>S(c({},"__esModule",{value:!0}),t);var Q={};V(Q,{RouteHandlerBuilder:()=>l,createSafeRoute:()=>E,valibotAdapter:()=>g,yupAdapter:()=>I,zodAdapter:()=>i});module.exports=k(Q);var v=class{async validate(e,a){let n=await e.safeParseAsync(a);return n.success?{success:!0,data:n.data}:{success:!1,issues:n.error.issues.map(({message:r,path:o})=>({message:r,path:o}))}}};function i(){return new v}var d=class extends Error{constructor(e){super(e),this.name="BodyParsingError"}},l=class t{config;middlewares;handleServerError;validationErrorHandler;validationAdapter;baseContext;constructor({config:e={paramsSchema:void 0,querySchema:void 0,bodySchema:void 0},validationAdapter:a=i(),middlewares:n=[],handleServerError:r,validationErrorHandler:o,baseContext:s}){this.config=e,this.middlewares=n,this.handleServerError=r,this.validationErrorHandler=o,this.validationAdapter=a,this.baseContext=s??{}}params(e){return new t({config:{...this.config,paramsSchema:e},middlewares:this.middlewares,handleServerError:this.handleServerError,validationErrorHandler:this.validationErrorHandler,validationAdapter:this.validationAdapter,baseContext:this.baseContext})}query(e){return new t({config:{...this.config,querySchema:e},middlewares:this.middlewares,handleServerError:this.handleServerError,validationErrorHandler:this.validationErrorHandler,validationAdapter:this.validationAdapter,baseContext:this.baseContext})}body(e){return new t({config:{...this.config,bodySchema:e},middlewares:this.middlewares,handleServerError:this.handleServerError,validationErrorHandler:this.validationErrorHandler,validationAdapter:this.validationAdapter,baseContext:this.baseContext})}use(e){return new t({config:this.config,middlewares:[...this.middlewares,e],handleServerError:this.handleServerError,validationErrorHandler:this.validationErrorHandler,validationAdapter:this.validationAdapter,baseContext:this.baseContext})}handler(e){return async(a,n)=>{try{let r=n??{params:{}},o=await this.resolveParams(r.params),s=this.getQueryParams(a),u=await this.parseRequestBody(a),p=await this.validateInput(this.config.paramsSchema,o,"Invalid params");if(p.success===!1)return p.response;let m=await this.validateInput(this.config.querySchema,s,"Invalid query");if(m.success===!1)return m.response;let f=await this.validateInput(this.config.bodySchema,u,"Invalid body");if(f.success===!1)return f.response;let y={...this.baseContext};for(let b of this.middlewares){let h=await b(a);if(h instanceof Response)return h;y={...y,...h}}return e(a,{params:p.data,query:m.data,body:f.data,data:y})}catch(r){return r instanceof d?this.buildErrorResponse(r.message,void 0,400):this.handleServerError?this.handleServerError(r):this.buildErrorResponse("Internal server error",void 0,500)}}}async resolveParams(e){return await Promise.resolve(e??{})??{}}async validateInput(e,a,n){if(!e)return{success:!0,data:a??{}};let r=await this.validationAdapter.validate(e,a);return r.success===!0?{success:!0,data:r.data}:this.validationErrorHandler?{success:!1,response:this.validationErrorHandler(r.issues)}:{success:!1,response:this.buildErrorResponse(n,r.issues)}}getQueryParams(e){let a=new URL(e.url),n={},r=Array.from(new Set(a.searchParams.keys()));for(let o of r){let s=a.searchParams.getAll(o);n[o]=s.length===1?s[0]:s}return n}async parseRequestBody(e){if(!this.config.bodySchema)return{};let a=e.headers.get("content-type")??"";if(a.includes("application/json")){let n=await e.text();if(n.length===0)return{};try{return JSON.parse(n)}catch{throw new d("Invalid JSON body.")}}if(a.includes("multipart/form-data")||a.includes("application/x-www-form-urlencoded"))try{let n=await e.formData(),r={},o=Array.from(new Set(n.keys()));for(let s of o){let u=n.getAll(s);r[s]=u.length===1?u[0]:u}return r}catch{throw new d("Invalid Form Data.")}throw new d("Unsupported content type. Expected application/json, multipart/form-data, or application/x-www-form-urlencoded.")}buildErrorResponse(e,a,n=400){return new Response(JSON.stringify({message:e,issues:a}),{status:n,headers:{"content-type":"application/json"}})}};function E(t){return new l({handleServerError:t?.handleServerError,validationErrorHandler:t?.validationErrorHandler,validationAdapter:t?.validationAdapter??i(),baseContext:t?.baseContext})}async function B(){try{return await import("valibot")}catch(t){throw new Error('valibotAdapter requires optional peer dependency "valibot". Install it with `npm install valibot`.',{cause:t instanceof Error?t:void 0})}}var w=class{async validate(e,a){let n=await B(),r=await n.safeParseAsync(e,a);return r.success?{success:!0,data:r.output}:{success:!1,issues:r.issues.map(o=>({message:o.message,path:n.getDotPath(o)?.split(".")}))}}};function g(){return new w}async function M(){try{return await import("yup")}catch(t){throw new Error('yupAdapter requires optional peer dependency "yup". Install it with `npm install yup`.',{cause:t instanceof Error?t:void 0})}}var T=class{async validate(e,a){let n=await M();try{return{success:!0,data:await e.validate(a,{strict:!0})}}catch(r){if(r instanceof n.ValidationError){let{message:o,path:s}=r;return{success:!1,issues:[{message:o,path:s&&s.length>0?s.split("."):void 0}]}}throw r}}};function I(){return new T}0&&(module.exports={RouteHandlerBuilder,createSafeRoute,valibotAdapter,yupAdapter,zodAdapter});
1
+ "use strict";var j=Object.create;var x=Object.defineProperty;var G=Object.getOwnPropertyDescriptor;var J=Object.getOwnPropertyNames;var Y=Object.getPrototypeOf,L=Object.prototype.hasOwnProperty;var U=(t,e)=>{for(var r in e)x(t,r,{get:e[r],enumerable:!0})},V=(t,e,r,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let a of J(e))!L.call(t,a)&&a!==r&&x(t,a,{get:()=>e[a],enumerable:!(n=G(e,a))||n.enumerable});return t};var P=(t,e,r)=>(r=t!=null?j(Y(t)):{},V(e||!t||!t.__esModule?x(r,"default",{value:t,enumerable:!0}):r,t)),$=t=>V(x({},"__esModule",{value:!0}),t);var te={};U(te,{RouteHandlerBuilder:()=>S,SafeActionBuilder:()=>T,createSafeActionClient:()=>F,createSafeRoute:()=>k,valibotAdapter:()=>D,yupAdapter:()=>Q,zodAdapter:()=>y});module.exports=$(te);var v=class{async validate(e,r){let n=await e.safeParseAsync(r);return n.success?{success:!0,data:n.data}:{success:!1,issues:n.error.issues.map(({message:a,path:o})=>({message:a,path:o}))}}};function y(){return new v}function K(t){let e=t?.body;return{query:{arrayStrategy:t?.query?.arrayStrategy??"auto",singleValueStrategy:t?.query?.singleValueStrategy??"last",coerce:t?.query?.coerce??"none"},body:{strictContentType:e?.strictContentType??!0,allowEmptyBody:e?.allowEmptyBody??!0,hasEmptyValue:!!(e&&"emptyValue"in e),emptyValue:e?.emptyValue,coerce:e?.coerce??"none",fallbackStrategy:e?.fallbackStrategy??"json-first",arrayStrategy:e?.arrayStrategy??"auto",singleValueStrategy:e?.singleValueStrategy??"last"}}}var f=class extends Error{constructor(e){super(e),this.name="BodyParsingError"}},S=class t{config;middlewares;handleServerError;validationErrorHandler;validationAdapter;baseContext;parserOptions;parserOptionsInput;constructor({config:e={paramsSchema:void 0,querySchema:void 0,bodySchema:void 0},validationAdapter:r=y(),middlewares:n=[],handleServerError:a,validationErrorHandler:o,baseContext:i,parserOptions:d}){this.config=e,this.middlewares=n,this.handleServerError=a,this.validationErrorHandler=o,this.validationAdapter=r,this.baseContext=i??{},this.parserOptionsInput=d,this.parserOptions=K(d)}params(e){return new t({config:{...this.config,paramsSchema:e},middlewares:this.middlewares,handleServerError:this.handleServerError,validationErrorHandler:this.validationErrorHandler,validationAdapter:this.validationAdapter,baseContext:this.baseContext,parserOptions:this.parserOptionsInput})}query(e){return new t({config:{...this.config,querySchema:e},middlewares:this.middlewares,handleServerError:this.handleServerError,validationErrorHandler:this.validationErrorHandler,validationAdapter:this.validationAdapter,baseContext:this.baseContext,parserOptions:this.parserOptionsInput})}body(e){return new t({config:{...this.config,bodySchema:e},middlewares:this.middlewares,handleServerError:this.handleServerError,validationErrorHandler:this.validationErrorHandler,validationAdapter:this.validationAdapter,baseContext:this.baseContext,parserOptions:this.parserOptionsInput})}use(e){return new t({config:this.config,middlewares:[...this.middlewares,e],handleServerError:this.handleServerError,validationErrorHandler:this.validationErrorHandler,validationAdapter:this.validationAdapter,baseContext:this.baseContext,parserOptions:this.parserOptionsInput})}handler(e){return async(r,n)=>{try{let a=n??{params:{}},o=await this.resolveParams(a.params),i=this.getQueryParams(r),d=await this.parseRequestBody(r),s=await this.validateInput(this.config.paramsSchema,o,"Invalid params");if(s.success===!1)return s.response;let u=await this.validateInput(this.config.querySchema,i,"Invalid query");if(u.success===!1)return u.response;let m=await this.validateInput(this.config.bodySchema,d,"Invalid body");if(m.success===!1)return m.response;let l={...this.baseContext};for(let g of this.middlewares){let p=await g(r,l);if(p instanceof Response)return p;l={...l,...p}}return e(r,{params:s.data,query:u.data,body:m.data,data:l})}catch(a){return a instanceof f?this.buildErrorResponse(a.message,void 0,400):this.handleServerError?this.handleServerError(a):this.buildErrorResponse("Internal server error",void 0,500)}}}async resolveParams(e){return await Promise.resolve(e??{})??{}}async validateInput(e,r,n){if(!e)return{success:!0,data:r??{}};let a=await this.validationAdapter.validate(e,r);return a.success===!0?{success:!0,data:a.data}:this.validationErrorHandler?{success:!1,response:this.validationErrorHandler(a.issues)}:{success:!1,response:this.buildErrorResponse(n,a.issues)}}getQueryParams(e){let r=new URL(e.url),n={},a=Array.from(new Set(r.searchParams.keys())),{arrayStrategy:o,singleValueStrategy:i,coerce:d}=this.parserOptions.query;for(let s of a){let m=r.searchParams.getAll(s).map(l=>this.coerceStringValue(l,s,d));n[s]=this.selectValues(m,o,i)}return n}selectValues(e,r,n){return r==="always"?e:r==="never"?this.pickSingleValue(e,n):e.length===1?e[0]:e}pickSingleValue(e,r){if(e.length!==0)return r==="first"?e[0]:e[e.length-1]}coerceStringValue(e,r,n){return typeof n=="function"?n(e,r):n==="primitive"?this.coercePrimitiveValue(e):e}coercePrimitiveValue(e){let r=e.trim();return r==="true"?!0:r==="false"?!1:r==="null"?null:/^-?(?:\d+|\d*\.\d+)$/.test(r)?Number(r):e}parseFormData(e,r,n,a){let o={},i=Array.from(new Set(e.keys()));for(let d of i){let s=e.getAll(d).map(u=>typeof u=="string"?this.coerceStringValue(u,d,a):u);o[d]=this.selectValues(s,r,n)}return o}resolveEmptyBody(){let{allowEmptyBody:e,hasEmptyValue:r,emptyValue:n}=this.parserOptions.body;if(e)return r?n:{};throw new f("Request body is required.")}async parseRequestBody(e){if(!this.config.bodySchema)return{};let r=(e.headers.get("content-type")??"").toLowerCase();if(r.includes("application/json")){let a=await e.text();if(a.length===0)return this.resolveEmptyBody();try{return JSON.parse(a)}catch{throw new f("Invalid JSON body.")}}if(r.includes("multipart/form-data")||r.includes("application/x-www-form-urlencoded"))try{let a=await e.formData(),{arrayStrategy:o,singleValueStrategy:i,coerce:d}=this.parserOptions.body,s=this.parseFormData(a,o,i,d);return Object.keys(s).length===0?this.resolveEmptyBody():s}catch{throw new f("Invalid Form Data.")}if(this.parserOptions.body.strictContentType)throw new f("Unsupported content type. Expected application/json, multipart/form-data, or application/x-www-form-urlencoded.");let n=await e.text();if(n.length===0)return this.resolveEmptyBody();if(this.parserOptions.body.fallbackStrategy==="text")return this.coerceStringValue(n,"body",this.parserOptions.body.coerce);try{return JSON.parse(n)}catch{return this.coerceStringValue(n,"body",this.parserOptions.body.coerce)}}buildErrorResponse(e,r,n=400){return new Response(JSON.stringify({message:e,issues:r}),{status:n,headers:{"content-type":"application/json"}})}};function k(t){return new S({handleServerError:t?.handleServerError,validationErrorHandler:t?.validationErrorHandler,validationAdapter:t?.validationAdapter??y(),baseContext:t?.baseContext,parserOptions:t?.parserOptions})}function W(t){if(!(!t||t.length===0))return t.map(e=>typeof e=="symbol"?e.description??e.toString():String(e)).join(".")}var X=t=>{let e={},r=[];for(let n of t){let a=W(n.path);if(!a){r.push(n.message);continue}e[a]||(e[a]=[]);let o=e[a]??[];o.push(n.message),e[a]=o}return{fieldErrors:e,formErrors:r}};function O(t){return{validationErrors:X(t)}}function A(t){return{serverError:t}}function B(t){if(!t||typeof t!="object")return!1;let e=t;return"data"in e||"validationErrors"in e||"serverError"in e}function M(t,e){return{...t,...e??{}}}function H(t,e,r){if(!r)return e;try{let n=r(t);return typeof n=="string"&&n.length>0?n:e}catch{return e}}var T=class t{config;constructor(e){this.config=e}inputSchema(e){return new t({...this.config,inputSchema:e})}outputSchema(e){return new t({...this.config,outputSchema:e})}metadata(e){return new t({...this.config,metadata:e,middlewares:this.config.middlewares})}use(e){return new t({...this.config,middlewares:[...this.config.middlewares,e],baseContext:this.config.baseContext})}action(e){let{inputSchema:r,outputSchema:n,validationAdapter:a,middlewares:o,metadata:i,baseContext:d,defaultServerError:s,handleServerError:u}=this.config;return async(...l)=>{try{let g=l.length>0?l[0]:void 0,p;if(!r)p=void 0;else{let c=await a.validate(r,g);if(c.success===!1)return O(c.issues);p=c.data}let q=async c=>{let h=await e({parsedInput:p,ctx:c,metadata:i});if(!n)return{data:h};let w=await a.validate(n,h);return w.success===!1?A(H(new Error("Invalid action output."),s,u)):{data:w.data}},I=async(c,h)=>{if(c>=o.length)return q(h);let w=o[c],E=!1,b=await w({parsedInput:p,ctx:h,metadata:i,next:async N=>{if(E)throw new Error("next() called more than once in the same middleware.");return E=!0,I(c+1,M(h,N?.ctx))}});if(!B(b))throw new Error("Middleware must return a SafeActionResult.");return b},z={...d};return await I(0,z)}catch(g){return A(H(g,s,u))}}}};var Z="Something went wrong while executing the action.";function F(t){return new T({validationAdapter:t?.validationAdapter??y(),baseContext:t?.baseContext??{},metadata:{},middlewares:[],defaultServerError:t?.defaultServerError??Z,handleServerError:t?.handleServerError})}async function _(){try{return await import("valibot")}catch(t){throw new Error('valibotAdapter requires optional peer dependency "valibot". Install it with `npm install valibot`.',{cause:t instanceof Error?t:void 0})}}var C=class{async validate(e,r){let n=await _(),a=await n.safeParseAsync(e,r);return a.success?{success:!0,data:a.output}:{success:!1,issues:a.issues.map(o=>({message:o.message,path:n.getDotPath(o)?.split(".")}))}}};function D(){return new C}async function ee(){try{return await import("yup")}catch(t){throw new Error('yupAdapter requires optional peer dependency "yup". Install it with `npm install yup`.',{cause:t instanceof Error?t:void 0})}}var R=class{async validate(e,r){let n=await ee();try{return{success:!0,data:await e.validate(r,{strict:!0})}}catch(a){if(a instanceof n.ValidationError){let{message:o,path:i}=a;return{success:!1,issues:[{message:o,path:i&&i.length>0?i.split("."):void 0}]}}throw a}}};function Q(){return new R}0&&(module.exports={RouteHandlerBuilder,SafeActionBuilder,createSafeActionClient,createSafeRoute,valibotAdapter,yupAdapter,zodAdapter});
2
2
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/adapters/zod.ts","../src/routeHandlerBuilder.ts","../src/createSafeRoute.ts","../src/adapters/valibot.ts","../src/adapters/yup.ts"],"sourcesContent":["export { createSafeRoute } from './createSafeRoute';\nexport { RouteHandlerBuilder } from './routeHandlerBuilder';\nexport {\n type HandlerFunction,\n type HandlerServerErrorFn,\n type InferMaybe,\n type OriginalRouteHandler,\n type RouteContext,\n} from './types';\nexport {\n type IfInstalled,\n type Infer,\n type Schema,\n type ValidationAdapter,\n type ValidationIssue,\n} from './adapters/types';\nexport { zodAdapter } from './adapters/zod';\nexport { valibotAdapter } from './adapters/valibot';\nexport { yupAdapter } from './adapters/yup';\n","// Code based on https://github.com/decs/typeschema/blob/main/packages/zod/src/validation.ts\n// MIT License\n// Copyright (c) 2023 André Costa\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n// The above copyright notice and this permission notice shall be included in all\n// copies or substantial portions of the Software.\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\nimport type { z } from 'zod';\n\nimport type { IfInstalled, Infer, ValidationAdapter } from './types';\n\nclass ZodAdapter implements ValidationAdapter {\n async validate<S extends IfInstalled<z.ZodTypeAny>>(schema: S, data: unknown) {\n const result = await schema.safeParseAsync(data);\n\n if (result.success) {\n return {\n success: true,\n data: result.data as Infer<S>,\n } as const;\n }\n\n return {\n success: false,\n issues: result.error.issues.map(({ message, path }) => ({ message, path })),\n } as const;\n }\n}\n\nexport function zodAdapter() {\n return new ZodAdapter();\n}\n","import { Schema, ValidationAdapter, ValidationIssue } from './adapters/types';\nimport { zodAdapter } from './adapters/zod';\nimport {\n HandlerFunction,\n HandlerServerErrorFn,\n InferMaybe,\n OriginalRouteHandler,\n RouteContext,\n ValidationErrorHandler,\n} from './types';\n\ntype Middleware<T = Record<string, unknown>> = (request: Request) => Promise<T | Response>;\n\ntype BuilderConfig<\n TParams extends Schema | undefined,\n TQuery extends Schema | undefined,\n TBody extends Schema | undefined,\n> = {\n paramsSchema: TParams;\n querySchema: TQuery;\n bodySchema: TBody;\n};\n\nclass BodyParsingError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'BodyParsingError';\n }\n}\n\nexport class RouteHandlerBuilder<\n TParams extends Schema | undefined = undefined,\n TQuery extends Schema | undefined = undefined,\n TBody extends Schema | undefined = undefined,\n TContext extends Record<string, unknown> = Record<string, unknown>,\n> {\n private config: BuilderConfig<TParams, TQuery, TBody>;\n private middlewares: Middleware[];\n private handleServerError?: HandlerServerErrorFn;\n private validationErrorHandler?: ValidationErrorHandler;\n private validationAdapter: ValidationAdapter;\n private baseContext: TContext;\n\n constructor({\n config = {\n paramsSchema: undefined as TParams,\n querySchema: undefined as TQuery,\n bodySchema: undefined as TBody,\n },\n validationAdapter = zodAdapter(),\n middlewares = [],\n handleServerError,\n validationErrorHandler,\n baseContext,\n }: {\n config?: BuilderConfig<TParams, TQuery, TBody>;\n middlewares?: Middleware[];\n handleServerError?: HandlerServerErrorFn;\n validationErrorHandler?: ValidationErrorHandler;\n validationAdapter?: ValidationAdapter;\n baseContext?: TContext;\n }) {\n this.config = config;\n this.middlewares = middlewares;\n this.handleServerError = handleServerError;\n this.validationErrorHandler = validationErrorHandler;\n this.validationAdapter = validationAdapter;\n this.baseContext = (baseContext ?? {}) as TContext;\n }\n\n params<T extends Schema>(schema: T): RouteHandlerBuilder<T, TQuery, TBody, TContext> {\n return new RouteHandlerBuilder<T, TQuery, TBody, TContext>({\n config: { ...this.config, paramsSchema: schema },\n middlewares: this.middlewares,\n handleServerError: this.handleServerError,\n validationErrorHandler: this.validationErrorHandler,\n validationAdapter: this.validationAdapter,\n baseContext: this.baseContext,\n });\n }\n\n query<T extends Schema>(schema: T): RouteHandlerBuilder<TParams, T, TBody, TContext> {\n return new RouteHandlerBuilder<TParams, T, TBody, TContext>({\n config: { ...this.config, querySchema: schema },\n middlewares: this.middlewares,\n handleServerError: this.handleServerError,\n validationErrorHandler: this.validationErrorHandler,\n validationAdapter: this.validationAdapter,\n baseContext: this.baseContext,\n });\n }\n\n body<T extends Schema>(schema: T): RouteHandlerBuilder<TParams, TQuery, T, TContext> {\n return new RouteHandlerBuilder<TParams, TQuery, T, TContext>({\n config: { ...this.config, bodySchema: schema },\n middlewares: this.middlewares,\n handleServerError: this.handleServerError,\n validationErrorHandler: this.validationErrorHandler,\n validationAdapter: this.validationAdapter,\n baseContext: this.baseContext,\n });\n }\n\n use<TReturnType extends Record<string, unknown>>(middleware: Middleware<TReturnType>) {\n return new RouteHandlerBuilder<TParams, TQuery, TBody, TContext & TReturnType>({\n config: this.config,\n middlewares: [...this.middlewares, middleware],\n handleServerError: this.handleServerError,\n validationErrorHandler: this.validationErrorHandler,\n validationAdapter: this.validationAdapter,\n baseContext: this.baseContext as unknown as TContext & TReturnType,\n });\n }\n\n handler(\n handler: HandlerFunction<InferMaybe<TParams>, InferMaybe<TQuery>, InferMaybe<TBody>, TContext>,\n ): OriginalRouteHandler {\n return async (request, context): Promise<Response> => {\n try {\n const routeContext = context ?? ({ params: {} } as RouteContext);\n const paramsInput = await this.resolveParams(routeContext.params);\n const queryInput = this.getQueryParams(request);\n const bodyInput = await this.parseRequestBody(request);\n\n const paramsValidation = await this.validateInput(this.config.paramsSchema, paramsInput, 'Invalid params');\n if (paramsValidation.success === false) {\n return paramsValidation.response;\n }\n\n const queryValidation = await this.validateInput(this.config.querySchema, queryInput, 'Invalid query');\n if (queryValidation.success === false) {\n return queryValidation.response;\n }\n\n const bodyValidation = await this.validateInput(this.config.bodySchema, bodyInput, 'Invalid body');\n if (bodyValidation.success === false) {\n return bodyValidation.response;\n }\n\n let middlewareContext: TContext = { ...this.baseContext };\n for (const middleware of this.middlewares) {\n const result = await middleware(request);\n if (result instanceof Response) {\n return result;\n }\n middlewareContext = { ...middlewareContext, ...(result as TContext) };\n }\n\n return handler(request, {\n params: paramsValidation.data,\n query: queryValidation.data,\n body: bodyValidation.data,\n data: middlewareContext,\n });\n } catch (error) {\n if (error instanceof BodyParsingError) {\n return this.buildErrorResponse(error.message, undefined, 400);\n }\n\n if (this.handleServerError) {\n return this.handleServerError(error as Error);\n }\n\n return this.buildErrorResponse('Internal server error', undefined, 500);\n }\n };\n }\n\n private async resolveParams(params: RouteContext['params'] | undefined) {\n const resolvedParams = await Promise.resolve(params ?? {});\n return (resolvedParams ?? {}) as Record<string, unknown>;\n }\n\n private async validateInput<S extends Schema | undefined>(\n schema: S,\n data: unknown,\n errorMessage: string,\n ): Promise<{ success: true; data: InferMaybe<S> } | { success: false; response: Response }> {\n if (!schema) {\n return { success: true, data: (data ?? {}) as InferMaybe<S> };\n }\n\n const result = await this.validationAdapter.validate(schema, data);\n if (result.success === true) {\n return { success: true, data: result.data as InferMaybe<S> };\n }\n\n if (this.validationErrorHandler) {\n return { success: false, response: this.validationErrorHandler(result.issues) };\n }\n\n return { success: false, response: this.buildErrorResponse(errorMessage, result.issues) };\n }\n\n private getQueryParams(request: Request) {\n const url = new URL(request.url);\n const params: Record<string, unknown> = {};\n const keys = Array.from(new Set(url.searchParams.keys()));\n\n for (const key of keys) {\n const values = url.searchParams.getAll(key);\n params[key] = values.length === 1 ? values[0] : values;\n }\n\n return params;\n }\n\n private async parseRequestBody(request: Request): Promise<unknown> {\n if (!this.config.bodySchema) {\n return {};\n }\n\n const contentType = request.headers.get('content-type') ?? '';\n\n if (contentType.includes('application/json')) {\n const rawBody = await request.text();\n\n if (rawBody.length === 0) {\n return {};\n }\n\n try {\n return JSON.parse(rawBody);\n } catch (error) {\n throw new BodyParsingError('Invalid JSON body.');\n }\n }\n\n if (contentType.includes('multipart/form-data') || contentType.includes('application/x-www-form-urlencoded')) {\n try {\n const formData = await request.formData();\n const data: Record<string, unknown> = {};\n const keys = Array.from(new Set(formData.keys()));\n\n for (const key of keys) {\n const values = formData.getAll(key);\n data[key] = values.length === 1 ? values[0] : values;\n }\n\n return data;\n } catch (error) {\n throw new BodyParsingError('Invalid Form Data.');\n }\n }\n\n throw new BodyParsingError(\n 'Unsupported content type. Expected application/json, multipart/form-data, or application/x-www-form-urlencoded.',\n );\n }\n\n private buildErrorResponse(message: string, issues?: ValidationIssue[], status = 400) {\n return new Response(JSON.stringify({ message, issues }), {\n status,\n headers: { 'content-type': 'application/json' },\n });\n }\n}\n","import { ValidationAdapter } from './adapters/types';\nimport { zodAdapter } from './adapters/zod';\nimport { RouteHandlerBuilder } from './routeHandlerBuilder';\nimport { HandlerServerErrorFn, ValidationErrorHandler } from './types';\n\ntype CreateSafeRouteParams<TContext extends Record<string, unknown>> = {\n handleServerError?: HandlerServerErrorFn;\n validationErrorHandler?: ValidationErrorHandler;\n validationAdapter?: ValidationAdapter;\n baseContext?: TContext;\n};\n\nexport function createSafeRoute<TContext extends Record<string, unknown> = Record<string, unknown>>(\n params?: CreateSafeRouteParams<TContext>,\n) {\n return new RouteHandlerBuilder<undefined, undefined, undefined, TContext>({\n handleServerError: params?.handleServerError,\n validationErrorHandler: params?.validationErrorHandler,\n validationAdapter: params?.validationAdapter ?? zodAdapter(),\n baseContext: params?.baseContext,\n });\n}\n","// Code based on https://github.com/decs/typeschema/blob/main/packages/valibot/src/validation.ts\n// MIT License\n// Copyright (c) 2023 André Costa\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n// The above copyright notice and this permission notice shall be included in all\n// copies or substantial portions of the Software.\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\nimport type { GenericSchema, GenericSchemaAsync } from 'valibot';\n\nimport type { IfInstalled, Infer, ValidationAdapter } from './types';\n\ntype ValibotModule = typeof import('valibot');\n\nasync function loadValibot(): Promise<ValibotModule> {\n try {\n return await import('valibot');\n } catch (error) {\n throw new Error(\n 'valibotAdapter requires optional peer dependency \"valibot\". Install it with `npm install valibot`.',\n { cause: error instanceof Error ? error : undefined },\n );\n }\n}\n\nclass ValibotAdapter implements ValidationAdapter {\n async validate<S extends IfInstalled<GenericSchema | GenericSchemaAsync>>(schema: S, data: unknown) {\n const valibot = await loadValibot();\n const result = await valibot.safeParseAsync(schema, data);\n\n if (result.success) {\n return {\n success: true,\n data: result.output as Infer<S>,\n } as const;\n }\n\n return {\n success: false,\n issues: result.issues.map((issue) => ({\n message: issue.message,\n path: valibot.getDotPath(issue)?.split('.'),\n })),\n } as const;\n }\n}\n\nexport function valibotAdapter() {\n return new ValibotAdapter();\n}\n","// Code based on https://github.com/decs/typeschema/blob/main/packages/yup/src/validation.ts\n// MIT License\n// Copyright (c) 2023 André Costa\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n// The above copyright notice and this permission notice shall be included in all\n// copies or substantial portions of the Software.\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\nimport type { Schema as YupSchema } from 'yup';\n\nimport type { IfInstalled, Infer, ValidationAdapter, ValidationIssue } from './types';\n\ntype YupModule = typeof import('yup');\n\nasync function loadYup(): Promise<YupModule> {\n try {\n return await import('yup');\n } catch (error) {\n throw new Error('yupAdapter requires optional peer dependency \"yup\". Install it with `npm install yup`.', {\n cause: error instanceof Error ? error : undefined,\n });\n }\n}\n\nclass YupAdapter implements ValidationAdapter {\n async validate<S extends IfInstalled<YupSchema>>(schema: S, data: unknown) {\n const yup = await loadYup();\n\n try {\n const result = await schema.validate(data, { strict: true });\n\n return {\n success: true,\n data: result as Infer<S>,\n } as const;\n } catch (e) {\n if (e instanceof yup.ValidationError) {\n const { message, path } = e;\n\n return {\n success: false,\n issues: [\n {\n message,\n path: path && path.length > 0 ? path.split('.') : undefined,\n },\n ] as ValidationIssue[],\n } as const;\n }\n\n throw e;\n }\n }\n}\n\nexport function yupAdapter() {\n return new YupAdapter();\n}\n"],"mappings":"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","names":["src_exports","__export","RouteHandlerBuilder","createSafeRoute","valibotAdapter","yupAdapter","zodAdapter","__toCommonJS","ZodAdapter","schema","data","result","message","path","zodAdapter","BodyParsingError","message","RouteHandlerBuilder","_RouteHandlerBuilder","config","validationAdapter","zodAdapter","middlewares","handleServerError","validationErrorHandler","baseContext","schema","middleware","handler","request","context","routeContext","paramsInput","queryInput","bodyInput","paramsValidation","queryValidation","bodyValidation","middlewareContext","result","error","params","data","errorMessage","url","keys","key","values","contentType","rawBody","formData","issues","status","createSafeRoute","params","RouteHandlerBuilder","zodAdapter","loadValibot","error","ValibotAdapter","schema","data","valibot","result","issue","valibotAdapter","loadYup","error","YupAdapter","schema","data","yup","e","message","path","yupAdapter"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/adapters/zod.ts","../src/routeHandlerBuilder.ts","../src/createSafeRoute.ts","../src/safeActionUtils.ts","../src/safeActionBuilder.ts","../src/createSafeActionClient.ts","../src/adapters/valibot.ts","../src/adapters/yup.ts"],"sourcesContent":["export { createSafeRoute } from './createSafeRoute';\nexport { createSafeActionClient } from './createSafeActionClient';\nexport { RouteHandlerBuilder } from './routeHandlerBuilder';\nexport { SafeActionBuilder } from './safeActionBuilder';\nexport {\n type BodyFallbackStrategy,\n type BodyParserOptions,\n type HandlerFunction,\n type HandlerServerErrorFn,\n type InferMaybe,\n type OriginalRouteHandler,\n type ParserOptions,\n type QueryArrayStrategy,\n type QueryParserOptions,\n type QuerySingleValueStrategy,\n type RouteContext,\n type ValueCoercion,\n type ValueCoercionFn,\n type ValueCoercionMode,\n} from './types';\nexport {\n type InferActionInput,\n type InferParsedActionInput,\n type SafeActionBuilderConfig,\n type SafeActionClientOptions,\n type SafeActionFn,\n type SafeActionHandler,\n type SafeActionMiddleware,\n type SafeActionMiddlewareNext,\n type SafeActionResult,\n type SafeActionServerErrorResult,\n type SafeActionSuccessResult,\n type SafeActionValidationErrorResult,\n type SafeActionValidationErrors,\n} from './safeActionTypes';\nexport {\n type IfInstalled,\n type Infer,\n type Schema,\n type ValidationAdapter,\n type ValidationIssue,\n} from './adapters/types';\nexport { zodAdapter } from './adapters/zod';\nexport { valibotAdapter } from './adapters/valibot';\nexport { yupAdapter } from './adapters/yup';\n","// Code based on https://github.com/decs/typeschema/blob/main/packages/zod/src/validation.ts\n// MIT License\n// Copyright (c) 2023 André Costa\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n// The above copyright notice and this permission notice shall be included in all\n// copies or substantial portions of the Software.\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\nimport type { z } from 'zod';\n\nimport type { IfInstalled, Infer, ValidationAdapter } from './types';\n\nclass ZodAdapter implements ValidationAdapter {\n async validate<S extends IfInstalled<z.ZodTypeAny>>(schema: S, data: unknown) {\n const result = await schema.safeParseAsync(data);\n\n if (result.success) {\n return {\n success: true,\n data: result.data as Infer<S>,\n } as const;\n }\n\n return {\n success: false,\n issues: result.error.issues.map(({ message, path }) => ({ message, path })),\n } as const;\n }\n}\n\nexport function zodAdapter() {\n return new ZodAdapter();\n}\n","import { Schema, ValidationAdapter, ValidationIssue } from './adapters/types';\nimport { zodAdapter } from './adapters/zod';\nimport {\n BodyFallbackStrategy,\n HandlerFunction,\n HandlerServerErrorFn,\n InferMaybe,\n OriginalRouteHandler,\n ParserOptions,\n QueryArrayStrategy,\n QuerySingleValueStrategy,\n RouteContext,\n ValidationErrorHandler,\n ValueCoercion,\n} from './types';\n\ntype Awaitable<T> = T | Promise<T>;\n\ntype Middleware<\n TContext extends Record<string, unknown> = Record<string, unknown>,\n TReturn extends Record<string, unknown> = Record<string, unknown>,\n> = (request: Request, data: TContext) => Awaitable<TReturn | Response>;\n\ntype AnyMiddleware = Middleware<Record<string, unknown>, Record<string, unknown>>;\n\ntype NormalizedParserOptions = {\n query: {\n arrayStrategy: QueryArrayStrategy;\n singleValueStrategy: QuerySingleValueStrategy;\n coerce: ValueCoercion;\n };\n body: {\n strictContentType: boolean;\n allowEmptyBody: boolean;\n hasEmptyValue: boolean;\n emptyValue: unknown;\n coerce: ValueCoercion;\n fallbackStrategy: BodyFallbackStrategy;\n arrayStrategy: QueryArrayStrategy;\n singleValueStrategy: QuerySingleValueStrategy;\n };\n};\n\nfunction normalizeParserOptions(parserOptions?: ParserOptions): NormalizedParserOptions {\n const bodyOptions = parserOptions?.body;\n\n return {\n query: {\n arrayStrategy: parserOptions?.query?.arrayStrategy ?? 'auto',\n singleValueStrategy: parserOptions?.query?.singleValueStrategy ?? 'last',\n coerce: parserOptions?.query?.coerce ?? 'none',\n },\n body: {\n strictContentType: bodyOptions?.strictContentType ?? true,\n allowEmptyBody: bodyOptions?.allowEmptyBody ?? true,\n hasEmptyValue: Boolean(bodyOptions && 'emptyValue' in bodyOptions),\n emptyValue: bodyOptions?.emptyValue,\n coerce: bodyOptions?.coerce ?? 'none',\n fallbackStrategy: bodyOptions?.fallbackStrategy ?? 'json-first',\n arrayStrategy: bodyOptions?.arrayStrategy ?? 'auto',\n singleValueStrategy: bodyOptions?.singleValueStrategy ?? 'last',\n },\n };\n}\n\ntype BuilderConfig<\n TParams extends Schema | undefined,\n TQuery extends Schema | undefined,\n TBody extends Schema | undefined,\n> = {\n paramsSchema: TParams;\n querySchema: TQuery;\n bodySchema: TBody;\n};\n\nclass BodyParsingError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'BodyParsingError';\n }\n}\n\nexport class RouteHandlerBuilder<\n TParams extends Schema | undefined = undefined,\n TQuery extends Schema | undefined = undefined,\n TBody extends Schema | undefined = undefined,\n TContext extends Record<string, unknown> = Record<string, unknown>,\n> {\n private config: BuilderConfig<TParams, TQuery, TBody>;\n private middlewares: AnyMiddleware[];\n private handleServerError?: HandlerServerErrorFn;\n private validationErrorHandler?: ValidationErrorHandler;\n private validationAdapter: ValidationAdapter;\n private baseContext: TContext;\n private parserOptions: NormalizedParserOptions;\n private parserOptionsInput?: ParserOptions;\n\n constructor({\n config = {\n paramsSchema: undefined as TParams,\n querySchema: undefined as TQuery,\n bodySchema: undefined as TBody,\n },\n validationAdapter = zodAdapter(),\n middlewares = [],\n handleServerError,\n validationErrorHandler,\n baseContext,\n parserOptions,\n }: {\n config?: BuilderConfig<TParams, TQuery, TBody>;\n middlewares?: AnyMiddleware[];\n handleServerError?: HandlerServerErrorFn;\n validationErrorHandler?: ValidationErrorHandler;\n validationAdapter?: ValidationAdapter;\n baseContext?: TContext;\n parserOptions?: ParserOptions;\n }) {\n this.config = config;\n this.middlewares = middlewares;\n this.handleServerError = handleServerError;\n this.validationErrorHandler = validationErrorHandler;\n this.validationAdapter = validationAdapter;\n this.baseContext = (baseContext ?? {}) as TContext;\n this.parserOptionsInput = parserOptions;\n this.parserOptions = normalizeParserOptions(parserOptions);\n }\n\n params<T extends Schema>(schema: T): RouteHandlerBuilder<T, TQuery, TBody, TContext> {\n return new RouteHandlerBuilder<T, TQuery, TBody, TContext>({\n config: { ...this.config, paramsSchema: schema },\n middlewares: this.middlewares,\n handleServerError: this.handleServerError,\n validationErrorHandler: this.validationErrorHandler,\n validationAdapter: this.validationAdapter,\n baseContext: this.baseContext,\n parserOptions: this.parserOptionsInput,\n });\n }\n\n query<T extends Schema>(schema: T): RouteHandlerBuilder<TParams, T, TBody, TContext> {\n return new RouteHandlerBuilder<TParams, T, TBody, TContext>({\n config: { ...this.config, querySchema: schema },\n middlewares: this.middlewares,\n handleServerError: this.handleServerError,\n validationErrorHandler: this.validationErrorHandler,\n validationAdapter: this.validationAdapter,\n baseContext: this.baseContext,\n parserOptions: this.parserOptionsInput,\n });\n }\n\n body<T extends Schema>(schema: T): RouteHandlerBuilder<TParams, TQuery, T, TContext> {\n return new RouteHandlerBuilder<TParams, TQuery, T, TContext>({\n config: { ...this.config, bodySchema: schema },\n middlewares: this.middlewares,\n handleServerError: this.handleServerError,\n validationErrorHandler: this.validationErrorHandler,\n validationAdapter: this.validationAdapter,\n baseContext: this.baseContext,\n parserOptions: this.parserOptionsInput,\n });\n }\n\n use<TReturnType extends Record<string, unknown>>(middleware: Middleware<TContext, TReturnType>) {\n return new RouteHandlerBuilder<TParams, TQuery, TBody, TContext & TReturnType>({\n config: this.config,\n middlewares: [...this.middlewares, middleware as unknown as AnyMiddleware],\n handleServerError: this.handleServerError,\n validationErrorHandler: this.validationErrorHandler,\n validationAdapter: this.validationAdapter,\n baseContext: this.baseContext as unknown as TContext & TReturnType,\n parserOptions: this.parserOptionsInput,\n });\n }\n\n handler(\n handler: HandlerFunction<InferMaybe<TParams>, InferMaybe<TQuery>, InferMaybe<TBody>, TContext>,\n ): OriginalRouteHandler {\n return async (request, context): Promise<Response> => {\n try {\n const routeContext = context ?? ({ params: {} } as RouteContext);\n const paramsInput = await this.resolveParams(routeContext.params);\n const queryInput = this.getQueryParams(request);\n const bodyInput = await this.parseRequestBody(request);\n\n const paramsValidation = await this.validateInput(this.config.paramsSchema, paramsInput, 'Invalid params');\n if (paramsValidation.success === false) {\n return paramsValidation.response;\n }\n\n const queryValidation = await this.validateInput(this.config.querySchema, queryInput, 'Invalid query');\n if (queryValidation.success === false) {\n return queryValidation.response;\n }\n\n const bodyValidation = await this.validateInput(this.config.bodySchema, bodyInput, 'Invalid body');\n if (bodyValidation.success === false) {\n return bodyValidation.response;\n }\n\n let middlewareContext: TContext = { ...this.baseContext };\n for (const middleware of this.middlewares) {\n const result = await middleware(request, middlewareContext);\n if (result instanceof Response) {\n return result;\n }\n middlewareContext = { ...middlewareContext, ...(result as TContext) };\n }\n\n return handler(request, {\n params: paramsValidation.data,\n query: queryValidation.data,\n body: bodyValidation.data,\n data: middlewareContext,\n });\n } catch (error) {\n if (error instanceof BodyParsingError) {\n return this.buildErrorResponse(error.message, undefined, 400);\n }\n\n if (this.handleServerError) {\n return this.handleServerError(error as Error);\n }\n\n return this.buildErrorResponse('Internal server error', undefined, 500);\n }\n };\n }\n\n private async resolveParams(params: RouteContext['params'] | undefined) {\n const resolvedParams = await Promise.resolve(params ?? {});\n return (resolvedParams ?? {}) as Record<string, unknown>;\n }\n\n private async validateInput<S extends Schema | undefined>(\n schema: S,\n data: unknown,\n errorMessage: string,\n ): Promise<{ success: true; data: InferMaybe<S> } | { success: false; response: Response }> {\n if (!schema) {\n return { success: true, data: (data ?? {}) as InferMaybe<S> };\n }\n\n const result = await this.validationAdapter.validate(schema, data);\n if (result.success === true) {\n return { success: true, data: result.data as InferMaybe<S> };\n }\n\n if (this.validationErrorHandler) {\n return { success: false, response: this.validationErrorHandler(result.issues) };\n }\n\n return { success: false, response: this.buildErrorResponse(errorMessage, result.issues) };\n }\n\n private getQueryParams(request: Request) {\n const url = new URL(request.url);\n const params: Record<string, unknown> = {};\n const keys = Array.from(new Set(url.searchParams.keys()));\n const { arrayStrategy, singleValueStrategy, coerce } = this.parserOptions.query;\n\n for (const key of keys) {\n const values = url.searchParams.getAll(key);\n const coercedValues = values.map((value) => this.coerceStringValue(value, key, coerce));\n params[key] = this.selectValues(coercedValues, arrayStrategy, singleValueStrategy);\n }\n\n return params;\n }\n\n private selectValues(\n values: unknown[],\n arrayStrategy: QueryArrayStrategy,\n singleValueStrategy: QuerySingleValueStrategy,\n ): unknown {\n if (arrayStrategy === 'always') {\n return values;\n }\n\n if (arrayStrategy === 'never') {\n return this.pickSingleValue(values, singleValueStrategy);\n }\n\n return values.length === 1 ? values[0] : values;\n }\n\n private pickSingleValue(values: unknown[], strategy: QuerySingleValueStrategy): unknown {\n if (values.length === 0) {\n return undefined;\n }\n\n return strategy === 'first' ? values[0] : values[values.length - 1];\n }\n\n private coerceStringValue(value: string, key: string, coercion: ValueCoercion): unknown {\n if (typeof coercion === 'function') {\n return coercion(value, key);\n }\n\n if (coercion === 'primitive') {\n return this.coercePrimitiveValue(value);\n }\n\n return value;\n }\n\n private coercePrimitiveValue(value: string): unknown {\n const trimmed = value.trim();\n\n if (trimmed === 'true') {\n return true;\n }\n\n if (trimmed === 'false') {\n return false;\n }\n\n if (trimmed === 'null') {\n return null;\n }\n\n if (/^-?(?:\\d+|\\d*\\.\\d+)$/.test(trimmed)) {\n return Number(trimmed);\n }\n\n return value;\n }\n\n private parseFormData(\n formData: FormData,\n arrayStrategy: QueryArrayStrategy,\n singleValueStrategy: QuerySingleValueStrategy,\n coercion: ValueCoercion,\n ): Record<string, unknown> {\n const data: Record<string, unknown> = {};\n const keys = Array.from(new Set(formData.keys()));\n\n for (const key of keys) {\n const values = formData\n .getAll(key)\n .map((value) => (typeof value === 'string' ? this.coerceStringValue(value, key, coercion) : value));\n data[key] = this.selectValues(values, arrayStrategy, singleValueStrategy);\n }\n\n return data;\n }\n\n private resolveEmptyBody() {\n const { allowEmptyBody, hasEmptyValue, emptyValue } = this.parserOptions.body;\n\n if (allowEmptyBody) {\n return hasEmptyValue ? emptyValue : {};\n }\n\n throw new BodyParsingError('Request body is required.');\n }\n\n private async parseRequestBody(request: Request): Promise<unknown> {\n if (!this.config.bodySchema) {\n return {};\n }\n\n const contentType = (request.headers.get('content-type') ?? '').toLowerCase();\n\n if (contentType.includes('application/json')) {\n const rawBody = await request.text();\n\n if (rawBody.length === 0) {\n return this.resolveEmptyBody();\n }\n\n try {\n return JSON.parse(rawBody);\n } catch (error) {\n throw new BodyParsingError('Invalid JSON body.');\n }\n }\n\n if (contentType.includes('multipart/form-data') || contentType.includes('application/x-www-form-urlencoded')) {\n try {\n const formData = await request.formData();\n const { arrayStrategy, singleValueStrategy, coerce } = this.parserOptions.body;\n const data = this.parseFormData(formData, arrayStrategy, singleValueStrategy, coerce);\n\n if (Object.keys(data).length === 0) {\n return this.resolveEmptyBody();\n }\n\n return data;\n } catch (error) {\n throw new BodyParsingError('Invalid Form Data.');\n }\n }\n\n if (this.parserOptions.body.strictContentType) {\n throw new BodyParsingError(\n 'Unsupported content type. Expected application/json, multipart/form-data, or application/x-www-form-urlencoded.',\n );\n }\n\n const rawBody = await request.text();\n if (rawBody.length === 0) {\n return this.resolveEmptyBody();\n }\n\n if (this.parserOptions.body.fallbackStrategy === 'text') {\n return this.coerceStringValue(rawBody, 'body', this.parserOptions.body.coerce);\n }\n\n try {\n return JSON.parse(rawBody);\n } catch (error) {\n return this.coerceStringValue(rawBody, 'body', this.parserOptions.body.coerce);\n }\n }\n\n private buildErrorResponse(message: string, issues?: ValidationIssue[], status = 400) {\n return new Response(JSON.stringify({ message, issues }), {\n status,\n headers: { 'content-type': 'application/json' },\n });\n }\n}\n","import { ValidationAdapter } from './adapters/types';\nimport { zodAdapter } from './adapters/zod';\nimport { RouteHandlerBuilder } from './routeHandlerBuilder';\nimport { HandlerServerErrorFn, ParserOptions, ValidationErrorHandler } from './types';\n\ntype CreateSafeRouteParams<TContext extends Record<string, unknown>> = {\n handleServerError?: HandlerServerErrorFn;\n validationErrorHandler?: ValidationErrorHandler;\n validationAdapter?: ValidationAdapter;\n baseContext?: TContext;\n parserOptions?: ParserOptions;\n};\n\nexport function createSafeRoute<TContext extends Record<string, unknown> = Record<string, unknown>>(\n params?: CreateSafeRouteParams<TContext>,\n) {\n return new RouteHandlerBuilder<undefined, undefined, undefined, TContext>({\n handleServerError: params?.handleServerError,\n validationErrorHandler: params?.validationErrorHandler,\n validationAdapter: params?.validationAdapter ?? zodAdapter(),\n baseContext: params?.baseContext,\n parserOptions: params?.parserOptions,\n });\n}\n","import type { ValidationIssue } from './adapters/types';\nimport type {\n NormalizeValidationIssuesFn,\n SafeActionResult,\n SafeActionValidationErrorResult,\n SafeActionValidationErrors,\n} from './safeActionTypes';\n\nfunction toDotPath(path?: Array<string | number | symbol>) {\n if (!path || path.length === 0) {\n return undefined;\n }\n\n return path.map((part) => (typeof part === 'symbol' ? part.description ?? part.toString() : String(part))).join('.');\n}\n\nexport const normalizeValidationIssues: NormalizeValidationIssuesFn = (issues) => {\n const fieldErrors: Record<string, string[]> = {};\n const formErrors: string[] = [];\n\n for (const issue of issues) {\n const path = toDotPath(issue.path);\n\n if (!path) {\n formErrors.push(issue.message);\n continue;\n }\n\n if (!fieldErrors[path]) {\n fieldErrors[path] = [];\n }\n\n const messages = fieldErrors[path] ?? [];\n messages.push(issue.message);\n fieldErrors[path] = messages;\n }\n\n return {\n fieldErrors,\n formErrors,\n };\n};\n\nexport function createValidationErrorResult(issues: ValidationIssue[]): SafeActionValidationErrorResult {\n return {\n validationErrors: normalizeValidationIssues(issues),\n };\n}\n\nexport function createServerErrorResult<TData = never>(serverError: string): SafeActionResult<TData> {\n return {\n serverError,\n };\n}\n\nexport function isSafeActionResult<TData>(value: unknown): value is SafeActionResult<TData> {\n if (!value || typeof value !== 'object') {\n return false;\n }\n\n const candidate = value as Partial<SafeActionResult<TData>>;\n return 'data' in candidate || 'validationErrors' in candidate || 'serverError' in candidate;\n}\n\nexport function mergeContexts<TContext extends Record<string, unknown>, TPatch extends Record<string, unknown>>(\n base: TContext,\n patch?: TPatch,\n): TContext & TPatch {\n return {\n ...base,\n ...(patch ?? {}),\n } as TContext & TPatch;\n}\n\nexport function isValidationFailure(\n result: SafeActionResult<unknown>,\n): result is { validationErrors: SafeActionValidationErrors } {\n return Boolean(result && typeof result === 'object' && 'validationErrors' in result && result.validationErrors);\n}\n","import type { Infer, Schema } from './adapters/types';\nimport {\n type AnySafeActionMiddleware,\n type InferActionInput,\n type InferParsedActionInput,\n type SafeActionBuilderConfig,\n type SafeActionFn,\n type SafeActionHandler,\n type SafeActionMiddleware,\n type SafeActionResult,\n} from './safeActionTypes';\nimport {\n createServerErrorResult,\n createValidationErrorResult,\n isSafeActionResult,\n mergeContexts,\n} from './safeActionUtils';\n\ntype DefaultObject = Record<string, never>;\n\nfunction toSafeServerError(error: unknown, defaultServerError: string, mapper?: (error: unknown) => string) {\n if (!mapper) {\n return defaultServerError;\n }\n\n try {\n const mapped = mapper(error);\n if (typeof mapped === 'string' && mapped.length > 0) {\n return mapped;\n }\n return defaultServerError;\n } catch (mappingError) {\n return defaultServerError;\n }\n}\n\nexport class SafeActionBuilder<\n TInputSchema extends Schema | undefined = undefined,\n TOutputSchema extends Schema | undefined = undefined,\n TContext extends Record<string, unknown> = Record<string, unknown>,\n TMetadata extends Record<string, unknown> = DefaultObject,\n> {\n private config: SafeActionBuilderConfig<TInputSchema, TOutputSchema, TContext, TMetadata>;\n\n constructor(config: SafeActionBuilderConfig<TInputSchema, TOutputSchema, TContext, TMetadata>) {\n this.config = config;\n }\n\n inputSchema<TSchema extends Schema>(schema: TSchema) {\n return new SafeActionBuilder<TSchema, TOutputSchema, TContext, TMetadata>({\n ...this.config,\n inputSchema: schema,\n });\n }\n\n outputSchema<TSchema extends Schema>(schema: TSchema) {\n return new SafeActionBuilder<TInputSchema, TSchema, TContext, TMetadata>({\n ...this.config,\n outputSchema: schema,\n });\n }\n\n metadata<TNextMetadata extends Record<string, unknown>>(metadata: TNextMetadata) {\n return new SafeActionBuilder<TInputSchema, TOutputSchema, TContext, TNextMetadata>({\n ...this.config,\n metadata,\n middlewares: this.config.middlewares as unknown as SafeActionBuilderConfig<\n TInputSchema,\n TOutputSchema,\n TContext,\n TNextMetadata\n >['middlewares'],\n });\n }\n\n use<TContextPatch extends Record<string, unknown>>(\n middleware: SafeActionMiddleware<InferParsedActionInput<TInputSchema>, TContext, TMetadata, TContextPatch>,\n ) {\n return new SafeActionBuilder<TInputSchema, TOutputSchema, TContext & TContextPatch, TMetadata>({\n ...this.config,\n middlewares: [...this.config.middlewares, middleware as unknown as AnySafeActionMiddleware],\n baseContext: this.config.baseContext as TContext & TContextPatch,\n });\n }\n\n action<TData>(\n handler: SafeActionHandler<InferParsedActionInput<TInputSchema>, TContext, TMetadata, TData>,\n ): SafeActionFn<InferActionInput<TInputSchema>, TOutputSchema extends Schema ? Infer<TOutputSchema> : TData> {\n type TParsedInput = InferParsedActionInput<TInputSchema>;\n type TFinalData = TOutputSchema extends Schema ? Infer<TOutputSchema> : TData;\n\n const {\n inputSchema,\n outputSchema,\n validationAdapter,\n middlewares,\n metadata,\n baseContext,\n defaultServerError,\n handleServerError,\n } = this.config;\n\n const actionFn = async (...args: unknown[]): Promise<SafeActionResult<TFinalData>> => {\n try {\n const rawInput = args.length > 0 ? args[0] : undefined;\n let parsedInput: TParsedInput;\n\n if (!inputSchema) {\n parsedInput = undefined as TParsedInput;\n } else {\n const inputResult = await validationAdapter.validate(inputSchema, rawInput);\n if (inputResult.success === false) {\n return createValidationErrorResult(inputResult.issues);\n }\n parsedInput = inputResult.data as TParsedInput;\n }\n\n const runHandler = async (ctx: Record<string, unknown>): Promise<SafeActionResult<TFinalData>> => {\n const handlerResult = await handler({\n parsedInput,\n ctx: ctx as TContext,\n metadata,\n });\n\n if (!outputSchema) {\n return {\n data: handlerResult as TFinalData,\n };\n }\n\n const outputResult = await validationAdapter.validate(outputSchema, handlerResult);\n if (outputResult.success === false) {\n return createServerErrorResult<TFinalData>(\n toSafeServerError(new Error('Invalid action output.'), defaultServerError, handleServerError),\n );\n }\n\n return {\n data: outputResult.data as TFinalData,\n };\n };\n\n const runMiddleware = async (\n index: number,\n ctx: Record<string, unknown>,\n ): Promise<SafeActionResult<TFinalData>> => {\n if (index >= middlewares.length) {\n return runHandler(ctx);\n }\n\n const middleware = middlewares[index] as SafeActionMiddleware<\n TParsedInput,\n Record<string, unknown>,\n TMetadata,\n Record<string, unknown>,\n TFinalData\n >;\n\n let nextCalled = false;\n\n const next = async (options?: { ctx?: Record<string, unknown> }) => {\n if (nextCalled) {\n throw new Error('next() called more than once in the same middleware.');\n }\n nextCalled = true;\n\n return runMiddleware(index + 1, mergeContexts(ctx, options?.ctx));\n };\n\n const middlewareResult = await middleware({\n parsedInput,\n ctx,\n metadata,\n next,\n });\n\n if (!isSafeActionResult<TFinalData>(middlewareResult)) {\n throw new Error('Middleware must return a SafeActionResult.');\n }\n\n return middlewareResult;\n };\n\n const initialContext = { ...baseContext } as Record<string, unknown>;\n return await runMiddleware(0, initialContext);\n } catch (error) {\n return createServerErrorResult<TFinalData>(toSafeServerError(error, defaultServerError, handleServerError));\n }\n };\n\n return actionFn as SafeActionFn<InferActionInput<TInputSchema>, TFinalData>;\n }\n}\n","import { zodAdapter } from './adapters/zod';\nimport { SafeActionBuilder } from './safeActionBuilder';\nimport type { SafeActionClientOptions } from './safeActionTypes';\n\nconst DEFAULT_SERVER_ERROR = 'Something went wrong while executing the action.';\n\nexport function createSafeActionClient<TContext extends Record<string, unknown> = Record<string, unknown>>(\n options?: SafeActionClientOptions<TContext>,\n) {\n return new SafeActionBuilder({\n validationAdapter: options?.validationAdapter ?? zodAdapter(),\n baseContext: (options?.baseContext ?? {}) as TContext,\n metadata: {} as Record<string, never>,\n middlewares: [],\n defaultServerError: options?.defaultServerError ?? DEFAULT_SERVER_ERROR,\n handleServerError: options?.handleServerError,\n });\n}\n","// Code based on https://github.com/decs/typeschema/blob/main/packages/valibot/src/validation.ts\n// MIT License\n// Copyright (c) 2023 André Costa\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n// The above copyright notice and this permission notice shall be included in all\n// copies or substantial portions of the Software.\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\nimport type { GenericSchema, GenericSchemaAsync } from 'valibot';\n\nimport type { IfInstalled, Infer, ValidationAdapter } from './types';\n\ntype ValibotModule = typeof import('valibot');\n\nasync function loadValibot(): Promise<ValibotModule> {\n try {\n return await import('valibot');\n } catch (error) {\n throw new Error(\n 'valibotAdapter requires optional peer dependency \"valibot\". Install it with `npm install valibot`.',\n { cause: error instanceof Error ? error : undefined },\n );\n }\n}\n\nclass ValibotAdapter implements ValidationAdapter {\n async validate<S extends IfInstalled<GenericSchema | GenericSchemaAsync>>(schema: S, data: unknown) {\n const valibot = await loadValibot();\n const result = await valibot.safeParseAsync(schema, data);\n\n if (result.success) {\n return {\n success: true,\n data: result.output as Infer<S>,\n } as const;\n }\n\n return {\n success: false,\n issues: result.issues.map((issue) => ({\n message: issue.message,\n path: valibot.getDotPath(issue)?.split('.'),\n })),\n } as const;\n }\n}\n\nexport function valibotAdapter() {\n return new ValibotAdapter();\n}\n","// Code based on https://github.com/decs/typeschema/blob/main/packages/yup/src/validation.ts\n// MIT License\n// Copyright (c) 2023 André Costa\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n// The above copyright notice and this permission notice shall be included in all\n// copies or substantial portions of the Software.\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\nimport type { Schema as YupSchema } from 'yup';\n\nimport type { IfInstalled, Infer, ValidationAdapter, ValidationIssue } from './types';\n\ntype YupModule = typeof import('yup');\n\nasync function loadYup(): Promise<YupModule> {\n try {\n return await import('yup');\n } catch (error) {\n throw new Error('yupAdapter requires optional peer dependency \"yup\". Install it with `npm install yup`.', {\n cause: error instanceof Error ? error : undefined,\n });\n }\n}\n\nclass YupAdapter implements ValidationAdapter {\n async validate<S extends IfInstalled<YupSchema>>(schema: S, data: unknown) {\n const yup = await loadYup();\n\n try {\n const result = await schema.validate(data, { strict: true });\n\n return {\n success: true,\n data: result as Infer<S>,\n } as const;\n } catch (e) {\n if (e instanceof yup.ValidationError) {\n const { message, path } = e;\n\n return {\n success: false,\n issues: [\n {\n message,\n path: path && path.length > 0 ? path.split('.') : undefined,\n },\n ] as ValidationIssue[],\n } as const;\n }\n\n throw e;\n }\n }\n}\n\nexport function yupAdapter() {\n return new YupAdapter();\n}\n"],"mappings":"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","names":["src_exports","__export","RouteHandlerBuilder","SafeActionBuilder","createSafeActionClient","createSafeRoute","valibotAdapter","yupAdapter","zodAdapter","__toCommonJS","ZodAdapter","schema","data","result","message","path","zodAdapter","normalizeParserOptions","parserOptions","bodyOptions","BodyParsingError","message","RouteHandlerBuilder","_RouteHandlerBuilder","config","validationAdapter","zodAdapter","middlewares","handleServerError","validationErrorHandler","baseContext","schema","middleware","handler","request","context","routeContext","paramsInput","queryInput","bodyInput","paramsValidation","queryValidation","bodyValidation","middlewareContext","result","error","params","data","errorMessage","url","keys","arrayStrategy","singleValueStrategy","coerce","key","coercedValues","value","values","strategy","coercion","trimmed","formData","allowEmptyBody","hasEmptyValue","emptyValue","contentType","rawBody","issues","status","createSafeRoute","params","RouteHandlerBuilder","zodAdapter","toDotPath","path","part","normalizeValidationIssues","issues","fieldErrors","formErrors","issue","messages","createValidationErrorResult","createServerErrorResult","serverError","isSafeActionResult","value","candidate","mergeContexts","base","patch","toSafeServerError","error","defaultServerError","mapper","mapped","SafeActionBuilder","_SafeActionBuilder","config","schema","metadata","middleware","handler","inputSchema","outputSchema","validationAdapter","middlewares","baseContext","handleServerError","args","rawInput","parsedInput","inputResult","createValidationErrorResult","runHandler","ctx","handlerResult","outputResult","createServerErrorResult","runMiddleware","index","nextCalled","middlewareResult","options","mergeContexts","isSafeActionResult","initialContext","DEFAULT_SERVER_ERROR","createSafeActionClient","options","SafeActionBuilder","zodAdapter","loadValibot","error","ValibotAdapter","schema","data","valibot","result","issue","valibotAdapter","loadYup","error","YupAdapter","schema","data","yup","e","message","path","yupAdapter"]}
package/dist/index.mjs CHANGED
@@ -1,2 +1,2 @@
1
- import{a as T}from"./chunk-SOIROJAG.mjs";import{a as x}from"./chunk-N3L74QA5.mjs";var y=class{async validate(e,r){let t=await e.safeParseAsync(r);return t.success?{success:!0,data:t.data}:{success:!1,issues:t.error.issues.map(({message:a,path:o})=>({message:a,path:o}))}}};function l(){return new y}var i=class extends Error{constructor(e){super(e),this.name="BodyParsingError"}},u=class n{config;middlewares;handleServerError;validationErrorHandler;validationAdapter;baseContext;constructor({config:e={paramsSchema:void 0,querySchema:void 0,bodySchema:void 0},validationAdapter:r=l(),middlewares:t=[],handleServerError:a,validationErrorHandler:o,baseContext:s}){this.config=e,this.middlewares=t,this.handleServerError=a,this.validationErrorHandler=o,this.validationAdapter=r,this.baseContext=s??{}}params(e){return new n({config:{...this.config,paramsSchema:e},middlewares:this.middlewares,handleServerError:this.handleServerError,validationErrorHandler:this.validationErrorHandler,validationAdapter:this.validationAdapter,baseContext:this.baseContext})}query(e){return new n({config:{...this.config,querySchema:e},middlewares:this.middlewares,handleServerError:this.handleServerError,validationErrorHandler:this.validationErrorHandler,validationAdapter:this.validationAdapter,baseContext:this.baseContext})}body(e){return new n({config:{...this.config,bodySchema:e},middlewares:this.middlewares,handleServerError:this.handleServerError,validationErrorHandler:this.validationErrorHandler,validationAdapter:this.validationAdapter,baseContext:this.baseContext})}use(e){return new n({config:this.config,middlewares:[...this.middlewares,e],handleServerError:this.handleServerError,validationErrorHandler:this.validationErrorHandler,validationAdapter:this.validationAdapter,baseContext:this.baseContext})}handler(e){return async(r,t)=>{try{let a=t??{params:{}},o=await this.resolveParams(a.params),s=this.getQueryParams(r),d=await this.parseRequestBody(r),c=await this.validateInput(this.config.paramsSchema,o,"Invalid params");if(c.success===!1)return c.response;let p=await this.validateInput(this.config.querySchema,s,"Invalid query");if(p.success===!1)return p.response;let m=await this.validateInput(this.config.bodySchema,d,"Invalid body");if(m.success===!1)return m.response;let f={...this.baseContext};for(let v of this.middlewares){let h=await v(r);if(h instanceof Response)return h;f={...f,...h}}return e(r,{params:c.data,query:p.data,body:m.data,data:f})}catch(a){return a instanceof i?this.buildErrorResponse(a.message,void 0,400):this.handleServerError?this.handleServerError(a):this.buildErrorResponse("Internal server error",void 0,500)}}}async resolveParams(e){return await Promise.resolve(e??{})??{}}async validateInput(e,r,t){if(!e)return{success:!0,data:r??{}};let a=await this.validationAdapter.validate(e,r);return a.success===!0?{success:!0,data:a.data}:this.validationErrorHandler?{success:!1,response:this.validationErrorHandler(a.issues)}:{success:!1,response:this.buildErrorResponse(t,a.issues)}}getQueryParams(e){let r=new URL(e.url),t={},a=Array.from(new Set(r.searchParams.keys()));for(let o of a){let s=r.searchParams.getAll(o);t[o]=s.length===1?s[0]:s}return t}async parseRequestBody(e){if(!this.config.bodySchema)return{};let r=e.headers.get("content-type")??"";if(r.includes("application/json")){let t=await e.text();if(t.length===0)return{};try{return JSON.parse(t)}catch{throw new i("Invalid JSON body.")}}if(r.includes("multipart/form-data")||r.includes("application/x-www-form-urlencoded"))try{let t=await e.formData(),a={},o=Array.from(new Set(t.keys()));for(let s of o){let d=t.getAll(s);a[s]=d.length===1?d[0]:d}return a}catch{throw new i("Invalid Form Data.")}throw new i("Unsupported content type. Expected application/json, multipart/form-data, or application/x-www-form-urlencoded.")}buildErrorResponse(e,r,t=400){return new Response(JSON.stringify({message:e,issues:r}),{status:t,headers:{"content-type":"application/json"}})}};function w(n){return new u({handleServerError:n?.handleServerError,validationErrorHandler:n?.validationErrorHandler,validationAdapter:n?.validationAdapter??l(),baseContext:n?.baseContext})}export{u as RouteHandlerBuilder,w as createSafeRoute,T as valibotAdapter,x as yupAdapter,l as zodAdapter};
1
+ import{a as B}from"./chunk-SOIROJAG.mjs";import{a as H}from"./chunk-N3L74QA5.mjs";var x=class{async validate(e,t){let n=await e.safeParseAsync(t);return n.success?{success:!0,data:n.data}:{success:!1,issues:n.error.issues.map(({message:a,path:o})=>({message:a,path:o}))}}};function y(){return new x}function F(r){let e=r?.body;return{query:{arrayStrategy:r?.query?.arrayStrategy??"auto",singleValueStrategy:r?.query?.singleValueStrategy??"last",coerce:r?.query?.coerce??"none"},body:{strictContentType:e?.strictContentType??!0,allowEmptyBody:e?.allowEmptyBody??!0,hasEmptyValue:!!(e&&"emptyValue"in e),emptyValue:e?.emptyValue,coerce:e?.coerce??"none",fallbackStrategy:e?.fallbackStrategy??"json-first",arrayStrategy:e?.arrayStrategy??"auto",singleValueStrategy:e?.singleValueStrategy??"last"}}}var f=class extends Error{constructor(e){super(e),this.name="BodyParsingError"}},T=class r{config;middlewares;handleServerError;validationErrorHandler;validationAdapter;baseContext;parserOptions;parserOptionsInput;constructor({config:e={paramsSchema:void 0,querySchema:void 0,bodySchema:void 0},validationAdapter:t=y(),middlewares:n=[],handleServerError:a,validationErrorHandler:o,baseContext:d,parserOptions:s}){this.config=e,this.middlewares=n,this.handleServerError=a,this.validationErrorHandler=o,this.validationAdapter=t,this.baseContext=d??{},this.parserOptionsInput=s,this.parserOptions=F(s)}params(e){return new r({config:{...this.config,paramsSchema:e},middlewares:this.middlewares,handleServerError:this.handleServerError,validationErrorHandler:this.validationErrorHandler,validationAdapter:this.validationAdapter,baseContext:this.baseContext,parserOptions:this.parserOptionsInput})}query(e){return new r({config:{...this.config,querySchema:e},middlewares:this.middlewares,handleServerError:this.handleServerError,validationErrorHandler:this.validationErrorHandler,validationAdapter:this.validationAdapter,baseContext:this.baseContext,parserOptions:this.parserOptionsInput})}body(e){return new r({config:{...this.config,bodySchema:e},middlewares:this.middlewares,handleServerError:this.handleServerError,validationErrorHandler:this.validationErrorHandler,validationAdapter:this.validationAdapter,baseContext:this.baseContext,parserOptions:this.parserOptionsInput})}use(e){return new r({config:this.config,middlewares:[...this.middlewares,e],handleServerError:this.handleServerError,validationErrorHandler:this.validationErrorHandler,validationAdapter:this.validationAdapter,baseContext:this.baseContext,parserOptions:this.parserOptionsInput})}handler(e){return async(t,n)=>{try{let a=n??{params:{}},o=await this.resolveParams(a.params),d=this.getQueryParams(t),s=await this.parseRequestBody(t),i=await this.validateInput(this.config.paramsSchema,o,"Invalid params");if(i.success===!1)return i.response;let u=await this.validateInput(this.config.querySchema,d,"Invalid query");if(u.success===!1)return u.response;let m=await this.validateInput(this.config.bodySchema,s,"Invalid body");if(m.success===!1)return m.response;let l={...this.baseContext};for(let S of this.middlewares){let p=await S(t,l);if(p instanceof Response)return p;l={...l,...p}}return e(t,{params:i.data,query:u.data,body:m.data,data:l})}catch(a){return a instanceof f?this.buildErrorResponse(a.message,void 0,400):this.handleServerError?this.handleServerError(a):this.buildErrorResponse("Internal server error",void 0,500)}}}async resolveParams(e){return await Promise.resolve(e??{})??{}}async validateInput(e,t,n){if(!e)return{success:!0,data:t??{}};let a=await this.validationAdapter.validate(e,t);return a.success===!0?{success:!0,data:a.data}:this.validationErrorHandler?{success:!1,response:this.validationErrorHandler(a.issues)}:{success:!1,response:this.buildErrorResponse(n,a.issues)}}getQueryParams(e){let t=new URL(e.url),n={},a=Array.from(new Set(t.searchParams.keys())),{arrayStrategy:o,singleValueStrategy:d,coerce:s}=this.parserOptions.query;for(let i of a){let m=t.searchParams.getAll(i).map(l=>this.coerceStringValue(l,i,s));n[i]=this.selectValues(m,o,d)}return n}selectValues(e,t,n){return t==="always"?e:t==="never"?this.pickSingleValue(e,n):e.length===1?e[0]:e}pickSingleValue(e,t){if(e.length!==0)return t==="first"?e[0]:e[e.length-1]}coerceStringValue(e,t,n){return typeof n=="function"?n(e,t):n==="primitive"?this.coercePrimitiveValue(e):e}coercePrimitiveValue(e){let t=e.trim();return t==="true"?!0:t==="false"?!1:t==="null"?null:/^-?(?:\d+|\d*\.\d+)$/.test(t)?Number(t):e}parseFormData(e,t,n,a){let o={},d=Array.from(new Set(e.keys()));for(let s of d){let i=e.getAll(s).map(u=>typeof u=="string"?this.coerceStringValue(u,s,a):u);o[s]=this.selectValues(i,t,n)}return o}resolveEmptyBody(){let{allowEmptyBody:e,hasEmptyValue:t,emptyValue:n}=this.parserOptions.body;if(e)return t?n:{};throw new f("Request body is required.")}async parseRequestBody(e){if(!this.config.bodySchema)return{};let t=(e.headers.get("content-type")??"").toLowerCase();if(t.includes("application/json")){let a=await e.text();if(a.length===0)return this.resolveEmptyBody();try{return JSON.parse(a)}catch{throw new f("Invalid JSON body.")}}if(t.includes("multipart/form-data")||t.includes("application/x-www-form-urlencoded"))try{let a=await e.formData(),{arrayStrategy:o,singleValueStrategy:d,coerce:s}=this.parserOptions.body,i=this.parseFormData(a,o,d,s);return Object.keys(i).length===0?this.resolveEmptyBody():i}catch{throw new f("Invalid Form Data.")}if(this.parserOptions.body.strictContentType)throw new f("Unsupported content type. Expected application/json, multipart/form-data, or application/x-www-form-urlencoded.");let n=await e.text();if(n.length===0)return this.resolveEmptyBody();if(this.parserOptions.body.fallbackStrategy==="text")return this.coerceStringValue(n,"body",this.parserOptions.body.coerce);try{return JSON.parse(n)}catch{return this.coerceStringValue(n,"body",this.parserOptions.body.coerce)}}buildErrorResponse(e,t,n=400){return new Response(JSON.stringify({message:e,issues:t}),{status:n,headers:{"content-type":"application/json"}})}};function M(r){return new T({handleServerError:r?.handleServerError,validationErrorHandler:r?.validationErrorHandler,validationAdapter:r?.validationAdapter??y(),baseContext:r?.baseContext,parserOptions:r?.parserOptions})}function D(r){if(!(!r||r.length===0))return r.map(e=>typeof e=="symbol"?e.description??e.toString():String(e)).join(".")}var Q=r=>{let e={},t=[];for(let n of r){let a=D(n.path);if(!a){t.push(n.message);continue}e[a]||(e[a]=[]);let o=e[a]??[];o.push(n.message),e[a]=o}return{fieldErrors:e,formErrors:t}};function E(r){return{validationErrors:Q(r)}}function v(r){return{serverError:r}}function I(r){if(!r||typeof r!="object")return!1;let e=r;return"data"in e||"validationErrors"in e||"serverError"in e}function b(r,e){return{...r,...e??{}}}function V(r,e,t){if(!t)return e;try{let n=t(r);return typeof n=="string"&&n.length>0?n:e}catch{return e}}var g=class r{config;constructor(e){this.config=e}inputSchema(e){return new r({...this.config,inputSchema:e})}outputSchema(e){return new r({...this.config,outputSchema:e})}metadata(e){return new r({...this.config,metadata:e,middlewares:this.config.middlewares})}use(e){return new r({...this.config,middlewares:[...this.config.middlewares,e],baseContext:this.config.baseContext})}action(e){let{inputSchema:t,outputSchema:n,validationAdapter:a,middlewares:o,metadata:d,baseContext:s,defaultServerError:i,handleServerError:u}=this.config;return async(...l)=>{try{let S=l.length>0?l[0]:void 0,p;if(!t)p=void 0;else{let c=await a.validate(t,S);if(c.success===!1)return E(c.issues);p=c.data}let k=async c=>{let h=await e({parsedInput:p,ctx:c,metadata:d});if(!n)return{data:h};let w=await a.validate(n,h);return w.success===!1?v(V(new Error("Invalid action output."),i,u)):{data:w.data}},A=async(c,h)=>{if(c>=o.length)return k(h);let w=o[c],C=!1,R=await w({parsedInput:p,ctx:h,metadata:d,next:async O=>{if(C)throw new Error("next() called more than once in the same middleware.");return C=!0,A(c+1,b(h,O?.ctx))}});if(!I(R))throw new Error("Middleware must return a SafeActionResult.");return R},P={...s};return await A(0,P)}catch(S){return v(V(S,i,u))}}}};var q="Something went wrong while executing the action.";function z(r){return new g({validationAdapter:r?.validationAdapter??y(),baseContext:r?.baseContext??{},metadata:{},middlewares:[],defaultServerError:r?.defaultServerError??q,handleServerError:r?.handleServerError})}export{T as RouteHandlerBuilder,g as SafeActionBuilder,z as createSafeActionClient,M as createSafeRoute,B as valibotAdapter,H as yupAdapter,y as zodAdapter};
2
2
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/adapters/zod.ts","../src/routeHandlerBuilder.ts","../src/createSafeRoute.ts"],"sourcesContent":["// Code based on https://github.com/decs/typeschema/blob/main/packages/zod/src/validation.ts\n// MIT License\n// Copyright (c) 2023 André Costa\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n// The above copyright notice and this permission notice shall be included in all\n// copies or substantial portions of the Software.\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\nimport type { z } from 'zod';\n\nimport type { IfInstalled, Infer, ValidationAdapter } from './types';\n\nclass ZodAdapter implements ValidationAdapter {\n async validate<S extends IfInstalled<z.ZodTypeAny>>(schema: S, data: unknown) {\n const result = await schema.safeParseAsync(data);\n\n if (result.success) {\n return {\n success: true,\n data: result.data as Infer<S>,\n } as const;\n }\n\n return {\n success: false,\n issues: result.error.issues.map(({ message, path }) => ({ message, path })),\n } as const;\n }\n}\n\nexport function zodAdapter() {\n return new ZodAdapter();\n}\n","import { Schema, ValidationAdapter, ValidationIssue } from './adapters/types';\nimport { zodAdapter } from './adapters/zod';\nimport {\n HandlerFunction,\n HandlerServerErrorFn,\n InferMaybe,\n OriginalRouteHandler,\n RouteContext,\n ValidationErrorHandler,\n} from './types';\n\ntype Middleware<T = Record<string, unknown>> = (request: Request) => Promise<T | Response>;\n\ntype BuilderConfig<\n TParams extends Schema | undefined,\n TQuery extends Schema | undefined,\n TBody extends Schema | undefined,\n> = {\n paramsSchema: TParams;\n querySchema: TQuery;\n bodySchema: TBody;\n};\n\nclass BodyParsingError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'BodyParsingError';\n }\n}\n\nexport class RouteHandlerBuilder<\n TParams extends Schema | undefined = undefined,\n TQuery extends Schema | undefined = undefined,\n TBody extends Schema | undefined = undefined,\n TContext extends Record<string, unknown> = Record<string, unknown>,\n> {\n private config: BuilderConfig<TParams, TQuery, TBody>;\n private middlewares: Middleware[];\n private handleServerError?: HandlerServerErrorFn;\n private validationErrorHandler?: ValidationErrorHandler;\n private validationAdapter: ValidationAdapter;\n private baseContext: TContext;\n\n constructor({\n config = {\n paramsSchema: undefined as TParams,\n querySchema: undefined as TQuery,\n bodySchema: undefined as TBody,\n },\n validationAdapter = zodAdapter(),\n middlewares = [],\n handleServerError,\n validationErrorHandler,\n baseContext,\n }: {\n config?: BuilderConfig<TParams, TQuery, TBody>;\n middlewares?: Middleware[];\n handleServerError?: HandlerServerErrorFn;\n validationErrorHandler?: ValidationErrorHandler;\n validationAdapter?: ValidationAdapter;\n baseContext?: TContext;\n }) {\n this.config = config;\n this.middlewares = middlewares;\n this.handleServerError = handleServerError;\n this.validationErrorHandler = validationErrorHandler;\n this.validationAdapter = validationAdapter;\n this.baseContext = (baseContext ?? {}) as TContext;\n }\n\n params<T extends Schema>(schema: T): RouteHandlerBuilder<T, TQuery, TBody, TContext> {\n return new RouteHandlerBuilder<T, TQuery, TBody, TContext>({\n config: { ...this.config, paramsSchema: schema },\n middlewares: this.middlewares,\n handleServerError: this.handleServerError,\n validationErrorHandler: this.validationErrorHandler,\n validationAdapter: this.validationAdapter,\n baseContext: this.baseContext,\n });\n }\n\n query<T extends Schema>(schema: T): RouteHandlerBuilder<TParams, T, TBody, TContext> {\n return new RouteHandlerBuilder<TParams, T, TBody, TContext>({\n config: { ...this.config, querySchema: schema },\n middlewares: this.middlewares,\n handleServerError: this.handleServerError,\n validationErrorHandler: this.validationErrorHandler,\n validationAdapter: this.validationAdapter,\n baseContext: this.baseContext,\n });\n }\n\n body<T extends Schema>(schema: T): RouteHandlerBuilder<TParams, TQuery, T, TContext> {\n return new RouteHandlerBuilder<TParams, TQuery, T, TContext>({\n config: { ...this.config, bodySchema: schema },\n middlewares: this.middlewares,\n handleServerError: this.handleServerError,\n validationErrorHandler: this.validationErrorHandler,\n validationAdapter: this.validationAdapter,\n baseContext: this.baseContext,\n });\n }\n\n use<TReturnType extends Record<string, unknown>>(middleware: Middleware<TReturnType>) {\n return new RouteHandlerBuilder<TParams, TQuery, TBody, TContext & TReturnType>({\n config: this.config,\n middlewares: [...this.middlewares, middleware],\n handleServerError: this.handleServerError,\n validationErrorHandler: this.validationErrorHandler,\n validationAdapter: this.validationAdapter,\n baseContext: this.baseContext as unknown as TContext & TReturnType,\n });\n }\n\n handler(\n handler: HandlerFunction<InferMaybe<TParams>, InferMaybe<TQuery>, InferMaybe<TBody>, TContext>,\n ): OriginalRouteHandler {\n return async (request, context): Promise<Response> => {\n try {\n const routeContext = context ?? ({ params: {} } as RouteContext);\n const paramsInput = await this.resolveParams(routeContext.params);\n const queryInput = this.getQueryParams(request);\n const bodyInput = await this.parseRequestBody(request);\n\n const paramsValidation = await this.validateInput(this.config.paramsSchema, paramsInput, 'Invalid params');\n if (paramsValidation.success === false) {\n return paramsValidation.response;\n }\n\n const queryValidation = await this.validateInput(this.config.querySchema, queryInput, 'Invalid query');\n if (queryValidation.success === false) {\n return queryValidation.response;\n }\n\n const bodyValidation = await this.validateInput(this.config.bodySchema, bodyInput, 'Invalid body');\n if (bodyValidation.success === false) {\n return bodyValidation.response;\n }\n\n let middlewareContext: TContext = { ...this.baseContext };\n for (const middleware of this.middlewares) {\n const result = await middleware(request);\n if (result instanceof Response) {\n return result;\n }\n middlewareContext = { ...middlewareContext, ...(result as TContext) };\n }\n\n return handler(request, {\n params: paramsValidation.data,\n query: queryValidation.data,\n body: bodyValidation.data,\n data: middlewareContext,\n });\n } catch (error) {\n if (error instanceof BodyParsingError) {\n return this.buildErrorResponse(error.message, undefined, 400);\n }\n\n if (this.handleServerError) {\n return this.handleServerError(error as Error);\n }\n\n return this.buildErrorResponse('Internal server error', undefined, 500);\n }\n };\n }\n\n private async resolveParams(params: RouteContext['params'] | undefined) {\n const resolvedParams = await Promise.resolve(params ?? {});\n return (resolvedParams ?? {}) as Record<string, unknown>;\n }\n\n private async validateInput<S extends Schema | undefined>(\n schema: S,\n data: unknown,\n errorMessage: string,\n ): Promise<{ success: true; data: InferMaybe<S> } | { success: false; response: Response }> {\n if (!schema) {\n return { success: true, data: (data ?? {}) as InferMaybe<S> };\n }\n\n const result = await this.validationAdapter.validate(schema, data);\n if (result.success === true) {\n return { success: true, data: result.data as InferMaybe<S> };\n }\n\n if (this.validationErrorHandler) {\n return { success: false, response: this.validationErrorHandler(result.issues) };\n }\n\n return { success: false, response: this.buildErrorResponse(errorMessage, result.issues) };\n }\n\n private getQueryParams(request: Request) {\n const url = new URL(request.url);\n const params: Record<string, unknown> = {};\n const keys = Array.from(new Set(url.searchParams.keys()));\n\n for (const key of keys) {\n const values = url.searchParams.getAll(key);\n params[key] = values.length === 1 ? values[0] : values;\n }\n\n return params;\n }\n\n private async parseRequestBody(request: Request): Promise<unknown> {\n if (!this.config.bodySchema) {\n return {};\n }\n\n const contentType = request.headers.get('content-type') ?? '';\n\n if (contentType.includes('application/json')) {\n const rawBody = await request.text();\n\n if (rawBody.length === 0) {\n return {};\n }\n\n try {\n return JSON.parse(rawBody);\n } catch (error) {\n throw new BodyParsingError('Invalid JSON body.');\n }\n }\n\n if (contentType.includes('multipart/form-data') || contentType.includes('application/x-www-form-urlencoded')) {\n try {\n const formData = await request.formData();\n const data: Record<string, unknown> = {};\n const keys = Array.from(new Set(formData.keys()));\n\n for (const key of keys) {\n const values = formData.getAll(key);\n data[key] = values.length === 1 ? values[0] : values;\n }\n\n return data;\n } catch (error) {\n throw new BodyParsingError('Invalid Form Data.');\n }\n }\n\n throw new BodyParsingError(\n 'Unsupported content type. Expected application/json, multipart/form-data, or application/x-www-form-urlencoded.',\n );\n }\n\n private buildErrorResponse(message: string, issues?: ValidationIssue[], status = 400) {\n return new Response(JSON.stringify({ message, issues }), {\n status,\n headers: { 'content-type': 'application/json' },\n });\n }\n}\n","import { ValidationAdapter } from './adapters/types';\nimport { zodAdapter } from './adapters/zod';\nimport { RouteHandlerBuilder } from './routeHandlerBuilder';\nimport { HandlerServerErrorFn, ValidationErrorHandler } from './types';\n\ntype CreateSafeRouteParams<TContext extends Record<string, unknown>> = {\n handleServerError?: HandlerServerErrorFn;\n validationErrorHandler?: ValidationErrorHandler;\n validationAdapter?: ValidationAdapter;\n baseContext?: TContext;\n};\n\nexport function createSafeRoute<TContext extends Record<string, unknown> = Record<string, unknown>>(\n params?: CreateSafeRouteParams<TContext>,\n) {\n return new RouteHandlerBuilder<undefined, undefined, undefined, TContext>({\n handleServerError: params?.handleServerError,\n validationErrorHandler: params?.validationErrorHandler,\n validationAdapter: params?.validationAdapter ?? zodAdapter(),\n baseContext: params?.baseContext,\n });\n}\n"],"mappings":"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","names":["ZodAdapter","schema","data","result","message","path","zodAdapter","BodyParsingError","message","RouteHandlerBuilder","_RouteHandlerBuilder","config","validationAdapter","zodAdapter","middlewares","handleServerError","validationErrorHandler","baseContext","schema","middleware","handler","request","context","routeContext","paramsInput","queryInput","bodyInput","paramsValidation","queryValidation","bodyValidation","middlewareContext","result","error","params","data","errorMessage","url","keys","key","values","contentType","rawBody","formData","issues","status","createSafeRoute","params","RouteHandlerBuilder","zodAdapter"]}
1
+ {"version":3,"sources":["../src/adapters/zod.ts","../src/routeHandlerBuilder.ts","../src/createSafeRoute.ts","../src/safeActionUtils.ts","../src/safeActionBuilder.ts","../src/createSafeActionClient.ts"],"sourcesContent":["// Code based on https://github.com/decs/typeschema/blob/main/packages/zod/src/validation.ts\n// MIT License\n// Copyright (c) 2023 André Costa\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n// The above copyright notice and this permission notice shall be included in all\n// copies or substantial portions of the Software.\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\nimport type { z } from 'zod';\n\nimport type { IfInstalled, Infer, ValidationAdapter } from './types';\n\nclass ZodAdapter implements ValidationAdapter {\n async validate<S extends IfInstalled<z.ZodTypeAny>>(schema: S, data: unknown) {\n const result = await schema.safeParseAsync(data);\n\n if (result.success) {\n return {\n success: true,\n data: result.data as Infer<S>,\n } as const;\n }\n\n return {\n success: false,\n issues: result.error.issues.map(({ message, path }) => ({ message, path })),\n } as const;\n }\n}\n\nexport function zodAdapter() {\n return new ZodAdapter();\n}\n","import { Schema, ValidationAdapter, ValidationIssue } from './adapters/types';\nimport { zodAdapter } from './adapters/zod';\nimport {\n BodyFallbackStrategy,\n HandlerFunction,\n HandlerServerErrorFn,\n InferMaybe,\n OriginalRouteHandler,\n ParserOptions,\n QueryArrayStrategy,\n QuerySingleValueStrategy,\n RouteContext,\n ValidationErrorHandler,\n ValueCoercion,\n} from './types';\n\ntype Awaitable<T> = T | Promise<T>;\n\ntype Middleware<\n TContext extends Record<string, unknown> = Record<string, unknown>,\n TReturn extends Record<string, unknown> = Record<string, unknown>,\n> = (request: Request, data: TContext) => Awaitable<TReturn | Response>;\n\ntype AnyMiddleware = Middleware<Record<string, unknown>, Record<string, unknown>>;\n\ntype NormalizedParserOptions = {\n query: {\n arrayStrategy: QueryArrayStrategy;\n singleValueStrategy: QuerySingleValueStrategy;\n coerce: ValueCoercion;\n };\n body: {\n strictContentType: boolean;\n allowEmptyBody: boolean;\n hasEmptyValue: boolean;\n emptyValue: unknown;\n coerce: ValueCoercion;\n fallbackStrategy: BodyFallbackStrategy;\n arrayStrategy: QueryArrayStrategy;\n singleValueStrategy: QuerySingleValueStrategy;\n };\n};\n\nfunction normalizeParserOptions(parserOptions?: ParserOptions): NormalizedParserOptions {\n const bodyOptions = parserOptions?.body;\n\n return {\n query: {\n arrayStrategy: parserOptions?.query?.arrayStrategy ?? 'auto',\n singleValueStrategy: parserOptions?.query?.singleValueStrategy ?? 'last',\n coerce: parserOptions?.query?.coerce ?? 'none',\n },\n body: {\n strictContentType: bodyOptions?.strictContentType ?? true,\n allowEmptyBody: bodyOptions?.allowEmptyBody ?? true,\n hasEmptyValue: Boolean(bodyOptions && 'emptyValue' in bodyOptions),\n emptyValue: bodyOptions?.emptyValue,\n coerce: bodyOptions?.coerce ?? 'none',\n fallbackStrategy: bodyOptions?.fallbackStrategy ?? 'json-first',\n arrayStrategy: bodyOptions?.arrayStrategy ?? 'auto',\n singleValueStrategy: bodyOptions?.singleValueStrategy ?? 'last',\n },\n };\n}\n\ntype BuilderConfig<\n TParams extends Schema | undefined,\n TQuery extends Schema | undefined,\n TBody extends Schema | undefined,\n> = {\n paramsSchema: TParams;\n querySchema: TQuery;\n bodySchema: TBody;\n};\n\nclass BodyParsingError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'BodyParsingError';\n }\n}\n\nexport class RouteHandlerBuilder<\n TParams extends Schema | undefined = undefined,\n TQuery extends Schema | undefined = undefined,\n TBody extends Schema | undefined = undefined,\n TContext extends Record<string, unknown> = Record<string, unknown>,\n> {\n private config: BuilderConfig<TParams, TQuery, TBody>;\n private middlewares: AnyMiddleware[];\n private handleServerError?: HandlerServerErrorFn;\n private validationErrorHandler?: ValidationErrorHandler;\n private validationAdapter: ValidationAdapter;\n private baseContext: TContext;\n private parserOptions: NormalizedParserOptions;\n private parserOptionsInput?: ParserOptions;\n\n constructor({\n config = {\n paramsSchema: undefined as TParams,\n querySchema: undefined as TQuery,\n bodySchema: undefined as TBody,\n },\n validationAdapter = zodAdapter(),\n middlewares = [],\n handleServerError,\n validationErrorHandler,\n baseContext,\n parserOptions,\n }: {\n config?: BuilderConfig<TParams, TQuery, TBody>;\n middlewares?: AnyMiddleware[];\n handleServerError?: HandlerServerErrorFn;\n validationErrorHandler?: ValidationErrorHandler;\n validationAdapter?: ValidationAdapter;\n baseContext?: TContext;\n parserOptions?: ParserOptions;\n }) {\n this.config = config;\n this.middlewares = middlewares;\n this.handleServerError = handleServerError;\n this.validationErrorHandler = validationErrorHandler;\n this.validationAdapter = validationAdapter;\n this.baseContext = (baseContext ?? {}) as TContext;\n this.parserOptionsInput = parserOptions;\n this.parserOptions = normalizeParserOptions(parserOptions);\n }\n\n params<T extends Schema>(schema: T): RouteHandlerBuilder<T, TQuery, TBody, TContext> {\n return new RouteHandlerBuilder<T, TQuery, TBody, TContext>({\n config: { ...this.config, paramsSchema: schema },\n middlewares: this.middlewares,\n handleServerError: this.handleServerError,\n validationErrorHandler: this.validationErrorHandler,\n validationAdapter: this.validationAdapter,\n baseContext: this.baseContext,\n parserOptions: this.parserOptionsInput,\n });\n }\n\n query<T extends Schema>(schema: T): RouteHandlerBuilder<TParams, T, TBody, TContext> {\n return new RouteHandlerBuilder<TParams, T, TBody, TContext>({\n config: { ...this.config, querySchema: schema },\n middlewares: this.middlewares,\n handleServerError: this.handleServerError,\n validationErrorHandler: this.validationErrorHandler,\n validationAdapter: this.validationAdapter,\n baseContext: this.baseContext,\n parserOptions: this.parserOptionsInput,\n });\n }\n\n body<T extends Schema>(schema: T): RouteHandlerBuilder<TParams, TQuery, T, TContext> {\n return new RouteHandlerBuilder<TParams, TQuery, T, TContext>({\n config: { ...this.config, bodySchema: schema },\n middlewares: this.middlewares,\n handleServerError: this.handleServerError,\n validationErrorHandler: this.validationErrorHandler,\n validationAdapter: this.validationAdapter,\n baseContext: this.baseContext,\n parserOptions: this.parserOptionsInput,\n });\n }\n\n use<TReturnType extends Record<string, unknown>>(middleware: Middleware<TContext, TReturnType>) {\n return new RouteHandlerBuilder<TParams, TQuery, TBody, TContext & TReturnType>({\n config: this.config,\n middlewares: [...this.middlewares, middleware as unknown as AnyMiddleware],\n handleServerError: this.handleServerError,\n validationErrorHandler: this.validationErrorHandler,\n validationAdapter: this.validationAdapter,\n baseContext: this.baseContext as unknown as TContext & TReturnType,\n parserOptions: this.parserOptionsInput,\n });\n }\n\n handler(\n handler: HandlerFunction<InferMaybe<TParams>, InferMaybe<TQuery>, InferMaybe<TBody>, TContext>,\n ): OriginalRouteHandler {\n return async (request, context): Promise<Response> => {\n try {\n const routeContext = context ?? ({ params: {} } as RouteContext);\n const paramsInput = await this.resolveParams(routeContext.params);\n const queryInput = this.getQueryParams(request);\n const bodyInput = await this.parseRequestBody(request);\n\n const paramsValidation = await this.validateInput(this.config.paramsSchema, paramsInput, 'Invalid params');\n if (paramsValidation.success === false) {\n return paramsValidation.response;\n }\n\n const queryValidation = await this.validateInput(this.config.querySchema, queryInput, 'Invalid query');\n if (queryValidation.success === false) {\n return queryValidation.response;\n }\n\n const bodyValidation = await this.validateInput(this.config.bodySchema, bodyInput, 'Invalid body');\n if (bodyValidation.success === false) {\n return bodyValidation.response;\n }\n\n let middlewareContext: TContext = { ...this.baseContext };\n for (const middleware of this.middlewares) {\n const result = await middleware(request, middlewareContext);\n if (result instanceof Response) {\n return result;\n }\n middlewareContext = { ...middlewareContext, ...(result as TContext) };\n }\n\n return handler(request, {\n params: paramsValidation.data,\n query: queryValidation.data,\n body: bodyValidation.data,\n data: middlewareContext,\n });\n } catch (error) {\n if (error instanceof BodyParsingError) {\n return this.buildErrorResponse(error.message, undefined, 400);\n }\n\n if (this.handleServerError) {\n return this.handleServerError(error as Error);\n }\n\n return this.buildErrorResponse('Internal server error', undefined, 500);\n }\n };\n }\n\n private async resolveParams(params: RouteContext['params'] | undefined) {\n const resolvedParams = await Promise.resolve(params ?? {});\n return (resolvedParams ?? {}) as Record<string, unknown>;\n }\n\n private async validateInput<S extends Schema | undefined>(\n schema: S,\n data: unknown,\n errorMessage: string,\n ): Promise<{ success: true; data: InferMaybe<S> } | { success: false; response: Response }> {\n if (!schema) {\n return { success: true, data: (data ?? {}) as InferMaybe<S> };\n }\n\n const result = await this.validationAdapter.validate(schema, data);\n if (result.success === true) {\n return { success: true, data: result.data as InferMaybe<S> };\n }\n\n if (this.validationErrorHandler) {\n return { success: false, response: this.validationErrorHandler(result.issues) };\n }\n\n return { success: false, response: this.buildErrorResponse(errorMessage, result.issues) };\n }\n\n private getQueryParams(request: Request) {\n const url = new URL(request.url);\n const params: Record<string, unknown> = {};\n const keys = Array.from(new Set(url.searchParams.keys()));\n const { arrayStrategy, singleValueStrategy, coerce } = this.parserOptions.query;\n\n for (const key of keys) {\n const values = url.searchParams.getAll(key);\n const coercedValues = values.map((value) => this.coerceStringValue(value, key, coerce));\n params[key] = this.selectValues(coercedValues, arrayStrategy, singleValueStrategy);\n }\n\n return params;\n }\n\n private selectValues(\n values: unknown[],\n arrayStrategy: QueryArrayStrategy,\n singleValueStrategy: QuerySingleValueStrategy,\n ): unknown {\n if (arrayStrategy === 'always') {\n return values;\n }\n\n if (arrayStrategy === 'never') {\n return this.pickSingleValue(values, singleValueStrategy);\n }\n\n return values.length === 1 ? values[0] : values;\n }\n\n private pickSingleValue(values: unknown[], strategy: QuerySingleValueStrategy): unknown {\n if (values.length === 0) {\n return undefined;\n }\n\n return strategy === 'first' ? values[0] : values[values.length - 1];\n }\n\n private coerceStringValue(value: string, key: string, coercion: ValueCoercion): unknown {\n if (typeof coercion === 'function') {\n return coercion(value, key);\n }\n\n if (coercion === 'primitive') {\n return this.coercePrimitiveValue(value);\n }\n\n return value;\n }\n\n private coercePrimitiveValue(value: string): unknown {\n const trimmed = value.trim();\n\n if (trimmed === 'true') {\n return true;\n }\n\n if (trimmed === 'false') {\n return false;\n }\n\n if (trimmed === 'null') {\n return null;\n }\n\n if (/^-?(?:\\d+|\\d*\\.\\d+)$/.test(trimmed)) {\n return Number(trimmed);\n }\n\n return value;\n }\n\n private parseFormData(\n formData: FormData,\n arrayStrategy: QueryArrayStrategy,\n singleValueStrategy: QuerySingleValueStrategy,\n coercion: ValueCoercion,\n ): Record<string, unknown> {\n const data: Record<string, unknown> = {};\n const keys = Array.from(new Set(formData.keys()));\n\n for (const key of keys) {\n const values = formData\n .getAll(key)\n .map((value) => (typeof value === 'string' ? this.coerceStringValue(value, key, coercion) : value));\n data[key] = this.selectValues(values, arrayStrategy, singleValueStrategy);\n }\n\n return data;\n }\n\n private resolveEmptyBody() {\n const { allowEmptyBody, hasEmptyValue, emptyValue } = this.parserOptions.body;\n\n if (allowEmptyBody) {\n return hasEmptyValue ? emptyValue : {};\n }\n\n throw new BodyParsingError('Request body is required.');\n }\n\n private async parseRequestBody(request: Request): Promise<unknown> {\n if (!this.config.bodySchema) {\n return {};\n }\n\n const contentType = (request.headers.get('content-type') ?? '').toLowerCase();\n\n if (contentType.includes('application/json')) {\n const rawBody = await request.text();\n\n if (rawBody.length === 0) {\n return this.resolveEmptyBody();\n }\n\n try {\n return JSON.parse(rawBody);\n } catch (error) {\n throw new BodyParsingError('Invalid JSON body.');\n }\n }\n\n if (contentType.includes('multipart/form-data') || contentType.includes('application/x-www-form-urlencoded')) {\n try {\n const formData = await request.formData();\n const { arrayStrategy, singleValueStrategy, coerce } = this.parserOptions.body;\n const data = this.parseFormData(formData, arrayStrategy, singleValueStrategy, coerce);\n\n if (Object.keys(data).length === 0) {\n return this.resolveEmptyBody();\n }\n\n return data;\n } catch (error) {\n throw new BodyParsingError('Invalid Form Data.');\n }\n }\n\n if (this.parserOptions.body.strictContentType) {\n throw new BodyParsingError(\n 'Unsupported content type. Expected application/json, multipart/form-data, or application/x-www-form-urlencoded.',\n );\n }\n\n const rawBody = await request.text();\n if (rawBody.length === 0) {\n return this.resolveEmptyBody();\n }\n\n if (this.parserOptions.body.fallbackStrategy === 'text') {\n return this.coerceStringValue(rawBody, 'body', this.parserOptions.body.coerce);\n }\n\n try {\n return JSON.parse(rawBody);\n } catch (error) {\n return this.coerceStringValue(rawBody, 'body', this.parserOptions.body.coerce);\n }\n }\n\n private buildErrorResponse(message: string, issues?: ValidationIssue[], status = 400) {\n return new Response(JSON.stringify({ message, issues }), {\n status,\n headers: { 'content-type': 'application/json' },\n });\n }\n}\n","import { ValidationAdapter } from './adapters/types';\nimport { zodAdapter } from './adapters/zod';\nimport { RouteHandlerBuilder } from './routeHandlerBuilder';\nimport { HandlerServerErrorFn, ParserOptions, ValidationErrorHandler } from './types';\n\ntype CreateSafeRouteParams<TContext extends Record<string, unknown>> = {\n handleServerError?: HandlerServerErrorFn;\n validationErrorHandler?: ValidationErrorHandler;\n validationAdapter?: ValidationAdapter;\n baseContext?: TContext;\n parserOptions?: ParserOptions;\n};\n\nexport function createSafeRoute<TContext extends Record<string, unknown> = Record<string, unknown>>(\n params?: CreateSafeRouteParams<TContext>,\n) {\n return new RouteHandlerBuilder<undefined, undefined, undefined, TContext>({\n handleServerError: params?.handleServerError,\n validationErrorHandler: params?.validationErrorHandler,\n validationAdapter: params?.validationAdapter ?? zodAdapter(),\n baseContext: params?.baseContext,\n parserOptions: params?.parserOptions,\n });\n}\n","import type { ValidationIssue } from './adapters/types';\nimport type {\n NormalizeValidationIssuesFn,\n SafeActionResult,\n SafeActionValidationErrorResult,\n SafeActionValidationErrors,\n} from './safeActionTypes';\n\nfunction toDotPath(path?: Array<string | number | symbol>) {\n if (!path || path.length === 0) {\n return undefined;\n }\n\n return path.map((part) => (typeof part === 'symbol' ? part.description ?? part.toString() : String(part))).join('.');\n}\n\nexport const normalizeValidationIssues: NormalizeValidationIssuesFn = (issues) => {\n const fieldErrors: Record<string, string[]> = {};\n const formErrors: string[] = [];\n\n for (const issue of issues) {\n const path = toDotPath(issue.path);\n\n if (!path) {\n formErrors.push(issue.message);\n continue;\n }\n\n if (!fieldErrors[path]) {\n fieldErrors[path] = [];\n }\n\n const messages = fieldErrors[path] ?? [];\n messages.push(issue.message);\n fieldErrors[path] = messages;\n }\n\n return {\n fieldErrors,\n formErrors,\n };\n};\n\nexport function createValidationErrorResult(issues: ValidationIssue[]): SafeActionValidationErrorResult {\n return {\n validationErrors: normalizeValidationIssues(issues),\n };\n}\n\nexport function createServerErrorResult<TData = never>(serverError: string): SafeActionResult<TData> {\n return {\n serverError,\n };\n}\n\nexport function isSafeActionResult<TData>(value: unknown): value is SafeActionResult<TData> {\n if (!value || typeof value !== 'object') {\n return false;\n }\n\n const candidate = value as Partial<SafeActionResult<TData>>;\n return 'data' in candidate || 'validationErrors' in candidate || 'serverError' in candidate;\n}\n\nexport function mergeContexts<TContext extends Record<string, unknown>, TPatch extends Record<string, unknown>>(\n base: TContext,\n patch?: TPatch,\n): TContext & TPatch {\n return {\n ...base,\n ...(patch ?? {}),\n } as TContext & TPatch;\n}\n\nexport function isValidationFailure(\n result: SafeActionResult<unknown>,\n): result is { validationErrors: SafeActionValidationErrors } {\n return Boolean(result && typeof result === 'object' && 'validationErrors' in result && result.validationErrors);\n}\n","import type { Infer, Schema } from './adapters/types';\nimport {\n type AnySafeActionMiddleware,\n type InferActionInput,\n type InferParsedActionInput,\n type SafeActionBuilderConfig,\n type SafeActionFn,\n type SafeActionHandler,\n type SafeActionMiddleware,\n type SafeActionResult,\n} from './safeActionTypes';\nimport {\n createServerErrorResult,\n createValidationErrorResult,\n isSafeActionResult,\n mergeContexts,\n} from './safeActionUtils';\n\ntype DefaultObject = Record<string, never>;\n\nfunction toSafeServerError(error: unknown, defaultServerError: string, mapper?: (error: unknown) => string) {\n if (!mapper) {\n return defaultServerError;\n }\n\n try {\n const mapped = mapper(error);\n if (typeof mapped === 'string' && mapped.length > 0) {\n return mapped;\n }\n return defaultServerError;\n } catch (mappingError) {\n return defaultServerError;\n }\n}\n\nexport class SafeActionBuilder<\n TInputSchema extends Schema | undefined = undefined,\n TOutputSchema extends Schema | undefined = undefined,\n TContext extends Record<string, unknown> = Record<string, unknown>,\n TMetadata extends Record<string, unknown> = DefaultObject,\n> {\n private config: SafeActionBuilderConfig<TInputSchema, TOutputSchema, TContext, TMetadata>;\n\n constructor(config: SafeActionBuilderConfig<TInputSchema, TOutputSchema, TContext, TMetadata>) {\n this.config = config;\n }\n\n inputSchema<TSchema extends Schema>(schema: TSchema) {\n return new SafeActionBuilder<TSchema, TOutputSchema, TContext, TMetadata>({\n ...this.config,\n inputSchema: schema,\n });\n }\n\n outputSchema<TSchema extends Schema>(schema: TSchema) {\n return new SafeActionBuilder<TInputSchema, TSchema, TContext, TMetadata>({\n ...this.config,\n outputSchema: schema,\n });\n }\n\n metadata<TNextMetadata extends Record<string, unknown>>(metadata: TNextMetadata) {\n return new SafeActionBuilder<TInputSchema, TOutputSchema, TContext, TNextMetadata>({\n ...this.config,\n metadata,\n middlewares: this.config.middlewares as unknown as SafeActionBuilderConfig<\n TInputSchema,\n TOutputSchema,\n TContext,\n TNextMetadata\n >['middlewares'],\n });\n }\n\n use<TContextPatch extends Record<string, unknown>>(\n middleware: SafeActionMiddleware<InferParsedActionInput<TInputSchema>, TContext, TMetadata, TContextPatch>,\n ) {\n return new SafeActionBuilder<TInputSchema, TOutputSchema, TContext & TContextPatch, TMetadata>({\n ...this.config,\n middlewares: [...this.config.middlewares, middleware as unknown as AnySafeActionMiddleware],\n baseContext: this.config.baseContext as TContext & TContextPatch,\n });\n }\n\n action<TData>(\n handler: SafeActionHandler<InferParsedActionInput<TInputSchema>, TContext, TMetadata, TData>,\n ): SafeActionFn<InferActionInput<TInputSchema>, TOutputSchema extends Schema ? Infer<TOutputSchema> : TData> {\n type TParsedInput = InferParsedActionInput<TInputSchema>;\n type TFinalData = TOutputSchema extends Schema ? Infer<TOutputSchema> : TData;\n\n const {\n inputSchema,\n outputSchema,\n validationAdapter,\n middlewares,\n metadata,\n baseContext,\n defaultServerError,\n handleServerError,\n } = this.config;\n\n const actionFn = async (...args: unknown[]): Promise<SafeActionResult<TFinalData>> => {\n try {\n const rawInput = args.length > 0 ? args[0] : undefined;\n let parsedInput: TParsedInput;\n\n if (!inputSchema) {\n parsedInput = undefined as TParsedInput;\n } else {\n const inputResult = await validationAdapter.validate(inputSchema, rawInput);\n if (inputResult.success === false) {\n return createValidationErrorResult(inputResult.issues);\n }\n parsedInput = inputResult.data as TParsedInput;\n }\n\n const runHandler = async (ctx: Record<string, unknown>): Promise<SafeActionResult<TFinalData>> => {\n const handlerResult = await handler({\n parsedInput,\n ctx: ctx as TContext,\n metadata,\n });\n\n if (!outputSchema) {\n return {\n data: handlerResult as TFinalData,\n };\n }\n\n const outputResult = await validationAdapter.validate(outputSchema, handlerResult);\n if (outputResult.success === false) {\n return createServerErrorResult<TFinalData>(\n toSafeServerError(new Error('Invalid action output.'), defaultServerError, handleServerError),\n );\n }\n\n return {\n data: outputResult.data as TFinalData,\n };\n };\n\n const runMiddleware = async (\n index: number,\n ctx: Record<string, unknown>,\n ): Promise<SafeActionResult<TFinalData>> => {\n if (index >= middlewares.length) {\n return runHandler(ctx);\n }\n\n const middleware = middlewares[index] as SafeActionMiddleware<\n TParsedInput,\n Record<string, unknown>,\n TMetadata,\n Record<string, unknown>,\n TFinalData\n >;\n\n let nextCalled = false;\n\n const next = async (options?: { ctx?: Record<string, unknown> }) => {\n if (nextCalled) {\n throw new Error('next() called more than once in the same middleware.');\n }\n nextCalled = true;\n\n return runMiddleware(index + 1, mergeContexts(ctx, options?.ctx));\n };\n\n const middlewareResult = await middleware({\n parsedInput,\n ctx,\n metadata,\n next,\n });\n\n if (!isSafeActionResult<TFinalData>(middlewareResult)) {\n throw new Error('Middleware must return a SafeActionResult.');\n }\n\n return middlewareResult;\n };\n\n const initialContext = { ...baseContext } as Record<string, unknown>;\n return await runMiddleware(0, initialContext);\n } catch (error) {\n return createServerErrorResult<TFinalData>(toSafeServerError(error, defaultServerError, handleServerError));\n }\n };\n\n return actionFn as SafeActionFn<InferActionInput<TInputSchema>, TFinalData>;\n }\n}\n","import { zodAdapter } from './adapters/zod';\nimport { SafeActionBuilder } from './safeActionBuilder';\nimport type { SafeActionClientOptions } from './safeActionTypes';\n\nconst DEFAULT_SERVER_ERROR = 'Something went wrong while executing the action.';\n\nexport function createSafeActionClient<TContext extends Record<string, unknown> = Record<string, unknown>>(\n options?: SafeActionClientOptions<TContext>,\n) {\n return new SafeActionBuilder({\n validationAdapter: options?.validationAdapter ?? zodAdapter(),\n baseContext: (options?.baseContext ?? {}) as TContext,\n metadata: {} as Record<string, never>,\n middlewares: [],\n defaultServerError: options?.defaultServerError ?? DEFAULT_SERVER_ERROR,\n handleServerError: options?.handleServerError,\n });\n}\n"],"mappings":"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","names":["ZodAdapter","schema","data","result","message","path","zodAdapter","normalizeParserOptions","parserOptions","bodyOptions","BodyParsingError","message","RouteHandlerBuilder","_RouteHandlerBuilder","config","validationAdapter","zodAdapter","middlewares","handleServerError","validationErrorHandler","baseContext","schema","middleware","handler","request","context","routeContext","paramsInput","queryInput","bodyInput","paramsValidation","queryValidation","bodyValidation","middlewareContext","result","error","params","data","errorMessage","url","keys","arrayStrategy","singleValueStrategy","coerce","key","coercedValues","value","values","strategy","coercion","trimmed","formData","allowEmptyBody","hasEmptyValue","emptyValue","contentType","rawBody","issues","status","createSafeRoute","params","RouteHandlerBuilder","zodAdapter","toDotPath","path","part","normalizeValidationIssues","issues","fieldErrors","formErrors","issue","messages","createValidationErrorResult","createServerErrorResult","serverError","isSafeActionResult","value","candidate","mergeContexts","base","patch","toSafeServerError","error","defaultServerError","mapper","mapped","SafeActionBuilder","_SafeActionBuilder","config","schema","metadata","middleware","handler","inputSchema","outputSchema","validationAdapter","middlewares","baseContext","handleServerError","args","rawInput","parsedInput","inputResult","createValidationErrorResult","runHandler","ctx","handlerResult","outputResult","createServerErrorResult","runMiddleware","index","nextCalled","middlewareResult","options","mergeContexts","isSafeActionResult","initialContext","DEFAULT_SERVER_ERROR","createSafeActionClient","options","SafeActionBuilder","zodAdapter"]}
@@ -0,0 +1,60 @@
1
+ import { S as Schema, I as InferIn, a as Infer, V as ValidationAdapter } from './types-DYbZEItT.js';
2
+
3
+ type Awaitable<T> = T | Promise<T>;
4
+ type ActionInputArgs<TInput> = [TInput] extends [void] ? [] | [TInput] : [input: TInput];
5
+ type InferActionInput<TSchema extends Schema | undefined> = TSchema extends Schema ? InferIn<TSchema> : void;
6
+ type InferParsedActionInput<TSchema extends Schema | undefined> = TSchema extends Schema ? Infer<TSchema> : undefined;
7
+ type SafeActionValidationErrors = {
8
+ fieldErrors: Record<string, string[]>;
9
+ formErrors: string[];
10
+ };
11
+ type SafeActionSuccessResult<TData> = {
12
+ data: TData;
13
+ validationErrors?: undefined;
14
+ serverError?: undefined;
15
+ };
16
+ type SafeActionValidationErrorResult = {
17
+ data?: undefined;
18
+ validationErrors: SafeActionValidationErrors;
19
+ serverError?: undefined;
20
+ };
21
+ type SafeActionServerErrorResult = {
22
+ data?: undefined;
23
+ validationErrors?: undefined;
24
+ serverError: string;
25
+ };
26
+ type SafeActionResult<TData> = SafeActionSuccessResult<TData> | SafeActionValidationErrorResult | SafeActionServerErrorResult;
27
+ type SafeActionFn<TInput, TData> = (...args: ActionInputArgs<TInput>) => Promise<SafeActionResult<TData>>;
28
+ type SafeActionMiddlewareNext<TCtxPatch extends Record<string, unknown>, TData> = (options?: {
29
+ ctx?: TCtxPatch;
30
+ }) => Promise<SafeActionResult<TData>>;
31
+ type SafeActionMiddleware<TParsedInput, TCtx extends Record<string, unknown>, TMetadata extends Record<string, unknown>, TCtxPatch extends Record<string, unknown> = Record<string, unknown>, TData = unknown> = (args: {
32
+ parsedInput: TParsedInput;
33
+ ctx: TCtx;
34
+ metadata: TMetadata;
35
+ next: SafeActionMiddlewareNext<TCtxPatch, TData>;
36
+ }) => Awaitable<SafeActionResult<TData>>;
37
+ type AnySafeActionMiddleware = SafeActionMiddleware<unknown, Record<string, unknown>, Record<string, unknown>, Record<string, unknown>, unknown>;
38
+ type SafeActionHandler<TParsedInput, TCtx extends Record<string, unknown>, TMetadata, TData> = (args: {
39
+ parsedInput: TParsedInput;
40
+ ctx: TCtx;
41
+ metadata: TMetadata;
42
+ }) => Awaitable<TData>;
43
+ type SafeActionClientOptions<TContext extends Record<string, unknown>> = {
44
+ validationAdapter?: ValidationAdapter;
45
+ baseContext?: TContext;
46
+ defaultServerError?: string;
47
+ handleServerError?: (error: unknown) => string;
48
+ };
49
+ type SafeActionBuilderConfig<TInputSchema extends Schema | undefined, TOutputSchema extends Schema | undefined, TContext extends Record<string, unknown>, TMetadata extends Record<string, unknown>> = {
50
+ inputSchema?: TInputSchema;
51
+ outputSchema?: TOutputSchema;
52
+ validationAdapter: ValidationAdapter;
53
+ baseContext: TContext;
54
+ metadata: TMetadata;
55
+ middlewares: AnySafeActionMiddleware[];
56
+ defaultServerError: string;
57
+ handleServerError?: (error: unknown) => string;
58
+ };
59
+
60
+ export type { InferParsedActionInput as I, SafeActionValidationErrors as S, SafeActionResult as a, SafeActionBuilderConfig as b, SafeActionMiddleware as c, SafeActionHandler as d, SafeActionFn as e, InferActionInput as f, SafeActionClientOptions as g, SafeActionMiddlewareNext as h, SafeActionServerErrorResult as i, SafeActionSuccessResult as j, SafeActionValidationErrorResult as k };