@fluojs/validation
Advanced tools
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../src/validation.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,WAAW,EAEZ,MAAM,cAAc,CAAC;AAatB,OAAO,KAAK,EAAmB,SAAS,EAAE,MAAM,YAAY,CAAC;AAgxB7D;;GAEG;AACH,qBAAa,gBAAiB,YAAW,SAAS;IAC1C,QAAQ,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAM5D,WAAW,CAAC,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;CAczE"} | ||
| {"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../src/validation.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,WAAW,EAEZ,MAAM,cAAc,CAAC;AAatB,OAAO,KAAK,EAAmB,SAAS,EAAE,MAAM,YAAY,CAAC;AAsyB7D;;GAEG;AACH,qBAAa,gBAAiB,YAAW,SAAS;IAC1C,QAAQ,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAQ5D,WAAW,CAAC,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;CAYzE"} |
+22
-15
| import validator from 'validator'; | ||
| import { getClassValidationRules, getDtoBindingSchema, getDtoValidationSchema } from '@fluojs/core/internal'; | ||
| import { DtoValidationError } from './errors.js'; | ||
| function isClassConstructor(dto) { | ||
| return typeof dto === 'function' && Function.prototype.toString.call(dto).startsWith('class '); | ||
| } | ||
| function resolveNestedDto(dto) { | ||
| if (typeof dto === 'function' && 'prototype' in dto && dto.prototype) { | ||
| if (isClassConstructor(dto)) { | ||
| return dto; | ||
@@ -286,3 +289,3 @@ } | ||
| describe: field => `${field} is invalid.`, | ||
| validate: (rule, value) => typeof value === 'string' && runValidatorJs(rule, value) | ||
| validate: (rule, value) => runValidatorJs(rule, value) | ||
| }, | ||
@@ -403,2 +406,11 @@ arrayContains: { | ||
| function runValidatorJs(rule, value) { | ||
| if (rule.validator === 'latitude') { | ||
| return typeof value === 'number' && Number.isFinite(value) && value >= -90 && value <= 90; | ||
| } | ||
| if (rule.validator === 'longitude') { | ||
| return typeof value === 'number' && Number.isFinite(value) && value >= -180 && value <= 180; | ||
| } | ||
| if (typeof value !== 'string') { | ||
| return false; | ||
| } | ||
| switch (rule.validator) { | ||
@@ -475,12 +487,2 @@ case 'alpha': | ||
| return validator.isISO8601(value); | ||
| case 'latitude': | ||
| { | ||
| const number = Number(value); | ||
| return !Number.isNaN(number) && number >= -90 && number <= 90; | ||
| } | ||
| case 'longitude': | ||
| { | ||
| const number = Number(value); | ||
| return !Number.isNaN(number) && number >= -180 && number <= 180; | ||
| } | ||
| case 'latLong': | ||
@@ -506,2 +508,8 @@ return validator.isLatLong(value); | ||
| } | ||
| function assertValidRootValue(value, target) { | ||
| if (value instanceof target || isPlainObject(value)) { | ||
| return; | ||
| } | ||
| throw new DtoValidationError('Validation failed.', [buildInvalidRootIssue()]); | ||
| } | ||
| function getRuleValues(value) { | ||
@@ -653,2 +661,3 @@ return getIterableValues(value) ?? [value]; | ||
| async validate(value, target) { | ||
| assertValidRootValue(value, target); | ||
| const issues = await collectValidationIssues(target, value); | ||
@@ -659,5 +668,3 @@ if (issues.length === 0) return; | ||
| async materialize(value, target) { | ||
| if (!(value instanceof target) && !isPlainObject(value)) { | ||
| throw new DtoValidationError('Validation failed.', [buildInvalidRootIssue()]); | ||
| } | ||
| assertValidRootValue(value, target); | ||
| const instance = createNestedDtoInstance(target, value, { | ||
@@ -664,0 +671,0 @@ active: new WeakSet() |
+2
-2
@@ -12,3 +12,3 @@ { | ||
| ], | ||
| "version": "1.0.0-beta.3", | ||
| "version": "1.0.0-beta.4", | ||
| "private": false, | ||
@@ -46,3 +46,3 @@ "license": "MIT", | ||
| "validator": "^13.15.26", | ||
| "@fluojs/core": "^1.0.0-beta.4" | ||
| "@fluojs/core": "^1.0.0-beta.5" | ||
| }, | ||
@@ -49,0 +49,0 @@ "devDependencies": { |
+9
-0
@@ -67,2 +67,7 @@ # @fluojs/validation | ||
| `validate()`는 문자열, 배열, `null`, `undefined` 같은 잘못된 루트 값을 field 또는 | ||
| class rule이 실행되기 전에 deterministic `DtoValidationError`로 거부합니다. 이미 | ||
| 생성된 대상 DTO 인스턴스와 plain 루트 객체는 허용하므로 request-pipeline binder가 | ||
| 준비한 DTO payload를 scalar coercion 없이 검증할 수 있습니다. | ||
| `materialize()`는 plain 입력 객체의 안전한 own enumerable 속성을 복사하고, | ||
@@ -72,2 +77,4 @@ DTO 바인딩 메타데이터를 적용한 뒤 `@ValidateNested(...)` 필드를 재귀적으로 | ||
| binder가 검증 전에 담당한다는 request-pipeline 계약을 유지합니다. | ||
| 선언된 중첩 DTO의 인스턴스인 기존 중첩 값은 그대로 보존하고, plain 중첩 값만 | ||
| 해당 필드 또는 collection entry 단위로 실체화합니다. | ||
| `materialize()`에 넘기는 루트 값은 plain 객체이거나 대상 DTO 인스턴스여야 합니다. | ||
@@ -129,2 +136,3 @@ 문자열, 배열, `null` 같은 잘못된 루트 값은 대상 DTO 생성자나 필드 initializer가 | ||
| `@ValidateNested(...)`는 객체 필드, 배열, `Set`, `Map`을 지원합니다. 중첩 DTO path는 validation issue에서 dot/index 표기법을 사용하며, cycle은 안전하게 감지되고 shared reference는 허용됩니다. | ||
| 중첩 타입을 지연 해석해야 할 때는 DTO 클래스 자체나 `() => ChildDto`, `function resolveChildDto() { return ChildDto; }` 같은 lazy constructor factory를 전달할 수 있습니다. | ||
@@ -134,2 +142,3 @@ ### 암묵적 scalar coercion 없음 | ||
| `materialize()`는 의도적으로 엄격합니다. Transport가 `'42'`를 넘기고 DTO가 `number`를 기대한다면, transport나 binding layer가 먼저 변환해야 합니다. | ||
| `@IsLatitude()`, `@IsLongitude()`를 포함한 numeric validator는 numeric string을 이미 변환된 number처럼 취급하지 않고 DTO의 numeric 값을 검증합니다. | ||
@@ -136,0 +145,0 @@ ## 공개 API |
+11
-0
@@ -69,2 +69,8 @@ # @fluojs/validation | ||
| `validate()` rejects malformed roots such as strings, arrays, `null`, and | ||
| `undefined` with a deterministic `DtoValidationError` before field or class rules | ||
| run. It accepts already-created target DTO instances and plain root objects so | ||
| request-pipeline binders can validate their prepared DTO payloads without scalar | ||
| coercion. | ||
| `materialize()` copies safe own enumerable properties from plain input objects, | ||
@@ -74,2 +80,5 @@ applies DTO binding metadata, and recursively hydrates `@ValidateNested(...)` | ||
| source selection and scalar conversion before validation runs. | ||
| Existing nested values that are already instances of the declared nested DTO are | ||
| preserved; plain nested values are hydrated only for the affected nested field or | ||
| collection entry. | ||
| The root value passed to `materialize()` must already be a plain object or an | ||
@@ -131,2 +140,3 @@ instance of the target DTO; malformed roots such as strings, arrays, and `null` | ||
| `@ValidateNested(...)` supports object fields, arrays, `Set`, and `Map`. Nested DTO paths use dot/index notation in validation issues, cycles are detected safely, and shared references are allowed. | ||
| Pass either a DTO class or a lazy constructor factory such as `() => ChildDto` or `function resolveChildDto() { return ChildDto; }` when nested types need deferred resolution. | ||
@@ -136,2 +146,3 @@ ### No implicit scalar coercion | ||
| `materialize()` is intentionally strict. If a transport gives you `'42'` and your DTO expects `number`, the transport or binding layer must convert it first. | ||
| Numeric validators, including `@IsLatitude()` and `@IsLongitude()`, validate numeric DTO values without treating numeric strings as already-converted numbers. | ||
@@ -138,0 +149,0 @@ ## Public API |
105367
1.94%2305
0.3%170
6.92%Updated