@lara-node/validator 0.1.3 → 0.1.4

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.
@@ -0,0 +1,33 @@
1
+ //#region src/index.d.ts
2
+ declare class ValidationError extends Error {
3
+ errors: Record<string, string[]>;
4
+ messages: Record<string, string[]>;
5
+ message: string;
6
+ constructor(errors: Record<string, string[]>, messages?: Record<string, string[]>);
7
+ }
8
+ type RuleFn = (value: any, field: string, payload?: any) => true | {
9
+ ok: boolean;
10
+ message?: string;
11
+ value?: any;
12
+ } | false | Promise<any>;
13
+ type RuleSpec = string | RuleFn | {
14
+ rule: string | RuleFn;
15
+ messages?: Record<string, string>;
16
+ };
17
+ declare function formatMessage(template: string, ctx: Record<string, any>): string;
18
+ declare function resolveMessage(field: string, code: string, meta: Record<string, any>, custom?: Record<string, string>): string;
19
+ declare function validate<T extends Record<string, any>>(payload: any, rules: Record<string, RuleSpec>, customMessages?: Record<string, string>): Promise<T>;
20
+ declare const requiredIf: (otherField: string, value: any) => RuleFn;
21
+ declare const requiredUnless: (otherField: string, value: any) => RuleFn;
22
+ declare const fileRule: RuleFn;
23
+ declare const mimes: (allowedTypes: string[]) => RuleFn;
24
+ declare const maxFileSize: (maxSizeInMB: number) => RuleFn;
25
+ declare const phoneRule: RuleFn;
26
+ declare const creditCardRule: RuleFn;
27
+ declare const nestedRule: (rules: Record<string, RuleSpec>) => RuleFn;
28
+ declare const arrayOfObjectsRule: (rules: Record<string, RuleSpec>) => RuleFn;
29
+ //# sourceMappingURL=index.d.ts.map
30
+
31
+ //#endregion
32
+ export { RuleFn, RuleSpec, ValidationError, arrayOfObjectsRule, creditCardRule, fileRule, formatMessage, maxFileSize, mimes, nestedRule, phoneRule, requiredIf, requiredUnless, resolveMessage, validate };
33
+ //# sourceMappingURL=index-CrEGL3K2.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index-CrEGL3K2.d.cts","names":[],"sources":["../src/index.ts"],"mappings":";cAEa,eAAA,SAAwB,KAAA;EAAxB,MAAA,EACH,MADG,CAAA,MAAgB,EAAA,MAAA,EAAA,CAAA;EAAA,QAAA,EAEjB,MAFiB,CAAA,MAAA,EAAA,MAAA,EAAA,CAAA;SACnB,EAAA,MAAA;aACE,CAAA,MAAA,EAGU,MAHV,CAAA,MAAA,EAAA,MAAA,EAAA,CAAA,EAAA,QAAA,CAAA,EAG+C,MAH/C,CAAA,MAAA,EAAA,MAAA,EAAA,CAAA;;AAG+C,KAqB/C,MAAA,GArB+C,CAAA,KAAA,EAAA,GAAA,EAAA,KAAA,EAAA,MAAA,EAAA,OAAA,CAAA,EAAA,GAAA,EAAA,GAAA,IAAA,GAAA;MALtB,OAAA;EAAK,OAAA,CAAA,EAAA,MAAA;EA0B9B,KAAA,CAAA,EAAA,GAAM;AAMlB,CAAA,GAAY,KAAA,GAFyD,OAEjD,CAAA,GAAA,CAAA;AAAA,KAAR,QAAA,GAAQ,MAAA,GAEhB,MAFgB,GAAA;MAEhB,EAAA,MAAA,GACiB,MADjB;UACiB,CAAA,EAAmB,MAAnB,CAAA,MAAA,EAAA,MAAA,CAAA;;AAAyB,iBAgG9B,aAAA,CAhG8B,QAAA,EAAA,MAAA,EAAA,GAAA,EAgGO,MAhGP,CAAA,MAAA,EAAA,GAAA,CAAA,CAAA,EAAA,MAAA;AAgG9B,iBAMA,cAAA,CAN2C,KAAA,EAAA,MAAA,EAAA,IAAA,EAAA,MAAA,EAAA,IAAA,EASnD,MATmD,CAAA,MAAA,EAAA,GAAA,CAAA,EAAA,MAAA,CAAA,EAUhD,MAVgD,CAAA,MAAA,EAAA,MAAA,CAAA,CAAA,EAAA,MAAA;AAM3C,iBAmBM,QAnBQ,CAAA,UAmBW,MAnBX,CAAA,MAAA,EAAA,GAAA,CAAA,CAAA,CAAA,OAAA,EAAA,GAAA,EAAA,KAAA,EAqBrB,MArBqB,CAAA,MAAA,EAqBN,QArBM,CAAA,EAAA,cAAA,CAAA,EAsBX,MAtBW,CAAA,MAAA,EAAA,MAAA,CAAA,CAAA,EAuB3B,OAvB2B,CAuBnB,CAvBmB,CAAA;AAAA,cAgiBjB,UAhiBiB,EAAA,CAAA,UAAA,EAAA,MAAA,EAAA,KAAA,EAAA,GAAA,EAAA,GAiiBM,MAjiBN;AAGtB,cAsiBK,cAtiBL,EAAA,CAAA,UAAA,EAAA,MAAA,EAAA,KAAA,EAAA,GAAA,EAAA,GAuiB4B,MAviB5B;AACG,cA8iBE,QA9iBF,EA8iBY,MA9iBZ;AAAM,cAojBJ,KApjBI,EAAA,CAAA,YAAA,EAAA,MAAA,EAAA,EAAA,GAqjBW,MArjBX;AAeK,cAyjBT,WAzjBiB,EAAA,CAAA,WAAA,EAAA,MAAA,EAAA,GA0jBL,MA1jBK;AAAA,cAkkBjB,SAlkBiB,EAkkBN,MAlkBM;AAAW,cA2kB5B,cA3kB4B,EA2kBZ,MA3kBY;AAEjB,cAulBX,UAvlBW,EAAA,CAAA,KAAA,EAwlBd,MAxlBc,CAAA,MAAA,EAwlBC,QAxlBD,CAAA,EAAA,GAwlBa,MAxlBb;AAAf,cAgmBI,kBAhmBJ,EAAA,CAAA,KAAA,EAimBC,MAjmBD,CAAA,MAAA,EAimBgB,QAjmBhB,CAAA,EAAA,GAimB4B,MAjmB5B"}
package/dist/index.cjs ADDED
@@ -0,0 +1,1119 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ let _lara_node_db = require("@lara-node/db");
3
+ //#region src/index.ts
4
+ var ValidationError = class ValidationError extends Error {
5
+ constructor(errors, messages) {
6
+ super("Validation failed");
7
+ this.errors = errors;
8
+ this.messages = messages || {};
9
+ this.message = "unknown error";
10
+ const totalErrors = Object.values(this.messages).reduce((sum, currentArray) => sum + currentArray.length, 0);
11
+ const firstError = Object.values(this.messages).find((arr) => arr.length > 0)?.[0];
12
+ if (firstError) if (totalErrors > 1) this.message = `${firstError} and ${totalErrors - 1} more error(s).`;
13
+ else this.message = firstError;
14
+ Object.setPrototypeOf(this, ValidationError.prototype);
15
+ }
16
+ };
17
+ const defaultMessages = {
18
+ required: ":attribute field is required.",
19
+ integer: ":attribute must be an integer.",
20
+ numeric: ":attribute must be a number.",
21
+ array: ":attribute must be an array.",
22
+ json: ":attribute must be valid JSON.",
23
+ email: ":attribute must be a valid email address.",
24
+ boolean: ":attribute must be true or false.",
25
+ date: ":attribute must be a valid date.",
26
+ url: ":attribute must be a valid URL.",
27
+ uuid: ":attribute must be a valid UUID.",
28
+ object: ":attribute must be an object.",
29
+ "min.numeric": ":attribute must be at least :min.",
30
+ "min.string": ":attribute must be at least :min characters.",
31
+ "min.array": ":attribute must have at least :min items.",
32
+ "max.numeric": ":attribute may not be greater than :max.",
33
+ "max.string": ":attribute may not be greater than :max characters.",
34
+ "max.array": ":attribute may not have more than :max items.",
35
+ "size.string": ":attribute must be exactly :size characters.",
36
+ "size.array": ":attribute must contain exactly :size items.",
37
+ "size.numeric": ":attribute must be exactly :size.",
38
+ "between.numeric": ":attribute must be between :min and :max.",
39
+ "between.string": ":attribute must be between :min and :max characters.",
40
+ "between.array": ":attribute must have between :min and :max items.",
41
+ regex: ":attribute format is invalid.",
42
+ regex_invalid: "Invalid regex pattern for :attribute validation.",
43
+ phone: ":attribute must be a valid phone number.",
44
+ credit_card: ":attribute must be a valid credit card number.",
45
+ in: ":attribute must be one of: :values.",
46
+ not_in: ":attribute must not be one of: :values.",
47
+ exists: "Selected :attribute is invalid.",
48
+ unique: ":attribute has already been taken.",
49
+ starts_with: ":attribute must start with one of: :prefixes.",
50
+ ends_with: ":attribute must end with one of: :suffixes.",
51
+ contains: ":attribute must contain :substring.",
52
+ accepted: ":attribute must be accepted.",
53
+ declined: ":attribute must be declined.",
54
+ confirmed: ":attribute confirmation does not match.",
55
+ different: ":attribute and :other must be different.",
56
+ same: ":attribute and :other must be the same.",
57
+ gt: ":attribute must be greater than :field.",
58
+ gte: ":attribute must be greater than or equal to :field.",
59
+ lt: ":attribute must be less than :field.",
60
+ lte: ":attribute must be less than or equal to :field.",
61
+ before: ":attribute must be a date before :field.",
62
+ before_or_equal: ":attribute must be a date before or equal to :field.",
63
+ after: ":attribute must be a date after :field.",
64
+ after_or_equal: ":attribute must be a date after or equal to :field.",
65
+ date_equals: ":attribute must be the same date as :field.",
66
+ date_format: ":attribute does not match the format :format.",
67
+ time: ":attribute must be a valid time (HH:MM or HH:MM:SS).",
68
+ datetime: ":attribute must be a valid date and time.",
69
+ timezone: ":attribute must be a valid timezone.",
70
+ file: ":attribute must be a file.",
71
+ mimes: ":attribute must be a file of type: :values.",
72
+ max_file_size: ":attribute may not be larger than :max MB.",
73
+ required_if: ":attribute is required.",
74
+ required_unless: ":attribute is required.",
75
+ required_with: ":attribute is required when :values is present.",
76
+ required_with_all: ":attribute is required when :values are all present.",
77
+ required_without: ":attribute is required when :values is not present.",
78
+ required_without_all: ":attribute is required when none of :values are present.",
79
+ present: ":attribute field must be present.",
80
+ invalid: ":attribute is invalid.",
81
+ nested_validation_failed: ":attribute contains invalid data.",
82
+ object_array: ":attribute must be an array of objects."
83
+ };
84
+ function parseDate(val) {
85
+ if (val instanceof Date) return isNaN(val.getTime()) ? null : val;
86
+ const str = String(val).trim();
87
+ for (const { re, y, m, d } of [
88
+ {
89
+ re: /^(\d{1,2})\/(\d{1,2})\/(\d{2,4})$/,
90
+ y: 3,
91
+ m: 2,
92
+ d: 1
93
+ },
94
+ {
95
+ re: /^(\d{1,2})-(\d{1,2})-(\d{2,4})$/,
96
+ y: 3,
97
+ m: 2,
98
+ d: 1
99
+ },
100
+ {
101
+ re: /^(\d{1,2})\.(\d{1,2})\.(\d{2,4})$/,
102
+ y: 3,
103
+ m: 2,
104
+ d: 1
105
+ },
106
+ {
107
+ re: /^(\d{4})\/(\d{1,2})\/(\d{1,2})$/,
108
+ y: 1,
109
+ m: 2,
110
+ d: 3
111
+ },
112
+ {
113
+ re: /^(\d{4})\.(\d{1,2})\.(\d{1,2})$/,
114
+ y: 1,
115
+ m: 2,
116
+ d: 3
117
+ }
118
+ ]) {
119
+ const match = str.match(re);
120
+ if (match) {
121
+ let year = parseInt(match[y], 10);
122
+ if (year < 100) year += year < 70 ? 2e3 : 1900;
123
+ const month = parseInt(match[m], 10) - 1;
124
+ const day = parseInt(match[d], 10);
125
+ const dt = new Date(year, month, day);
126
+ if (!isNaN(dt.getTime()) && dt.getMonth() === month) return dt;
127
+ }
128
+ }
129
+ const native = new Date(str);
130
+ if (!isNaN(native.getTime())) return native;
131
+ return null;
132
+ }
133
+ function formatMessage(template, ctx) {
134
+ return template.replace(/:([a-zA-Z_]+)/g, (_, key) => ctx[key] !== void 0 ? String(ctx[key]) : ":" + key);
135
+ }
136
+ function resolveMessage(field, code, meta, custom) {
137
+ let variantCode = code;
138
+ if ([
139
+ "min",
140
+ "max",
141
+ "size",
142
+ "between"
143
+ ].includes(code) && meta.kind) variantCode = `${code}.${meta.kind}`;
144
+ const attrLabel = custom && custom[`attributes.${field}`] ? custom[`attributes.${field}`] : field;
145
+ const candidates = [
146
+ `${field}.${variantCode}`,
147
+ `${field}.${code}`,
148
+ variantCode,
149
+ code
150
+ ];
151
+ for (const c of candidates) {
152
+ if (custom && custom[c]) return formatMessage(custom[c], {
153
+ ...meta,
154
+ attribute: attrLabel
155
+ });
156
+ if (defaultMessages[c]) return formatMessage(defaultMessages[c], {
157
+ ...meta,
158
+ attribute: attrLabel
159
+ });
160
+ }
161
+ return formatMessage(defaultMessages.invalid || "Invalid value", {
162
+ ...meta,
163
+ attribute: attrLabel
164
+ });
165
+ }
166
+ async function validate(payload, rules, customMessages) {
167
+ const out = { ...payload || {} };
168
+ const errors = {};
169
+ const messageErrors = {};
170
+ const metaErrors = {};
171
+ const fieldMessagesMap = {};
172
+ function normalizePattern(p) {
173
+ return p.replace(/\.\.+/g, ".*").replace(/(^|\.)\*(\.|$)/g, (_, a, b) => "*" + (b ? "." : "")).replace(/\.\*/g, ".*");
174
+ }
175
+ function splitSegments(pattern) {
176
+ return pattern.split(".").map((s) => s === "" ? "*" : s);
177
+ }
178
+ function getAtPath(obj, path) {
179
+ if (!obj) return void 0;
180
+ const segs = path.split(".");
181
+ let cur = obj;
182
+ for (const s of segs) {
183
+ if (cur === void 0 || cur === null) return void 0;
184
+ if (/^\d+$/.test(s)) cur = cur[Number(s)];
185
+ else cur = cur[s];
186
+ }
187
+ return cur;
188
+ }
189
+ function setAtPath(obj, path, value) {
190
+ const segs = path.split(".");
191
+ let cur = obj;
192
+ for (let i = 0; i < segs.length; i++) {
193
+ const s = segs[i];
194
+ const isLast = i === segs.length - 1;
195
+ const key = /^\d+$/.test(s) ? Number(s) : s;
196
+ if (isLast) {
197
+ cur[key] = value;
198
+ return;
199
+ }
200
+ if (cur[key] === void 0 || cur[key] === null) {
201
+ const nextSeg = segs[i + 1];
202
+ cur[key] = /^\d+$/.test(nextSeg) ? [] : {};
203
+ }
204
+ cur = cur[key];
205
+ }
206
+ }
207
+ function expandFieldPaths(obj, pattern) {
208
+ if (!pattern) return [];
209
+ const segs = splitSegments(normalizePattern(pattern));
210
+ const results = [];
211
+ function recurse(current, idx, prefix) {
212
+ if (idx >= segs.length) {
213
+ results.push(prefix.replace(/^\./, ""));
214
+ return;
215
+ }
216
+ const seg = segs[idx];
217
+ if (seg === "*" || seg === "") if (Array.isArray(current)) for (let i = 0; i < current.length; i++) recurse(current[i], idx + 1, prefix + "." + i);
218
+ else if (current && typeof current === "object") for (const k of Object.keys(current)) recurse(current[k], idx + 1, prefix + "." + k);
219
+ else {
220
+ const parentPath = prefix.replace(/^\./, "");
221
+ let parentVal = void 0;
222
+ try {
223
+ parentVal = parentPath ? getAtPath(obj, parentPath) : obj;
224
+ } catch {
225
+ parentVal = void 0;
226
+ }
227
+ if (typeof parentVal === "string") {
228
+ try {
229
+ const parsed = JSON.parse(parentVal);
230
+ if (Array.isArray(parsed)) {
231
+ for (let i = 0; i < parsed.length; i++) recurse(parsed[i], idx + 1, prefix + "." + i);
232
+ return;
233
+ }
234
+ } catch {}
235
+ if (parentVal.includes(",")) {
236
+ const parts = parentVal.split(",").map((s) => s.trim()).filter(Boolean);
237
+ for (let i = 0; i < parts.length; i++) recurse(parts[i], idx + 1, prefix + "." + i);
238
+ return;
239
+ }
240
+ }
241
+ recurse(void 0, idx + 1, prefix + ".*");
242
+ }
243
+ else if (current && (typeof current === "object" || Array.isArray(current)) && seg in current) recurse(current[seg], idx + 1, prefix + "." + seg);
244
+ else if (current && Array.isArray(current) && /^\d+$/.test(seg)) recurse(current[Number(seg)], idx + 1, prefix + "." + seg);
245
+ else recurse(void 0, idx + 1, prefix + "." + seg);
246
+ }
247
+ recurse(obj, 0, "");
248
+ if (results.length === 0) {
249
+ if (!pattern.includes("*")) return [pattern];
250
+ return [];
251
+ }
252
+ return Array.from(new Set(results));
253
+ }
254
+ for (const fieldPattern of Object.keys(rules)) {
255
+ const spec = rules[fieldPattern];
256
+ let fieldRule;
257
+ if (typeof spec === "object" && spec && typeof spec !== "function" && "rule" in spec) {
258
+ fieldRule = spec.rule;
259
+ if (spec.messages) {
260
+ const prefixed = {};
261
+ for (const [k, v] of Object.entries(spec.messages)) prefixed[k.startsWith(fieldPattern + ".") ? k : `${fieldPattern}.${k}`] = v;
262
+ fieldMessagesMap[fieldPattern] = prefixed;
263
+ }
264
+ } else fieldRule = spec;
265
+ const targetPaths = expandFieldPaths(out, fieldPattern);
266
+ if (fieldPattern.includes("*") && targetPaths.length === 0) continue;
267
+ let pathsToValidate = [];
268
+ for (const tp of targetPaths.length ? targetPaths : [fieldPattern]) if (tp.includes(".*") || tp.includes("*")) {
269
+ const parentPath = tp.replace(/\.(?:\*|\.)+$/, "").replace(/\*$/, "");
270
+ const parentVal = parentPath ? getAtPath(out, parentPath) : out;
271
+ if (Array.isArray(parentVal)) {
272
+ for (let i = 0; i < parentVal.length; i++) pathsToValidate.push(`${parentPath}.${i}`);
273
+ continue;
274
+ }
275
+ if (typeof parentVal === "string") {
276
+ try {
277
+ const parsed = JSON.parse(parentVal);
278
+ if (Array.isArray(parsed)) {
279
+ for (let i = 0; i < parsed.length; i++) pathsToValidate.push(`${parentPath}.${i}`);
280
+ continue;
281
+ }
282
+ } catch {}
283
+ if (parentVal.includes(",")) {
284
+ const parts = parentVal.split(",").map((s) => s.trim()).filter(Boolean);
285
+ for (let i = 0; i < parts.length; i++) pathsToValidate.push(`${parentPath}.${i}`);
286
+ continue;
287
+ }
288
+ }
289
+ pathsToValidate.push(tp);
290
+ } else pathsToValidate.push(tp);
291
+ for (const field of pathsToValidate) {
292
+ const raw = getAtPath(out, field);
293
+ if (typeof fieldRule === "function") {
294
+ const res = await fieldRule(raw, field, out);
295
+ if (res === true) continue;
296
+ if (res === false) {
297
+ pushError(field, "invalid", {
298
+ value: raw,
299
+ kind: typeof raw
300
+ });
301
+ continue;
302
+ }
303
+ if (res && res.ok === false) {
304
+ pushError(field, res.message || "invalid", {
305
+ value: raw,
306
+ kind: typeof raw
307
+ });
308
+ continue;
309
+ }
310
+ if (res && res.ok === true && "value" in res) {
311
+ setAtPath(out, field, res.value);
312
+ continue;
313
+ }
314
+ continue;
315
+ }
316
+ const parts = String(fieldRule).split("|").map((s) => s.trim()).filter(Boolean);
317
+ const isRequired = parts.includes("required");
318
+ const isNullable = parts.includes("nullable");
319
+ const isSometimes = parts.includes("sometimes");
320
+ const isPresent = parts.includes("present");
321
+ const present = raw !== void 0 && raw !== null && raw !== "";
322
+ let conditionalRequired = false;
323
+ for (const p of parts) if (p.startsWith("required_if:")) {
324
+ const [condField, ...values] = p.slice(12).split(",").map((s) => s.trim());
325
+ if (values.some((v) => v === String(getAtPath(out, condField) ?? ""))) conditionalRequired = true;
326
+ } else if (p.startsWith("required_unless:")) {
327
+ const [condField, ...values] = p.slice(16).split(",").map((s) => s.trim());
328
+ if (!values.some((v) => v === String(getAtPath(out, condField) ?? ""))) conditionalRequired = true;
329
+ } else if (p.startsWith("required_with:")) {
330
+ if (p.slice(14).split(",").map((s) => s.trim()).some((f) => {
331
+ const v = getAtPath(out, f);
332
+ return v !== void 0 && v !== null && v !== "";
333
+ })) conditionalRequired = true;
334
+ } else if (p.startsWith("required_with_all:")) {
335
+ if (p.slice(18).split(",").map((s) => s.trim()).every((f) => {
336
+ const v = getAtPath(out, f);
337
+ return v !== void 0 && v !== null && v !== "";
338
+ })) conditionalRequired = true;
339
+ } else if (p.startsWith("required_without:")) {
340
+ if (p.slice(17).split(",").map((s) => s.trim()).some((f) => {
341
+ const v = getAtPath(out, f);
342
+ return v === void 0 || v === null || v === "";
343
+ })) conditionalRequired = true;
344
+ } else if (p.startsWith("required_without_all:")) {
345
+ if (p.slice(21).split(",").map((s) => s.trim()).every((f) => {
346
+ const v = getAtPath(out, f);
347
+ return v === void 0 || v === null || v === "";
348
+ })) conditionalRequired = true;
349
+ }
350
+ const effectiveRequired = isRequired || conditionalRequired;
351
+ if (isSometimes && !present) continue;
352
+ if (isPresent && raw === void 0) {
353
+ pushError(field, "present", { value: raw });
354
+ continue;
355
+ }
356
+ if (effectiveRequired && !present) {
357
+ pushError(field, "required", { value: raw });
358
+ continue;
359
+ }
360
+ if (!present && (isNullable || !effectiveRequired)) continue;
361
+ let val = raw;
362
+ let failed = false;
363
+ for (const p of parts) {
364
+ if ([
365
+ "required",
366
+ "nullable",
367
+ "sometimes",
368
+ "present"
369
+ ].includes(p)) continue;
370
+ if (p.startsWith("required_if:") || p.startsWith("required_unless:") || p.startsWith("required_with:") || p.startsWith("required_with_all:") || p.startsWith("required_without:") || p.startsWith("required_without_all:")) continue;
371
+ if (failed) break;
372
+ switch (true) {
373
+ case p === "string":
374
+ if (typeof val !== "string") val = String(val);
375
+ break;
376
+ case p === "email":
377
+ if (typeof val !== "string") val = String(val);
378
+ if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(val)) {
379
+ pushError(field, "email", {
380
+ value: val,
381
+ kind: "string"
382
+ });
383
+ failed = true;
384
+ }
385
+ break;
386
+ case p === "int" || p === "integer": {
387
+ const intVal = parseInt(val, 10);
388
+ if (Number.isNaN(intVal)) {
389
+ pushError(field, "integer", {
390
+ value: val,
391
+ kind: typeof val
392
+ });
393
+ failed = true;
394
+ } else val = intVal;
395
+ break;
396
+ }
397
+ case p === "numeric" || p === "float" || p === "double": {
398
+ const numVal = Number(val);
399
+ if (Number.isNaN(numVal)) {
400
+ pushError(field, "numeric", {
401
+ value: val,
402
+ kind: typeof val
403
+ });
404
+ failed = true;
405
+ } else val = numVal;
406
+ break;
407
+ }
408
+ case p === "boolean":
409
+ if (typeof val === "string") {
410
+ const lv = val.toLowerCase();
411
+ if ([
412
+ "true",
413
+ "1",
414
+ "yes",
415
+ "on"
416
+ ].includes(lv)) val = true;
417
+ else if ([
418
+ "false",
419
+ "0",
420
+ "no",
421
+ "off"
422
+ ].includes(lv)) val = false;
423
+ else {
424
+ pushError(field, "boolean", {
425
+ value: val,
426
+ kind: typeof val
427
+ });
428
+ failed = true;
429
+ }
430
+ } else if (typeof val !== "boolean") {
431
+ pushError(field, "boolean", {
432
+ value: val,
433
+ kind: typeof val
434
+ });
435
+ failed = true;
436
+ }
437
+ break;
438
+ case p === "array":
439
+ if (!Array.isArray(val)) if (typeof val === "string") try {
440
+ const p2 = JSON.parse(val);
441
+ if (Array.isArray(p2)) val = p2;
442
+ else {
443
+ pushError(field, "array", {
444
+ value: val,
445
+ kind: typeof val
446
+ });
447
+ failed = true;
448
+ }
449
+ } catch {
450
+ pushError(field, "array", {
451
+ value: val,
452
+ kind: typeof val
453
+ });
454
+ failed = true;
455
+ }
456
+ else {
457
+ pushError(field, "array", {
458
+ value: val,
459
+ kind: typeof val
460
+ });
461
+ failed = true;
462
+ }
463
+ break;
464
+ case p === "json":
465
+ if (typeof val === "string") try {
466
+ val = JSON.parse(val);
467
+ } catch {
468
+ pushError(field, "json", {
469
+ value: val,
470
+ kind: typeof val
471
+ });
472
+ failed = true;
473
+ }
474
+ break;
475
+ case p === "date": {
476
+ const date = parseDate(val);
477
+ if (!date) {
478
+ pushError(field, "date", {
479
+ value: val,
480
+ kind: typeof val
481
+ });
482
+ failed = true;
483
+ } else val = date;
484
+ break;
485
+ }
486
+ case p === "url":
487
+ try {
488
+ new URL(val);
489
+ } catch {
490
+ pushError(field, "url", {
491
+ value: val,
492
+ kind: typeof val
493
+ });
494
+ failed = true;
495
+ }
496
+ break;
497
+ case p === "uuid":
498
+ if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(String(val))) {
499
+ pushError(field, "uuid", {
500
+ value: val,
501
+ kind: typeof val
502
+ });
503
+ failed = true;
504
+ }
505
+ break;
506
+ case p === "phone": {
507
+ const phoneRegex = /^(\+\d{1,3}[\s-]?)?([0-9]|\(\d{1,4}\))[\d\s-]{5,}$/;
508
+ const dc = String(val).replace(/[\s\-()]/g, "").replace(/\D/g, "").length;
509
+ if (!phoneRegex.test(String(val)) || dc < 7 || dc > 15) {
510
+ pushError(field, "phone", {
511
+ value: val,
512
+ kind: typeof val
513
+ });
514
+ failed = true;
515
+ }
516
+ break;
517
+ }
518
+ case p.startsWith("min:"):
519
+ await handleMinRule(field, val, p);
520
+ break;
521
+ case p.startsWith("max:"):
522
+ await handleMaxRule(field, val, p);
523
+ break;
524
+ case p.startsWith("size:"):
525
+ await handleSizeRule(field, val, p);
526
+ break;
527
+ case p.startsWith("between:"):
528
+ await handleBetweenRule(field, val, p);
529
+ break;
530
+ case p.startsWith("in:"):
531
+ await handleInRule(field, val, p);
532
+ break;
533
+ case p.startsWith("not_in:"):
534
+ await handleNotInRule(field, val, p);
535
+ break;
536
+ case p.startsWith("exists:"):
537
+ if (!((raw === void 0 || raw === null || raw === "") && parts.includes("nullable"))) await handleExistsRule(field, val, p);
538
+ break;
539
+ case p.startsWith("unique:"):
540
+ await handleUniqueRule(field, val, p);
541
+ break;
542
+ case p.startsWith("regex:"):
543
+ await handleRegexRule(field, val, p);
544
+ break;
545
+ case p.startsWith("starts_with:"):
546
+ await handleStartsWithRule(field, val, p);
547
+ break;
548
+ case p.startsWith("ends_with:"):
549
+ await handleEndsWithRule(field, val, p);
550
+ break;
551
+ case p.startsWith("contains:"):
552
+ await handleContainsRule(field, val, p);
553
+ break;
554
+ case p.startsWith("gt:"):
555
+ await handleComparisonRule(field, val, p, "gt", out);
556
+ break;
557
+ case p.startsWith("gte:"):
558
+ await handleComparisonRule(field, val, p, "gte", out);
559
+ break;
560
+ case p.startsWith("lt:"):
561
+ await handleComparisonRule(field, val, p, "lt", out);
562
+ break;
563
+ case p.startsWith("lte:"):
564
+ await handleComparisonRule(field, val, p, "lte", out);
565
+ break;
566
+ case p.startsWith("before:"):
567
+ await handleDateComparisonRule(field, val, p, "before", out);
568
+ break;
569
+ case p.startsWith("before_or_equal:"):
570
+ await handleDateComparisonRule(field, val, p, "before_or_equal", out);
571
+ break;
572
+ case p.startsWith("after:"):
573
+ await handleDateComparisonRule(field, val, p, "after", out);
574
+ break;
575
+ case p.startsWith("after_or_equal:"):
576
+ await handleDateComparisonRule(field, val, p, "after_or_equal", out);
577
+ break;
578
+ case p.startsWith("date_equals:"):
579
+ await handleDateComparisonRule(field, val, p, "date_equals", out);
580
+ break;
581
+ case p.startsWith("date_format:"):
582
+ await handleDateFormatRule(field, raw, p);
583
+ break;
584
+ case p === "time":
585
+ if (typeof val !== "string" || !/^\d{2}:\d{2}(:\d{2})?$/.test(val)) {
586
+ pushError(field, "time", { value: val });
587
+ failed = true;
588
+ }
589
+ break;
590
+ case p === "datetime": {
591
+ const dtVal = parseDate(val);
592
+ if (!dtVal) {
593
+ pushError(field, "datetime", { value: val });
594
+ failed = true;
595
+ } else val = dtVal;
596
+ break;
597
+ }
598
+ case p === "timezone":
599
+ try {
600
+ Intl.DateTimeFormat(void 0, { timeZone: String(val) });
601
+ } catch {
602
+ pushError(field, "timezone", { value: val });
603
+ failed = true;
604
+ }
605
+ break;
606
+ case p === "accepted":
607
+ if (![
608
+ true,
609
+ 1,
610
+ "1",
611
+ "yes",
612
+ "on"
613
+ ].includes(val)) {
614
+ pushError(field, "accepted", { value: val });
615
+ failed = true;
616
+ }
617
+ break;
618
+ case p === "declined":
619
+ if (![
620
+ false,
621
+ 0,
622
+ "0",
623
+ "no",
624
+ "off"
625
+ ].includes(val)) {
626
+ pushError(field, "declined", { value: val });
627
+ failed = true;
628
+ }
629
+ break;
630
+ case p === "confirmed": {
631
+ const cf = `${field}_confirmation`;
632
+ if (out && (out[cf] || out["confirmation_" + field] || out[field + "_confirmed"] || out["confirmed_" + field] || out["confirm_" + field]) !== val) {
633
+ pushError(field, "confirmed", { other: cf });
634
+ failed = true;
635
+ }
636
+ break;
637
+ }
638
+ case p.startsWith("different:"): {
639
+ const df = p.split(":")[1];
640
+ if (out && out[df] === val) {
641
+ pushError(field, "different", { other: df });
642
+ failed = true;
643
+ }
644
+ break;
645
+ }
646
+ case p.startsWith("same:"): {
647
+ const sf = p.split(":")[1];
648
+ if (out && out[sf] !== val) {
649
+ pushError(field, "same", { other: sf });
650
+ failed = true;
651
+ }
652
+ break;
653
+ }
654
+ default: break;
655
+ }
656
+ }
657
+ if (!failed) setAtPath(out, field, val);
658
+ }
659
+ }
660
+ function pushError(field, code, meta = {}) {
661
+ errors[field] = errors[field] || [];
662
+ errors[field].push(code);
663
+ metaErrors[field] = metaErrors[field] || [];
664
+ metaErrors[field].push({
665
+ code,
666
+ meta
667
+ });
668
+ }
669
+ for (const field of Object.keys(metaErrors)) for (const item of metaErrors[field]) {
670
+ const merged = {
671
+ ...customMessages || {},
672
+ ...fieldMessagesMap[field] || {}
673
+ };
674
+ const msg = resolveMessage(field, item.code, item.meta, merged);
675
+ messageErrors[field] = messageErrors[field] || [];
676
+ messageErrors[field].push(msg);
677
+ }
678
+ if (Object.keys(errors).length) throw new ValidationError(errors, messageErrors);
679
+ return out;
680
+ async function handleMinRule(field, val, rule) {
681
+ const arg = Number(rule.split(":")[1]);
682
+ if (typeof val === "number" && val < arg) pushError(field, "min", {
683
+ min: arg,
684
+ value: val,
685
+ kind: "numeric"
686
+ });
687
+ else if (typeof val === "string" && val.length < arg) pushError(field, "min", {
688
+ min: arg,
689
+ value: val,
690
+ kind: "string"
691
+ });
692
+ else if (Array.isArray(val) && val.length < arg) pushError(field, "min", {
693
+ min: arg,
694
+ value: val,
695
+ kind: "array"
696
+ });
697
+ }
698
+ async function handleMaxRule(field, val, rule) {
699
+ const arg = Number(rule.split(":")[1]);
700
+ if (typeof val === "number" && val > arg) pushError(field, "max", {
701
+ max: arg,
702
+ value: val,
703
+ kind: "numeric"
704
+ });
705
+ else if (typeof val === "string" && val.length > arg) pushError(field, "max", {
706
+ max: arg,
707
+ value: val,
708
+ kind: "string"
709
+ });
710
+ else if (Array.isArray(val) && val.length > arg) pushError(field, "max", {
711
+ max: arg,
712
+ value: val,
713
+ kind: "array"
714
+ });
715
+ }
716
+ async function handleSizeRule(field, val, rule) {
717
+ const arg = Number(rule.split(":")[1]);
718
+ if (typeof val === "number" && val !== arg) pushError(field, "size", {
719
+ size: arg,
720
+ value: val,
721
+ kind: "numeric"
722
+ });
723
+ else if (typeof val === "string" && val.length !== arg) pushError(field, "size", {
724
+ size: arg,
725
+ value: val,
726
+ kind: "string"
727
+ });
728
+ else if (Array.isArray(val) && val.length !== arg) pushError(field, "size", {
729
+ size: arg,
730
+ value: val,
731
+ kind: "array"
732
+ });
733
+ }
734
+ async function handleBetweenRule(field, val, rule) {
735
+ const [min, max] = rule.split(":")[1].split(",").map(Number);
736
+ if (typeof val === "number" && (val < min || val > max)) pushError(field, "between", {
737
+ min,
738
+ max,
739
+ value: val,
740
+ kind: "numeric"
741
+ });
742
+ else if (typeof val === "string" && (val.length < min || val.length > max)) pushError(field, "between", {
743
+ min,
744
+ max,
745
+ value: val,
746
+ kind: "string"
747
+ });
748
+ else if (Array.isArray(val) && (val.length < min || val.length > max)) pushError(field, "between", {
749
+ min,
750
+ max,
751
+ value: val,
752
+ kind: "array"
753
+ });
754
+ }
755
+ async function handleInRule(field, val, rule) {
756
+ const opts = rule.split(":")[1].split(",").map((s) => s.trim());
757
+ if (!opts.includes(String(val))) pushError(field, "in", {
758
+ value: val,
759
+ values: opts.join(", ")
760
+ });
761
+ }
762
+ async function handleNotInRule(field, val, rule) {
763
+ const opts = rule.split(":")[1].split(",").map((s) => s.trim());
764
+ if (opts.includes(String(val))) pushError(field, "not_in", {
765
+ value: val,
766
+ values: opts.join(", ")
767
+ });
768
+ }
769
+ async function handleExistsRule(field, val, rule) {
770
+ try {
771
+ let [table, column] = rule.split(":")[1].split(",");
772
+ table = (table || "").trim();
773
+ column = (column || "id").trim();
774
+ class VM extends _lara_node_db.Model {
775
+ static {
776
+ this.table = table;
777
+ }
778
+ static {
779
+ this.primaryKey = "id";
780
+ }
781
+ static {
782
+ this.fillable = [column];
783
+ }
784
+ }
785
+ if (!await VM.query().where(column, "=", val).exists()) pushError(field, "exists", {
786
+ value: val,
787
+ table,
788
+ column
789
+ });
790
+ } catch {
791
+ pushError(field, "exists", { value: val });
792
+ }
793
+ }
794
+ async function handleUniqueRule(field, val, rule) {
795
+ try {
796
+ const partsSpec = rule.split(":")[1].split(",").map((s) => s.trim());
797
+ const table = partsSpec[0];
798
+ const column = partsSpec[1] || "id";
799
+ const exceptArg = partsSpec[2];
800
+ const exceptArg2 = partsSpec[3];
801
+ class VM extends _lara_node_db.Model {
802
+ static {
803
+ this.table = table;
804
+ }
805
+ static {
806
+ this.primaryKey = "id";
807
+ }
808
+ static {
809
+ this.fillable = [column];
810
+ }
811
+ }
812
+ let exceptColumn = VM.primaryKey, exceptValue = exceptArg;
813
+ if (exceptArg2 !== void 0) {
814
+ exceptColumn = exceptArg;
815
+ exceptValue = exceptArg2;
816
+ }
817
+ let q = VM.query().where(column, "=", val);
818
+ if (exceptValue !== void 0 && exceptValue !== null && String(exceptValue) !== "") q = q.where(function(query) {
819
+ query.where(exceptColumn, "!=", exceptValue);
820
+ });
821
+ if (await q.exists()) pushError(field, "unique", {
822
+ value: val,
823
+ table,
824
+ column
825
+ });
826
+ } catch {
827
+ pushError(field, "unique", { value: val });
828
+ }
829
+ }
830
+ async function handleRegexRule(field, val, rule) {
831
+ const pattern = rule.split(":")[1];
832
+ try {
833
+ if (!new RegExp(pattern).test(String(val))) pushError(field, "regex", {
834
+ value: val,
835
+ pattern
836
+ });
837
+ } catch {
838
+ pushError(field, "regex_invalid", {
839
+ value: val,
840
+ pattern
841
+ });
842
+ }
843
+ }
844
+ async function handleStartsWithRule(field, val, rule) {
845
+ const prefixes = rule.split(":")[1].split(",").map((s) => s.trim());
846
+ if (!prefixes.some((p2) => String(val).startsWith(p2))) pushError(field, "starts_with", {
847
+ value: val,
848
+ prefixes: prefixes.join(", ")
849
+ });
850
+ }
851
+ async function handleEndsWithRule(field, val, rule) {
852
+ const suffixes = rule.split(":")[1].split(",").map((s) => s.trim());
853
+ if (!suffixes.some((s) => String(val).endsWith(s))) pushError(field, "ends_with", {
854
+ value: val,
855
+ suffixes: suffixes.join(", ")
856
+ });
857
+ }
858
+ async function handleContainsRule(field, val, rule) {
859
+ const substring = rule.split(":")[1];
860
+ if (!String(val).includes(substring)) pushError(field, "contains", {
861
+ value: val,
862
+ substring
863
+ });
864
+ }
865
+ async function handleComparisonRule(field, val, rule, operator, payload) {
866
+ const otherField = rule.split(":")[1];
867
+ const otherValue = payload ? getAtPath(payload, otherField) : void 0;
868
+ if (otherValue === void 0) return;
869
+ const numVal = Number(val), numOther = Number(otherValue);
870
+ if (!isNaN(numVal) && !isNaN(numOther)) {
871
+ if (!(operator === "gt" ? numVal > numOther : operator === "gte" ? numVal >= numOther : operator === "lt" ? numVal < numOther : numVal <= numOther)) pushError(field, operator, {
872
+ field: otherField,
873
+ value: val,
874
+ other: otherValue
875
+ });
876
+ return;
877
+ }
878
+ const dv = parseDate(val), dOther = parseDate(otherValue);
879
+ if (dv && dOther) {
880
+ const tv = dv.getTime(), to = dOther.getTime();
881
+ if (!(operator === "gt" ? tv > to : operator === "gte" ? tv >= to : operator === "lt" ? tv < to : tv <= to)) pushError(field, operator, {
882
+ field: otherField,
883
+ value: val,
884
+ other: otherValue
885
+ });
886
+ return;
887
+ }
888
+ const sv = String(val), so = String(otherValue);
889
+ if (!(operator === "gt" ? sv > so : operator === "gte" ? sv >= so : operator === "lt" ? sv < so : sv <= so)) pushError(field, operator, {
890
+ field: otherField,
891
+ value: val,
892
+ other: otherValue
893
+ });
894
+ }
895
+ async function handleDateComparisonRule(field, val, rule, operator, payload) {
896
+ const otherField = rule.split(":")[1];
897
+ let otherRaw = payload ? getAtPath(payload, otherField) : void 0;
898
+ if (otherRaw === void 0) otherRaw = otherField;
899
+ if (typeof otherRaw === "string") {
900
+ const lower = otherRaw.toLowerCase().trim();
901
+ if (lower === "today") {
902
+ const t = /* @__PURE__ */ new Date();
903
+ t.setHours(0, 0, 0, 0);
904
+ otherRaw = t;
905
+ } else if (lower === "yesterday") {
906
+ const t = /* @__PURE__ */ new Date();
907
+ t.setDate(t.getDate() - 1);
908
+ t.setHours(0, 0, 0, 0);
909
+ otherRaw = t;
910
+ } else if (lower === "tomorrow") {
911
+ const t = /* @__PURE__ */ new Date();
912
+ t.setDate(t.getDate() + 1);
913
+ t.setHours(0, 0, 0, 0);
914
+ otherRaw = t;
915
+ } else {
916
+ const rm = lower.match(/^(today|yesterday|tomorrow|now)([+-])(\d+)(days?|weeks?|months?|years?)$/);
917
+ if (rm) {
918
+ const [, anchor, sign, numStr, unit] = rm;
919
+ const n = parseInt(numStr, 10) * (sign === "+" ? 1 : -1);
920
+ const t = /* @__PURE__ */ new Date();
921
+ t.setHours(0, 0, 0, 0);
922
+ if (anchor === "yesterday") t.setDate(t.getDate() - 1);
923
+ if (anchor === "tomorrow") t.setDate(t.getDate() + 1);
924
+ if (unit.startsWith("day")) t.setDate(t.getDate() + n);
925
+ if (unit.startsWith("week")) t.setDate(t.getDate() + n * 7);
926
+ if (unit.startsWith("month")) t.setMonth(t.getMonth() + n);
927
+ if (unit.startsWith("year")) t.setFullYear(t.getFullYear() + n);
928
+ otherRaw = t;
929
+ }
930
+ }
931
+ }
932
+ const dv = parseDate(val), dOther = parseDate(otherRaw);
933
+ if (!dv) {
934
+ pushError(field, "date", { value: val });
935
+ return;
936
+ }
937
+ if (!dOther) return;
938
+ const tv = dv.getTime(), to = dOther.getTime();
939
+ if (!(operator === "before" ? tv < to : operator === "before_or_equal" ? tv <= to : operator === "after" ? tv > to : operator === "after_or_equal" ? tv >= to : tv === to)) pushError(field, operator, {
940
+ field: otherField,
941
+ value: val,
942
+ other: otherRaw
943
+ });
944
+ }
945
+ async function handleDateFormatRule(field, val, rule) {
946
+ const format = rule.slice(12);
947
+ const strVal = String(val);
948
+ const tokenMap = {
949
+ YYYY: "\\d{4}",
950
+ YY: "\\d{2}",
951
+ MM: "(?:0[1-9]|1[0-2])",
952
+ DD: "(?:0[1-9]|[12]\\d|3[01])",
953
+ HH: "(?:[01]\\d|2[0-3])",
954
+ mm: "[0-5]\\d",
955
+ ss: "[0-5]\\d",
956
+ SSS: "\\d{3}",
957
+ Z: "(?:Z|[+-]\\d{2}:\\d{2})"
958
+ };
959
+ let pattern = format.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
960
+ for (const token of Object.keys(tokenMap).sort((a, b) => b.length - a.length)) pattern = pattern.replace(new RegExp(token.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "g"), tokenMap[token]);
961
+ if (!new RegExp("^" + pattern + "$").test(strVal)) pushError(field, "date_format", {
962
+ value: val,
963
+ format
964
+ });
965
+ }
966
+ }
967
+ const requiredIf = (otherField, value) => async (val, _field, payload) => {
968
+ if (payload && payload[otherField] === value) {
969
+ if (val === void 0 || val === null || val === "") return {
970
+ ok: false,
971
+ message: "required"
972
+ };
973
+ }
974
+ return true;
975
+ };
976
+ const requiredUnless = (otherField, value) => async (val, _field, payload) => {
977
+ if (payload && payload[otherField] !== value) {
978
+ if (val === void 0 || val === null || val === "") return {
979
+ ok: false,
980
+ message: "required"
981
+ };
982
+ }
983
+ return true;
984
+ };
985
+ const fileRule = async (value) => {
986
+ if (!value) return true;
987
+ if (typeof value === "object" && (value instanceof File || "name" in value && "size" in value)) return true;
988
+ return {
989
+ ok: false,
990
+ message: "file"
991
+ };
992
+ };
993
+ const mimes = (allowedTypes) => async (value) => {
994
+ if (!value) return true;
995
+ const extension = (typeof value === "string" ? value : value?.name || "").split(".").pop()?.toLowerCase() || "";
996
+ const mimeTypes = {
997
+ jpg: ["image/jpeg"],
998
+ jpeg: ["image/jpeg"],
999
+ png: ["image/png"],
1000
+ gif: ["image/gif"],
1001
+ pdf: ["application/pdf"],
1002
+ doc: ["application/msword"],
1003
+ docx: ["application/vnd.openxmlformats-officedocument.wordprocessingml.document"]
1004
+ };
1005
+ if (!allowedTypes.map((type) => {
1006
+ for (const [ext, mimes2] of Object.entries(mimeTypes)) if (mimes2.includes(type)) return ext;
1007
+ return type.split("/").pop();
1008
+ }).filter(Boolean).includes(extension)) return {
1009
+ ok: false,
1010
+ message: "mimes",
1011
+ value: allowedTypes.join(", ")
1012
+ };
1013
+ return true;
1014
+ };
1015
+ const maxFileSize = (maxSizeInMB) => async (value) => {
1016
+ if (!value) return true;
1017
+ if ((value instanceof File ? value.size : value?.size || 0) > maxSizeInMB * 1024 * 1024) return {
1018
+ ok: false,
1019
+ message: "max_file_size",
1020
+ value: maxSizeInMB
1021
+ };
1022
+ return true;
1023
+ };
1024
+ const phoneRule = (value) => {
1025
+ if (!value) return true;
1026
+ const phoneRegex = /^(\+\d{1,3}[\s-]?)?([0-9]|\(\d{1,4}\))[\d\s-]{5,}$/;
1027
+ const dc = String(value).replace(/[\s\-()]/g, "").replace(/\D/g, "").length;
1028
+ if (!phoneRegex.test(String(value)) || dc < 7 || dc > 15) return {
1029
+ ok: false,
1030
+ message: "phone"
1031
+ };
1032
+ return true;
1033
+ };
1034
+ const creditCardRule = (value) => {
1035
+ if (!value) return true;
1036
+ const str = String(value).replace(/\s+/g, "");
1037
+ if (!/^\d+$/.test(str)) return {
1038
+ ok: false,
1039
+ message: "credit_card"
1040
+ };
1041
+ let sum = 0, isEven = false;
1042
+ for (let i = str.length - 1; i >= 0; i--) {
1043
+ let digit = parseInt(str.charAt(i), 10);
1044
+ if (isEven) {
1045
+ digit *= 2;
1046
+ if (digit > 9) digit -= 9;
1047
+ }
1048
+ sum += digit;
1049
+ isEven = !isEven;
1050
+ }
1051
+ if (sum % 10 !== 0) return {
1052
+ ok: false,
1053
+ message: "credit_card"
1054
+ };
1055
+ return true;
1056
+ };
1057
+ const nestedRule = (rules) => async (value) => {
1058
+ if (value === void 0 || value === null) return true;
1059
+ if (typeof value !== "object" || Array.isArray(value)) return {
1060
+ ok: false,
1061
+ message: "object"
1062
+ };
1063
+ try {
1064
+ await validate(value, rules);
1065
+ return true;
1066
+ } catch {
1067
+ return {
1068
+ ok: false,
1069
+ message: "nested_validation_failed"
1070
+ };
1071
+ }
1072
+ };
1073
+ const arrayOfObjectsRule = (rules) => async (value) => {
1074
+ if (value === void 0 || value === null) return true;
1075
+ let array;
1076
+ if (Array.isArray(value)) array = value;
1077
+ else if (typeof value === "string") try {
1078
+ const p = JSON.parse(value);
1079
+ array = Array.isArray(p) ? p : [p];
1080
+ } catch {
1081
+ return {
1082
+ ok: false,
1083
+ message: "array"
1084
+ };
1085
+ }
1086
+ else array = [value];
1087
+ for (let i = 0; i < array.length; i++) {
1088
+ if (typeof array[i] !== "object" || Array.isArray(array[i])) return {
1089
+ ok: false,
1090
+ message: "object_array"
1091
+ };
1092
+ try {
1093
+ await validate(array[i], rules);
1094
+ } catch {
1095
+ return {
1096
+ ok: false,
1097
+ message: `items[${i}].validation_failed`
1098
+ };
1099
+ }
1100
+ }
1101
+ return {
1102
+ ok: true,
1103
+ value: array
1104
+ };
1105
+ };
1106
+ //#endregion
1107
+ exports.ValidationError = ValidationError;
1108
+ exports.arrayOfObjectsRule = arrayOfObjectsRule;
1109
+ exports.creditCardRule = creditCardRule;
1110
+ exports.fileRule = fileRule;
1111
+ exports.formatMessage = formatMessage;
1112
+ exports.maxFileSize = maxFileSize;
1113
+ exports.mimes = mimes;
1114
+ exports.nestedRule = nestedRule;
1115
+ exports.phoneRule = phoneRule;
1116
+ exports.requiredIf = requiredIf;
1117
+ exports.requiredUnless = requiredUnless;
1118
+ exports.resolveMessage = resolveMessage;
1119
+ exports.validate = validate;
package/package.json CHANGED
@@ -1,14 +1,16 @@
1
1
  {
2
2
  "name": "@lara-node/validator",
3
- "version": "0.1.3",
3
+ "version": "0.1.4",
4
4
  "description": "Laravel-inspired validation engine for Lara-Node",
5
5
  "type": "module",
6
- "main": "./dist/index.js",
6
+ "main": "./dist/index.cjs",
7
+ "module": "./dist/index.js",
7
8
  "types": "./dist/index.d.ts",
8
9
  "exports": {
9
10
  ".": {
10
- "types": "./dist/index.d.ts",
11
- "import": "./dist/index.js"
11
+ "import": "./dist/index.js",
12
+ "require": "./dist/index.cjs",
13
+ "types": "./dist/index.d.ts"
12
14
  }
13
15
  },
14
16
  "files": [
@@ -18,19 +20,19 @@
18
20
  "publishConfig": {
19
21
  "access": "public"
20
22
  },
23
+ "scripts": {
24
+ "build": "tsdown src/index.ts --format esm,cjs --out-dir dist && tsc --emitDeclarationOnly",
25
+ "dev": "tsdown src/index.ts --format esm,cjs --out-dir dist --watch",
26
+ "clean": "rimraf dist",
27
+ "typecheck": "tsc --noEmit"
28
+ },
21
29
  "dependencies": {
22
- "@lara-node/db": "0.1.1"
30
+ "@lara-node/db": "workspace:*"
23
31
  },
24
32
  "devDependencies": {
25
33
  "@types/node": "^24.12.2",
26
34
  "rimraf": "^6.0.1",
27
35
  "tsdown": "^0.12.9",
28
36
  "typescript": "^5.9.3"
29
- },
30
- "scripts": {
31
- "build": "tsdown src/index.ts --format esm --out-dir dist && tsc --emitDeclarationOnly",
32
- "dev": "tsdown src/index.ts --format esm --out-dir dist --watch",
33
- "clean": "rimraf dist",
34
- "typecheck": "tsc --noEmit"
35
37
  }
36
- }
38
+ }