next-form-request 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +294 -0
- package/dist/ZodAdapter-D7D3Sc-a.d.mts +95 -0
- package/dist/ZodAdapter-D7D3Sc-a.d.ts +95 -0
- package/dist/adapters/validators/ZodAdapter.d.mts +3 -0
- package/dist/adapters/validators/ZodAdapter.d.ts +3 -0
- package/dist/adapters/validators/ZodAdapter.js +56 -0
- package/dist/adapters/validators/ZodAdapter.js.map +1 -0
- package/dist/adapters/validators/ZodAdapter.mjs +54 -0
- package/dist/adapters/validators/ZodAdapter.mjs.map +1 -0
- package/dist/index.d.mts +365 -0
- package/dist/index.d.ts +365 -0
- package/dist/index.js +497 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +486 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +60 -0
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,486 @@
|
|
|
1
|
+
// src/core/types.ts
|
|
2
|
+
function isAppRouterRequest(request) {
|
|
3
|
+
return "headers" in request && request.headers instanceof Headers;
|
|
4
|
+
}
|
|
5
|
+
function isPagesRouterRequest(request) {
|
|
6
|
+
return "query" in request && !("headers" in request && request.headers instanceof Headers);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
// src/core/errors.ts
|
|
10
|
+
var ValidationError = class _ValidationError extends Error {
|
|
11
|
+
constructor(errors) {
|
|
12
|
+
const firstError = Object.values(errors)[0]?.[0] ?? "Validation failed";
|
|
13
|
+
super(firstError);
|
|
14
|
+
this.name = "ValidationError";
|
|
15
|
+
this.errors = errors;
|
|
16
|
+
if (Error.captureStackTrace) {
|
|
17
|
+
Error.captureStackTrace(this, _ValidationError);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Get all error messages as a flat array
|
|
22
|
+
*/
|
|
23
|
+
getAllMessages() {
|
|
24
|
+
return Object.values(this.errors).flat();
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Get errors for a specific field
|
|
28
|
+
*/
|
|
29
|
+
getFieldErrors(field) {
|
|
30
|
+
return this.errors[field] ?? [];
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Check if a specific field has errors
|
|
34
|
+
*/
|
|
35
|
+
hasFieldError(field) {
|
|
36
|
+
return field in this.errors && this.errors[field].length > 0;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Convert to JSON-serializable object
|
|
40
|
+
*/
|
|
41
|
+
toJSON() {
|
|
42
|
+
return {
|
|
43
|
+
name: this.name,
|
|
44
|
+
message: this.message,
|
|
45
|
+
errors: this.errors
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
var AuthorizationError = class _AuthorizationError extends Error {
|
|
50
|
+
constructor(message = "Unauthorized") {
|
|
51
|
+
super(message);
|
|
52
|
+
this.name = "AuthorizationError";
|
|
53
|
+
if (Error.captureStackTrace) {
|
|
54
|
+
Error.captureStackTrace(this, _AuthorizationError);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
toJSON() {
|
|
58
|
+
return {
|
|
59
|
+
name: this.name,
|
|
60
|
+
message: this.message
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
// src/core/FormRequest.ts
|
|
66
|
+
var FormRequest = class {
|
|
67
|
+
constructor() {
|
|
68
|
+
/**
|
|
69
|
+
* Parsed request body (mutable for beforeValidation hook)
|
|
70
|
+
*/
|
|
71
|
+
this.body = {};
|
|
72
|
+
/**
|
|
73
|
+
* Query parameters from the URL
|
|
74
|
+
*/
|
|
75
|
+
this.query = {};
|
|
76
|
+
/**
|
|
77
|
+
* Route parameters (e.g., /users/[id])
|
|
78
|
+
*/
|
|
79
|
+
this.params = {};
|
|
80
|
+
/**
|
|
81
|
+
* Request headers
|
|
82
|
+
*/
|
|
83
|
+
this.headers = {};
|
|
84
|
+
/**
|
|
85
|
+
* Validated data (populated after successful validation)
|
|
86
|
+
*/
|
|
87
|
+
this._validated = null;
|
|
88
|
+
/**
|
|
89
|
+
* Partial validated data (fields that passed validation)
|
|
90
|
+
*/
|
|
91
|
+
this._safe = {};
|
|
92
|
+
}
|
|
93
|
+
// ─────────────────────────────────────────────────────────────
|
|
94
|
+
// LIFECYCLE HOOKS (can be overridden)
|
|
95
|
+
// ─────────────────────────────────────────────────────────────
|
|
96
|
+
/**
|
|
97
|
+
* Determine if the user is authorized to make this request.
|
|
98
|
+
* Override this method to add authorization logic.
|
|
99
|
+
*
|
|
100
|
+
* @returns true if authorized, false otherwise
|
|
101
|
+
*/
|
|
102
|
+
authorize() {
|
|
103
|
+
return true;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Called before validation runs.
|
|
107
|
+
* Use this to normalize or transform input data.
|
|
108
|
+
*
|
|
109
|
+
* @example
|
|
110
|
+
* ```typescript
|
|
111
|
+
* beforeValidation() {
|
|
112
|
+
* this.body.email = this.body.email?.toLowerCase().trim();
|
|
113
|
+
* }
|
|
114
|
+
* ```
|
|
115
|
+
*/
|
|
116
|
+
beforeValidation() {
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Called after validation succeeds.
|
|
120
|
+
* Use this for logging, analytics, or post-processing.
|
|
121
|
+
*
|
|
122
|
+
* @param data The validated data
|
|
123
|
+
*/
|
|
124
|
+
afterValidation(_data) {
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Called when validation fails.
|
|
128
|
+
* Use this for logging or custom error handling.
|
|
129
|
+
*
|
|
130
|
+
* @param errors The validation errors
|
|
131
|
+
*/
|
|
132
|
+
onValidationFailed(_errors) {
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Called when authorization fails.
|
|
136
|
+
* Use this for logging or custom error handling.
|
|
137
|
+
*/
|
|
138
|
+
onAuthorizationFailed() {
|
|
139
|
+
}
|
|
140
|
+
// ─────────────────────────────────────────────────────────────
|
|
141
|
+
// CUSTOM MESSAGES (can be overridden)
|
|
142
|
+
// ─────────────────────────────────────────────────────────────
|
|
143
|
+
/**
|
|
144
|
+
* Custom validation error messages.
|
|
145
|
+
* Keys can be field names or "field.rule" patterns.
|
|
146
|
+
*
|
|
147
|
+
* @example
|
|
148
|
+
* ```typescript
|
|
149
|
+
* messages() {
|
|
150
|
+
* return {
|
|
151
|
+
* 'email.email': 'Please provide a valid email address',
|
|
152
|
+
* 'password.min': 'Password must be at least 8 characters',
|
|
153
|
+
* };
|
|
154
|
+
* }
|
|
155
|
+
* ```
|
|
156
|
+
*/
|
|
157
|
+
messages() {
|
|
158
|
+
return {};
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Custom attribute names for error messages.
|
|
162
|
+
* Used to replace field names in error messages.
|
|
163
|
+
*
|
|
164
|
+
* @example
|
|
165
|
+
* ```typescript
|
|
166
|
+
* attributes() {
|
|
167
|
+
* return {
|
|
168
|
+
* 'email': 'email address',
|
|
169
|
+
* 'dob': 'date of birth',
|
|
170
|
+
* };
|
|
171
|
+
* }
|
|
172
|
+
* ```
|
|
173
|
+
*/
|
|
174
|
+
attributes() {
|
|
175
|
+
return {};
|
|
176
|
+
}
|
|
177
|
+
// ─────────────────────────────────────────────────────────────
|
|
178
|
+
// STATIC FACTORY METHODS
|
|
179
|
+
// ─────────────────────────────────────────────────────────────
|
|
180
|
+
/**
|
|
181
|
+
* Create a FormRequest instance from an App Router Request.
|
|
182
|
+
*
|
|
183
|
+
* @param request The incoming Request object
|
|
184
|
+
* @param params Route parameters (from params in route handler)
|
|
185
|
+
*/
|
|
186
|
+
static async fromAppRouter(request, params = {}) {
|
|
187
|
+
const instance = new this();
|
|
188
|
+
instance.request = request;
|
|
189
|
+
instance.params = params;
|
|
190
|
+
instance.headers = {};
|
|
191
|
+
request.headers.forEach((value, key) => {
|
|
192
|
+
instance.headers[key] = value;
|
|
193
|
+
});
|
|
194
|
+
const url = new URL(request.url);
|
|
195
|
+
instance.query = {};
|
|
196
|
+
url.searchParams.forEach((value, key) => {
|
|
197
|
+
const existing = instance.query[key];
|
|
198
|
+
if (existing !== void 0) {
|
|
199
|
+
instance.query[key] = Array.isArray(existing) ? [...existing, value] : [existing, value];
|
|
200
|
+
} else {
|
|
201
|
+
instance.query[key] = value;
|
|
202
|
+
}
|
|
203
|
+
});
|
|
204
|
+
const method = request.method.toUpperCase();
|
|
205
|
+
if (["POST", "PUT", "PATCH", "DELETE"].includes(method)) {
|
|
206
|
+
try {
|
|
207
|
+
const contentType = request.headers.get("content-type") ?? "";
|
|
208
|
+
if (contentType.includes("application/json")) {
|
|
209
|
+
instance.body = await request.clone().json();
|
|
210
|
+
} else if (contentType.includes("application/x-www-form-urlencoded")) {
|
|
211
|
+
const text = await request.clone().text();
|
|
212
|
+
const formData = new URLSearchParams(text);
|
|
213
|
+
instance.body = Object.fromEntries(formData.entries());
|
|
214
|
+
} else if (contentType.includes("multipart/form-data")) {
|
|
215
|
+
const formData = await request.clone().formData();
|
|
216
|
+
instance.body = Object.fromEntries(formData.entries());
|
|
217
|
+
}
|
|
218
|
+
} catch {
|
|
219
|
+
instance.body = {};
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
return instance;
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* Create a FormRequest instance from a Pages Router NextApiRequest.
|
|
226
|
+
*
|
|
227
|
+
* @param request The incoming NextApiRequest object
|
|
228
|
+
* @param params Route parameters
|
|
229
|
+
*/
|
|
230
|
+
static async fromPagesRouter(request, params = {}) {
|
|
231
|
+
const instance = new this();
|
|
232
|
+
instance.request = request;
|
|
233
|
+
instance.params = { ...params, ...request.query };
|
|
234
|
+
instance.headers = request.headers;
|
|
235
|
+
instance.query = request.query;
|
|
236
|
+
instance.body = request.body ?? {};
|
|
237
|
+
return instance;
|
|
238
|
+
}
|
|
239
|
+
// ─────────────────────────────────────────────────────────────
|
|
240
|
+
// CORE VALIDATION METHODS
|
|
241
|
+
// ─────────────────────────────────────────────────────────────
|
|
242
|
+
/**
|
|
243
|
+
* Run validation and return the validated data.
|
|
244
|
+
* Throws ValidationError if validation fails.
|
|
245
|
+
* Throws AuthorizationError if authorization fails.
|
|
246
|
+
*
|
|
247
|
+
* @returns The validated data with full type inference
|
|
248
|
+
*/
|
|
249
|
+
async validate() {
|
|
250
|
+
const isAuthorized = await this.authorize();
|
|
251
|
+
if (!isAuthorized) {
|
|
252
|
+
await this.onAuthorizationFailed();
|
|
253
|
+
throw new AuthorizationError();
|
|
254
|
+
}
|
|
255
|
+
await this.beforeValidation();
|
|
256
|
+
const validator = this.rules();
|
|
257
|
+
const result = await validator.validate(this.getDataForValidation(), {
|
|
258
|
+
messages: this.messages(),
|
|
259
|
+
attributes: this.attributes()
|
|
260
|
+
});
|
|
261
|
+
if (!result.success) {
|
|
262
|
+
const errors = result.errors ?? { _error: ["Validation failed"] };
|
|
263
|
+
await this.onValidationFailed(errors);
|
|
264
|
+
throw new ValidationError(errors);
|
|
265
|
+
}
|
|
266
|
+
this._validated = result.data;
|
|
267
|
+
this._safe = result.data;
|
|
268
|
+
await this.afterValidation(this._validated);
|
|
269
|
+
return this._validated;
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* Get the validated data (after calling validate()).
|
|
273
|
+
* Throws if validate() hasn't been called successfully.
|
|
274
|
+
*/
|
|
275
|
+
validated() {
|
|
276
|
+
if (this._validated === null) {
|
|
277
|
+
throw new Error(
|
|
278
|
+
"Cannot access validated data before calling validate(). Call validate() first and handle any errors."
|
|
279
|
+
);
|
|
280
|
+
}
|
|
281
|
+
return this._validated;
|
|
282
|
+
}
|
|
283
|
+
/**
|
|
284
|
+
* Get only the fields that passed validation.
|
|
285
|
+
* Safe to call even if validation hasn't completed.
|
|
286
|
+
*/
|
|
287
|
+
safe() {
|
|
288
|
+
return { ...this._safe };
|
|
289
|
+
}
|
|
290
|
+
/**
|
|
291
|
+
* Get raw input data (body merged with query for GET requests).
|
|
292
|
+
*/
|
|
293
|
+
all() {
|
|
294
|
+
return { ...this.body };
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* Get a specific input value.
|
|
298
|
+
*/
|
|
299
|
+
input(key, defaultValue) {
|
|
300
|
+
const value = this.body[key] ?? this.query[key];
|
|
301
|
+
return value ?? defaultValue;
|
|
302
|
+
}
|
|
303
|
+
/**
|
|
304
|
+
* Check if input has a specific key.
|
|
305
|
+
*/
|
|
306
|
+
has(key) {
|
|
307
|
+
return key in this.body || key in this.query;
|
|
308
|
+
}
|
|
309
|
+
/**
|
|
310
|
+
* Get only specified keys from input.
|
|
311
|
+
*/
|
|
312
|
+
only(...keys) {
|
|
313
|
+
const result = {};
|
|
314
|
+
for (const key of keys) {
|
|
315
|
+
if (this.has(key)) {
|
|
316
|
+
result[key] = this.input(key);
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
return result;
|
|
320
|
+
}
|
|
321
|
+
/**
|
|
322
|
+
* Get all input except specified keys.
|
|
323
|
+
*/
|
|
324
|
+
except(...keys) {
|
|
325
|
+
const result = { ...this.body };
|
|
326
|
+
for (const key of keys) {
|
|
327
|
+
delete result[key];
|
|
328
|
+
}
|
|
329
|
+
return result;
|
|
330
|
+
}
|
|
331
|
+
/**
|
|
332
|
+
* Get the original request object.
|
|
333
|
+
*/
|
|
334
|
+
getRequest() {
|
|
335
|
+
return this.request;
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* Check if this is an App Router request.
|
|
339
|
+
*/
|
|
340
|
+
isAppRouter() {
|
|
341
|
+
return isAppRouterRequest(this.request);
|
|
342
|
+
}
|
|
343
|
+
/**
|
|
344
|
+
* Get a header value.
|
|
345
|
+
*/
|
|
346
|
+
header(name) {
|
|
347
|
+
const lowerName = name.toLowerCase();
|
|
348
|
+
if (isAppRouterRequest(this.request)) {
|
|
349
|
+
return this.request.headers.get(lowerName) ?? void 0;
|
|
350
|
+
}
|
|
351
|
+
return this.headers[lowerName];
|
|
352
|
+
}
|
|
353
|
+
/**
|
|
354
|
+
* Get a route parameter.
|
|
355
|
+
*/
|
|
356
|
+
param(name) {
|
|
357
|
+
return this.params[name];
|
|
358
|
+
}
|
|
359
|
+
// ─────────────────────────────────────────────────────────────
|
|
360
|
+
// PROTECTED HELPERS
|
|
361
|
+
// ─────────────────────────────────────────────────────────────
|
|
362
|
+
/**
|
|
363
|
+
* Get the data that will be validated.
|
|
364
|
+
* Override this to customize what data is passed to the validator.
|
|
365
|
+
*/
|
|
366
|
+
getDataForValidation() {
|
|
367
|
+
return this.body;
|
|
368
|
+
}
|
|
369
|
+
};
|
|
370
|
+
|
|
371
|
+
// src/middleware/withRequest.ts
|
|
372
|
+
function withRequest(RequestClass, handler) {
|
|
373
|
+
return async (request, context) => {
|
|
374
|
+
const params = context?.params ? context.params instanceof Promise ? await context.params : context.params : {};
|
|
375
|
+
const formRequest = await RequestClass.fromAppRouter(request, params);
|
|
376
|
+
const validated = await formRequest.validate();
|
|
377
|
+
return handler(validated, request, formRequest);
|
|
378
|
+
};
|
|
379
|
+
}
|
|
380
|
+
function withApiRequest(RequestClass, handler) {
|
|
381
|
+
return async (req, res) => {
|
|
382
|
+
const formRequest = await RequestClass.fromPagesRouter(req);
|
|
383
|
+
const validated = await formRequest.validate();
|
|
384
|
+
await handler(validated, req, res, formRequest);
|
|
385
|
+
};
|
|
386
|
+
}
|
|
387
|
+
function createAppRouterWrapper(options) {
|
|
388
|
+
return (RequestClass, handler) => {
|
|
389
|
+
return async (request, context) => {
|
|
390
|
+
try {
|
|
391
|
+
const params = context?.params ? context.params instanceof Promise ? await context.params : context.params : {};
|
|
392
|
+
const formRequest = await RequestClass.fromAppRouter(request, params);
|
|
393
|
+
const validated = await formRequest.validate();
|
|
394
|
+
return handler(validated, request, formRequest);
|
|
395
|
+
} catch (error) {
|
|
396
|
+
if (error instanceof ValidationError && options.onValidationError) {
|
|
397
|
+
return options.onValidationError(error);
|
|
398
|
+
}
|
|
399
|
+
if (error instanceof AuthorizationError && options.onAuthorizationError) {
|
|
400
|
+
return options.onAuthorizationError(error);
|
|
401
|
+
}
|
|
402
|
+
if (options.onError) {
|
|
403
|
+
return options.onError(error);
|
|
404
|
+
}
|
|
405
|
+
throw error;
|
|
406
|
+
}
|
|
407
|
+
};
|
|
408
|
+
};
|
|
409
|
+
}
|
|
410
|
+
function createPagesRouterWrapper(options) {
|
|
411
|
+
return (RequestClass, handler) => {
|
|
412
|
+
return async (req, res) => {
|
|
413
|
+
try {
|
|
414
|
+
const formRequest = await RequestClass.fromPagesRouter(req);
|
|
415
|
+
const validated = await formRequest.validate();
|
|
416
|
+
await handler(validated, req, res, formRequest);
|
|
417
|
+
} catch (error) {
|
|
418
|
+
if (error instanceof ValidationError && options.onValidationError) {
|
|
419
|
+
return options.onValidationError(error, req, res);
|
|
420
|
+
}
|
|
421
|
+
if (error instanceof AuthorizationError && options.onAuthorizationError) {
|
|
422
|
+
return options.onAuthorizationError(error, req, res);
|
|
423
|
+
}
|
|
424
|
+
if (options.onError) {
|
|
425
|
+
return options.onError(error, req, res);
|
|
426
|
+
}
|
|
427
|
+
throw error;
|
|
428
|
+
}
|
|
429
|
+
};
|
|
430
|
+
};
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
// src/adapters/validators/ZodAdapter.ts
|
|
434
|
+
var ZodAdapter = class {
|
|
435
|
+
constructor(schema) {
|
|
436
|
+
this.schema = schema;
|
|
437
|
+
}
|
|
438
|
+
async validate(data, config) {
|
|
439
|
+
return this.validateSync(data, config);
|
|
440
|
+
}
|
|
441
|
+
validateSync(data, config) {
|
|
442
|
+
const result = this.schema.safeParse(data);
|
|
443
|
+
if (result.success) {
|
|
444
|
+
return {
|
|
445
|
+
success: true,
|
|
446
|
+
data: result.data
|
|
447
|
+
};
|
|
448
|
+
}
|
|
449
|
+
const errors = this.formatZodErrors(result.error, config);
|
|
450
|
+
return {
|
|
451
|
+
success: false,
|
|
452
|
+
errors
|
|
453
|
+
};
|
|
454
|
+
}
|
|
455
|
+
formatZodErrors(error, config) {
|
|
456
|
+
const errors = {};
|
|
457
|
+
const customMessages = config?.messages ?? {};
|
|
458
|
+
const customAttributes = config?.attributes ?? {};
|
|
459
|
+
for (const issue of error.issues) {
|
|
460
|
+
const path = issue.path.join(".");
|
|
461
|
+
const fieldName = path || "_root";
|
|
462
|
+
const attributeName = customAttributes[fieldName] ?? fieldName;
|
|
463
|
+
const messageKey = `${fieldName}.${issue.code}`;
|
|
464
|
+
let message;
|
|
465
|
+
if (customMessages[messageKey]) {
|
|
466
|
+
message = customMessages[messageKey];
|
|
467
|
+
} else if (customMessages[fieldName]) {
|
|
468
|
+
message = customMessages[fieldName];
|
|
469
|
+
} else {
|
|
470
|
+
message = issue.message.replace(
|
|
471
|
+
new RegExp(`\\b${fieldName}\\b`, "gi"),
|
|
472
|
+
attributeName
|
|
473
|
+
);
|
|
474
|
+
}
|
|
475
|
+
if (!errors[fieldName]) {
|
|
476
|
+
errors[fieldName] = [];
|
|
477
|
+
}
|
|
478
|
+
errors[fieldName].push(message);
|
|
479
|
+
}
|
|
480
|
+
return errors;
|
|
481
|
+
}
|
|
482
|
+
};
|
|
483
|
+
|
|
484
|
+
export { AuthorizationError, FormRequest, ValidationError, ZodAdapter, createAppRouterWrapper, createPagesRouterWrapper, isAppRouterRequest, isPagesRouterRequest, withApiRequest, withRequest };
|
|
485
|
+
//# sourceMappingURL=index.mjs.map
|
|
486
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/core/types.ts","../src/core/errors.ts","../src/core/FormRequest.ts","../src/middleware/withRequest.ts","../src/adapters/validators/ZodAdapter.ts"],"names":[],"mappings":";AAyDO,SAAS,mBAAmB,OAAA,EAA+C;AAChF,EAAA,OAAO,SAAA,IAAa,OAAA,IAAW,OAAA,CAAQ,OAAA,YAAmB,OAAA;AAC5D;AAKO,SAAS,qBAAqB,OAAA,EAAsD;AACzF,EAAA,OAAO,WAAW,OAAA,IAAW,EAAE,SAAA,IAAa,OAAA,IAAW,QAAQ,OAAA,YAAmB,OAAA,CAAA;AACpF;;;AC7DO,IAAM,eAAA,GAAN,MAAM,gBAAA,SAAwB,KAAA,CAAM;AAAA,EAGzC,YAAY,MAAA,EAA0B;AACpC,IAAA,MAAM,UAAA,GAAa,OAAO,MAAA,CAAO,MAAM,EAAE,CAAC,CAAA,GAAI,CAAC,CAAA,IAAK,mBAAA;AACpD,IAAA,KAAA,CAAM,UAAU,CAAA;AAChB,IAAA,IAAA,CAAK,IAAA,GAAO,iBAAA;AACZ,IAAA,IAAA,CAAK,MAAA,GAAS,MAAA;AAGd,IAAA,IAAI,MAAM,iBAAA,EAAmB;AAC3B,MAAA,KAAA,CAAM,iBAAA,CAAkB,MAAM,gBAAe,CAAA;AAAA,IAC/C;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,cAAA,GAA2B;AACzB,IAAA,OAAO,MAAA,CAAO,MAAA,CAAO,IAAA,CAAK,MAAM,EAAE,IAAA,EAAK;AAAA,EACzC;AAAA;AAAA;AAAA;AAAA,EAKA,eAAe,KAAA,EAAyB;AACtC,IAAA,OAAO,IAAA,CAAK,MAAA,CAAO,KAAK,CAAA,IAAK,EAAC;AAAA,EAChC;AAAA;AAAA;AAAA;AAAA,EAKA,cAAc,KAAA,EAAwB;AACpC,IAAA,OAAO,SAAS,IAAA,CAAK,MAAA,IAAU,KAAK,MAAA,CAAO,KAAK,EAAE,MAAA,GAAS,CAAA;AAAA,EAC7D;AAAA;AAAA;AAAA;AAAA,EAKA,MAAA,GAAS;AACP,IAAA,OAAO;AAAA,MACL,MAAM,IAAA,CAAK,IAAA;AAAA,MACX,SAAS,IAAA,CAAK,OAAA;AAAA,MACd,QAAQ,IAAA,CAAK;AAAA,KACf;AAAA,EACF;AACF;AAKO,IAAM,kBAAA,GAAN,MAAM,mBAAA,SAA2B,KAAA,CAAM;AAAA,EAC5C,WAAA,CAAY,UAAU,cAAA,EAAgB;AACpC,IAAA,KAAA,CAAM,OAAO,CAAA;AACb,IAAA,IAAA,CAAK,IAAA,GAAO,oBAAA;AAEZ,IAAA,IAAI,MAAM,iBAAA,EAAmB;AAC3B,MAAA,KAAA,CAAM,iBAAA,CAAkB,MAAM,mBAAkB,CAAA;AAAA,IAClD;AAAA,EACF;AAAA,EAEA,MAAA,GAAS;AACP,IAAA,OAAO;AAAA,MACL,MAAM,IAAA,CAAK,IAAA;AAAA,MACX,SAAS,IAAA,CAAK;AAAA,KAChB;AAAA,EACF;AACF;;;ACjCO,IAAe,cAAf,MAAiD;AAAA,EAAjD,WAAA,GAAA;AASL;AAAA;AAAA;AAAA,IAAA,IAAA,CAAU,OAAgC,EAAC;AAK3C;AAAA;AAAA;AAAA,IAAA,IAAA,CAAU,QAAuD,EAAC;AAKlE;AAAA;AAAA;AAAA,IAAA,IAAA,CAAU,SAAiC,EAAC;AAK5C;AAAA;AAAA;AAAA,IAAA,IAAA,CAAU,UAAyD,EAAC;AAKpE;AAAA;AAAA;AAAA,IAAA,IAAA,CAAQ,UAAA,GAAgC,IAAA;AAKxC;AAAA;AAAA;AAAA,IAAA,IAAA,CAAQ,QAA6B,EAAC;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAsBtC,SAAA,GAAwC;AACtC,IAAA,OAAO,IAAA;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAaA,gBAAA,GAAyC;AAAA,EAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQ1C,gBAAgB,KAAA,EAAyC;AAAA,EAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQ1D,mBAAmB,OAAA,EAAiD;AAAA,EAAC;AAAA;AAAA;AAAA;AAAA;AAAA,EAMrE,qBAAA,GAA8C;AAAA,EAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAoB/C,QAAA,GAAmC;AACjC,IAAA,OAAO,EAAC;AAAA,EACV;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAgBA,UAAA,GAAqC;AACnC,IAAA,OAAO,EAAC;AAAA,EACV;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYA,aAAa,aAAA,CAEX,OAAA,EACA,MAAA,GAAiC,EAAC,EACtB;AACZ,IAAA,MAAM,QAAA,GAAW,IAAI,IAAA,EAAK;AAC1B,IAAA,QAAA,CAAS,OAAA,GAAU,OAAA;AACnB,IAAA,QAAA,CAAS,MAAA,GAAS,MAAA;AAGlB,IAAA,QAAA,CAAS,UAAU,EAAC;AACpB,IAAA,OAAA,CAAQ,OAAA,CAAQ,OAAA,CAAQ,CAAC,KAAA,EAAO,GAAA,KAAQ;AACtC,MAAA,QAAA,CAAS,OAAA,CAAQ,GAAG,CAAA,GAAI,KAAA;AAAA,IAC1B,CAAC,CAAA;AAGD,IAAA,MAAM,GAAA,GAAM,IAAI,GAAA,CAAI,OAAA,CAAQ,GAAG,CAAA;AAC/B,IAAA,QAAA,CAAS,QAAQ,EAAC;AAClB,IAAA,GAAA,CAAI,YAAA,CAAa,OAAA,CAAQ,CAAC,KAAA,EAAO,GAAA,KAAQ;AACvC,MAAA,MAAM,QAAA,GAAW,QAAA,CAAS,KAAA,CAAM,GAAG,CAAA;AACnC,MAAA,IAAI,aAAa,MAAA,EAAW;AAC1B,QAAA,QAAA,CAAS,KAAA,CAAM,GAAG,CAAA,GAAI,KAAA,CAAM,QAAQ,QAAQ,CAAA,GACxC,CAAC,GAAG,QAAA,EAAU,KAAK,CAAA,GACnB,CAAC,UAAU,KAAK,CAAA;AAAA,MACtB,CAAA,MAAO;AACL,QAAA,QAAA,CAAS,KAAA,CAAM,GAAG,CAAA,GAAI,KAAA;AAAA,MACxB;AAAA,IACF,CAAC,CAAA;AAGD,IAAA,MAAM,MAAA,GAAS,OAAA,CAAQ,MAAA,CAAO,WAAA,EAAY;AAC1C,IAAA,IAAI,CAAC,QAAQ,KAAA,EAAO,OAAA,EAAS,QAAQ,CAAA,CAAE,QAAA,CAAS,MAAM,CAAA,EAAG;AACvD,MAAA,IAAI;AACF,QAAA,MAAM,WAAA,GAAc,OAAA,CAAQ,OAAA,CAAQ,GAAA,CAAI,cAAc,CAAA,IAAK,EAAA;AAC3D,QAAA,IAAI,WAAA,CAAY,QAAA,CAAS,kBAAkB,CAAA,EAAG;AAC5C,UAAA,QAAA,CAAS,IAAA,GAAO,MAAM,OAAA,CAAQ,KAAA,GAAQ,IAAA,EAAK;AAAA,QAC7C,CAAA,MAAA,IAAW,WAAA,CAAY,QAAA,CAAS,mCAAmC,CAAA,EAAG;AACpE,UAAA,MAAM,IAAA,GAAO,MAAM,OAAA,CAAQ,KAAA,GAAQ,IAAA,EAAK;AACxC,UAAA,MAAM,QAAA,GAAW,IAAI,eAAA,CAAgB,IAAI,CAAA;AACzC,UAAA,QAAA,CAAS,IAAA,GAAO,MAAA,CAAO,WAAA,CAAY,QAAA,CAAS,SAAS,CAAA;AAAA,QACvD,CAAA,MAAA,IAAW,WAAA,CAAY,QAAA,CAAS,qBAAqB,CAAA,EAAG;AACtD,UAAA,MAAM,QAAA,GAAW,MAAM,OAAA,CAAQ,KAAA,GAAQ,QAAA,EAAS;AAChD,UAAA,QAAA,CAAS,IAAA,GAAO,MAAA,CAAO,WAAA,CAAY,QAAA,CAAS,SAAS,CAAA;AAAA,QACvD;AAAA,MACF,CAAA,CAAA,MAAQ;AACN,QAAA,QAAA,CAAS,OAAO,EAAC;AAAA,MACnB;AAAA,IACF;AAEA,IAAA,OAAO,QAAA;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,aAAa,eAAA,CAEX,OAAA,EACA,MAAA,GAAiC,EAAC,EACtB;AACZ,IAAA,MAAM,QAAA,GAAW,IAAI,IAAA,EAAK;AAC1B,IAAA,QAAA,CAAS,OAAA,GAAU,OAAA;AACnB,IAAA,QAAA,CAAS,SAAS,EAAE,GAAG,MAAA,EAAQ,GAAI,QAAQ,KAAA,EAAiC;AAG5E,IAAA,QAAA,CAAS,UAAU,OAAA,CAAQ,OAAA;AAG3B,IAAA,QAAA,CAAS,QAAQ,OAAA,CAAQ,KAAA;AAGzB,IAAA,QAAA,CAAS,IAAA,GAAQ,OAAA,CAAQ,IAAA,IAAoC,EAAC;AAE9D,IAAA,OAAO,QAAA;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAaA,MAAM,QAAA,GAAgC;AAEpC,IAAA,MAAM,YAAA,GAAe,MAAM,IAAA,CAAK,SAAA,EAAU;AAC1C,IAAA,IAAI,CAAC,YAAA,EAAc;AACjB,MAAA,MAAM,KAAK,qBAAA,EAAsB;AACjC,MAAA,MAAM,IAAI,kBAAA,EAAmB;AAAA,IAC/B;AAGA,IAAA,MAAM,KAAK,gBAAA,EAAiB;AAG5B,IAAA,MAAM,SAAA,GAAY,KAAK,KAAA,EAAM;AAC7B,IAAA,MAAM,SAAS,MAAM,SAAA,CAAU,QAAA,CAAS,IAAA,CAAK,sBAAqB,EAAG;AAAA,MACnE,QAAA,EAAU,KAAK,QAAA,EAAS;AAAA,MACxB,UAAA,EAAY,KAAK,UAAA;AAAW,KAC7B,CAAA;AAED,IAAA,IAAI,CAAC,OAAO,OAAA,EAAS;AACnB,MAAA,MAAM,SAAS,MAAA,CAAO,MAAA,IAAU,EAAE,MAAA,EAAQ,CAAC,mBAAmB,CAAA,EAAE;AAChE,MAAA,MAAM,IAAA,CAAK,mBAAmB,MAAM,CAAA;AACpC,MAAA,MAAM,IAAI,gBAAgB,MAAM,CAAA;AAAA,IAClC;AAGA,IAAA,IAAA,CAAK,aAAa,MAAA,CAAO,IAAA;AACzB,IAAA,IAAA,CAAK,QAAQ,MAAA,CAAO,IAAA;AAGpB,IAAA,MAAM,IAAA,CAAK,eAAA,CAAgB,IAAA,CAAK,UAAU,CAAA;AAE1C,IAAA,OAAO,IAAA,CAAK,UAAA;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,SAAA,GAAwB;AACtB,IAAA,IAAI,IAAA,CAAK,eAAe,IAAA,EAAM;AAC5B,MAAA,MAAM,IAAI,KAAA;AAAA,QACR;AAAA,OAEF;AAAA,IACF;AACA,IAAA,OAAO,IAAA,CAAK,UAAA;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,IAAA,GAA4B;AAC1B,IAAA,OAAO,EAAE,GAAG,IAAA,CAAK,KAAA,EAAM;AAAA,EACzB;AAAA;AAAA;AAAA;AAAA,EAKA,GAAA,GAA+B;AAC7B,IAAA,OAAO,EAAE,GAAG,IAAA,CAAK,IAAA,EAAK;AAAA,EACxB;AAAA;AAAA;AAAA;AAAA,EAKA,KAAA,CAAmB,KAAa,YAAA,EAAiC;AAC/D,IAAA,MAAM,QAAQ,IAAA,CAAK,IAAA,CAAK,GAAG,CAAA,IAAK,IAAA,CAAK,MAAM,GAAG,CAAA;AAC9C,IAAA,OAAQ,KAAA,IAAe,YAAA;AAAA,EACzB;AAAA;AAAA;AAAA;AAAA,EAKA,IAAI,GAAA,EAAsB;AACxB,IAAA,OAAO,GAAA,IAAO,IAAA,CAAK,IAAA,IAAQ,GAAA,IAAO,IAAA,CAAK,KAAA;AAAA,EACzC;AAAA;AAAA;AAAA;AAAA,EAKA,QAA0B,IAAA,EAA6C;AACrE,IAAA,MAAM,SAAkC,EAAC;AACzC,IAAA,KAAA,MAAW,OAAO,IAAA,EAAM;AACtB,MAAA,IAAI,IAAA,CAAK,GAAA,CAAI,GAAG,CAAA,EAAG;AACjB,QAAA,MAAA,CAAO,GAAG,CAAA,GAAI,IAAA,CAAK,KAAA,CAAM,GAAG,CAAA;AAAA,MAC9B;AAAA,IACF;AACA,IAAA,OAAO,MAAA;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,UAAU,IAAA,EAAyC;AACjD,IAAA,MAAM,MAAA,GAAS,EAAE,GAAG,IAAA,CAAK,IAAA,EAAK;AAC9B,IAAA,KAAA,MAAW,OAAO,IAAA,EAAM;AACtB,MAAA,OAAO,OAAO,GAAG,CAAA;AAAA,IACnB;AACA,IAAA,OAAO,MAAA;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,UAAA,GAA+B;AAC7B,IAAA,OAAO,IAAA,CAAK,OAAA;AAAA,EACd;AAAA;AAAA;AAAA;AAAA,EAKA,WAAA,GAAuB;AACrB,IAAA,OAAO,kBAAA,CAAmB,KAAK,OAAO,CAAA;AAAA,EACxC;AAAA;AAAA;AAAA;AAAA,EAKA,OAAO,IAAA,EAA6C;AAClD,IAAA,MAAM,SAAA,GAAY,KAAK,WAAA,EAAY;AACnC,IAAA,IAAI,kBAAA,CAAmB,IAAA,CAAK,OAAO,CAAA,EAAG;AACpC,MAAA,OAAO,IAAA,CAAK,OAAA,CAAQ,OAAA,CAAQ,GAAA,CAAI,SAAS,CAAA,IAAK,MAAA;AAAA,IAChD;AACA,IAAA,OAAO,IAAA,CAAK,QAAQ,SAAS,CAAA;AAAA,EAC/B;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,IAAA,EAAkC;AACtC,IAAA,OAAO,IAAA,CAAK,OAAO,IAAI,CAAA;AAAA,EACzB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUU,oBAAA,GAAgC;AACxC,IAAA,OAAO,IAAA,CAAK,IAAA;AAAA,EACd;AACF;;;ACtWO,SAAS,WAAA,CACd,cACA,OAAA,EACqE;AACrE,EAAA,OAAO,OAAO,SAAkB,OAAA,KAA+B;AAE7D,IAAA,MAAM,MAAA,GAAS,OAAA,EAAS,MAAA,GACnB,OAAA,CAAQ,MAAA,YAAkB,OAAA,GAAU,MAAM,OAAA,CAAQ,MAAA,GAAS,OAAA,CAAQ,MAAA,GACpE,EAAC;AAEL,IAAA,MAAM,WAAA,GAAc,MAAM,YAAA,CAAa,aAAA,CAAc,SAAS,MAAM,CAAA;AACpE,IAAA,MAAM,SAAA,GAAY,MAAM,WAAA,CAAY,QAAA,EAAS;AAE7C,IAAA,OAAO,OAAA,CAAQ,SAAA,EAAW,OAAA,EAAS,WAAW,CAAA;AAAA,EAChD,CAAA;AACF;AAwBO,SAAS,cAAA,CACd,cACA,OAAA,EAC8D;AAC9D,EAAA,OAAO,OAAO,KAAqB,GAAA,KAAyB;AAC1D,IAAA,MAAM,WAAA,GAAc,MAAM,YAAA,CAAa,eAAA,CAAgB,GAAG,CAAA;AAC1D,IAAA,MAAM,SAAA,GAAY,MAAM,WAAA,CAAY,QAAA,EAAS;AAE7C,IAAA,MAAM,OAAA,CAAQ,SAAA,EAAW,GAAA,EAAK,GAAA,EAAK,WAAW,CAAA;AAAA,EAChD,CAAA;AACF;AAoBO,SAAS,uBAAuB,OAAA,EAOkC;AACvE,EAAA,OAAO,CACL,cACA,OAAA,KACG;AACH,IAAA,OAAO,OAAO,SAAkB,OAAA,KAA+B;AAC7D,MAAA,IAAI;AACF,QAAA,MAAM,MAAA,GAAS,OAAA,EAAS,MAAA,GACnB,OAAA,CAAQ,MAAA,YAAkB,OAAA,GAAU,MAAM,OAAA,CAAQ,MAAA,GAAS,OAAA,CAAQ,MAAA,GACpE,EAAC;AAEL,QAAA,MAAM,WAAA,GAAc,MAAM,YAAA,CAAa,aAAA,CAAc,SAAS,MAAM,CAAA;AACpE,QAAA,MAAM,SAAA,GAAY,MAAM,WAAA,CAAY,QAAA,EAAS;AAE7C,QAAA,OAAO,OAAA,CAAQ,SAAA,EAAW,OAAA,EAAS,WAAW,CAAA;AAAA,MAChD,SAAS,KAAA,EAAO;AACd,QAAA,IAAI,KAAA,YAAiB,eAAA,IAAmB,OAAA,CAAQ,iBAAA,EAAmB;AACjE,UAAA,OAAO,OAAA,CAAQ,kBAAkB,KAAK,CAAA;AAAA,QACxC;AACA,QAAA,IAAI,KAAA,YAAiB,kBAAA,IAAsB,OAAA,CAAQ,oBAAA,EAAsB;AACvE,UAAA,OAAO,OAAA,CAAQ,qBAAqB,KAAK,CAAA;AAAA,QAC3C;AACA,QAAA,IAAI,QAAQ,OAAA,EAAS;AACnB,UAAA,OAAO,OAAA,CAAQ,QAAQ,KAAK,CAAA;AAAA,QAC9B;AACA,QAAA,MAAM,KAAA;AAAA,MACR;AAAA,IACF,CAAA;AAAA,EACF,CAAA;AACF;AAoBO,SAAS,yBAAyB,OAAA,EAmByB;AAChE,EAAA,OAAO,CACL,cACA,OAAA,KACG;AACH,IAAA,OAAO,OAAO,KAAqB,GAAA,KAAyB;AAC1D,MAAA,IAAI;AACF,QAAA,MAAM,WAAA,GAAc,MAAM,YAAA,CAAa,eAAA,CAAgB,GAAG,CAAA;AAC1D,QAAA,MAAM,SAAA,GAAY,MAAM,WAAA,CAAY,QAAA,EAAS;AAE7C,QAAA,MAAM,OAAA,CAAQ,SAAA,EAAW,GAAA,EAAK,GAAA,EAAK,WAAW,CAAA;AAAA,MAChD,SAAS,KAAA,EAAO;AACd,QAAA,IAAI,KAAA,YAAiB,eAAA,IAAmB,OAAA,CAAQ,iBAAA,EAAmB;AACjE,UAAA,OAAO,OAAA,CAAQ,iBAAA,CAAkB,KAAA,EAAO,GAAA,EAAK,GAAG,CAAA;AAAA,QAClD;AACA,QAAA,IAAI,KAAA,YAAiB,kBAAA,IAAsB,OAAA,CAAQ,oBAAA,EAAsB;AACvE,UAAA,OAAO,OAAA,CAAQ,oBAAA,CAAqB,KAAA,EAAO,GAAA,EAAK,GAAG,CAAA;AAAA,QACrD;AACA,QAAA,IAAI,QAAQ,OAAA,EAAS;AACnB,UAAA,OAAO,OAAA,CAAQ,OAAA,CAAQ,KAAA,EAAO,GAAA,EAAK,GAAG,CAAA;AAAA,QACxC;AACA,QAAA,MAAM,KAAA;AAAA,MACR;AAAA,IACF,CAAA;AAAA,EACF,CAAA;AACF;;;AC/MO,IAAM,aAAN,MAAmD;AAAA,EACxD,YAAoB,MAAA,EAAsB;AAAtB,IAAA,IAAA,CAAA,MAAA,GAAA,MAAA;AAAA,EAAuB;AAAA,EAE3C,MAAM,QAAA,CAAS,IAAA,EAAe,MAAA,EAAyD;AACrF,IAAA,OAAO,IAAA,CAAK,YAAA,CAAa,IAAA,EAAM,MAAM,CAAA;AAAA,EACvC;AAAA,EAEA,YAAA,CAAa,MAAe,MAAA,EAAgD;AAC1E,IAAA,MAAM,MAAA,GAAS,IAAA,CAAK,MAAA,CAAO,SAAA,CAAU,IAAI,CAAA;AAEzC,IAAA,IAAI,OAAO,OAAA,EAAS;AAClB,MAAA,OAAO;AAAA,QACL,OAAA,EAAS,IAAA;AAAA,QACT,MAAM,MAAA,CAAO;AAAA,OACf;AAAA,IACF;AAEA,IAAA,MAAM,MAAA,GAAS,IAAA,CAAK,eAAA,CAAgB,MAAA,CAAO,OAAO,MAAM,CAAA;AAExD,IAAA,OAAO;AAAA,MACL,OAAA,EAAS,KAAA;AAAA,MACT;AAAA,KACF;AAAA,EACF;AAAA,EAEQ,eAAA,CAAgB,OAAiB,MAAA,EAA6C;AACpF,IAAA,MAAM,SAA2B,EAAC;AAClC,IAAA,MAAM,cAAA,GAAiB,MAAA,EAAQ,QAAA,IAAY,EAAC;AAC5C,IAAA,MAAM,gBAAA,GAAmB,MAAA,EAAQ,UAAA,IAAc,EAAC;AAEhD,IAAA,KAAA,MAAW,KAAA,IAAS,MAAM,MAAA,EAAQ;AAChC,MAAA,MAAM,IAAA,GAAO,KAAA,CAAM,IAAA,CAAK,IAAA,CAAK,GAAG,CAAA;AAChC,MAAA,MAAM,YAAY,IAAA,IAAQ,OAAA;AAG1B,MAAA,MAAM,aAAA,GAAgB,gBAAA,CAAiB,SAAS,CAAA,IAAK,SAAA;AAGrD,MAAA,MAAM,UAAA,GAAa,CAAA,EAAG,SAAS,CAAA,CAAA,EAAI,MAAM,IAAI,CAAA,CAAA;AAG7C,MAAA,IAAI,OAAA;AACJ,MAAA,IAAI,cAAA,CAAe,UAAU,CAAA,EAAG;AAC9B,QAAA,OAAA,GAAU,eAAe,UAAU,CAAA;AAAA,MACrC,CAAA,MAAA,IAAW,cAAA,CAAe,SAAS,CAAA,EAAG;AACpC,QAAA,OAAA,GAAU,eAAe,SAAS,CAAA;AAAA,MACpC,CAAA,MAAO;AAEL,QAAA,OAAA,GAAU,MAAM,OAAA,CAAQ,OAAA;AAAA,UACtB,IAAI,MAAA,CAAO,CAAA,GAAA,EAAM,SAAS,OAAO,IAAI,CAAA;AAAA,UACrC;AAAA,SACF;AAAA,MACF;AAEA,MAAA,IAAI,CAAC,MAAA,CAAO,SAAS,CAAA,EAAG;AACtB,QAAA,MAAA,CAAO,SAAS,IAAI,EAAC;AAAA,MACvB;AACA,MAAA,MAAA,CAAO,SAAS,CAAA,CAAE,IAAA,CAAK,OAAO,CAAA;AAAA,IAChC;AAEA,IAAA,OAAO,MAAA;AAAA,EACT;AACF","file":"index.mjs","sourcesContent":["import type { NextApiRequest } from 'next';\n\n/**\n * Validation errors mapped by field name to array of error messages\n */\nexport type ValidationErrors = Record<string, string[]>;\n\n/**\n * Result of a validation operation\n */\nexport interface ValidationResult<T> {\n success: boolean;\n data?: T;\n errors?: ValidationErrors;\n}\n\n/**\n * Configuration options for custom error messages and attribute names\n */\nexport interface ValidationConfig {\n messages?: Record<string, string>;\n attributes?: Record<string, string>;\n}\n\n/**\n * Adapter interface for validation libraries (Zod, Yup, etc.)\n */\nexport interface ValidatorAdapter<T> {\n /**\n * Validate data asynchronously\n */\n validate(data: unknown, config?: ValidationConfig): Promise<ValidationResult<T>>;\n\n /**\n * Validate data synchronously (optional)\n */\n validateSync?(data: unknown, config?: ValidationConfig): ValidationResult<T>;\n}\n\n/**\n * Data extracted from the incoming request\n */\nexport interface RequestData {\n body: unknown;\n query: Record<string, string | string[] | undefined>;\n params: Record<string, string>;\n headers: Record<string, string | string[] | undefined>;\n}\n\n/**\n * Union type for supported request types\n */\nexport type SupportedRequest = Request | NextApiRequest;\n\n/**\n * Type guard to check if request is App Router Request\n */\nexport function isAppRouterRequest(request: SupportedRequest): request is Request {\n return 'headers' in request && request.headers instanceof Headers;\n}\n\n/**\n * Type guard to check if request is Pages Router NextApiRequest\n */\nexport function isPagesRouterRequest(request: SupportedRequest): request is NextApiRequest {\n return 'query' in request && !('headers' in request && request.headers instanceof Headers);\n}\n\n/**\n * Constructor type for FormRequest classes\n */\nexport interface FormRequestConstructor<T, TValidated> {\n new (): T;\n fromAppRouter(request: Request, params?: Record<string, string>): Promise<T>;\n fromPagesRouter(request: NextApiRequest, params?: Record<string, string>): Promise<T>;\n}\n\n/**\n * Handler function for App Router withRequest wrapper\n */\nexport type AppRouterHandler<TValidated> = (\n validated: TValidated,\n request: Request\n) => Response | Promise<Response>;\n\n/**\n * Handler function for Pages Router withApiRequest wrapper\n */\nexport type PagesRouterHandler<TValidated> = (\n validated: TValidated,\n request: NextApiRequest,\n response: import('next').NextApiResponse\n) => void | Promise<void>;\n","import type { ValidationErrors } from './types';\n\n/**\n * Error thrown when request validation fails\n */\nexport class ValidationError extends Error {\n public readonly errors: ValidationErrors;\n\n constructor(errors: ValidationErrors) {\n const firstError = Object.values(errors)[0]?.[0] ?? 'Validation failed';\n super(firstError);\n this.name = 'ValidationError';\n this.errors = errors;\n\n // Maintains proper stack trace in V8 environments\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, ValidationError);\n }\n }\n\n /**\n * Get all error messages as a flat array\n */\n getAllMessages(): string[] {\n return Object.values(this.errors).flat();\n }\n\n /**\n * Get errors for a specific field\n */\n getFieldErrors(field: string): string[] {\n return this.errors[field] ?? [];\n }\n\n /**\n * Check if a specific field has errors\n */\n hasFieldError(field: string): boolean {\n return field in this.errors && this.errors[field].length > 0;\n }\n\n /**\n * Convert to JSON-serializable object\n */\n toJSON() {\n return {\n name: this.name,\n message: this.message,\n errors: this.errors,\n };\n }\n}\n\n/**\n * Error thrown when request authorization fails\n */\nexport class AuthorizationError extends Error {\n constructor(message = 'Unauthorized') {\n super(message);\n this.name = 'AuthorizationError';\n\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, AuthorizationError);\n }\n }\n\n toJSON() {\n return {\n name: this.name,\n message: this.message,\n };\n }\n}\n","import type { NextApiRequest } from 'next';\nimport type {\n ValidatorAdapter,\n RequestData,\n SupportedRequest,\n ValidationErrors,\n} from './types';\nimport { isAppRouterRequest } from './types';\nimport { ValidationError, AuthorizationError } from './errors';\n\n/**\n * Abstract base class for form request validation.\n * Inspired by Laravel's Form Request pattern.\n *\n * @example\n * ```typescript\n * import { FormRequest, ZodAdapter } from 'next-request';\n * import { z } from 'zod';\n *\n * const schema = z.object({\n * email: z.string().email(),\n * password: z.string().min(8),\n * });\n *\n * export class LoginRequest extends FormRequest<z.infer<typeof schema>> {\n * rules() {\n * return new ZodAdapter(schema);\n * }\n *\n * async authorize() {\n * return true; // Allow all requests\n * }\n *\n * beforeValidation() {\n * this.body.email = this.body.email?.toLowerCase().trim();\n * }\n * }\n * ```\n */\nexport abstract class FormRequest<TValidated = unknown> {\n /**\n * The original request object\n */\n protected request!: SupportedRequest;\n\n /**\n * Parsed request body (mutable for beforeValidation hook)\n */\n protected body: Record<string, unknown> = {};\n\n /**\n * Query parameters from the URL\n */\n protected query: Record<string, string | string[] | undefined> = {};\n\n /**\n * Route parameters (e.g., /users/[id])\n */\n protected params: Record<string, string> = {};\n\n /**\n * Request headers\n */\n protected headers: Record<string, string | string[] | undefined> = {};\n\n /**\n * Validated data (populated after successful validation)\n */\n private _validated: TValidated | null = null;\n\n /**\n * Partial validated data (fields that passed validation)\n */\n private _safe: Partial<TValidated> = {};\n\n // ─────────────────────────────────────────────────────────────\n // ABSTRACT METHODS (must be implemented by subclasses)\n // ─────────────────────────────────────────────────────────────\n\n /**\n * Define the validation rules for this request.\n * Return a ValidatorAdapter instance (e.g., ZodAdapter, YupAdapter).\n */\n abstract rules(): ValidatorAdapter<TValidated>;\n\n // ─────────────────────────────────────────────────────────────\n // LIFECYCLE HOOKS (can be overridden)\n // ─────────────────────────────────────────────────────────────\n\n /**\n * Determine if the user is authorized to make this request.\n * Override this method to add authorization logic.\n *\n * @returns true if authorized, false otherwise\n */\n authorize(): boolean | Promise<boolean> {\n return true;\n }\n\n /**\n * Called before validation runs.\n * Use this to normalize or transform input data.\n *\n * @example\n * ```typescript\n * beforeValidation() {\n * this.body.email = this.body.email?.toLowerCase().trim();\n * }\n * ```\n */\n beforeValidation(): void | Promise<void> {}\n\n /**\n * Called after validation succeeds.\n * Use this for logging, analytics, or post-processing.\n *\n * @param data The validated data\n */\n afterValidation(_data: TValidated): void | Promise<void> {}\n\n /**\n * Called when validation fails.\n * Use this for logging or custom error handling.\n *\n * @param errors The validation errors\n */\n onValidationFailed(_errors: ValidationErrors): void | Promise<void> {}\n\n /**\n * Called when authorization fails.\n * Use this for logging or custom error handling.\n */\n onAuthorizationFailed(): void | Promise<void> {}\n\n // ─────────────────────────────────────────────────────────────\n // CUSTOM MESSAGES (can be overridden)\n // ─────────────────────────────────────────────────────────────\n\n /**\n * Custom validation error messages.\n * Keys can be field names or \"field.rule\" patterns.\n *\n * @example\n * ```typescript\n * messages() {\n * return {\n * 'email.email': 'Please provide a valid email address',\n * 'password.min': 'Password must be at least 8 characters',\n * };\n * }\n * ```\n */\n messages(): Record<string, string> {\n return {};\n }\n\n /**\n * Custom attribute names for error messages.\n * Used to replace field names in error messages.\n *\n * @example\n * ```typescript\n * attributes() {\n * return {\n * 'email': 'email address',\n * 'dob': 'date of birth',\n * };\n * }\n * ```\n */\n attributes(): Record<string, string> {\n return {};\n }\n\n // ─────────────────────────────────────────────────────────────\n // STATIC FACTORY METHODS\n // ─────────────────────────────────────────────────────────────\n\n /**\n * Create a FormRequest instance from an App Router Request.\n *\n * @param request The incoming Request object\n * @param params Route parameters (from params in route handler)\n */\n static async fromAppRouter<T extends FormRequest>(\n this: new () => T,\n request: Request,\n params: Record<string, string> = {}\n ): Promise<T> {\n const instance = new this();\n instance.request = request;\n instance.params = params;\n\n // Parse headers\n instance.headers = {};\n request.headers.forEach((value, key) => {\n instance.headers[key] = value;\n });\n\n // Parse query from URL\n const url = new URL(request.url);\n instance.query = {};\n url.searchParams.forEach((value, key) => {\n const existing = instance.query[key];\n if (existing !== undefined) {\n instance.query[key] = Array.isArray(existing)\n ? [...existing, value]\n : [existing, value];\n } else {\n instance.query[key] = value;\n }\n });\n\n // Parse body for appropriate methods\n const method = request.method.toUpperCase();\n if (['POST', 'PUT', 'PATCH', 'DELETE'].includes(method)) {\n try {\n const contentType = request.headers.get('content-type') ?? '';\n if (contentType.includes('application/json')) {\n instance.body = await request.clone().json();\n } else if (contentType.includes('application/x-www-form-urlencoded')) {\n const text = await request.clone().text();\n const formData = new URLSearchParams(text);\n instance.body = Object.fromEntries(formData.entries());\n } else if (contentType.includes('multipart/form-data')) {\n const formData = await request.clone().formData();\n instance.body = Object.fromEntries(formData.entries());\n }\n } catch {\n instance.body = {};\n }\n }\n\n return instance;\n }\n\n /**\n * Create a FormRequest instance from a Pages Router NextApiRequest.\n *\n * @param request The incoming NextApiRequest object\n * @param params Route parameters\n */\n static async fromPagesRouter<T extends FormRequest>(\n this: new () => T,\n request: NextApiRequest,\n params: Record<string, string> = {}\n ): Promise<T> {\n const instance = new this();\n instance.request = request;\n instance.params = { ...params, ...(request.query as Record<string, string>) };\n\n // Parse headers\n instance.headers = request.headers as Record<string, string | string[] | undefined>;\n\n // Parse query - Next.js already parses this\n instance.query = request.query as Record<string, string | string[] | undefined>;\n\n // Body is already parsed by Next.js\n instance.body = (request.body as Record<string, unknown>) ?? {};\n\n return instance;\n }\n\n // ─────────────────────────────────────────────────────────────\n // CORE VALIDATION METHODS\n // ─────────────────────────────────────────────────────────────\n\n /**\n * Run validation and return the validated data.\n * Throws ValidationError if validation fails.\n * Throws AuthorizationError if authorization fails.\n *\n * @returns The validated data with full type inference\n */\n async validate(): Promise<TValidated> {\n // Check authorization first\n const isAuthorized = await this.authorize();\n if (!isAuthorized) {\n await this.onAuthorizationFailed();\n throw new AuthorizationError();\n }\n\n // Run beforeValidation hook\n await this.beforeValidation();\n\n // Get the validator and run validation\n const validator = this.rules();\n const result = await validator.validate(this.getDataForValidation(), {\n messages: this.messages(),\n attributes: this.attributes(),\n });\n\n if (!result.success) {\n const errors = result.errors ?? { _error: ['Validation failed'] };\n await this.onValidationFailed(errors);\n throw new ValidationError(errors);\n }\n\n // Store validated data\n this._validated = result.data!;\n this._safe = result.data as Partial<TValidated>;\n\n // Run afterValidation hook\n await this.afterValidation(this._validated);\n\n return this._validated;\n }\n\n /**\n * Get the validated data (after calling validate()).\n * Throws if validate() hasn't been called successfully.\n */\n validated(): TValidated {\n if (this._validated === null) {\n throw new Error(\n 'Cannot access validated data before calling validate(). ' +\n 'Call validate() first and handle any errors.'\n );\n }\n return this._validated;\n }\n\n /**\n * Get only the fields that passed validation.\n * Safe to call even if validation hasn't completed.\n */\n safe(): Partial<TValidated> {\n return { ...this._safe };\n }\n\n /**\n * Get raw input data (body merged with query for GET requests).\n */\n all(): Record<string, unknown> {\n return { ...this.body };\n }\n\n /**\n * Get a specific input value.\n */\n input<T = unknown>(key: string, defaultValue?: T): T | undefined {\n const value = this.body[key] ?? this.query[key];\n return (value as T) ?? defaultValue;\n }\n\n /**\n * Check if input has a specific key.\n */\n has(key: string): boolean {\n return key in this.body || key in this.query;\n }\n\n /**\n * Get only specified keys from input.\n */\n only<K extends string>(...keys: K[]): Pick<Record<string, unknown>, K> {\n const result: Record<string, unknown> = {};\n for (const key of keys) {\n if (this.has(key)) {\n result[key] = this.input(key);\n }\n }\n return result as Pick<Record<string, unknown>, K>;\n }\n\n /**\n * Get all input except specified keys.\n */\n except(...keys: string[]): Record<string, unknown> {\n const result = { ...this.body };\n for (const key of keys) {\n delete result[key];\n }\n return result;\n }\n\n /**\n * Get the original request object.\n */\n getRequest(): SupportedRequest {\n return this.request;\n }\n\n /**\n * Check if this is an App Router request.\n */\n isAppRouter(): boolean {\n return isAppRouterRequest(this.request);\n }\n\n /**\n * Get a header value.\n */\n header(name: string): string | string[] | undefined {\n const lowerName = name.toLowerCase();\n if (isAppRouterRequest(this.request)) {\n return this.request.headers.get(lowerName) ?? undefined;\n }\n return this.headers[lowerName];\n }\n\n /**\n * Get a route parameter.\n */\n param(name: string): string | undefined {\n return this.params[name];\n }\n\n // ─────────────────────────────────────────────────────────────\n // PROTECTED HELPERS\n // ─────────────────────────────────────────────────────────────\n\n /**\n * Get the data that will be validated.\n * Override this to customize what data is passed to the validator.\n */\n protected getDataForValidation(): unknown {\n return this.body;\n }\n}\n\nexport default FormRequest;\n","import type { NextApiRequest, NextApiResponse } from 'next';\nimport { FormRequest } from '../core/FormRequest';\nimport { ValidationError, AuthorizationError } from '../core/errors';\n\n/**\n * Type for FormRequest constructor\n */\ntype FormRequestClass<TValidated> = {\n new (): FormRequest<TValidated>;\n fromAppRouter(request: Request, params?: Record<string, string>): Promise<FormRequest<TValidated>>;\n fromPagesRouter(request: NextApiRequest, params?: Record<string, string>): Promise<FormRequest<TValidated>>;\n};\n\n/**\n * Handler function for App Router\n */\ntype AppRouterHandler<TValidated> = (\n validated: TValidated,\n request: Request,\n formRequest: FormRequest<TValidated>\n) => Response | Promise<Response>;\n\n/**\n * Handler function for Pages Router\n */\ntype PagesRouterHandler<TValidated> = (\n validated: TValidated,\n req: NextApiRequest,\n res: NextApiResponse,\n formRequest: FormRequest<TValidated>\n) => void | Promise<void>;\n\n/**\n * Context parameter for App Router (Next.js 13+)\n */\ninterface AppRouterContext {\n params?: Record<string, string> | Promise<Record<string, string>>;\n}\n\n/**\n * Wrap an App Router route handler with form request validation.\n *\n * The handler receives validated data and only executes if:\n * 1. Authorization passes (authorize() returns true)\n * 2. Validation passes (rules() validates successfully)\n *\n * Errors are thrown, not auto-handled - catch ValidationError and\n * AuthorizationError in your handler or error boundary.\n *\n * @example\n * ```typescript\n * // app/api/users/route.ts\n * import { withRequest } from 'next-request';\n * import { CreateUserRequest } from '@/requests/CreateUserRequest';\n *\n * export const POST = withRequest(CreateUserRequest, async (data, request) => {\n * const user = await db.users.create({ data });\n * return Response.json({ user }, { status: 201 });\n * });\n * ```\n */\nexport function withRequest<TValidated>(\n RequestClass: FormRequestClass<TValidated>,\n handler: AppRouterHandler<TValidated>\n): (request: Request, context?: AppRouterContext) => Promise<Response> {\n return async (request: Request, context?: AppRouterContext) => {\n // Resolve params (may be a Promise in Next.js 15+)\n const params = context?.params\n ? (context.params instanceof Promise ? await context.params : context.params)\n : {};\n\n const formRequest = await RequestClass.fromAppRouter(request, params);\n const validated = await formRequest.validate();\n\n return handler(validated, request, formRequest);\n };\n}\n\n/**\n * Wrap a Pages Router API handler with form request validation.\n *\n * The handler receives validated data and only executes if:\n * 1. Authorization passes (authorize() returns true)\n * 2. Validation passes (rules() validates successfully)\n *\n * Errors are thrown, not auto-handled - catch ValidationError and\n * AuthorizationError in your handler.\n *\n * @example\n * ```typescript\n * // pages/api/users.ts\n * import { withApiRequest } from 'next-request';\n * import { CreateUserRequest } from '@/requests/CreateUserRequest';\n *\n * export default withApiRequest(CreateUserRequest, async (data, req, res) => {\n * const user = await db.users.create({ data });\n * res.status(201).json({ user });\n * });\n * ```\n */\nexport function withApiRequest<TValidated>(\n RequestClass: FormRequestClass<TValidated>,\n handler: PagesRouterHandler<TValidated>\n): (req: NextApiRequest, res: NextApiResponse) => Promise<void> {\n return async (req: NextApiRequest, res: NextApiResponse) => {\n const formRequest = await RequestClass.fromPagesRouter(req);\n const validated = await formRequest.validate();\n\n await handler(validated, req, res, formRequest);\n };\n}\n\n/**\n * Create a wrapper with custom error handling for App Router.\n *\n * @example\n * ```typescript\n * import { createAppRouterWrapper, ValidationError, AuthorizationError } from 'next-request';\n *\n * const withValidation = createAppRouterWrapper({\n * onValidationError: (error) => Response.json({ errors: error.errors }, { status: 422 }),\n * onAuthorizationError: () => Response.json({ message: 'Forbidden' }, { status: 403 }),\n * });\n *\n * export const POST = withValidation(CreateUserRequest, async (data) => {\n * const user = await db.users.create({ data });\n * return Response.json({ user }, { status: 201 });\n * });\n * ```\n */\nexport function createAppRouterWrapper(options: {\n onValidationError?: (error: ValidationError) => Response;\n onAuthorizationError?: (error: AuthorizationError) => Response;\n onError?: (error: unknown) => Response;\n}): <TValidated>(\n RequestClass: FormRequestClass<TValidated>,\n handler: AppRouterHandler<TValidated>\n) => (request: Request, context?: AppRouterContext) => Promise<Response> {\n return <TValidated>(\n RequestClass: FormRequestClass<TValidated>,\n handler: AppRouterHandler<TValidated>\n ) => {\n return async (request: Request, context?: AppRouterContext) => {\n try {\n const params = context?.params\n ? (context.params instanceof Promise ? await context.params : context.params)\n : {};\n\n const formRequest = await RequestClass.fromAppRouter(request, params);\n const validated = await formRequest.validate();\n\n return handler(validated, request, formRequest);\n } catch (error) {\n if (error instanceof ValidationError && options.onValidationError) {\n return options.onValidationError(error);\n }\n if (error instanceof AuthorizationError && options.onAuthorizationError) {\n return options.onAuthorizationError(error);\n }\n if (options.onError) {\n return options.onError(error);\n }\n throw error;\n }\n };\n };\n}\n\n/**\n * Create a wrapper with custom error handling for Pages Router.\n *\n * @example\n * ```typescript\n * import { createPagesRouterWrapper, ValidationError, AuthorizationError } from 'next-request';\n *\n * const withValidation = createPagesRouterWrapper({\n * onValidationError: (error, req, res) => res.status(422).json({ errors: error.errors }),\n * onAuthorizationError: (error, req, res) => res.status(403).json({ message: 'Forbidden' }),\n * });\n *\n * export default withValidation(CreateUserRequest, async (data, req, res) => {\n * const user = await db.users.create({ data });\n * res.status(201).json({ user });\n * });\n * ```\n */\nexport function createPagesRouterWrapper(options: {\n onValidationError?: (\n error: ValidationError,\n req: NextApiRequest,\n res: NextApiResponse\n ) => void | Promise<void>;\n onAuthorizationError?: (\n error: AuthorizationError,\n req: NextApiRequest,\n res: NextApiResponse\n ) => void | Promise<void>;\n onError?: (\n error: unknown,\n req: NextApiRequest,\n res: NextApiResponse\n ) => void | Promise<void>;\n}): <TValidated>(\n RequestClass: FormRequestClass<TValidated>,\n handler: PagesRouterHandler<TValidated>\n) => (req: NextApiRequest, res: NextApiResponse) => Promise<void> {\n return <TValidated>(\n RequestClass: FormRequestClass<TValidated>,\n handler: PagesRouterHandler<TValidated>\n ) => {\n return async (req: NextApiRequest, res: NextApiResponse) => {\n try {\n const formRequest = await RequestClass.fromPagesRouter(req);\n const validated = await formRequest.validate();\n\n await handler(validated, req, res, formRequest);\n } catch (error) {\n if (error instanceof ValidationError && options.onValidationError) {\n return options.onValidationError(error, req, res);\n }\n if (error instanceof AuthorizationError && options.onAuthorizationError) {\n return options.onAuthorizationError(error, req, res);\n }\n if (options.onError) {\n return options.onError(error, req, res);\n }\n throw error;\n }\n };\n };\n}\n","import type { ZodSchema, ZodError } from 'zod';\nimport type { ValidatorAdapter, ValidationResult, ValidationConfig, ValidationErrors } from '../../core/types';\n\n/**\n * Validator adapter for Zod schemas\n *\n * @example\n * ```typescript\n * import { z } from 'zod';\n * import { ZodAdapter } from 'next-request/zod';\n *\n * const schema = z.object({\n * email: z.string().email(),\n * name: z.string().min(2),\n * });\n *\n * class MyRequest extends FormRequest<z.infer<typeof schema>> {\n * rules() {\n * return new ZodAdapter(schema);\n * }\n * }\n * ```\n */\nexport class ZodAdapter<T> implements ValidatorAdapter<T> {\n constructor(private schema: ZodSchema<T>) {}\n\n async validate(data: unknown, config?: ValidationConfig): Promise<ValidationResult<T>> {\n return this.validateSync(data, config);\n }\n\n validateSync(data: unknown, config?: ValidationConfig): ValidationResult<T> {\n const result = this.schema.safeParse(data);\n\n if (result.success) {\n return {\n success: true,\n data: result.data,\n };\n }\n\n const errors = this.formatZodErrors(result.error, config);\n\n return {\n success: false,\n errors,\n };\n }\n\n private formatZodErrors(error: ZodError, config?: ValidationConfig): ValidationErrors {\n const errors: ValidationErrors = {};\n const customMessages = config?.messages ?? {};\n const customAttributes = config?.attributes ?? {};\n\n for (const issue of error.issues) {\n const path = issue.path.join('.');\n const fieldName = path || '_root';\n\n // Get custom attribute name if available\n const attributeName = customAttributes[fieldName] ?? fieldName;\n\n // Build the message key for custom messages (e.g., \"email.email\", \"password.min\")\n const messageKey = `${fieldName}.${issue.code}`;\n\n // Check for custom message, otherwise use Zod's message with custom attribute\n let message: string;\n if (customMessages[messageKey]) {\n message = customMessages[messageKey];\n } else if (customMessages[fieldName]) {\n message = customMessages[fieldName];\n } else {\n // Replace field references in Zod's default message\n message = issue.message.replace(\n new RegExp(`\\\\b${fieldName}\\\\b`, 'gi'),\n attributeName\n );\n }\n\n if (!errors[fieldName]) {\n errors[fieldName] = [];\n }\n errors[fieldName].push(message);\n }\n\n return errors;\n }\n}\n"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "next-form-request",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Laravel-inspired Form Request validation for Next.js API routes",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"module": "dist/index.mjs",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.mjs",
|
|
12
|
+
"require": "./dist/index.js"
|
|
13
|
+
},
|
|
14
|
+
"./zod": {
|
|
15
|
+
"types": "./dist/adapters/validators/ZodAdapter.d.ts",
|
|
16
|
+
"import": "./dist/adapters/validators/ZodAdapter.mjs",
|
|
17
|
+
"require": "./dist/adapters/validators/ZodAdapter.js"
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
"files": [
|
|
21
|
+
"dist"
|
|
22
|
+
],
|
|
23
|
+
"scripts": {
|
|
24
|
+
"build": "tsup",
|
|
25
|
+
"dev": "tsup --watch",
|
|
26
|
+
"typecheck": "tsc --noEmit",
|
|
27
|
+
"test": "vitest run",
|
|
28
|
+
"test:watch": "vitest",
|
|
29
|
+
"test:coverage": "vitest run --coverage",
|
|
30
|
+
"prepublishOnly": "npm run build"
|
|
31
|
+
},
|
|
32
|
+
"keywords": [
|
|
33
|
+
"next.js",
|
|
34
|
+
"nextjs",
|
|
35
|
+
"validation",
|
|
36
|
+
"form-request",
|
|
37
|
+
"laravel",
|
|
38
|
+
"api",
|
|
39
|
+
"typescript"
|
|
40
|
+
],
|
|
41
|
+
"author": "Sam Street <samstreet.dev@gmail.com> (https://github.com/samstreet/next-request)",
|
|
42
|
+
"license": "MIT",
|
|
43
|
+
"peerDependencies": {
|
|
44
|
+
"next": ">=13.0.0"
|
|
45
|
+
},
|
|
46
|
+
"peerDependenciesMeta": {
|
|
47
|
+
"zod": {
|
|
48
|
+
"optional": true
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
"devDependencies": {
|
|
52
|
+
"@types/node": "^20.10.0",
|
|
53
|
+
"@vitest/coverage-v8": "^2.0.0",
|
|
54
|
+
"next": "^14.0.0",
|
|
55
|
+
"tsup": "^8.0.0",
|
|
56
|
+
"typescript": "^5.3.0",
|
|
57
|
+
"vitest": "^2.0.0",
|
|
58
|
+
"zod": "^3.22.0"
|
|
59
|
+
}
|
|
60
|
+
}
|