skyguard-js 1.1.1 → 1.1.2
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 +11 -9
- package/dist/crypto/jwt.js +2 -2
- package/dist/http/logger.js +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +3 -2
- package/dist/sessions/fileSessionStorage.js +4 -5
- package/dist/sessions/sessionStorage.d.ts +1 -1
- package/dist/static/contentDisposition.js +6 -1
- package/dist/storage/storage.js +1 -4
- package/dist/validators/rules/arrayRule.d.ts +3 -8
- package/dist/validators/rules/arrayRule.js +18 -44
- package/dist/validators/rules/bigIntRule.d.ts +20 -0
- package/dist/validators/rules/bigIntRule.js +53 -0
- package/dist/validators/rules/booleanRule.d.ts +1 -1
- package/dist/validators/rules/dateRule.d.ts +1 -1
- package/dist/validators/rules/index.d.ts +3 -1
- package/dist/validators/rules/index.js +7 -3
- package/dist/validators/rules/numberRule.d.ts +1 -1
- package/dist/validators/rules/numberRule.js +6 -7
- package/dist/validators/rules/objectRule.d.ts +7 -0
- package/dist/validators/rules/objectRule.js +27 -0
- package/dist/validators/rules/stringRule.d.ts +11 -2
- package/dist/validators/rules/stringRule.js +31 -7
- package/dist/validators/rules/{requiredRule.d.ts → unionRule.d.ts} +3 -7
- package/dist/validators/rules/unionRule.js +31 -0
- package/dist/validators/types.d.ts +2 -5
- package/dist/validators/validationRule.d.ts +5 -14
- package/dist/validators/validationRule.js +8 -14
- package/dist/validators/validationSchema.d.ts +58 -20
- package/dist/validators/validationSchema.js +82 -46
- package/dist/validators/validator.d.ts +3 -3
- package/dist/validators/validator.js +18 -9
- package/package.json +7 -7
- package/dist/validators/rules/requiredRule.js +0 -21
package/README.md
CHANGED
|
@@ -165,17 +165,17 @@ app.staticFiles(join(__dirname, "..", "static"));
|
|
|
165
165
|
|
|
166
166
|
## ⛔ Data Validation
|
|
167
167
|
|
|
168
|
-
|
|
168
|
+
To validate data in the body of client requests, the framework provides the creation of validation schemas, which are created as follows:
|
|
169
169
|
|
|
170
170
|
```ts
|
|
171
|
-
import {
|
|
172
|
-
|
|
173
|
-
const userSchema =
|
|
174
|
-
name:
|
|
175
|
-
email:
|
|
176
|
-
age:
|
|
177
|
-
active:
|
|
178
|
-
birthdate:
|
|
171
|
+
import { v, schema } from "skyguard-js";
|
|
172
|
+
|
|
173
|
+
const userSchema = schema({
|
|
174
|
+
name: v.string({ maxLength: 60 }),
|
|
175
|
+
email: v.email(),
|
|
176
|
+
age: v.number({ min: 18 }),
|
|
177
|
+
active: v.boolean().default(false),
|
|
178
|
+
birthdate: v.date({ max: new Date() }),
|
|
179
179
|
});
|
|
180
180
|
|
|
181
181
|
app.post("/users", (request: Request) => {
|
|
@@ -188,6 +188,8 @@ app.post("/users", (request: Request) => {
|
|
|
188
188
|
});
|
|
189
189
|
```
|
|
190
190
|
|
|
191
|
+
By default each property you define in the schema is required, to define it optional you use the `.optional()` or `.default(value)` function
|
|
192
|
+
|
|
191
193
|
Validation is:
|
|
192
194
|
|
|
193
195
|
- Fail-fast per field
|
package/dist/crypto/jwt.js
CHANGED
|
@@ -85,7 +85,7 @@ const verifyJWT = (token, secret) => {
|
|
|
85
85
|
return null;
|
|
86
86
|
return payload;
|
|
87
87
|
}
|
|
88
|
-
catch
|
|
88
|
+
catch {
|
|
89
89
|
return null;
|
|
90
90
|
}
|
|
91
91
|
};
|
|
@@ -105,7 +105,7 @@ const decodeJWT = (token) => {
|
|
|
105
105
|
const payload = JSON.parse(base64UrlDecode(parts[1]));
|
|
106
106
|
return { header, payload };
|
|
107
107
|
}
|
|
108
|
-
catch
|
|
108
|
+
catch {
|
|
109
109
|
return null;
|
|
110
110
|
}
|
|
111
111
|
};
|
package/dist/http/logger.js
CHANGED
|
@@ -13,7 +13,7 @@ class Logger {
|
|
|
13
13
|
const diff = process.hrtime.bigint() - startTime;
|
|
14
14
|
const responseTime = (Number(diff) / 1_000_000).toFixed(3);
|
|
15
15
|
const coloredStatus = this.colorizeStatus(res.statusCode);
|
|
16
|
-
const logLine = `${method} ${url} ${coloredStatus} ${responseTime} ms - ${contentLength}`;
|
|
16
|
+
const logLine = `${method} ${url} ${coloredStatus} ${responseTime} ms - ${contentLength.toString()}`;
|
|
17
17
|
this.stream.write(logLine + "\n");
|
|
18
18
|
}
|
|
19
19
|
colorizeStatus(statusCode) {
|
package/dist/index.d.ts
CHANGED
|
@@ -6,4 +6,4 @@ export { FileSessionStorage, MemorySessionStorage } from "./sessions";
|
|
|
6
6
|
export { HttpMethods } from "./http/httpMethods";
|
|
7
7
|
export { createUploader } from "./storage/uploader";
|
|
8
8
|
export { StorageType } from "./storage/types";
|
|
9
|
-
export {
|
|
9
|
+
export { v, schema } from "./validators/validationSchema";
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.schema = exports.v = exports.StorageType = exports.createUploader = exports.HttpMethods = exports.MemorySessionStorage = exports.FileSessionStorage = exports.RouterGroup = exports.Response = exports.Request = exports.createApp = void 0;
|
|
4
4
|
var app_1 = require("./app");
|
|
5
5
|
Object.defineProperty(exports, "createApp", { enumerable: true, get: function () { return app_1.createApp; } });
|
|
6
6
|
var http_1 = require("./http");
|
|
@@ -18,4 +18,5 @@ Object.defineProperty(exports, "createUploader", { enumerable: true, get: functi
|
|
|
18
18
|
var types_1 = require("./storage/types");
|
|
19
19
|
Object.defineProperty(exports, "StorageType", { enumerable: true, get: function () { return types_1.StorageType; } });
|
|
20
20
|
var validationSchema_1 = require("./validators/validationSchema");
|
|
21
|
-
Object.defineProperty(exports, "
|
|
21
|
+
Object.defineProperty(exports, "v", { enumerable: true, get: function () { return validationSchema_1.v; } });
|
|
22
|
+
Object.defineProperty(exports, "schema", { enumerable: true, get: function () { return validationSchema_1.schema; } });
|
|
@@ -213,10 +213,7 @@ class FileSessionStorage {
|
|
|
213
213
|
}
|
|
214
214
|
catch {
|
|
215
215
|
// Corrupt/unreadable → best-effort cleanup
|
|
216
|
-
|
|
217
|
-
await (0, promises_1.unlink)(full);
|
|
218
|
-
}
|
|
219
|
-
catch { }
|
|
216
|
+
await (0, promises_1.unlink)(full);
|
|
220
217
|
}
|
|
221
218
|
}));
|
|
222
219
|
}
|
|
@@ -280,7 +277,9 @@ class FileSessionStorage {
|
|
|
280
277
|
try {
|
|
281
278
|
await (0, promises_1.unlink)(path);
|
|
282
279
|
}
|
|
283
|
-
catch {
|
|
280
|
+
catch {
|
|
281
|
+
/** eslint-disable-next-line no-empty */
|
|
282
|
+
}
|
|
284
283
|
}
|
|
285
284
|
/**
|
|
286
285
|
* Generates a cryptographically strong session id (64 hex chars).
|
|
@@ -59,7 +59,12 @@ class ContentDisposition {
|
|
|
59
59
|
}
|
|
60
60
|
sanitizeFilename(filename) {
|
|
61
61
|
return filename
|
|
62
|
-
.
|
|
62
|
+
.split("")
|
|
63
|
+
.filter(char => {
|
|
64
|
+
const code = char.charCodeAt(0);
|
|
65
|
+
return !(code <= 0x1f || (code >= 0x7f && code <= 0x9f));
|
|
66
|
+
})
|
|
67
|
+
.join("")
|
|
63
68
|
.replace(/["\r\n]/g, "")
|
|
64
69
|
.replace(/[/\\]/g, "")
|
|
65
70
|
.replace(/\s+/g, " ")
|
package/dist/storage/storage.js
CHANGED
|
@@ -4,13 +4,8 @@ export interface ArrayRuleOptions extends RuleOptions {
|
|
|
4
4
|
minLength?: number;
|
|
5
5
|
maxLength?: number;
|
|
6
6
|
}
|
|
7
|
-
export declare class ArrayRule extends BaseValidationRule {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
string(message?: string): this;
|
|
11
|
-
number(message?: string): this;
|
|
12
|
-
}
|
|
13
|
-
export declare class ArrayNumberRule extends BaseValidationRule {
|
|
14
|
-
constructor();
|
|
7
|
+
export declare class ArrayRule extends BaseValidationRule<Array<unknown>> {
|
|
8
|
+
private readonly typeValid?;
|
|
9
|
+
constructor(typeValid?: BaseValidationRule);
|
|
15
10
|
validate(context: ValidationContext, options?: ArrayRuleOptions): ValidationError | null;
|
|
16
11
|
}
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.ArrayRule = void 0;
|
|
4
4
|
const validationRule_1 = require("../validationRule");
|
|
5
5
|
class ArrayRule extends validationRule_1.BaseValidationRule {
|
|
6
|
-
|
|
6
|
+
typeValid;
|
|
7
|
+
constructor(typeValid) {
|
|
7
8
|
super("array");
|
|
9
|
+
this.typeValid = typeValid;
|
|
8
10
|
}
|
|
9
11
|
validate(context, options) {
|
|
10
12
|
const { field, value } = context;
|
|
@@ -17,50 +19,22 @@ class ArrayRule extends validationRule_1.BaseValidationRule {
|
|
|
17
19
|
if (options?.maxLength && value.length > options.maxLength) {
|
|
18
20
|
return this.createError(field, `${field} must have at most ${options.maxLength} items`, value);
|
|
19
21
|
}
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
rule:
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
});
|
|
34
|
-
return this;
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
exports.ArrayRule = ArrayRule;
|
|
38
|
-
class ArrayStringRule extends validationRule_1.BaseValidationRule {
|
|
39
|
-
constructor() {
|
|
40
|
-
super("arrayString");
|
|
41
|
-
}
|
|
42
|
-
validate(context, options) {
|
|
43
|
-
const { field, value } = context;
|
|
44
|
-
for (let i = 0; i < value.length; i++) {
|
|
45
|
-
if (typeof value[i] !== "string") {
|
|
46
|
-
return this.createError(field, options?.message || `${field} must be an array of strings`, value);
|
|
22
|
+
if (!this.typeValid)
|
|
23
|
+
return null;
|
|
24
|
+
const itemRules = this.typeValid.rules.length
|
|
25
|
+
? this.typeValid.rules
|
|
26
|
+
: [{ rule: this.typeValid, options: undefined }];
|
|
27
|
+
for (const [index, item] of value.entries()) {
|
|
28
|
+
for (const { rule, options: itemOptions } of itemRules) {
|
|
29
|
+
const error = rule.validate({
|
|
30
|
+
field: `${field}[${index}]`,
|
|
31
|
+
value: item,
|
|
32
|
+
}, itemOptions);
|
|
33
|
+
if (error)
|
|
34
|
+
return error;
|
|
47
35
|
}
|
|
48
36
|
}
|
|
49
37
|
return null;
|
|
50
38
|
}
|
|
51
39
|
}
|
|
52
|
-
|
|
53
|
-
constructor() {
|
|
54
|
-
super("arrayNumber");
|
|
55
|
-
}
|
|
56
|
-
validate(context, options) {
|
|
57
|
-
const { field, value } = context;
|
|
58
|
-
for (let i = 0; i < value.length; i++) {
|
|
59
|
-
if (typeof value[i] !== "number") {
|
|
60
|
-
return this.createError(field, options?.message || `${field} must be an array of numbers`, value);
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
return null;
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
exports.ArrayNumberRule = ArrayNumberRule;
|
|
40
|
+
exports.ArrayRule = ArrayRule;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { ValidationContext, RuleOptions, ValidationError } from "../types";
|
|
2
|
+
import { BaseValidationRule } from "../validationRule";
|
|
3
|
+
export interface BigIntRuleOptions extends RuleOptions {
|
|
4
|
+
gt?: bigint;
|
|
5
|
+
gte?: bigint;
|
|
6
|
+
lt?: bigint;
|
|
7
|
+
lte?: bigint;
|
|
8
|
+
positive?: boolean;
|
|
9
|
+
negative?: boolean;
|
|
10
|
+
}
|
|
11
|
+
export declare class BigIntRule extends BaseValidationRule<bigint> {
|
|
12
|
+
constructor();
|
|
13
|
+
validate(context: ValidationContext, options?: BigIntRuleOptions): ValidationError | null;
|
|
14
|
+
gt(limit: bigint, message?: string): this;
|
|
15
|
+
gte(limit: bigint, message?: string): this;
|
|
16
|
+
lt(limit: bigint, message?: string): this;
|
|
17
|
+
lte(limit: bigint, message?: string): this;
|
|
18
|
+
positive(message?: string): this;
|
|
19
|
+
negative(message?: string): this;
|
|
20
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.BigIntRule = void 0;
|
|
4
|
+
const validationRule_1 = require("../validationRule");
|
|
5
|
+
class BigIntRule extends validationRule_1.BaseValidationRule {
|
|
6
|
+
constructor() {
|
|
7
|
+
super("bigint");
|
|
8
|
+
}
|
|
9
|
+
validate(context, options) {
|
|
10
|
+
const { field, value } = context;
|
|
11
|
+
if (typeof value !== "bigint") {
|
|
12
|
+
return this.createError(field, options?.message || `${field} must be an bigint`, value);
|
|
13
|
+
}
|
|
14
|
+
if (options?.positive && value <= 0n)
|
|
15
|
+
return this.createError(field, `${field} must be positive`, value);
|
|
16
|
+
if (options?.negative && value >= 0n)
|
|
17
|
+
return this.createError(field, `${field} must be negative`, value);
|
|
18
|
+
if (options?.gt && value <= options.gt)
|
|
19
|
+
return this.createError(field, `${field} must be greater than ${options.gt}`, value);
|
|
20
|
+
if (options?.gte && value < options.gte)
|
|
21
|
+
return this.createError(field, `${field} must be greater than or equal to ${options.gte}`, value);
|
|
22
|
+
if (options?.lt && value >= options.lt)
|
|
23
|
+
return this.createError(field, `${field} must be less than ${options.lt}`, value);
|
|
24
|
+
if (options?.lte && value > options.lte)
|
|
25
|
+
return this.createError(field, `${field} must be less than or equal to ${options.lte}`, value);
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
gt(limit, message) {
|
|
29
|
+
this.rules.push({ rule: this, options: { gt: limit, message } });
|
|
30
|
+
return this;
|
|
31
|
+
}
|
|
32
|
+
gte(limit, message) {
|
|
33
|
+
this.rules.push({ rule: this, options: { gte: limit, message } });
|
|
34
|
+
return this;
|
|
35
|
+
}
|
|
36
|
+
lt(limit, message) {
|
|
37
|
+
this.rules.push({ rule: this, options: { lt: limit, message } });
|
|
38
|
+
return this;
|
|
39
|
+
}
|
|
40
|
+
lte(limit, message) {
|
|
41
|
+
this.rules.push({ rule: this, options: { lte: limit, message } });
|
|
42
|
+
return this;
|
|
43
|
+
}
|
|
44
|
+
positive(message) {
|
|
45
|
+
this.rules.push({ rule: this, options: { positive: true, message } });
|
|
46
|
+
return this;
|
|
47
|
+
}
|
|
48
|
+
negative(message) {
|
|
49
|
+
this.rules.push({ rule: this, options: { negative: true, message } });
|
|
50
|
+
return this;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
exports.BigIntRule = BigIntRule;
|
|
@@ -5,7 +5,7 @@ import { BaseValidationRule } from "../validationRule";
|
|
|
5
5
|
*
|
|
6
6
|
* Validates that a value is a boolean (`true` or `false`).
|
|
7
7
|
*/
|
|
8
|
-
export declare class BooleanRule extends BaseValidationRule {
|
|
8
|
+
export declare class BooleanRule extends BaseValidationRule<boolean> {
|
|
9
9
|
constructor();
|
|
10
10
|
validate(context: ValidationContext, options?: RuleOptions): ValidationError | null;
|
|
11
11
|
}
|
|
@@ -10,7 +10,7 @@ export interface DateRuleOptions extends RuleOptions {
|
|
|
10
10
|
*
|
|
11
11
|
* Validates that a value represents a valid date.
|
|
12
12
|
*/
|
|
13
|
-
export declare class DateRule extends BaseValidationRule {
|
|
13
|
+
export declare class DateRule extends BaseValidationRule<Date> {
|
|
14
14
|
constructor();
|
|
15
15
|
validate(context: ValidationContext, options?: DateRuleOptions): ValidationError | null;
|
|
16
16
|
}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
export { BooleanRule } from "./booleanRule";
|
|
2
2
|
export { DateRule, DateRuleOptions } from "./dateRule";
|
|
3
3
|
export { NumberRule, NumberRuleOptions } from "./numberRule";
|
|
4
|
-
export { RequiredRule } from "./requiredRule";
|
|
5
4
|
export { StringRule, StringRuleOptions } from "./stringRule";
|
|
6
5
|
export { ArrayRule, ArrayRuleOptions } from "./arrayRule";
|
|
7
6
|
export { LiteralRule } from "./literalRule";
|
|
7
|
+
export { ObjectRule } from "./objectRule";
|
|
8
|
+
export { UnionRule } from "./unionRule";
|
|
9
|
+
export { BigIntRule, BigIntRuleOptions } from "./bigIntRule";
|
|
@@ -1,17 +1,21 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.BigIntRule = exports.UnionRule = exports.ObjectRule = exports.LiteralRule = exports.ArrayRule = exports.StringRule = exports.NumberRule = exports.DateRule = exports.BooleanRule = void 0;
|
|
4
4
|
var booleanRule_1 = require("./booleanRule");
|
|
5
5
|
Object.defineProperty(exports, "BooleanRule", { enumerable: true, get: function () { return booleanRule_1.BooleanRule; } });
|
|
6
6
|
var dateRule_1 = require("./dateRule");
|
|
7
7
|
Object.defineProperty(exports, "DateRule", { enumerable: true, get: function () { return dateRule_1.DateRule; } });
|
|
8
8
|
var numberRule_1 = require("./numberRule");
|
|
9
9
|
Object.defineProperty(exports, "NumberRule", { enumerable: true, get: function () { return numberRule_1.NumberRule; } });
|
|
10
|
-
var requiredRule_1 = require("./requiredRule");
|
|
11
|
-
Object.defineProperty(exports, "RequiredRule", { enumerable: true, get: function () { return requiredRule_1.RequiredRule; } });
|
|
12
10
|
var stringRule_1 = require("./stringRule");
|
|
13
11
|
Object.defineProperty(exports, "StringRule", { enumerable: true, get: function () { return stringRule_1.StringRule; } });
|
|
14
12
|
var arrayRule_1 = require("./arrayRule");
|
|
15
13
|
Object.defineProperty(exports, "ArrayRule", { enumerable: true, get: function () { return arrayRule_1.ArrayRule; } });
|
|
16
14
|
var literalRule_1 = require("./literalRule");
|
|
17
15
|
Object.defineProperty(exports, "LiteralRule", { enumerable: true, get: function () { return literalRule_1.LiteralRule; } });
|
|
16
|
+
var objectRule_1 = require("./objectRule");
|
|
17
|
+
Object.defineProperty(exports, "ObjectRule", { enumerable: true, get: function () { return objectRule_1.ObjectRule; } });
|
|
18
|
+
var unionRule_1 = require("./unionRule");
|
|
19
|
+
Object.defineProperty(exports, "UnionRule", { enumerable: true, get: function () { return unionRule_1.UnionRule; } });
|
|
20
|
+
var bigIntRule_1 = require("./bigIntRule");
|
|
21
|
+
Object.defineProperty(exports, "BigIntRule", { enumerable: true, get: function () { return bigIntRule_1.BigIntRule; } });
|
|
@@ -12,7 +12,7 @@ export interface NumberRuleOptions extends RuleOptions {
|
|
|
12
12
|
*
|
|
13
13
|
* Validates that a value is a number.
|
|
14
14
|
*/
|
|
15
|
-
export declare class NumberRule extends BaseValidationRule {
|
|
15
|
+
export declare class NumberRule extends BaseValidationRule<number> {
|
|
16
16
|
constructor();
|
|
17
17
|
validate(context: ValidationContext, options?: NumberRuleOptions): ValidationError | null;
|
|
18
18
|
}
|
|
@@ -13,18 +13,17 @@ class NumberRule extends validationRule_1.BaseValidationRule {
|
|
|
13
13
|
}
|
|
14
14
|
validate(context, options) {
|
|
15
15
|
const { field, value } = context;
|
|
16
|
-
|
|
17
|
-
if (typeof num !== "number" || isNaN(num))
|
|
16
|
+
if (typeof value !== "number")
|
|
18
17
|
return this.createError(field, options?.message || `${field} must be a number`, value);
|
|
19
|
-
if (options?.integer && !Number.isInteger(
|
|
18
|
+
if (options?.integer && !Number.isInteger(value))
|
|
20
19
|
return this.createError(field, `${field} must be an integer`, value);
|
|
21
|
-
if (options?.positive &&
|
|
20
|
+
if (options?.positive && value <= 0)
|
|
22
21
|
return this.createError(field, `${field} must be positive`, value);
|
|
23
|
-
if (options?.negative &&
|
|
22
|
+
if (options?.negative && value >= 0)
|
|
24
23
|
return this.createError(field, `${field} must be negative`, value);
|
|
25
|
-
if (options?.min &&
|
|
24
|
+
if (options?.min && value < options.min)
|
|
26
25
|
return this.createError(field, `${field} must be at least ${options.min}`, value);
|
|
27
|
-
if (options?.max &&
|
|
26
|
+
if (options?.max && value > options.max)
|
|
28
27
|
return this.createError(field, `${field} must be at most ${options.max}`, value);
|
|
29
28
|
return null;
|
|
30
29
|
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { FieldDefinition, RuleOptions, ValidationContext, ValidationError } from "../types";
|
|
2
|
+
import { BaseValidationRule } from "../validationRule";
|
|
3
|
+
export declare class ObjectRule extends BaseValidationRule<Record<string, unknown>> {
|
|
4
|
+
private readonly objectSchema;
|
|
5
|
+
constructor(objectSchema: Map<string, FieldDefinition>);
|
|
6
|
+
validate(context: ValidationContext, options?: RuleOptions): ValidationError | null;
|
|
7
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ObjectRule = void 0;
|
|
4
|
+
const validator_1 = require("../validator");
|
|
5
|
+
const validationRule_1 = require("../validationRule");
|
|
6
|
+
class ObjectRule extends validationRule_1.BaseValidationRule {
|
|
7
|
+
objectSchema;
|
|
8
|
+
constructor(objectSchema) {
|
|
9
|
+
super("object");
|
|
10
|
+
this.objectSchema = objectSchema;
|
|
11
|
+
}
|
|
12
|
+
validate(context, options) {
|
|
13
|
+
const { field, value } = context;
|
|
14
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
15
|
+
return this.createError(field, options?.message || `${field} must be an object`, value);
|
|
16
|
+
}
|
|
17
|
+
const nestedResult = validator_1.Validator.validate(value, this.objectSchema);
|
|
18
|
+
if (nestedResult.errors.length === 0)
|
|
19
|
+
return null;
|
|
20
|
+
const firstError = nestedResult.errors[0];
|
|
21
|
+
return {
|
|
22
|
+
...firstError,
|
|
23
|
+
field: `${field}.${firstError.field}`,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
exports.ObjectRule = ObjectRule;
|
|
@@ -4,14 +4,14 @@ export interface StringRuleOptions extends RuleOptions {
|
|
|
4
4
|
isEmpty?: boolean;
|
|
5
5
|
minLength?: number;
|
|
6
6
|
maxLength?: number;
|
|
7
|
-
|
|
7
|
+
length?: number;
|
|
8
8
|
}
|
|
9
9
|
/**
|
|
10
10
|
* String validation rule.
|
|
11
11
|
*
|
|
12
12
|
* Validates that a value is a string.
|
|
13
13
|
*/
|
|
14
|
-
export declare class StringRule extends BaseValidationRule {
|
|
14
|
+
export declare class StringRule extends BaseValidationRule<string> {
|
|
15
15
|
constructor();
|
|
16
16
|
validate(context: ValidationContext, options?: StringRuleOptions): ValidationError | null;
|
|
17
17
|
/**
|
|
@@ -46,6 +46,7 @@ export declare class StringRule extends BaseValidationRule {
|
|
|
46
46
|
* validator.string().uuid()
|
|
47
47
|
*/
|
|
48
48
|
uuid(message?: string): this;
|
|
49
|
+
regex(pattern: RegExp, message?: string): this;
|
|
49
50
|
}
|
|
50
51
|
/**
|
|
51
52
|
* Email validation rule.
|
|
@@ -72,3 +73,11 @@ export declare class UuidRule extends BaseValidationRule {
|
|
|
72
73
|
constructor();
|
|
73
74
|
validate(context: ValidationContext, options?: RuleOptions): ValidationError | null;
|
|
74
75
|
}
|
|
76
|
+
/**
|
|
77
|
+
* UUID validation rule
|
|
78
|
+
*/
|
|
79
|
+
export declare class RegExRule extends BaseValidationRule {
|
|
80
|
+
private readonly regex;
|
|
81
|
+
constructor(pattern: RegExp);
|
|
82
|
+
validate(context: ValidationContext, options?: RuleOptions): ValidationError | null;
|
|
83
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.UuidRule = exports.UrlRule = exports.EmailRule = exports.StringRule = void 0;
|
|
3
|
+
exports.RegExRule = exports.UuidRule = exports.UrlRule = exports.EmailRule = exports.StringRule = void 0;
|
|
4
4
|
const validationRule_1 = require("../validationRule");
|
|
5
5
|
/**
|
|
6
6
|
* String validation rule.
|
|
@@ -15,15 +15,14 @@ class StringRule extends validationRule_1.BaseValidationRule {
|
|
|
15
15
|
const { field, value } = context;
|
|
16
16
|
if (typeof value !== "string")
|
|
17
17
|
return this.createError(field, options?.message || `${field} must be a string`, value);
|
|
18
|
-
|
|
19
|
-
if (options?.isEmpty && str === "")
|
|
18
|
+
if (options?.isEmpty && value === "")
|
|
20
19
|
return this.createError(field, `${field} cannot be empty`, value);
|
|
21
|
-
if (options?.minLength &&
|
|
20
|
+
if (options?.minLength && value.length < options.minLength)
|
|
22
21
|
return this.createError(field, `${field} must be at least ${options.minLength} characters`, value);
|
|
23
|
-
if (options?.maxLength &&
|
|
22
|
+
if (options?.maxLength && value.length > options.maxLength)
|
|
24
23
|
return this.createError(field, `${field} must be at most ${options.maxLength} characters`, value);
|
|
25
|
-
if (options?.
|
|
26
|
-
return this.createError(field,
|
|
24
|
+
if (options?.length && value.length !== options.length)
|
|
25
|
+
return this.createError(field, `${field} must be exactly ${options.length} characters`, value);
|
|
27
26
|
return null;
|
|
28
27
|
}
|
|
29
28
|
/**
|
|
@@ -76,6 +75,13 @@ class StringRule extends validationRule_1.BaseValidationRule {
|
|
|
76
75
|
});
|
|
77
76
|
return this;
|
|
78
77
|
}
|
|
78
|
+
regex(pattern, message) {
|
|
79
|
+
this.rules.push({
|
|
80
|
+
rule: new RegExRule(pattern),
|
|
81
|
+
options: { message },
|
|
82
|
+
});
|
|
83
|
+
return this;
|
|
84
|
+
}
|
|
79
85
|
}
|
|
80
86
|
exports.StringRule = StringRule;
|
|
81
87
|
/**
|
|
@@ -132,3 +138,21 @@ class UuidRule extends validationRule_1.BaseValidationRule {
|
|
|
132
138
|
}
|
|
133
139
|
}
|
|
134
140
|
exports.UuidRule = UuidRule;
|
|
141
|
+
/**
|
|
142
|
+
* UUID validation rule
|
|
143
|
+
*/
|
|
144
|
+
class RegExRule extends validationRule_1.BaseValidationRule {
|
|
145
|
+
regex;
|
|
146
|
+
constructor(pattern) {
|
|
147
|
+
super("regex");
|
|
148
|
+
this.regex = pattern;
|
|
149
|
+
}
|
|
150
|
+
validate(context, options) {
|
|
151
|
+
const { field, value } = context;
|
|
152
|
+
if (!this.regex.test(value)) {
|
|
153
|
+
return this.createError(field, options?.message || `${field} format is invalid`, value);
|
|
154
|
+
}
|
|
155
|
+
return null;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
exports.RegExRule = RegExRule;
|
|
@@ -1,11 +1,7 @@
|
|
|
1
1
|
import type { RuleOptions, ValidationContext, ValidationError } from "../types";
|
|
2
2
|
import { BaseValidationRule } from "../validationRule";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
* Validates that a value is present and not empty.
|
|
7
|
-
*/
|
|
8
|
-
export declare class RequiredRule extends BaseValidationRule {
|
|
9
|
-
constructor();
|
|
3
|
+
export declare class UnionRule extends BaseValidationRule {
|
|
4
|
+
private readonly unionRules;
|
|
5
|
+
constructor(unionRules: BaseValidationRule[]);
|
|
10
6
|
validate(context: ValidationContext, options?: RuleOptions): ValidationError | null;
|
|
11
7
|
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.UnionRule = void 0;
|
|
4
|
+
const validationRule_1 = require("../validationRule");
|
|
5
|
+
class UnionRule extends validationRule_1.BaseValidationRule {
|
|
6
|
+
unionRules;
|
|
7
|
+
constructor(unionRules) {
|
|
8
|
+
super("union");
|
|
9
|
+
this.unionRules = unionRules;
|
|
10
|
+
}
|
|
11
|
+
validate(context, options) {
|
|
12
|
+
const { field, value } = context;
|
|
13
|
+
for (const unionRule of this.unionRules) {
|
|
14
|
+
const rules = unionRule.rules.length
|
|
15
|
+
? unionRule.rules
|
|
16
|
+
: [{ rule: unionRule, options: undefined }];
|
|
17
|
+
let hasError = false;
|
|
18
|
+
for (const { rule, options: ruleOptions } of rules) {
|
|
19
|
+
const error = rule.validate({ field, value }, ruleOptions);
|
|
20
|
+
if (error) {
|
|
21
|
+
hasError = true;
|
|
22
|
+
break;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
if (!hasError)
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
return this.createError(field, options?.message || `${field} must match at least one union rule`, value);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
exports.UnionRule = UnionRule;
|
|
@@ -16,12 +16,10 @@ export interface ValidationError {
|
|
|
16
16
|
* Full validation result.
|
|
17
17
|
*/
|
|
18
18
|
export interface ValidationResult {
|
|
19
|
-
/** Indicates whether the validation passed */
|
|
20
|
-
valid: boolean;
|
|
21
19
|
/** List of validation errors */
|
|
22
20
|
errors: ValidationError[];
|
|
23
21
|
/** Optional validated data */
|
|
24
|
-
data
|
|
22
|
+
data: Record<string, unknown>;
|
|
25
23
|
}
|
|
26
24
|
/**
|
|
27
25
|
* Options for a validation rule.
|
|
@@ -40,8 +38,6 @@ export interface ValidationContext {
|
|
|
40
38
|
field: string;
|
|
41
39
|
/** Field value */
|
|
42
40
|
value: unknown;
|
|
43
|
-
/** Full input data */
|
|
44
|
-
data: unknown;
|
|
45
41
|
}
|
|
46
42
|
/**
|
|
47
43
|
* Field definition used by the validation engine.
|
|
@@ -52,4 +48,5 @@ export interface FieldDefinition {
|
|
|
52
48
|
options?: RuleOptions;
|
|
53
49
|
}>;
|
|
54
50
|
optional: boolean;
|
|
51
|
+
defaultValue?: unknown;
|
|
55
52
|
}
|
|
@@ -8,10 +8,6 @@ import type { RuleOptions, ValidationContext, ValidationError } from "./types";
|
|
|
8
8
|
* A rule must only evaluate the value and return an error when validation fails.
|
|
9
9
|
*/
|
|
10
10
|
export interface ValidationRule {
|
|
11
|
-
/**
|
|
12
|
-
* Unique rule name.
|
|
13
|
-
*/
|
|
14
|
-
readonly name: string;
|
|
15
11
|
/**
|
|
16
12
|
* Executes the rule validation.
|
|
17
13
|
*
|
|
@@ -26,30 +22,25 @@ export interface ValidationRule {
|
|
|
26
22
|
/**
|
|
27
23
|
* Abstract base class for implementing validation rules.
|
|
28
24
|
*/
|
|
29
|
-
export declare abstract class BaseValidationRule implements ValidationRule {
|
|
30
|
-
readonly name
|
|
25
|
+
export declare abstract class BaseValidationRule<T = any> implements ValidationRule {
|
|
26
|
+
private readonly name;
|
|
31
27
|
readonly rules: Array<{
|
|
32
28
|
rule: ValidationRule;
|
|
33
29
|
options?: RuleOptions;
|
|
34
30
|
}>;
|
|
35
|
-
|
|
31
|
+
hasOptional: boolean;
|
|
32
|
+
defaultValue?: T;
|
|
36
33
|
constructor(name: string, rules?: Array<{
|
|
37
34
|
rule: ValidationRule;
|
|
38
35
|
options?: RuleOptions;
|
|
39
36
|
}>);
|
|
40
37
|
abstract validate(context: ValidationContext, options?: RuleOptions): ValidationError | null;
|
|
41
|
-
/**
|
|
42
|
-
* Marks the field as required.
|
|
43
|
-
*
|
|
44
|
-
* @param message - Optional custom error message
|
|
45
|
-
* @returns This rule instance for chaining
|
|
46
|
-
*/
|
|
47
|
-
required(message?: string): this;
|
|
48
38
|
/**
|
|
49
39
|
* Marks the field as optional.
|
|
50
40
|
*
|
|
51
41
|
* @returns This rule instance for chaining
|
|
52
42
|
*/
|
|
53
43
|
optional(): this;
|
|
44
|
+
default(value: T): this;
|
|
54
45
|
protected createError(field: string, message: string, value?: unknown): ValidationError;
|
|
55
46
|
}
|
|
@@ -1,36 +1,30 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.BaseValidationRule = void 0;
|
|
4
|
-
const rules_1 = require("./rules");
|
|
5
4
|
/**
|
|
6
5
|
* Abstract base class for implementing validation rules.
|
|
7
6
|
*/
|
|
8
7
|
class BaseValidationRule {
|
|
9
8
|
name;
|
|
10
9
|
rules;
|
|
11
|
-
|
|
10
|
+
hasOptional = false;
|
|
11
|
+
defaultValue = undefined;
|
|
12
12
|
constructor(name, rules = []) {
|
|
13
13
|
this.name = name;
|
|
14
14
|
this.rules = rules;
|
|
15
15
|
}
|
|
16
|
-
/**
|
|
17
|
-
* Marks the field as required.
|
|
18
|
-
*
|
|
19
|
-
* @param message - Optional custom error message
|
|
20
|
-
* @returns This rule instance for chaining
|
|
21
|
-
*/
|
|
22
|
-
required(message) {
|
|
23
|
-
this._optional = false;
|
|
24
|
-
this.rules.push({ rule: new rules_1.RequiredRule(), options: { message } });
|
|
25
|
-
return this;
|
|
26
|
-
}
|
|
27
16
|
/**
|
|
28
17
|
* Marks the field as optional.
|
|
29
18
|
*
|
|
30
19
|
* @returns This rule instance for chaining
|
|
31
20
|
*/
|
|
32
21
|
optional() {
|
|
33
|
-
this.
|
|
22
|
+
this.hasOptional = true;
|
|
23
|
+
return this;
|
|
24
|
+
}
|
|
25
|
+
default(value) {
|
|
26
|
+
this.defaultValue = value;
|
|
27
|
+
this.hasOptional = true;
|
|
34
28
|
return this;
|
|
35
29
|
}
|
|
36
30
|
createError(field, message, value) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { FieldDefinition } from "./types";
|
|
1
|
+
import type { FieldDefinition, RuleOptions } from "./types";
|
|
2
2
|
import type { BaseValidationRule } from "./validationRule";
|
|
3
|
-
import { BooleanRule, DateRule, type DateRuleOptions, NumberRule, type NumberRuleOptions, StringRule, type StringRuleOptions, ArrayRule, type ArrayRuleOptions, LiteralRule } from "./rules";
|
|
3
|
+
import { BooleanRule, DateRule, type DateRuleOptions, NumberRule, type NumberRuleOptions, StringRule, type StringRuleOptions, ArrayRule, type ArrayRuleOptions, LiteralRule, ObjectRule, BigIntRule, UnionRule } from "./rules";
|
|
4
4
|
/**
|
|
5
5
|
* Main validator class - provides factory methods for creating validators
|
|
6
6
|
*/
|
|
@@ -25,6 +25,16 @@ declare class Validator {
|
|
|
25
25
|
* validator.number({ min: 18, max: 65 })
|
|
26
26
|
*/
|
|
27
27
|
number(options?: NumberRuleOptions): NumberRule;
|
|
28
|
+
/**
|
|
29
|
+
* Creates a bigint validator.
|
|
30
|
+
*
|
|
31
|
+
* @param message - Optional custom error message
|
|
32
|
+
* @returns BigIntRule instance
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* validator.bigint()
|
|
36
|
+
*/
|
|
37
|
+
bigint(message?: string): BigIntRule;
|
|
28
38
|
/**
|
|
29
39
|
* Creates a boolean validator
|
|
30
40
|
*
|
|
@@ -32,7 +42,7 @@ declare class Validator {
|
|
|
32
42
|
* @returns BooleanValidator instance
|
|
33
43
|
*
|
|
34
44
|
* @example
|
|
35
|
-
* validator.boolean()
|
|
45
|
+
* validator.boolean()
|
|
36
46
|
*/
|
|
37
47
|
boolean(message?: string): BooleanRule;
|
|
38
48
|
/**
|
|
@@ -54,32 +64,60 @@ declare class Validator {
|
|
|
54
64
|
* @example
|
|
55
65
|
* validator.array({ minLength: 1 }).string()
|
|
56
66
|
*/
|
|
57
|
-
array(options?: ArrayRuleOptions): ArrayRule;
|
|
67
|
+
array(typeValid?: BaseValidationRule, options?: ArrayRuleOptions): ArrayRule;
|
|
58
68
|
/**
|
|
59
|
-
* Creates a
|
|
69
|
+
* Creates an object validator with a nested schema.
|
|
60
70
|
*
|
|
61
|
-
* @param
|
|
62
|
-
* @param
|
|
63
|
-
* @returns
|
|
71
|
+
* @param objectSchemaDefinition - Record mapping field names to their validators
|
|
72
|
+
* @param options - Optional rule options (message, etc.)
|
|
73
|
+
* @returns ObjectRule instance
|
|
64
74
|
*
|
|
65
75
|
* @example
|
|
66
|
-
* validator.
|
|
76
|
+
* validator.object({
|
|
77
|
+
* id: validator.number(),
|
|
78
|
+
* name: validator.string({ maxLength: 100 })
|
|
79
|
+
* })
|
|
67
80
|
*/
|
|
68
|
-
|
|
81
|
+
object(objectSchemaDefinition: Record<string, BaseValidationRule>, options?: RuleOptions): ObjectRule;
|
|
69
82
|
/**
|
|
70
|
-
* Creates a
|
|
83
|
+
* Creates a union validator that passes if any of the supplied rules pass.
|
|
71
84
|
*
|
|
72
|
-
* @param
|
|
73
|
-
* @
|
|
85
|
+
* @param unionRules - Array of validation rules to try in order
|
|
86
|
+
* @param options - Optional rule options (message, etc.)
|
|
87
|
+
* @returns UnionRule instance
|
|
74
88
|
*
|
|
75
89
|
* @example
|
|
76
|
-
*
|
|
77
|
-
*
|
|
78
|
-
*
|
|
79
|
-
*
|
|
80
|
-
* })
|
|
90
|
+
* validator.union([
|
|
91
|
+
* validator.string(),
|
|
92
|
+
* validator.number()
|
|
93
|
+
* ])
|
|
81
94
|
*/
|
|
82
|
-
|
|
95
|
+
union(unionRules: BaseValidationRule[], options?: RuleOptions): UnionRule;
|
|
96
|
+
/**
|
|
97
|
+
* Creates a literal value validator
|
|
98
|
+
*
|
|
99
|
+
* @param value - The literal value to validate against
|
|
100
|
+
* @param message - Optional custom error message
|
|
101
|
+
* @returns LiteralRule instance
|
|
102
|
+
*
|
|
103
|
+
* @example
|
|
104
|
+
* validator.literal("admin")
|
|
105
|
+
*/
|
|
106
|
+
literal(value: unknown, message?: string): LiteralRule;
|
|
83
107
|
}
|
|
84
|
-
|
|
108
|
+
/**
|
|
109
|
+
* Creates a validation schema from a field definition object
|
|
110
|
+
*
|
|
111
|
+
* @param schemaDefinition - Object mapping field names to validators
|
|
112
|
+
* @returns ValidationSchema instance
|
|
113
|
+
*
|
|
114
|
+
* @example
|
|
115
|
+
* const userSchema = validator.schema({
|
|
116
|
+
* name: validator.string({ maxLength: 60 }),
|
|
117
|
+
* email: validator.email(),
|
|
118
|
+
* age: validator.number({ min: 18 })
|
|
119
|
+
* })
|
|
120
|
+
*/
|
|
121
|
+
export declare const schema: (schemaDefinition: Record<string, BaseValidationRule>) => Map<string, FieldDefinition>;
|
|
122
|
+
export declare const v: Validator;
|
|
85
123
|
export {};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.v = exports.schema = void 0;
|
|
4
4
|
const rules_1 = require("./rules");
|
|
5
5
|
/**
|
|
6
6
|
* Main validator class - provides factory methods for creating validators
|
|
@@ -34,6 +34,20 @@ class Validator {
|
|
|
34
34
|
numberRule.rules.push({ rule: numberRule, options });
|
|
35
35
|
return numberRule;
|
|
36
36
|
}
|
|
37
|
+
/**
|
|
38
|
+
* Creates a bigint validator.
|
|
39
|
+
*
|
|
40
|
+
* @param message - Optional custom error message
|
|
41
|
+
* @returns BigIntRule instance
|
|
42
|
+
*
|
|
43
|
+
* @example
|
|
44
|
+
* validator.bigint()
|
|
45
|
+
*/
|
|
46
|
+
bigint(message) {
|
|
47
|
+
const bigIntRule = new rules_1.BigIntRule();
|
|
48
|
+
bigIntRule.rules.push({ rule: bigIntRule, options: { message } });
|
|
49
|
+
return bigIntRule;
|
|
50
|
+
}
|
|
37
51
|
/**
|
|
38
52
|
* Creates a boolean validator
|
|
39
53
|
*
|
|
@@ -41,7 +55,7 @@ class Validator {
|
|
|
41
55
|
* @returns BooleanValidator instance
|
|
42
56
|
*
|
|
43
57
|
* @example
|
|
44
|
-
* validator.boolean()
|
|
58
|
+
* validator.boolean()
|
|
45
59
|
*/
|
|
46
60
|
boolean(message) {
|
|
47
61
|
const booleanRule = new rules_1.BooleanRule();
|
|
@@ -71,11 +85,48 @@ class Validator {
|
|
|
71
85
|
* @example
|
|
72
86
|
* validator.array({ minLength: 1 }).string()
|
|
73
87
|
*/
|
|
74
|
-
array(options) {
|
|
75
|
-
const arrayRule = new rules_1.ArrayRule();
|
|
88
|
+
array(typeValid, options) {
|
|
89
|
+
const arrayRule = new rules_1.ArrayRule(typeValid);
|
|
76
90
|
arrayRule.rules.push({ rule: arrayRule, options });
|
|
77
91
|
return arrayRule;
|
|
78
92
|
}
|
|
93
|
+
/**
|
|
94
|
+
* Creates an object validator with a nested schema.
|
|
95
|
+
*
|
|
96
|
+
* @param objectSchemaDefinition - Record mapping field names to their validators
|
|
97
|
+
* @param options - Optional rule options (message, etc.)
|
|
98
|
+
* @returns ObjectRule instance
|
|
99
|
+
*
|
|
100
|
+
* @example
|
|
101
|
+
* validator.object({
|
|
102
|
+
* id: validator.number(),
|
|
103
|
+
* name: validator.string({ maxLength: 100 })
|
|
104
|
+
* })
|
|
105
|
+
*/
|
|
106
|
+
object(objectSchemaDefinition, options) {
|
|
107
|
+
const objectSchema = (0, exports.schema)(objectSchemaDefinition);
|
|
108
|
+
const objectRule = new rules_1.ObjectRule(objectSchema);
|
|
109
|
+
objectRule.rules.push({ rule: objectRule, options });
|
|
110
|
+
return objectRule;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Creates a union validator that passes if any of the supplied rules pass.
|
|
114
|
+
*
|
|
115
|
+
* @param unionRules - Array of validation rules to try in order
|
|
116
|
+
* @param options - Optional rule options (message, etc.)
|
|
117
|
+
* @returns UnionRule instance
|
|
118
|
+
*
|
|
119
|
+
* @example
|
|
120
|
+
* validator.union([
|
|
121
|
+
* validator.string(),
|
|
122
|
+
* validator.number()
|
|
123
|
+
* ])
|
|
124
|
+
*/
|
|
125
|
+
union(unionRules, options) {
|
|
126
|
+
const unionRule = new rules_1.UnionRule(unionRules);
|
|
127
|
+
unionRule.rules.push({ rule: unionRule, options });
|
|
128
|
+
return unionRule;
|
|
129
|
+
}
|
|
79
130
|
/**
|
|
80
131
|
* Creates a literal value validator
|
|
81
132
|
*
|
|
@@ -84,7 +135,7 @@ class Validator {
|
|
|
84
135
|
* @returns LiteralRule instance
|
|
85
136
|
*
|
|
86
137
|
* @example
|
|
87
|
-
* validator.literal("admin")
|
|
138
|
+
* validator.literal("admin")
|
|
88
139
|
*/
|
|
89
140
|
literal(value, message) {
|
|
90
141
|
const literalRule = new rules_1.LiteralRule(value);
|
|
@@ -94,29 +145,6 @@ class Validator {
|
|
|
94
145
|
});
|
|
95
146
|
return literalRule;
|
|
96
147
|
}
|
|
97
|
-
/**
|
|
98
|
-
* Creates a validation schema from a field definition object
|
|
99
|
-
*
|
|
100
|
-
* @param schemaDefinition - Object mapping field names to validators
|
|
101
|
-
* @returns ValidationSchema instance
|
|
102
|
-
*
|
|
103
|
-
* @example
|
|
104
|
-
* const userSchema = validator.schema({
|
|
105
|
-
* name: validator.string({ maxLength: 60 }),
|
|
106
|
-
* email: validator.email().required(),
|
|
107
|
-
* age: validator.number({ min: 18 })
|
|
108
|
-
* })
|
|
109
|
-
*/
|
|
110
|
-
schema(schemaDefinition) {
|
|
111
|
-
const schema = new ValidationSchema();
|
|
112
|
-
for (const [fieldName, validator] of Object.entries(schemaDefinition)) {
|
|
113
|
-
schema.addField(fieldName, {
|
|
114
|
-
rules: validator.rules,
|
|
115
|
-
optional: validator._optional,
|
|
116
|
-
});
|
|
117
|
-
}
|
|
118
|
-
return schema.build();
|
|
119
|
-
}
|
|
120
148
|
}
|
|
121
149
|
/**
|
|
122
150
|
* ValidationSchema - Internal representation of validation rules
|
|
@@ -145,23 +173,31 @@ class ValidationSchema {
|
|
|
145
173
|
build() {
|
|
146
174
|
return this.fields;
|
|
147
175
|
}
|
|
148
|
-
/**
|
|
149
|
-
* Gets a specific field definition
|
|
150
|
-
*
|
|
151
|
-
* @param name - Field name
|
|
152
|
-
* @returns Field definition or undefined
|
|
153
|
-
*/
|
|
154
|
-
getField(name) {
|
|
155
|
-
return this.fields.get(name);
|
|
156
|
-
}
|
|
157
|
-
/**
|
|
158
|
-
* Gets all field names in the schema
|
|
159
|
-
*
|
|
160
|
-
* @returns Array of field names
|
|
161
|
-
*/
|
|
162
|
-
getFieldNames() {
|
|
163
|
-
return Array.from(this.fields.keys());
|
|
164
|
-
}
|
|
165
176
|
}
|
|
177
|
+
/**
|
|
178
|
+
* Creates a validation schema from a field definition object
|
|
179
|
+
*
|
|
180
|
+
* @param schemaDefinition - Object mapping field names to validators
|
|
181
|
+
* @returns ValidationSchema instance
|
|
182
|
+
*
|
|
183
|
+
* @example
|
|
184
|
+
* const userSchema = validator.schema({
|
|
185
|
+
* name: validator.string({ maxLength: 60 }),
|
|
186
|
+
* email: validator.email(),
|
|
187
|
+
* age: validator.number({ min: 18 })
|
|
188
|
+
* })
|
|
189
|
+
*/
|
|
190
|
+
const schema = (schemaDefinition) => {
|
|
191
|
+
const schema = new ValidationSchema();
|
|
192
|
+
for (const [fieldName, validator] of Object.entries(schemaDefinition)) {
|
|
193
|
+
schema.addField(fieldName, {
|
|
194
|
+
rules: validator.rules,
|
|
195
|
+
optional: validator.hasOptional,
|
|
196
|
+
defaultValue: validator.defaultValue,
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
return schema.build();
|
|
200
|
+
};
|
|
201
|
+
exports.schema = schema;
|
|
166
202
|
// Export a singleton instance for convenience
|
|
167
|
-
exports.
|
|
203
|
+
exports.v = new Validator();
|
|
@@ -47,9 +47,9 @@ export declare class Validator {
|
|
|
47
47
|
* @throws {ValidationException} When validation fails
|
|
48
48
|
*
|
|
49
49
|
* @example
|
|
50
|
-
* const schema =
|
|
51
|
-
*
|
|
52
|
-
*
|
|
50
|
+
* const schema = schema({
|
|
51
|
+
* email: validator.string().emaiil().required()
|
|
52
|
+
* })
|
|
53
53
|
*
|
|
54
54
|
* const data = Validator.validateOrFail({ email: "a@b.com" }, schema);
|
|
55
55
|
* // `data` is the original input when valid
|
|
@@ -43,14 +43,20 @@ class Validator {
|
|
|
43
43
|
static validate(data, schema) {
|
|
44
44
|
const errors = [];
|
|
45
45
|
for (const [fieldName, fieldDef] of schema.entries()) {
|
|
46
|
-
const value = data[fieldName];
|
|
47
46
|
const context = {
|
|
48
47
|
field: fieldName,
|
|
49
|
-
value,
|
|
50
|
-
data,
|
|
48
|
+
value: data[fieldName],
|
|
51
49
|
};
|
|
52
|
-
if (fieldDef.optional &&
|
|
50
|
+
if (fieldDef.optional && data[fieldName] === undefined)
|
|
53
51
|
continue;
|
|
52
|
+
if (data[fieldName] === undefined || data[fieldName] === null) {
|
|
53
|
+
errors.push({
|
|
54
|
+
field: fieldName,
|
|
55
|
+
message: `${fieldName} is required`,
|
|
56
|
+
rule: "required",
|
|
57
|
+
});
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
54
60
|
for (const { rule, options } of fieldDef.rules) {
|
|
55
61
|
const error = rule.validate(context, options);
|
|
56
62
|
if (error) {
|
|
@@ -60,7 +66,6 @@ class Validator {
|
|
|
60
66
|
}
|
|
61
67
|
}
|
|
62
68
|
return {
|
|
63
|
-
valid: errors.length === 0,
|
|
64
69
|
errors,
|
|
65
70
|
data: errors.length === 0 ? data : undefined,
|
|
66
71
|
};
|
|
@@ -74,17 +79,21 @@ class Validator {
|
|
|
74
79
|
* @throws {ValidationException} When validation fails
|
|
75
80
|
*
|
|
76
81
|
* @example
|
|
77
|
-
* const schema =
|
|
78
|
-
*
|
|
79
|
-
*
|
|
82
|
+
* const schema = schema({
|
|
83
|
+
* email: validator.string().emaiil().required()
|
|
84
|
+
* })
|
|
80
85
|
*
|
|
81
86
|
* const data = Validator.validateOrFail({ email: "a@b.com" }, schema);
|
|
82
87
|
* // `data` is the original input when valid
|
|
83
88
|
*/
|
|
84
89
|
static validateOrFail(data, schema) {
|
|
85
90
|
const result = this.validate(data, schema);
|
|
86
|
-
if (
|
|
91
|
+
if (result.errors.length !== 0)
|
|
87
92
|
throw new validationException_1.ValidationException(result.errors);
|
|
93
|
+
for (const [fieldName, fieldDef] of schema.entries()) {
|
|
94
|
+
if (!(fieldName in result) && fieldDef.defaultValue !== undefined)
|
|
95
|
+
result.data[fieldName] = fieldDef.defaultValue;
|
|
96
|
+
}
|
|
88
97
|
return result.data;
|
|
89
98
|
}
|
|
90
99
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "skyguard-js",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.2",
|
|
4
4
|
"description": "A lightweight, dependency-free TypeScript backend framework",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -72,19 +72,19 @@
|
|
|
72
72
|
},
|
|
73
73
|
"license": "MIT",
|
|
74
74
|
"devDependencies": {
|
|
75
|
-
"@eslint/js": "
|
|
75
|
+
"@eslint/js": "10.0.1",
|
|
76
76
|
"@jest/globals": "30.2.0",
|
|
77
77
|
"@types/jest": "30.0.0",
|
|
78
|
-
"@types/node": "25.0
|
|
79
|
-
"eslint": "
|
|
78
|
+
"@types/node": "25.3.0",
|
|
79
|
+
"eslint": "10.0.2",
|
|
80
80
|
"eslint-config-prettier": "10.1.8",
|
|
81
|
-
"globals": "17.
|
|
81
|
+
"globals": "17.3.0",
|
|
82
82
|
"jest": "30.2.0",
|
|
83
|
-
"nodemon": "3.1.
|
|
83
|
+
"nodemon": "3.1.14",
|
|
84
84
|
"prettier": "3.8.1",
|
|
85
85
|
"ts-jest": "29.4.6",
|
|
86
86
|
"ts-node": "10.9.2",
|
|
87
87
|
"typescript": "5.9.3",
|
|
88
|
-
"typescript-eslint": "8.
|
|
88
|
+
"typescript-eslint": "8.56.1"
|
|
89
89
|
}
|
|
90
90
|
}
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.RequiredRule = void 0;
|
|
4
|
-
const validationRule_1 = require("../validationRule");
|
|
5
|
-
/**
|
|
6
|
-
* Required field validation rule.
|
|
7
|
-
*
|
|
8
|
-
* Validates that a value is present and not empty.
|
|
9
|
-
*/
|
|
10
|
-
class RequiredRule extends validationRule_1.BaseValidationRule {
|
|
11
|
-
constructor() {
|
|
12
|
-
super("required");
|
|
13
|
-
}
|
|
14
|
-
validate(context, options) {
|
|
15
|
-
const { field, value } = context;
|
|
16
|
-
if (value === null || value === undefined)
|
|
17
|
-
return this.createError(field, options?.message || `${field} is required`, value);
|
|
18
|
-
return null;
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
exports.RequiredRule = RequiredRule;
|