next-form-request 0.1.1 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{ZodAdapter-D7D3Sc-a.d.mts → ZodAdapter-Ni7VwvnR.d.mts} +1 -1
- package/dist/{ZodAdapter-D7D3Sc-a.d.ts → ZodAdapter-Ni7VwvnR.d.ts} +1 -1
- package/dist/adapters/validators/ZodAdapter.d.mts +1 -1
- package/dist/adapters/validators/ZodAdapter.d.ts +1 -1
- package/dist/index.d.mts +849 -15
- package/dist/index.d.ts +849 -15
- package/dist/index.js +987 -0
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +958 -1
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -1,7 +1,229 @@
|
|
|
1
1
|
import { NextApiRequest, NextApiResponse } from 'next';
|
|
2
|
-
import { S as SupportedRequest, V as ValidatorAdapter, a as ValidationErrors } from './ZodAdapter-
|
|
3
|
-
export { A as AppRouterHandler, P as PagesRouterHandler, R as RequestData,
|
|
4
|
-
import 'zod';
|
|
2
|
+
import { S as SupportedRequest, V as ValidatorAdapter, a as ValidationErrors, b as ValidationConfig, c as ValidationResult } from './ZodAdapter-Ni7VwvnR.mjs';
|
|
3
|
+
export { A as AppRouterHandler, P as PagesRouterHandler, R as RequestData, Z as ZodAdapter, i as isAppRouterRequest, d as isPagesRouterRequest } from './ZodAdapter-Ni7VwvnR.mjs';
|
|
4
|
+
import { z } from 'zod';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Rate limit configuration
|
|
8
|
+
*/
|
|
9
|
+
interface RateLimitConfig {
|
|
10
|
+
/**
|
|
11
|
+
* Maximum number of requests allowed in the time window
|
|
12
|
+
*/
|
|
13
|
+
maxAttempts: number;
|
|
14
|
+
/**
|
|
15
|
+
* Time window in milliseconds
|
|
16
|
+
*/
|
|
17
|
+
windowMs: number;
|
|
18
|
+
/**
|
|
19
|
+
* Function to generate the rate limit key from the request
|
|
20
|
+
* Common options: IP address, user ID, API key, etc.
|
|
21
|
+
* @default Uses IP address
|
|
22
|
+
*/
|
|
23
|
+
key?: (request: Request | NextApiRequest) => string | Promise<string>;
|
|
24
|
+
/**
|
|
25
|
+
* Custom store for rate limit data
|
|
26
|
+
* Defaults to in-memory store (not suitable for production with multiple instances)
|
|
27
|
+
*/
|
|
28
|
+
store?: RateLimitStore;
|
|
29
|
+
/**
|
|
30
|
+
* Whether to skip rate limiting for certain requests
|
|
31
|
+
*/
|
|
32
|
+
skip?: (request: Request | NextApiRequest) => boolean | Promise<boolean>;
|
|
33
|
+
/**
|
|
34
|
+
* Custom error message when rate limited
|
|
35
|
+
*/
|
|
36
|
+
message?: string;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Rate limit state for a specific key
|
|
40
|
+
*/
|
|
41
|
+
interface RateLimitState {
|
|
42
|
+
/** Number of requests made in the current window */
|
|
43
|
+
count: number;
|
|
44
|
+
/** Timestamp when the window resets */
|
|
45
|
+
resetAt: number;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Rate limit check result
|
|
49
|
+
*/
|
|
50
|
+
interface RateLimitResult {
|
|
51
|
+
/** Whether the request is allowed */
|
|
52
|
+
allowed: boolean;
|
|
53
|
+
/** Number of requests remaining in the current window */
|
|
54
|
+
remaining: number;
|
|
55
|
+
/** Total limit */
|
|
56
|
+
limit: number;
|
|
57
|
+
/** Timestamp when the window resets */
|
|
58
|
+
resetAt: number;
|
|
59
|
+
/** Number of seconds until reset */
|
|
60
|
+
retryAfter: number;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Interface for rate limit storage
|
|
64
|
+
*/
|
|
65
|
+
interface RateLimitStore {
|
|
66
|
+
/** Get the current state for a key */
|
|
67
|
+
get(key: string): Promise<RateLimitState | null>;
|
|
68
|
+
/** Set the state for a key */
|
|
69
|
+
set(key: string, state: RateLimitState, ttlMs: number): Promise<void>;
|
|
70
|
+
/** Increment the count for a key, returns new state */
|
|
71
|
+
increment(key: string, windowMs: number): Promise<RateLimitState>;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* In-memory rate limit store (for development/testing only)
|
|
75
|
+
* In production, use Redis or another distributed store
|
|
76
|
+
*/
|
|
77
|
+
declare class MemoryRateLimitStore implements RateLimitStore {
|
|
78
|
+
private store;
|
|
79
|
+
get(key: string): Promise<RateLimitState | null>;
|
|
80
|
+
set(key: string, state: RateLimitState, _ttlMs: number): Promise<void>;
|
|
81
|
+
increment(key: string, windowMs: number): Promise<RateLimitState>;
|
|
82
|
+
private cleanup;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Set a custom default rate limit store
|
|
86
|
+
* Useful for setting up Redis or other distributed stores
|
|
87
|
+
*/
|
|
88
|
+
declare function setDefaultRateLimitStore(store: RateLimitStore): void;
|
|
89
|
+
/**
|
|
90
|
+
* Check rate limit for a request
|
|
91
|
+
*/
|
|
92
|
+
declare function checkRateLimit(request: Request | NextApiRequest, config: RateLimitConfig): Promise<RateLimitResult>;
|
|
93
|
+
/**
|
|
94
|
+
* Rate limit error thrown when a request exceeds the rate limit
|
|
95
|
+
*/
|
|
96
|
+
declare class RateLimitError extends Error {
|
|
97
|
+
readonly retryAfter: number;
|
|
98
|
+
readonly remaining: number;
|
|
99
|
+
readonly limit: number;
|
|
100
|
+
readonly resetAt: number;
|
|
101
|
+
constructor(result: RateLimitResult, message?: string);
|
|
102
|
+
/**
|
|
103
|
+
* Get headers to send with the rate limit response
|
|
104
|
+
*/
|
|
105
|
+
getHeaders(): Record<string, string>;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Create rate limit configuration with sensible defaults
|
|
109
|
+
*/
|
|
110
|
+
declare function rateLimit(config: Partial<RateLimitConfig> & {
|
|
111
|
+
maxAttempts: number;
|
|
112
|
+
windowMs: number;
|
|
113
|
+
}): RateLimitConfig;
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Coercion utilities for automatically converting string values from form data
|
|
117
|
+
* to their appropriate JavaScript types.
|
|
118
|
+
*/
|
|
119
|
+
/**
|
|
120
|
+
* Options for coercion
|
|
121
|
+
*/
|
|
122
|
+
interface CoercionOptions {
|
|
123
|
+
/** Coerce "true"/"false" strings to booleans */
|
|
124
|
+
booleans?: boolean;
|
|
125
|
+
/** Coerce numeric strings to numbers */
|
|
126
|
+
numbers?: boolean;
|
|
127
|
+
/** Coerce ISO date strings to Date objects */
|
|
128
|
+
dates?: boolean;
|
|
129
|
+
/** Coerce "null" string to null */
|
|
130
|
+
nulls?: boolean;
|
|
131
|
+
/** Coerce empty strings to undefined */
|
|
132
|
+
emptyStrings?: boolean;
|
|
133
|
+
/** Coerce JSON strings to objects/arrays */
|
|
134
|
+
json?: boolean;
|
|
135
|
+
/** Custom coercion functions for specific fields */
|
|
136
|
+
fields?: Record<string, (value: unknown) => unknown>;
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Coerce form data values to their appropriate JavaScript types.
|
|
140
|
+
*
|
|
141
|
+
* Form submissions typically send everything as strings. This function
|
|
142
|
+
* automatically converts common patterns:
|
|
143
|
+
* - "true" / "false" → boolean
|
|
144
|
+
* - "123" / "45.67" → number
|
|
145
|
+
* - "2024-01-01" → Date
|
|
146
|
+
* - "null" → null
|
|
147
|
+
*
|
|
148
|
+
* @example
|
|
149
|
+
* ```typescript
|
|
150
|
+
* const formData = {
|
|
151
|
+
* name: "John",
|
|
152
|
+
* age: "25",
|
|
153
|
+
* active: "true",
|
|
154
|
+
* createdAt: "2024-01-01",
|
|
155
|
+
* };
|
|
156
|
+
*
|
|
157
|
+
* const coerced = coerceFormData(formData);
|
|
158
|
+
* // {
|
|
159
|
+
* // name: "John",
|
|
160
|
+
* // age: 25,
|
|
161
|
+
* // active: true,
|
|
162
|
+
* // createdAt: Date("2024-01-01"),
|
|
163
|
+
* // }
|
|
164
|
+
* ```
|
|
165
|
+
*/
|
|
166
|
+
declare function coerceFormData<T extends Record<string, unknown>>(data: T, options?: CoercionOptions): T;
|
|
167
|
+
/**
|
|
168
|
+
* Create a Zod preprocessor for automatic coercion
|
|
169
|
+
*
|
|
170
|
+
* @example
|
|
171
|
+
* ```typescript
|
|
172
|
+
* import { z } from 'zod';
|
|
173
|
+
* import { zodCoerce } from 'next-request';
|
|
174
|
+
*
|
|
175
|
+
* const schema = z.preprocess(
|
|
176
|
+
* zodCoerce(),
|
|
177
|
+
* z.object({
|
|
178
|
+
* name: z.string(),
|
|
179
|
+
* age: z.number(),
|
|
180
|
+
* active: z.boolean(),
|
|
181
|
+
* })
|
|
182
|
+
* );
|
|
183
|
+
* ```
|
|
184
|
+
*/
|
|
185
|
+
declare function zodCoerce(options?: CoercionOptions): (data: unknown) => unknown;
|
|
186
|
+
/**
|
|
187
|
+
* Common coercion presets
|
|
188
|
+
*/
|
|
189
|
+
declare const coercionPresets: {
|
|
190
|
+
/** Coerce all supported types */
|
|
191
|
+
all: {
|
|
192
|
+
booleans: true;
|
|
193
|
+
numbers: true;
|
|
194
|
+
dates: true;
|
|
195
|
+
nulls: true;
|
|
196
|
+
emptyStrings: true;
|
|
197
|
+
json: true;
|
|
198
|
+
};
|
|
199
|
+
/** Only coerce booleans and numbers (safest) */
|
|
200
|
+
safe: {
|
|
201
|
+
booleans: true;
|
|
202
|
+
numbers: true;
|
|
203
|
+
dates: false;
|
|
204
|
+
nulls: false;
|
|
205
|
+
emptyStrings: false;
|
|
206
|
+
json: false;
|
|
207
|
+
};
|
|
208
|
+
/** Coerce booleans, numbers, and dates */
|
|
209
|
+
standard: {
|
|
210
|
+
booleans: true;
|
|
211
|
+
numbers: true;
|
|
212
|
+
dates: true;
|
|
213
|
+
nulls: false;
|
|
214
|
+
emptyStrings: false;
|
|
215
|
+
json: false;
|
|
216
|
+
};
|
|
217
|
+
/** No coercion */
|
|
218
|
+
none: {
|
|
219
|
+
booleans: false;
|
|
220
|
+
numbers: false;
|
|
221
|
+
dates: false;
|
|
222
|
+
nulls: false;
|
|
223
|
+
emptyStrings: false;
|
|
224
|
+
json: false;
|
|
225
|
+
};
|
|
226
|
+
};
|
|
5
227
|
|
|
6
228
|
/**
|
|
7
229
|
* Abstract base class for form request validation.
|
|
@@ -73,6 +295,38 @@ declare abstract class FormRequest<TValidated = unknown> {
|
|
|
73
295
|
* @returns true if authorized, false otherwise
|
|
74
296
|
*/
|
|
75
297
|
authorize(): boolean | Promise<boolean>;
|
|
298
|
+
/**
|
|
299
|
+
* Define rate limiting for this request.
|
|
300
|
+
* Override this method to add rate limiting.
|
|
301
|
+
*
|
|
302
|
+
* @example
|
|
303
|
+
* ```typescript
|
|
304
|
+
* rateLimit() {
|
|
305
|
+
* return {
|
|
306
|
+
* maxAttempts: 5,
|
|
307
|
+
* windowMs: 60000, // 1 minute
|
|
308
|
+
* key: (req) => this.input('email') || 'anonymous',
|
|
309
|
+
* };
|
|
310
|
+
* }
|
|
311
|
+
* ```
|
|
312
|
+
*/
|
|
313
|
+
rateLimit(): RateLimitConfig | null;
|
|
314
|
+
/**
|
|
315
|
+
* Define coercion options for form data.
|
|
316
|
+
* Override this method to enable automatic type coercion.
|
|
317
|
+
*
|
|
318
|
+
* @example
|
|
319
|
+
* ```typescript
|
|
320
|
+
* coercion() {
|
|
321
|
+
* return {
|
|
322
|
+
* booleans: true, // "true" → true
|
|
323
|
+
* numbers: true, // "123" → 123
|
|
324
|
+
* dates: true, // "2024-01-01" → Date
|
|
325
|
+
* };
|
|
326
|
+
* }
|
|
327
|
+
* ```
|
|
328
|
+
*/
|
|
329
|
+
coercion(): CoercionOptions | null;
|
|
76
330
|
/**
|
|
77
331
|
* Called before validation runs.
|
|
78
332
|
* Use this to normalize or transform input data.
|
|
@@ -152,6 +406,7 @@ declare abstract class FormRequest<TValidated = unknown> {
|
|
|
152
406
|
* Run validation and return the validated data.
|
|
153
407
|
* Throws ValidationError if validation fails.
|
|
154
408
|
* Throws AuthorizationError if authorization fails.
|
|
409
|
+
* Throws RateLimitError if rate limit is exceeded.
|
|
155
410
|
*
|
|
156
411
|
* @returns The validated data with full type inference
|
|
157
412
|
*/
|
|
@@ -248,13 +503,13 @@ declare class AuthorizationError extends Error {
|
|
|
248
503
|
}
|
|
249
504
|
|
|
250
505
|
/**
|
|
251
|
-
*
|
|
506
|
+
* Extract the validated type from a FormRequest constructor
|
|
507
|
+
* This works by:
|
|
508
|
+
* 1. Getting the instance type from the constructor: InstanceType<T>
|
|
509
|
+
* 2. Checking if that instance extends FormRequest<infer V>
|
|
510
|
+
* 3. Extracting V which is the TValidated generic parameter
|
|
252
511
|
*/
|
|
253
|
-
type
|
|
254
|
-
new (): FormRequest<TValidated>;
|
|
255
|
-
fromAppRouter(request: Request, params?: Record<string, string>): Promise<FormRequest<TValidated>>;
|
|
256
|
-
fromPagesRouter(request: NextApiRequest, params?: Record<string, string>): Promise<FormRequest<TValidated>>;
|
|
257
|
-
};
|
|
512
|
+
type InferValidatedType$1<T extends new () => any> = InstanceType<T> extends FormRequest<infer V> ? V : never;
|
|
258
513
|
/**
|
|
259
514
|
* Handler function for App Router
|
|
260
515
|
*/
|
|
@@ -266,7 +521,7 @@ type PagesRouterHandler<TValidated> = (validated: TValidated, req: NextApiReques
|
|
|
266
521
|
/**
|
|
267
522
|
* Context parameter for App Router (Next.js 13+)
|
|
268
523
|
*/
|
|
269
|
-
interface AppRouterContext {
|
|
524
|
+
interface AppRouterContext$1 {
|
|
270
525
|
params?: Record<string, string> | Promise<Record<string, string>>;
|
|
271
526
|
}
|
|
272
527
|
/**
|
|
@@ -291,7 +546,9 @@ interface AppRouterContext {
|
|
|
291
546
|
* });
|
|
292
547
|
* ```
|
|
293
548
|
*/
|
|
294
|
-
declare function withRequest<
|
|
549
|
+
declare function withRequest<T extends new () => FormRequest<any>>(RequestClass: T & {
|
|
550
|
+
fromAppRouter(request: Request, params?: Record<string, string>): Promise<InstanceType<T>>;
|
|
551
|
+
}, handler: AppRouterHandler<InferValidatedType$1<T>>): (request: Request, context?: AppRouterContext$1) => Promise<Response>;
|
|
295
552
|
/**
|
|
296
553
|
* Wrap a Pages Router API handler with form request validation.
|
|
297
554
|
*
|
|
@@ -314,7 +571,9 @@ declare function withRequest<TValidated>(RequestClass: FormRequestClass<TValidat
|
|
|
314
571
|
* });
|
|
315
572
|
* ```
|
|
316
573
|
*/
|
|
317
|
-
declare function withApiRequest<
|
|
574
|
+
declare function withApiRequest<T extends new () => FormRequest<any>>(RequestClass: T & {
|
|
575
|
+
fromPagesRouter(request: NextApiRequest, params?: Record<string, string>): Promise<InstanceType<T>>;
|
|
576
|
+
}, handler: PagesRouterHandler<InferValidatedType$1<T>>): (req: NextApiRequest, res: NextApiResponse) => Promise<void>;
|
|
318
577
|
/**
|
|
319
578
|
* Create a wrapper with custom error handling for App Router.
|
|
320
579
|
*
|
|
@@ -337,7 +596,9 @@ declare function createAppRouterWrapper(options: {
|
|
|
337
596
|
onValidationError?: (error: ValidationError) => Response;
|
|
338
597
|
onAuthorizationError?: (error: AuthorizationError) => Response;
|
|
339
598
|
onError?: (error: unknown) => Response;
|
|
340
|
-
}): <
|
|
599
|
+
}): <T extends new () => FormRequest<any>>(RequestClass: T & {
|
|
600
|
+
fromAppRouter(request: Request, params?: Record<string, string>): Promise<InstanceType<T>>;
|
|
601
|
+
}, handler: AppRouterHandler<InferValidatedType$1<T>>) => (request: Request, context?: AppRouterContext$1) => Promise<Response>;
|
|
341
602
|
/**
|
|
342
603
|
* Create a wrapper with custom error handling for Pages Router.
|
|
343
604
|
*
|
|
@@ -360,6 +621,579 @@ declare function createPagesRouterWrapper(options: {
|
|
|
360
621
|
onValidationError?: (error: ValidationError, req: NextApiRequest, res: NextApiResponse) => void | Promise<void>;
|
|
361
622
|
onAuthorizationError?: (error: AuthorizationError, req: NextApiRequest, res: NextApiResponse) => void | Promise<void>;
|
|
362
623
|
onError?: (error: unknown, req: NextApiRequest, res: NextApiResponse) => void | Promise<void>;
|
|
363
|
-
}): <
|
|
624
|
+
}): <T extends new () => FormRequest<any>>(RequestClass: T & {
|
|
625
|
+
fromPagesRouter(request: NextApiRequest, params?: Record<string, string>): Promise<InstanceType<T>>;
|
|
626
|
+
}, handler: PagesRouterHandler<InferValidatedType$1<T>>) => (req: NextApiRequest, res: NextApiResponse) => Promise<void>;
|
|
627
|
+
|
|
628
|
+
/**
|
|
629
|
+
* Infer the validated type from a ValidationAdapter
|
|
630
|
+
* This extracts the generic type parameter T from ValidatorAdapter<T>
|
|
631
|
+
*/
|
|
632
|
+
type InferValidatedType<T> = T extends ValidatorAdapter<infer V> ? V : never;
|
|
633
|
+
/**
|
|
634
|
+
* Handler function for App Router with schema validation
|
|
635
|
+
*/
|
|
636
|
+
type SchemaHandler<TValidated> = (data: TValidated, request: Request) => Response | Promise<Response>;
|
|
637
|
+
/**
|
|
638
|
+
* Handler function for Pages Router with schema validation
|
|
639
|
+
*/
|
|
640
|
+
type ApiSchemaHandler<TValidated> = (data: TValidated, req: NextApiRequest, res: NextApiResponse) => void | Promise<void>;
|
|
641
|
+
/**
|
|
642
|
+
* Context parameter for App Router (Next.js 13+)
|
|
643
|
+
*/
|
|
644
|
+
interface AppRouterContext {
|
|
645
|
+
params?: Record<string, string> | Promise<Record<string, string>>;
|
|
646
|
+
}
|
|
647
|
+
/**
|
|
648
|
+
* Wrap an App Router route handler with simple schema validation.
|
|
649
|
+
*
|
|
650
|
+
* This is a lightweight alternative to withRequest for cases where you don't need
|
|
651
|
+
* hooks or authorization - just schema validation.
|
|
652
|
+
*
|
|
653
|
+
* The handler receives validated data and only executes if validation passes.
|
|
654
|
+
* ValidationError is thrown on validation failure.
|
|
655
|
+
*
|
|
656
|
+
* @example
|
|
657
|
+
* ```typescript
|
|
658
|
+
* // app/api/users/route.ts
|
|
659
|
+
* import { withSchema, ZodAdapter } from 'next-request';
|
|
660
|
+
* import { z } from 'zod';
|
|
661
|
+
*
|
|
662
|
+
* const userSchema = z.object({
|
|
663
|
+
* name: z.string().min(2),
|
|
664
|
+
* email: z.string().email(),
|
|
665
|
+
* });
|
|
666
|
+
*
|
|
667
|
+
* export const POST = withSchema(new ZodAdapter(userSchema), async (data) => {
|
|
668
|
+
* // data is typed as { name: string; email: string }
|
|
669
|
+
* const user = await db.users.create({ data });
|
|
670
|
+
* return Response.json({ user }, { status: 201 });
|
|
671
|
+
* });
|
|
672
|
+
* ```
|
|
673
|
+
*/
|
|
674
|
+
declare function withSchema<T extends ValidatorAdapter<any>>(adapter: T, handler: SchemaHandler<InferValidatedType<T>>): (request: Request, context?: AppRouterContext) => Promise<Response>;
|
|
675
|
+
/**
|
|
676
|
+
* Wrap a Pages Router API handler with simple schema validation.
|
|
677
|
+
*
|
|
678
|
+
* This is a lightweight alternative to withApiRequest for cases where you don't need
|
|
679
|
+
* hooks or authorization - just schema validation.
|
|
680
|
+
*
|
|
681
|
+
* The handler receives validated data and only executes if validation passes.
|
|
682
|
+
* ValidationError is thrown on validation failure.
|
|
683
|
+
*
|
|
684
|
+
* @example
|
|
685
|
+
* ```typescript
|
|
686
|
+
* // pages/api/users.ts
|
|
687
|
+
* import { withApiSchema, ZodAdapter } from 'next-request';
|
|
688
|
+
* import { z } from 'zod';
|
|
689
|
+
*
|
|
690
|
+
* const userSchema = z.object({
|
|
691
|
+
* name: z.string().min(2),
|
|
692
|
+
* email: z.string().email(),
|
|
693
|
+
* });
|
|
694
|
+
*
|
|
695
|
+
* export default withApiSchema(new ZodAdapter(userSchema), async (data, req, res) => {
|
|
696
|
+
* // data is typed as { name: string; email: string }
|
|
697
|
+
* const user = await db.users.create({ data });
|
|
698
|
+
* res.status(201).json({ user });
|
|
699
|
+
* });
|
|
700
|
+
* ```
|
|
701
|
+
*/
|
|
702
|
+
declare function withApiSchema<T extends ValidatorAdapter<any>>(adapter: T, handler: ApiSchemaHandler<InferValidatedType<T>>): (req: NextApiRequest, res: NextApiResponse) => Promise<void>;
|
|
703
|
+
|
|
704
|
+
interface YupSchema<T> {
|
|
705
|
+
validate(data: unknown, options?: {
|
|
706
|
+
abortEarly?: boolean;
|
|
707
|
+
stripUnknown?: boolean;
|
|
708
|
+
}): Promise<T>;
|
|
709
|
+
validateSync(data: unknown, options?: {
|
|
710
|
+
abortEarly?: boolean;
|
|
711
|
+
stripUnknown?: boolean;
|
|
712
|
+
}): T;
|
|
713
|
+
}
|
|
714
|
+
/**
|
|
715
|
+
* Validator adapter for Yup schemas
|
|
716
|
+
*
|
|
717
|
+
* @example
|
|
718
|
+
* ```typescript
|
|
719
|
+
* import * as yup from 'yup';
|
|
720
|
+
* import { YupAdapter } from 'next-request';
|
|
721
|
+
*
|
|
722
|
+
* const schema = yup.object({
|
|
723
|
+
* email: yup.string().email().required(),
|
|
724
|
+
* name: yup.string().min(2).required(),
|
|
725
|
+
* });
|
|
726
|
+
*
|
|
727
|
+
* class MyRequest extends FormRequest<yup.InferType<typeof schema>> {
|
|
728
|
+
* rules() {
|
|
729
|
+
* return new YupAdapter(schema);
|
|
730
|
+
* }
|
|
731
|
+
* }
|
|
732
|
+
* ```
|
|
733
|
+
*/
|
|
734
|
+
declare class YupAdapter<T> implements ValidatorAdapter<T> {
|
|
735
|
+
private schema;
|
|
736
|
+
constructor(schema: YupSchema<T>);
|
|
737
|
+
validate(data: unknown, config?: ValidationConfig): Promise<ValidationResult<T>>;
|
|
738
|
+
validateSync(data: unknown, config?: ValidationConfig): ValidationResult<T>;
|
|
739
|
+
private isYupValidationError;
|
|
740
|
+
private formatYupErrors;
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
interface ValibotIssue {
|
|
744
|
+
path?: Array<{
|
|
745
|
+
key: string | number;
|
|
746
|
+
} | string | number>;
|
|
747
|
+
message?: string;
|
|
748
|
+
type?: string;
|
|
749
|
+
}
|
|
750
|
+
interface SafeParseResult<T> {
|
|
751
|
+
success: boolean;
|
|
752
|
+
output?: T;
|
|
753
|
+
issues?: ValibotIssue[];
|
|
754
|
+
}
|
|
755
|
+
interface ValibotSchema<TOutput = unknown> {
|
|
756
|
+
_output?: TOutput;
|
|
757
|
+
}
|
|
758
|
+
/**
|
|
759
|
+
* Validator adapter for Valibot schemas.
|
|
760
|
+
*
|
|
761
|
+
* Since valibot is an optional peer dependency, you must pass the safeParse function
|
|
762
|
+
* from your valibot installation.
|
|
763
|
+
*
|
|
764
|
+
* @example
|
|
765
|
+
* ```typescript
|
|
766
|
+
* import * as v from 'valibot';
|
|
767
|
+
* import { ValibotAdapter } from 'next-request';
|
|
768
|
+
*
|
|
769
|
+
* const schema = v.object({
|
|
770
|
+
* email: v.pipe(v.string(), v.email()),
|
|
771
|
+
* name: v.pipe(v.string(), v.minLength(2)),
|
|
772
|
+
* });
|
|
773
|
+
*
|
|
774
|
+
* class MyRequest extends FormRequest<v.InferOutput<typeof schema>> {
|
|
775
|
+
* rules() {
|
|
776
|
+
* return new ValibotAdapter(schema, v.safeParse);
|
|
777
|
+
* }
|
|
778
|
+
* }
|
|
779
|
+
* ```
|
|
780
|
+
*/
|
|
781
|
+
declare class ValibotAdapter<T> implements ValidatorAdapter<T> {
|
|
782
|
+
private schema;
|
|
783
|
+
private safeParseFunc;
|
|
784
|
+
constructor(schema: ValibotSchema<T>, safeParse: (schema: ValibotSchema<T>, data: unknown) => SafeParseResult<T>);
|
|
785
|
+
validate(data: unknown, config?: ValidationConfig): Promise<ValidationResult<T>>;
|
|
786
|
+
validateSync(data: unknown, config?: ValidationConfig): ValidationResult<T>;
|
|
787
|
+
private formatValibotErrors;
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
interface ArkTypeSchema<T = unknown> {
|
|
791
|
+
(data: unknown): T | ArkTypeErrors;
|
|
792
|
+
}
|
|
793
|
+
interface ArkTypeErrors {
|
|
794
|
+
summary: string;
|
|
795
|
+
errors?: Array<{
|
|
796
|
+
path: string[];
|
|
797
|
+
message: string;
|
|
798
|
+
code?: string;
|
|
799
|
+
}>;
|
|
800
|
+
[Symbol.iterator]?: () => Iterator<{
|
|
801
|
+
path: string[];
|
|
802
|
+
message: string;
|
|
803
|
+
}>;
|
|
804
|
+
}
|
|
805
|
+
/**
|
|
806
|
+
* Validator adapter for ArkType schemas
|
|
807
|
+
*
|
|
808
|
+
* @example
|
|
809
|
+
* ```typescript
|
|
810
|
+
* import { type } from 'arktype';
|
|
811
|
+
* import { ArkTypeAdapter } from 'next-request';
|
|
812
|
+
*
|
|
813
|
+
* const schema = type({
|
|
814
|
+
* email: 'string.email',
|
|
815
|
+
* name: 'string >= 2',
|
|
816
|
+
* });
|
|
817
|
+
*
|
|
818
|
+
* class MyRequest extends FormRequest<typeof schema.infer> {
|
|
819
|
+
* rules() {
|
|
820
|
+
* return new ArkTypeAdapter(schema);
|
|
821
|
+
* }
|
|
822
|
+
* }
|
|
823
|
+
* ```
|
|
824
|
+
*/
|
|
825
|
+
declare class ArkTypeAdapter<T> implements ValidatorAdapter<T> {
|
|
826
|
+
private schema;
|
|
827
|
+
constructor(schema: ArkTypeSchema<T>);
|
|
828
|
+
validate(data: unknown, config?: ValidationConfig): Promise<ValidationResult<T>>;
|
|
829
|
+
validateSync(data: unknown, config?: ValidationConfig): ValidationResult<T>;
|
|
830
|
+
private formatArkTypeErrors;
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
/**
|
|
834
|
+
* Options for file validation
|
|
835
|
+
*/
|
|
836
|
+
interface FormFileOptions {
|
|
837
|
+
/**
|
|
838
|
+
* Maximum file size. Can be a number (bytes) or a string like "5mb"
|
|
839
|
+
*/
|
|
840
|
+
maxSize?: string | number;
|
|
841
|
+
/**
|
|
842
|
+
* Minimum file size. Can be a number (bytes) or a string like "1kb"
|
|
843
|
+
*/
|
|
844
|
+
minSize?: string | number;
|
|
845
|
+
/**
|
|
846
|
+
* Allowed MIME types. Supports wildcards like "image/*"
|
|
847
|
+
* @example ['image/*', 'application/pdf']
|
|
848
|
+
*/
|
|
849
|
+
types?: string[];
|
|
850
|
+
/**
|
|
851
|
+
* Allowed file extensions (without the dot)
|
|
852
|
+
* @example ['jpg', 'png', 'pdf']
|
|
853
|
+
*/
|
|
854
|
+
extensions?: string[];
|
|
855
|
+
/**
|
|
856
|
+
* Whether the file is required
|
|
857
|
+
* @default true
|
|
858
|
+
*/
|
|
859
|
+
required?: boolean;
|
|
860
|
+
}
|
|
861
|
+
/**
|
|
862
|
+
* Validated file data returned after successful validation
|
|
863
|
+
*/
|
|
864
|
+
interface ValidatedFile {
|
|
865
|
+
/** Original file name */
|
|
866
|
+
name: string;
|
|
867
|
+
/** File size in bytes */
|
|
868
|
+
size: number;
|
|
869
|
+
/** MIME type */
|
|
870
|
+
type: string;
|
|
871
|
+
/** The File object (browser) or Blob */
|
|
872
|
+
file: File | Blob;
|
|
873
|
+
/** File extension (lowercase, without dot) */
|
|
874
|
+
extension: string;
|
|
875
|
+
/** Get file as ArrayBuffer */
|
|
876
|
+
arrayBuffer(): Promise<ArrayBuffer>;
|
|
877
|
+
/** Get file as text */
|
|
878
|
+
text(): Promise<string>;
|
|
879
|
+
/** Get file as readable stream */
|
|
880
|
+
stream(): ReadableStream<Uint8Array>;
|
|
881
|
+
}
|
|
882
|
+
/**
|
|
883
|
+
* Create a Zod schema for file validation with common file constraints.
|
|
884
|
+
*
|
|
885
|
+
* @example
|
|
886
|
+
* ```typescript
|
|
887
|
+
* import { formFile } from 'next-request';
|
|
888
|
+
* import { z } from 'zod';
|
|
889
|
+
*
|
|
890
|
+
* const schema = z.object({
|
|
891
|
+
* avatar: formFile({ maxSize: '5mb', types: ['image/*'] }),
|
|
892
|
+
* document: formFile({ maxSize: '10mb', types: ['application/pdf'] }),
|
|
893
|
+
* attachment: formFile({ maxSize: '20mb', extensions: ['pdf', 'doc', 'docx'] }),
|
|
894
|
+
* });
|
|
895
|
+
* ```
|
|
896
|
+
*/
|
|
897
|
+
declare function formFile(options?: FormFileOptions): z.ZodType<ValidatedFile>;
|
|
898
|
+
/**
|
|
899
|
+
* Create a Zod schema for multiple file uploads
|
|
900
|
+
*
|
|
901
|
+
* @example
|
|
902
|
+
* ```typescript
|
|
903
|
+
* import { formFiles } from 'next-request';
|
|
904
|
+
* import { z } from 'zod';
|
|
905
|
+
*
|
|
906
|
+
* const schema = z.object({
|
|
907
|
+
* images: formFiles({ maxSize: '5mb', types: ['image/*'], maxFiles: 5 }),
|
|
908
|
+
* });
|
|
909
|
+
* ```
|
|
910
|
+
*/
|
|
911
|
+
declare function formFiles(options?: FormFileOptions & {
|
|
912
|
+
/** Minimum number of files */
|
|
913
|
+
minFiles?: number;
|
|
914
|
+
/** Maximum number of files */
|
|
915
|
+
maxFiles?: number;
|
|
916
|
+
}): z.ZodType<ValidatedFile[]>;
|
|
917
|
+
/**
|
|
918
|
+
* Type helper to extract the inferred type from a formFile schema
|
|
919
|
+
*/
|
|
920
|
+
type InferFormFile = ValidatedFile;
|
|
921
|
+
type InferFormFiles = ValidatedFile[];
|
|
922
|
+
|
|
923
|
+
/**
|
|
924
|
+
* Options for error formatting
|
|
925
|
+
*/
|
|
926
|
+
interface ErrorFormattingOptions {
|
|
927
|
+
/** Include field path in error messages */
|
|
928
|
+
includePath?: boolean;
|
|
929
|
+
/** Custom path separator for nested fields */
|
|
930
|
+
pathSeparator?: string;
|
|
931
|
+
/** Maximum number of errors per field */
|
|
932
|
+
maxErrorsPerField?: number;
|
|
933
|
+
/** Include array indices in paths */
|
|
934
|
+
includeArrayIndices?: boolean;
|
|
935
|
+
}
|
|
936
|
+
/**
|
|
937
|
+
* Structured error format for nested objects and arrays
|
|
938
|
+
*/
|
|
939
|
+
interface StructuredErrors {
|
|
940
|
+
/** Flat errors (field path → messages) */
|
|
941
|
+
flat: ValidationErrors;
|
|
942
|
+
/** Nested errors matching the original object structure */
|
|
943
|
+
nested: Record<string, unknown>;
|
|
944
|
+
/** Array of all error messages */
|
|
945
|
+
all: string[];
|
|
946
|
+
/** Count of total errors */
|
|
947
|
+
count: number;
|
|
948
|
+
/** Check if a specific field has errors */
|
|
949
|
+
has(field: string): boolean;
|
|
950
|
+
/** Get errors for a specific field */
|
|
951
|
+
get(field: string): string[];
|
|
952
|
+
/** Get first error for a specific field */
|
|
953
|
+
first(field: string): string | undefined;
|
|
954
|
+
}
|
|
955
|
+
/**
|
|
956
|
+
* Format validation errors with improved support for nested objects and arrays.
|
|
957
|
+
*
|
|
958
|
+
* @example
|
|
959
|
+
* ```typescript
|
|
960
|
+
* const errors = {
|
|
961
|
+
* 'user.email': ['Invalid email'],
|
|
962
|
+
* 'items.0.name': ['Name is required'],
|
|
963
|
+
* 'items.1.price': ['Price must be positive'],
|
|
964
|
+
* };
|
|
965
|
+
*
|
|
966
|
+
* const formatted = formatErrors(errors);
|
|
967
|
+
* // formatted.nested = {
|
|
968
|
+
* // user: { email: ['Invalid email'] },
|
|
969
|
+
* // items: [
|
|
970
|
+
* // { name: ['Name is required'] },
|
|
971
|
+
* // { price: ['Price must be positive'] },
|
|
972
|
+
* // ],
|
|
973
|
+
* // }
|
|
974
|
+
* ```
|
|
975
|
+
*/
|
|
976
|
+
declare function formatErrors(errors: ValidationErrors, options?: ErrorFormattingOptions): StructuredErrors;
|
|
977
|
+
/**
|
|
978
|
+
* Flatten nested errors into dot-notation paths
|
|
979
|
+
*
|
|
980
|
+
* @example
|
|
981
|
+
* ```typescript
|
|
982
|
+
* const nested = {
|
|
983
|
+
* user: { email: ['Invalid email'] },
|
|
984
|
+
* items: [
|
|
985
|
+
* { name: ['Name is required'] },
|
|
986
|
+
* ],
|
|
987
|
+
* };
|
|
988
|
+
*
|
|
989
|
+
* const flat = flattenErrors(nested);
|
|
990
|
+
* // { 'user.email': ['Invalid email'], 'items.0.name': ['Name is required'] }
|
|
991
|
+
* ```
|
|
992
|
+
*/
|
|
993
|
+
declare function flattenErrors(nested: Record<string, unknown>, options?: ErrorFormattingOptions): ValidationErrors;
|
|
994
|
+
/**
|
|
995
|
+
* Get human-readable error summary
|
|
996
|
+
*/
|
|
997
|
+
declare function summarizeErrors(errors: ValidationErrors): string;
|
|
998
|
+
/**
|
|
999
|
+
* Filter errors to only include specific fields
|
|
1000
|
+
*/
|
|
1001
|
+
declare function filterErrors(errors: ValidationErrors, fields: string[]): ValidationErrors;
|
|
1002
|
+
/**
|
|
1003
|
+
* Merge multiple error objects
|
|
1004
|
+
*/
|
|
1005
|
+
declare function mergeErrors(...errorSets: ValidationErrors[]): ValidationErrors;
|
|
1006
|
+
|
|
1007
|
+
/**
|
|
1008
|
+
* Result of a mock validation
|
|
1009
|
+
*/
|
|
1010
|
+
interface MockValidationResult<T> {
|
|
1011
|
+
/** Whether validation succeeded */
|
|
1012
|
+
success: boolean;
|
|
1013
|
+
/** Validated data (if successful) */
|
|
1014
|
+
data?: T;
|
|
1015
|
+
/** Validation errors (if failed) */
|
|
1016
|
+
errors?: ValidationErrors;
|
|
1017
|
+
/** Whether authorization was denied */
|
|
1018
|
+
unauthorized?: boolean;
|
|
1019
|
+
/** The FormRequest instance */
|
|
1020
|
+
instance: FormRequest<T>;
|
|
1021
|
+
}
|
|
1022
|
+
/**
|
|
1023
|
+
* Options for creating a mock request
|
|
1024
|
+
*/
|
|
1025
|
+
interface MockRequestOptions {
|
|
1026
|
+
/** HTTP method */
|
|
1027
|
+
method?: string;
|
|
1028
|
+
/** Request headers */
|
|
1029
|
+
headers?: Record<string, string>;
|
|
1030
|
+
/** Query parameters */
|
|
1031
|
+
query?: Record<string, string>;
|
|
1032
|
+
/** Route parameters */
|
|
1033
|
+
params?: Record<string, string>;
|
|
1034
|
+
/** Base URL for the request */
|
|
1035
|
+
baseUrl?: string;
|
|
1036
|
+
}
|
|
1037
|
+
/**
|
|
1038
|
+
* Create a mock Request object for testing
|
|
1039
|
+
*/
|
|
1040
|
+
declare function createMockRequest(body: unknown, options?: MockRequestOptions): Request;
|
|
1041
|
+
/**
|
|
1042
|
+
* Test a FormRequest with mock data
|
|
1043
|
+
*
|
|
1044
|
+
* @example
|
|
1045
|
+
* ```typescript
|
|
1046
|
+
* import { testFormRequest } from 'next-request/testing';
|
|
1047
|
+
*
|
|
1048
|
+
* describe('CreateUserRequest', () => {
|
|
1049
|
+
* it('should validate valid data', async () => {
|
|
1050
|
+
* const result = await testFormRequest(CreateUserRequest, {
|
|
1051
|
+
* email: 'user@example.com',
|
|
1052
|
+
* password: 'securepassword123',
|
|
1053
|
+
* });
|
|
1054
|
+
*
|
|
1055
|
+
* expect(result.success).toBe(true);
|
|
1056
|
+
* expect(result.data?.email).toBe('user@example.com');
|
|
1057
|
+
* });
|
|
1058
|
+
*
|
|
1059
|
+
* it('should reject invalid email', async () => {
|
|
1060
|
+
* const result = await testFormRequest(CreateUserRequest, {
|
|
1061
|
+
* email: 'invalid-email',
|
|
1062
|
+
* password: 'securepassword123',
|
|
1063
|
+
* });
|
|
1064
|
+
*
|
|
1065
|
+
* expect(result.success).toBe(false);
|
|
1066
|
+
* expect(result.errors?.email).toBeDefined();
|
|
1067
|
+
* });
|
|
1068
|
+
* });
|
|
1069
|
+
* ```
|
|
1070
|
+
*/
|
|
1071
|
+
declare function testFormRequest<T>(RequestClass: new () => FormRequest<T>, body: unknown, options?: MockRequestOptions): Promise<MockValidationResult<T>>;
|
|
1072
|
+
/**
|
|
1073
|
+
* Add static mock method to FormRequest class
|
|
1074
|
+
* This allows calling CreateUserRequest.mock({ ... }) directly
|
|
1075
|
+
*
|
|
1076
|
+
* @example
|
|
1077
|
+
* ```typescript
|
|
1078
|
+
* import { addMockMethod } from 'next-request/testing';
|
|
1079
|
+
*
|
|
1080
|
+
* // Add mock method to a specific request class
|
|
1081
|
+
* addMockMethod(CreateUserRequest);
|
|
1082
|
+
*
|
|
1083
|
+
* // Now you can use it directly
|
|
1084
|
+
* const result = await CreateUserRequest.mock({ email: 'invalid' });
|
|
1085
|
+
* expect(result.errors?.email).toBeDefined();
|
|
1086
|
+
* ```
|
|
1087
|
+
*/
|
|
1088
|
+
declare function addMockMethod<T>(RequestClass: new () => FormRequest<T>): void;
|
|
1089
|
+
/**
|
|
1090
|
+
* Type helper for FormRequest classes with mock method
|
|
1091
|
+
*/
|
|
1092
|
+
interface MockableFormRequest<T> {
|
|
1093
|
+
new (): FormRequest<T>;
|
|
1094
|
+
mock(body: unknown, options?: MockRequestOptions): Promise<MockValidationResult<T>>;
|
|
1095
|
+
}
|
|
1096
|
+
/**
|
|
1097
|
+
* Assert that validation passes
|
|
1098
|
+
*/
|
|
1099
|
+
declare function expectValid<T>(result: MockValidationResult<T>): asserts result is MockValidationResult<T> & {
|
|
1100
|
+
success: true;
|
|
1101
|
+
data: T;
|
|
1102
|
+
};
|
|
1103
|
+
/**
|
|
1104
|
+
* Assert that validation fails
|
|
1105
|
+
*/
|
|
1106
|
+
declare function expectInvalid<T>(result: MockValidationResult<T>): asserts result is MockValidationResult<T> & {
|
|
1107
|
+
success: false;
|
|
1108
|
+
errors: ValidationErrors;
|
|
1109
|
+
};
|
|
1110
|
+
/**
|
|
1111
|
+
* Assert that a specific field has errors
|
|
1112
|
+
*/
|
|
1113
|
+
declare function expectFieldError<T>(result: MockValidationResult<T>, field: string, messagePattern?: string | RegExp): void;
|
|
1114
|
+
|
|
1115
|
+
/**
|
|
1116
|
+
* Create an authenticated request base class
|
|
1117
|
+
*
|
|
1118
|
+
* @example
|
|
1119
|
+
* ```typescript
|
|
1120
|
+
* const AuthenticatedRequest = createAuthenticatedRequest(async (request) => {
|
|
1121
|
+
* const token = request.header('authorization');
|
|
1122
|
+
* return !!token && await verifyToken(token);
|
|
1123
|
+
* });
|
|
1124
|
+
*
|
|
1125
|
+
* class MyRequest extends AuthenticatedRequest<MyData> {
|
|
1126
|
+
* rules() { return new ZodAdapter(schema); }
|
|
1127
|
+
* }
|
|
1128
|
+
* ```
|
|
1129
|
+
*/
|
|
1130
|
+
declare function createAuthenticatedRequest<TBase = unknown>(authorizeFn: (request: FormRequest<TBase>) => boolean | Promise<boolean>): abstract new <T>() => FormRequest<T>;
|
|
1131
|
+
/**
|
|
1132
|
+
* Compose multiple authorization checks
|
|
1133
|
+
*
|
|
1134
|
+
* @example
|
|
1135
|
+
* ```typescript
|
|
1136
|
+
* const authorize = composeAuthorization(
|
|
1137
|
+
* isAuthenticated,
|
|
1138
|
+
* hasRole('admin'),
|
|
1139
|
+
* hasPermission('products.create')
|
|
1140
|
+
* );
|
|
1141
|
+
*
|
|
1142
|
+
* class CreateProductRequest extends FormRequest<CreateProductData> {
|
|
1143
|
+
* authorize = authorize;
|
|
1144
|
+
* rules() { return new ZodAdapter(schema); }
|
|
1145
|
+
* }
|
|
1146
|
+
* ```
|
|
1147
|
+
*/
|
|
1148
|
+
declare function composeAuthorization(...checks: Array<(request: FormRequest<unknown>) => boolean | Promise<boolean>>): (this: FormRequest<unknown>) => Promise<boolean>;
|
|
1149
|
+
/**
|
|
1150
|
+
* Common authorization helpers
|
|
1151
|
+
*/
|
|
1152
|
+
declare const authHelpers: {
|
|
1153
|
+
/**
|
|
1154
|
+
* Check if request has a specific header
|
|
1155
|
+
*/
|
|
1156
|
+
hasHeader: (headerName: string) => (request: FormRequest<unknown>) => boolean;
|
|
1157
|
+
/**
|
|
1158
|
+
* Check if request has authorization header
|
|
1159
|
+
*/
|
|
1160
|
+
isAuthenticated: (request: FormRequest<unknown>) => boolean;
|
|
1161
|
+
/**
|
|
1162
|
+
* Check if request has a bearer token
|
|
1163
|
+
*/
|
|
1164
|
+
hasBearerToken: (request: FormRequest<unknown>) => boolean;
|
|
1165
|
+
/**
|
|
1166
|
+
* Extract bearer token from request
|
|
1167
|
+
*/
|
|
1168
|
+
getBearerToken: (request: FormRequest<unknown>) => string | null;
|
|
1169
|
+
/**
|
|
1170
|
+
* Check if request has API key
|
|
1171
|
+
*/
|
|
1172
|
+
hasApiKey: (headerName?: string) => (request: FormRequest<unknown>) => boolean;
|
|
1173
|
+
};
|
|
1174
|
+
/**
|
|
1175
|
+
* Lifecycle hook composition utilities
|
|
1176
|
+
*/
|
|
1177
|
+
declare const hookHelpers: {
|
|
1178
|
+
/**
|
|
1179
|
+
* Compose multiple beforeValidation hooks
|
|
1180
|
+
*/
|
|
1181
|
+
beforeValidation: (...hooks: Array<(this: FormRequest<unknown>) => void | Promise<void>>) => (this: FormRequest<unknown>) => Promise<void>;
|
|
1182
|
+
/**
|
|
1183
|
+
* Compose multiple afterValidation hooks
|
|
1184
|
+
*/
|
|
1185
|
+
afterValidation: <T>(...hooks: Array<(this: FormRequest<T>, data: T) => void | Promise<void>>) => (this: FormRequest<T>, data: T) => Promise<void>;
|
|
1186
|
+
/**
|
|
1187
|
+
* Common beforeValidation transformations
|
|
1188
|
+
*/
|
|
1189
|
+
transforms: {
|
|
1190
|
+
/** Trim all string values */
|
|
1191
|
+
trimStrings: (this: FormRequest<unknown>) => void;
|
|
1192
|
+
/** Lowercase specific fields */
|
|
1193
|
+
lowercase: (...fields: string[]) => (this: FormRequest<unknown>) => void;
|
|
1194
|
+
/** Uppercase specific fields */
|
|
1195
|
+
uppercase: (...fields: string[]) => (this: FormRequest<unknown>) => void;
|
|
1196
|
+
};
|
|
1197
|
+
};
|
|
364
1198
|
|
|
365
|
-
export { AuthorizationError, FormRequest, SupportedRequest, ValidationError, ValidationErrors, ValidatorAdapter, createAppRouterWrapper, createPagesRouterWrapper, withApiRequest, withRequest };
|
|
1199
|
+
export { ArkTypeAdapter, AuthorizationError, type CoercionOptions, type ErrorFormattingOptions, type FormFileOptions, FormRequest, type InferFormFile, type InferFormFiles, MemoryRateLimitStore, type MockRequestOptions, type MockValidationResult, type MockableFormRequest, type RateLimitConfig, RateLimitError, type RateLimitResult, type RateLimitState, type RateLimitStore, type StructuredErrors, SupportedRequest, ValibotAdapter, type ValidatedFile, ValidationConfig, ValidationError, ValidationErrors, ValidationResult, ValidatorAdapter, YupAdapter, addMockMethod, authHelpers, checkRateLimit, coerceFormData, coercionPresets, composeAuthorization, createAppRouterWrapper, createAuthenticatedRequest, createMockRequest, createPagesRouterWrapper, expectFieldError, expectInvalid, expectValid, filterErrors, flattenErrors, formFile, formFiles, formatErrors, hookHelpers, mergeErrors, rateLimit, setDefaultRateLimitStore, summarizeErrors, testFormRequest, withApiRequest, withApiSchema, withRequest, withSchema, zodCoerce };
|