@vytches/ddd-validation 0.26.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,54 @@
1
+ import { IValidationErrors, IValidator } from '../../../contracts/src/index.ts';
2
+ import { Result } from '../../../utils/src/index.ts';
3
+ import { ValidationError, ValidationErrors } from '../validation-error';
4
+ export declare abstract class BaseValidationAdapter<T, TSchema = unknown> implements IValidator<T> {
5
+ protected readonly schema: TSchema;
6
+ constructor(schema: TSchema);
7
+ /**
8
+ * Główna metoda walidacji - musi być zaimplementowana przez konkretne adaptery
9
+ */
10
+ abstract validate(value: T): Result<T, IValidationErrors>;
11
+ /**
12
+ * Helper method do tworzenia ValidationError z kontekstem
13
+ */
14
+ protected createValidationError(property: string, message: string, context?: Record<string, unknown>): ValidationError;
15
+ /**
16
+ * Helper method do tworzenia ValidationErrors z tablicy błędów
17
+ */
18
+ protected createValidationErrors(errors: ValidationError[]): ValidationErrors;
19
+ /**
20
+ * Helper method do konwersji ścieżki na string
21
+ */
22
+ protected pathToString(path: (string | number)[]): string;
23
+ /**
24
+ * Helper method do tworzenia Result.fail z błędami
25
+ */
26
+ protected failWithErrors(errors: ValidationError[]): Result<T, IValidationErrors>;
27
+ /**
28
+ * Helper method do tworzenia Result.ok
29
+ */
30
+ protected success(value: T): Result<T, IValidationErrors>;
31
+ }
32
+ export interface ErrorMapper<TExternalError> {
33
+ (error: TExternalError): ValidationError;
34
+ }
35
+ export declare class AdapterUtils {
36
+ /**
37
+ * Tworzy prosty adapter z funkcji walidacyjnej
38
+ */
39
+ static create<T>(validateFn: (value: T) => {
40
+ success: boolean;
41
+ errors?: string[] | undefined;
42
+ }, errorProperty?: string): IValidator<T>;
43
+ /**
44
+ * Łączy wiele adapterów w jeden
45
+ */
46
+ static combine<T>(...adapters: IValidator<T>[]): IValidator<T>;
47
+ /**
48
+ * Tworzy adapter z custom error mapping
49
+ */
50
+ static withErrorMapping<T, TError>(validateFn: (value: T) => {
51
+ success: boolean;
52
+ errors?: TError[] | undefined;
53
+ }, errorMapper: ErrorMapper<TError>): IValidator<T>;
54
+ }
@@ -0,0 +1,2 @@
1
+ export * from './base-adapter';
2
+ export { BaseValidationAdapter, AdapterUtils } from './base-adapter';
@@ -0,0 +1,29 @@
1
+ import { ISpecification } from '../../../contracts/src/index.ts';
2
+ import { Result } from '../../../utils/src/index.ts';
3
+ import { ValidationErrors } from '../validation-error';
4
+ import { BusinessRuleValidator } from './business-rule-validator';
5
+ export declare class BusinessRuleValidatorExtension<T> {
6
+ private validator;
7
+ constructor(validator: BusinessRuleValidator<T>);
8
+ /**
9
+ * Converts the validator to a specification
10
+ */
11
+ toSpecification(_errorMessage?: string): ISpecification<T>;
12
+ /**
13
+ * Validates an object using additional specifications
14
+ */
15
+ validateWithSpecifications(value: T, ...specs: ISpecification<T>[]): Result<T, ValidationErrors>;
16
+ }
17
+ declare module './business-rule-validator' {
18
+ interface BusinessRuleValidator<T> {
19
+ /**
20
+ * Converts this validator to a specification
21
+ */
22
+ toSpecification(errorMessage?: string): ISpecification<T>;
23
+ /**
24
+ * Validates value with additional specifications
25
+ */
26
+ validateWithSpecifications(value: T, ...specs: ISpecification<T>[]): Result<T, ValidationErrors>;
27
+ apply(rule: (validator: BusinessRuleValidator<T>) => BusinessRuleValidator<T>): BusinessRuleValidator<T>;
28
+ }
29
+ }
@@ -0,0 +1,51 @@
1
+ import { ISpecification, IValidator } from '../../../contracts/src/index.ts';
2
+ import { Result } from '../../../utils/src/index.ts';
3
+ import { ValidationErrors } from '../validation-error';
4
+ export declare class BusinessRuleValidator<T> implements IValidator<T> {
5
+ private rules;
6
+ private stopOnFirstFailure;
7
+ private lastCondition;
8
+ addRule(property: string, validationFn: (value: T) => boolean, message: string, context?: Record<string, any>): BusinessRuleValidator<T>;
9
+ addSpecification(property: string, specification: ISpecification<T>, message: string, context?: Record<string, any>): BusinessRuleValidator<T>;
10
+ /**
11
+ * Dodaje globalną specyfikację, która dotyczy całego obiektu
12
+ */
13
+ mustSatisfy(specification: ISpecification<T>, message: string, context?: Record<string, any>): BusinessRuleValidator<T>;
14
+ /**
15
+ * Dodaje walidację dla konkretnej właściwości z użyciem specyfikacji
16
+ */
17
+ propertyMustSatisfy<P>(property: keyof T & string, specification: ISpecification<P>, message: string, getValue: (obj: T) => P, context?: Record<string, any>): BusinessRuleValidator<T>;
18
+ /**
19
+ * Dodaje walidację dla zagnieżdżonego obiektu
20
+ */
21
+ addNested<P>(property: string, validator: IValidator<P>, getValue: (obj: T) => P | undefined | null): BusinessRuleValidator<T>;
22
+ /**
23
+ * Dodaje walidację warunkową
24
+ */
25
+ when(condition: (value: T) => boolean, thenValidator: (validator: BusinessRuleValidator<T>) => void): BusinessRuleValidator<T>;
26
+ /**
27
+ * Dodaje walidację warunkową
28
+ */
29
+ otherwise(elseValidator: (validator: BusinessRuleValidator<T>) => void): BusinessRuleValidator<T>;
30
+ /**
31
+ * Dodaje walidację warunkową opartą na specyfikacji
32
+ */
33
+ whenSatisfies(specification: ISpecification<T>, thenValidator: (validator: BusinessRuleValidator<T>) => void): BusinessRuleValidator<T>;
34
+ /**
35
+ * Konfiguruje walidator do zatrzymania po pierwszym błędzie
36
+ */
37
+ setStopOnFirstFailure(): BusinessRuleValidator<T>;
38
+ /**
39
+ * Przeprowadza walidację
40
+ */
41
+ validate(value: T): Result<T, ValidationErrors>;
42
+ /**
43
+ * Łączy ten walidator z innym
44
+ */
45
+ and(other: IValidator<T>): BusinessRuleValidator<T>;
46
+ /**
47
+ * Tworzy nowy walidator ze specyfikacji
48
+ */
49
+ static fromSpecification<T>(specification: ISpecification<T>, message: string, context?: Record<string, any>): BusinessRuleValidator<T>;
50
+ static create<T>(): BusinessRuleValidator<T>;
51
+ }
@@ -0,0 +1,2 @@
1
+ export * from './business-rule-validator';
2
+ export * from './business-rule-validator-extension';
package/dist/index.cjs ADDED
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const o=require("@vytches/ddd-utils");class u{and(t){return new R(this,t)}or(t){return new v(this,t)}not(){return new B(this)}static create(t){return new N(t)}}let N=class extends u{constructor(t){super(),this.predicate=t}isSatisfiedBy(t){return this.predicate(t)}};class R extends u{constructor(t,e){super(),this.left=t,this.right=e}isSatisfiedBy(t){return this.left.isSatisfiedBy(t)&&this.right.isSatisfiedBy(t)}}class v extends u{constructor(t,e){super(),this.left=t,this.right=e}isSatisfiedBy(t){return this.left.isSatisfiedBy(t)||this.right.isSatisfiedBy(t)}}class B extends u{constructor(t){super(),this.spec=t}isSatisfiedBy(t){return!this.spec.isSatisfiedBy(t)}}class y{and(t){return new x(this,t)}or(t){return new F(this,t)}not(){return new E(this)}static create(t,e,i){return new b(t,e,i)}async explainFailureAsync(t,e){return null}}class b extends y{constructor(t,e,i){super(),this.predicate=t,this.name=e,this.description=i}async isSatisfiedByAsync(t,e){return this.predicate(t,e)}}class x extends y{constructor(t,e){super(),this.left=t,this.right=e,this.name=`(${this.left.name||"spec"} AND ${this.right.name||"spec"})`,this.description="Both specifications must be satisfied"}async isSatisfiedByAsync(t,e){const[i,s]=await Promise.all([this.left.isSatisfiedByAsync(t,e),this.right.isSatisfiedByAsync(t,e)]);return i&&s}async explainFailureAsync(t,e){const[i,s]=await Promise.all([this.left.isSatisfiedByAsync(t,e),this.right.isSatisfiedByAsync(t,e)]),n=[];if(!i&&this.left.explainFailureAsync){const a=await this.left.explainFailureAsync(t,e);a&&n.push(a)}if(!s&&this.right.explainFailureAsync){const a=await this.right.explainFailureAsync(t,e);a&&n.push(a)}return n.length>0?n.join(" AND "):null}}class F extends y{constructor(t,e){super(),this.left=t,this.right=e,this.name=`(${this.left.name||"spec"} OR ${this.right.name||"spec"})`,this.description="At least one specification must be satisfied"}async isSatisfiedByAsync(t,e){const[i,s]=await Promise.all([this.left.isSatisfiedByAsync(t,e),this.right.isSatisfiedByAsync(t,e)]);return i||s}async explainFailureAsync(t,e){const[i,s]=await Promise.all([this.left.isSatisfiedByAsync(t,e),this.right.isSatisfiedByAsync(t,e)]);if(!i&&!s){const n=[];if(this.left.explainFailureAsync){const a=await this.left.explainFailureAsync(t,e);a&&n.push(a)}if(this.right.explainFailureAsync){const a=await this.right.explainFailureAsync(t,e);a&&n.push(a)}return n.length>0?`(${n.join(" OR ")})`:null}return null}}class E extends y{constructor(t){super(),this.spec=t,this.name=`NOT(${this.spec.name||"spec"})`,this.description="The specification must NOT be satisfied"}async isSatisfiedByAsync(t,e){return!await this.spec.isSatisfiedByAsync(t,e)}async explainFailureAsync(t,e){return await this.spec.isSatisfiedByAsync(t,e)?`Expected specification to fail: ${this.spec.name||"specification"}`:null}}class O extends u{constructor(t){super(),this.inner=t,this.cache=new WeakMap}isSatisfiedBy(t){if(t===null||typeof t!="object"&&typeof t!="function")return this.inner.isSatisfiedBy(t);const e=this.cache.get(t);if(e!==void 0)return e;const i=this.inner.isSatisfiedBy(t);return this.cache.set(t,i),i}invalidate(t){this.cache.delete(t)}explainFailure(t){const e=this.inner;return e.explainFailure?e.explainFailure(t)??null:null}}class m extends u{isSatisfiedBy(t){return!0}}class g extends u{isSatisfiedBy(t){return!1}}class P extends u{constructor(t){super(),this.predicate=t}isSatisfiedBy(t){return this.predicate(t)}}class C extends u{constructor(t,e){super(),this.propertyName=t,this.expectedValue=e}isSatisfiedBy(t){return t[this.propertyName]===this.expectedValue}}class V extends u{constructor(t,e){super(),this.propertyName=t,this.possibleValues=e}isSatisfiedBy(t){return this.possibleValues.includes(t[this.propertyName])}}class $ extends u{constructor(t,e,i){super(),this.propertyName=t,this.min=e,this.max=i}isSatisfiedBy(t){const e=t[this.propertyName];return e>=this.min&&e<=this.max}}const k={alwaysTrue(){return new m},alwaysFalse(){return new g},create(r){return new P(r)},propertyEquals(r,t){return new C(r,t)},propertyIn(r,t){return new V(r,t)},propertyBetween(r,t,e){return new $(r,t,e)},and(...r){if(r.length===0)return new m;let t=r[0];for(let e=1;e<r.length;e++)t=new R(t,r[e]);return t},or(...r){if(r.length===0)return new g;let t=r[0];for(let e=1;e<r.length;e++)t=new v(t,r[e]);return t},not(r){return new B(r)}};class h{constructor(t,e,i){this.property=t,this.message=e,this.context=i}toString(){return`${this.property}: ${this.message}`}}class d extends Error{constructor(t){super(`Validation failed with ${t.length} error(s): ${t.map(e=>e.toString()).join("; ")}`),this.name="ValidationErrors",this.errors=t}get length(){return this.errors.length}}class p{constructor(){this.validationRules=[]}addRule(t,e,i,s){const n={specification:t,message:e,property:i||""};return s!==void 0&&(n.context=s),this.validationRules.push(n),this}addPropertyRule(t,e,i,s,n){const a={isSatisfiedBy:c=>e.isSatisfiedBy(s(c)),and:()=>{throw new Error("Operation not supported")},or:()=>{throw new Error("Operation not supported")},not:()=>{throw new Error("Operation not supported")}};return this.addRule(a,i,t,n)}validate(t){const e=[];for(const i of this.validationRules)i.specification.isSatisfiedBy(t)||e.push(new h(i.property||"",i.message,i.context));return e.length>0?o.Result.fail(new d(e)):o.Result.ok(t)}static fromSpecification(t,e,i,s){return new p().addRule(t,e,i,s)}static create(){return new p}}class f{constructor(){this.rules=[],this.stopOnFirstFailure=!1,this.lastCondition=null}addRule(t,e,i,s){return this.rules.push({property:t,validate:n=>e(n)?o.Result.ok(!0):o.Result.fail(new h(t,i,s))}),this}addSpecification(t,e,i,s){return this.addRule(t,n=>e.isSatisfiedBy(n),i,s)}mustSatisfy(t,e,i){return this.addSpecification("",t,e,i)}propertyMustSatisfy(t,e,i,s,n){return this.addRule(t,a=>e.isSatisfiedBy(s(a)),i,n)}addNested(t,e,i){return this.rules.push({property:t,validate:s=>{const n=i(s);if(n==null)return o.Result.fail(new h(t,"Cannot validate undefined or null nested object",{path:t}));const a=e.validate(n);if(a.isFailure){const c=a.error.errors.map(l=>{const w={...l.context||{},path:t+(l.property?`.${l.property}`:""),originalPath:l.property||"",parentPath:t};return new h(`${t}${l.property?`.${l.property}`:""}`,l.message,w)});return o.Result.fail(new h(t,"Nested validation failed",{errors:c,path:t,errorCount:c.length}))}return o.Result.ok(!0)}}),this}when(t,e){if(typeof t!="function")throw new Error("Condition must be a function");this.lastCondition=t;const i=new f;e(i);for(const s of i.rules)this.rules.push({...s,condition:t});return this}otherwise(t){if(this.lastCondition===null||this.lastCondition===void 0)throw new Error("Cannot call otherwise() without a preceding when() - lastCondition is null/undefined");if(typeof this.lastCondition!="function")throw new Error(`Cannot call otherwise() - lastCondition is not a function but a ${typeof this.lastCondition}`);const e=this.lastCondition,i=n=>!e(n),s=new f;t(s);for(const n of s.rules)this.rules.push({...n,condition:i});return this.lastCondition=null,this}whenSatisfies(t,e){return this.when(i=>t.isSatisfiedBy(i),e)}setStopOnFirstFailure(){return this.stopOnFirstFailure=!0,this}validate(t){const e=[];for(const i of this.rules){if(i.condition&&!i.condition(t))continue;const s=i.validate(t);if(s.isFailure&&(e.push(s.error),this.stopOnFirstFailure))break}return e.length>0?o.Result.fail(new d(e)):o.Result.ok(t)}and(t){const e=new f;return e.rules=[...this.rules],e.addRule("",i=>t.validate(i).isSuccess,"Failed combined validation"),e}static fromSpecification(t,e,i){return new f().mustSatisfy(t,e,i)}static create(){return new f}}const M={create(){return f.create()},fromSpecification(r,t,e){return p.fromSpecification(r,t,e)},combine(...r){return{validate:t=>{const e=[];for(const i of r){const s=i.validate(t);s.isFailure&&e.push(s.error)}if(e.length>0){const i=e.flatMap(s=>s.errors);return o.Result.fail(new d(i))}return o.Result.ok(t)}}},validateWithSpecification(r,t,e){return this.fromSpecification(t,e).validate(r)},validateWithRules(r,t){const e=p.create();for(const i of t)e.addRule(i.specification,i.message,i.property);return e.validate(r)},specificationToValidator(r,t,e){return this.fromSpecification(r,t,e)},validatorToSpecification(r){return u.create(t=>r.validate(t).isSuccess)},forNestedPath(r,t){if(r.length===0)return t;let e=t;for(let i=r.length-1;i>=0;i--){const s=r[i],n=f.create();n.addNested(s,e,a=>a?a[s]:void 0),e=n}return e},validatePath(r,t,e){const s=((a,c)=>{let l=a;for(const w of c){if(l==null)return;if(typeof l=="object"&&l!==null)l=l[w];else return}return l})(r,t);if(s===void 0)return o.Result.fail(new d([new h(t.join("."),"Path does not exist",{path:t})]));const n=e.validate(s);if(n.isFailure){const a=n.error.errors.map(c=>{const l=[...t];return c.property&&l.push(c.property),new h(l.join("."),c.message,{...c.context,fullPath:l})});return o.Result.fail(new d(a))}return o.Result.ok(r)},useExternal(r){return r}};class T{constructor(){this.name="core",this.required=(t,e="Field is required")=>i=>i.addRule(t,s=>s[t]!==void 0&&s[t]!==null,e),this.minLength=(t,e,i)=>s=>s.addRule(t,n=>String(n[t]).length>=e,i||`Minimum length is ${e}`),this.maxLength=(t,e,i)=>s=>s.addRule(t,n=>String(n[t]).length<=e,i||`Maximum length is ${e}`),this.pattern=(t,e,i="Invalid format")=>s=>s.addRule(t,n=>e.test(String(n[t])),i),this.range=(t,e,i,s)=>n=>n.addRule(t,a=>{const c=Number(a[t]);return!isNaN(c)&&c>=e&&c<=i},s||`Value must be between ${e} and ${i}`),this.email=(t,e="Invalid email address")=>i=>i.addRule(t,s=>/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(String(s[t])),e),this.satisfies=(t,e)=>i=>i.mustSatisfy(t,e),this.propertySatisfies=(t,e,i,s)=>n=>n.propertyMustSatisfy(t,e,i,s),this.whenSatisfies=(t,e)=>i=>i.whenSatisfies(t,e),this.otherwise=t=>e=>e.otherwise(t),this.when=(t,e)=>i=>i.when(t,e)}}const S=class S{static register(t){if(this.providers.has(t.name))throw new Error(`Rule provider with name "${t.name}" is already registered`);this.providers.set(t.name,t)}static getProvider(t){const e=this.providers.get(t);if(!e)throw new Error(`Rule provider "${t}" not found`);return e}static get Rules(){return this.core}static forDomain(t){return this.getProvider(t)}};S.providers=new Map,S.core=new T;let A=S;class j{constructor(t){this.schema=t}createValidationError(t,e,i){return new h(t,e,i)}createValidationErrors(t){return new d(t)}pathToString(t){return t.join(".")}failWithErrors(t){return o.Result.fail(this.createValidationErrors(t))}success(t){return o.Result.ok(t)}}class q{static create(t,e=""){return{validate:i=>{const s=t(i);if(!s.success&&s.errors){const n=s.errors.map(a=>new h(e,a));return o.Result.fail(new d(n))}return o.Result.ok(i)}}}static combine(...t){return{validate:e=>{const i=[];for(const s of t){const n=s.validate(e);n.isFailure&&i.push(...n.error.errors)}return i.length>0?o.Result.fail(new d(i)):o.Result.ok(e)}}}static withErrorMapping(t,e){return{validate:i=>{const s=t(i);if(!s.success&&s.errors){const n=s.errors.map(e);return o.Result.fail(new d(n))}return o.Result.ok(i)}}}}exports.AdapterUtils=q;exports.AlwaysFalseSpecification=g;exports.AlwaysTrueSpecification=m;exports.AndAsyncSpecification=x;exports.AndSpecification=R;exports.AsyncCompositeSpecification=y;exports.BaseValidationAdapter=j;exports.BusinessRuleValidator=f;exports.CompositeSpecification=u;exports.MemoizedSpecification=O;exports.NotAsyncSpecification=E;exports.NotSpecification=B;exports.OrAsyncSpecification=F;exports.OrSpecification=v;exports.PredicateSpecification=P;exports.PropertyBetweenSpecification=$;exports.PropertyEqualsSpecification=C;exports.PropertyInSpecification=V;exports.RulesRegistry=A;exports.Specification=k;exports.SpecificationValidator=p;exports.ValidationError=h;exports.ValidationErrors=d;exports.ValidationFacade=M;
@@ -0,0 +1,6 @@
1
+ export { Specification, CompositeSpecification, AsyncCompositeSpecification, MemoizedSpecification, NotSpecification, AndSpecification, OrSpecification, AndAsyncSpecification, OrAsyncSpecification, NotAsyncSpecification, AlwaysFalseSpecification, AlwaysTrueSpecification, SpecificationValidator, PredicateSpecification, PropertyBetweenSpecification, PropertyInSpecification, PropertyEqualsSpecification, } from './specifications';
2
+ export { BusinessRuleValidator } from './business-rules';
3
+ export { ValidationError, ValidationErrors } from './validation-error';
4
+ export { Validation as ValidationFacade } from './validation-facade';
5
+ export { RulesRegistry } from './rules-registry';
6
+ export { BaseValidationAdapter, AdapterUtils } from './adapters';