@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.
- package/LICENSE +21 -0
- package/LLMGUIDE.md +207 -0
- package/README.md +1562 -0
- package/dist/adapters/base-adapter.d.ts +54 -0
- package/dist/adapters/index.d.ts +2 -0
- package/dist/business-rules/business-rule-validator-extension.d.ts +29 -0
- package/dist/business-rules/business-rule-validator.d.ts +51 -0
- package/dist/business-rules/index.d.ts +2 -0
- package/dist/index.cjs +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +803 -0
- package/dist/rules-registry.d.ts +69 -0
- package/dist/specifications/async-composite-specification.d.ts +72 -0
- package/dist/specifications/composite-specification.d.ts +25 -0
- package/dist/specifications/index.d.ts +5 -0
- package/dist/specifications/memoized-specification.d.ts +84 -0
- package/dist/specifications/specification-operators.d.ts +70 -0
- package/dist/specifications/specification-validator.d.ts +26 -0
- package/dist/validation-error.d.ts +13 -0
- package/dist/validation-facade.d.ts +51 -0
- package/package.json +69 -0
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { ISpecification } from '@vytches/ddd-contracts';
|
|
2
|
+
import { BusinessRuleValidator } from './business-rules/business-rule-validator';
|
|
3
|
+
export interface RuleFunction<T> {
|
|
4
|
+
(validator: BusinessRuleValidator<T>): BusinessRuleValidator<T>;
|
|
5
|
+
}
|
|
6
|
+
export interface IRulesProvider {
|
|
7
|
+
readonly name: string;
|
|
8
|
+
}
|
|
9
|
+
export interface ICoreRules {
|
|
10
|
+
required: <T>(property: keyof T, message?: string) => RuleFunction<T>;
|
|
11
|
+
minLength: <T>(property: keyof T, length: number, message?: string) => RuleFunction<T>;
|
|
12
|
+
maxLength: <T>(property: keyof T, length: number, message?: string) => RuleFunction<T>;
|
|
13
|
+
/**
|
|
14
|
+
* Pattern-match a property against a RegExp.
|
|
15
|
+
*
|
|
16
|
+
* **Security warning (REL-007):** the `regex` argument is executed against
|
|
17
|
+
* input data. Do **NOT** construct the RegExp dynamically from
|
|
18
|
+
* user-controlled strings (e.g. `new RegExp(userInput)`) — this exposes
|
|
19
|
+
* your application to ReDoS (Regular Expression Denial of Service)
|
|
20
|
+
* attacks. Use only literal regex patterns in code review-able form, or
|
|
21
|
+
* pre-validate user input against a strict allow-list before constructing
|
|
22
|
+
* a RegExp.
|
|
23
|
+
*
|
|
24
|
+
* The library performs no internal validation of the regex; that is the
|
|
25
|
+
* consumer's responsibility.
|
|
26
|
+
*/
|
|
27
|
+
pattern: <T>(property: keyof T, regex: RegExp, message?: string) => RuleFunction<T>;
|
|
28
|
+
range: <T>(property: keyof T, min: number, max: number, message?: string) => RuleFunction<T>;
|
|
29
|
+
email: <T>(property: keyof T, message?: string) => RuleFunction<T>;
|
|
30
|
+
satisfies: <T>(specification: ISpecification<T>, message: string) => RuleFunction<T>;
|
|
31
|
+
propertySatisfies: <T, P>(property: keyof T & string, specification: ISpecification<P>, message: string, getValue: (obj: T) => P) => RuleFunction<T>;
|
|
32
|
+
when: <T>(condition: (value: T) => boolean, thenRules: (validator: BusinessRuleValidator<T>) => void) => RuleFunction<T>;
|
|
33
|
+
whenSatisfies: <T>(specification: ISpecification<T>, thenRules: (validator: BusinessRuleValidator<T>) => void) => RuleFunction<T>;
|
|
34
|
+
otherwise: <T>(elseRules: (validator: BusinessRuleValidator<T>) => void) => RuleFunction<T>;
|
|
35
|
+
}
|
|
36
|
+
export declare class CoreRules implements ICoreRules, IRulesProvider {
|
|
37
|
+
readonly name = "core";
|
|
38
|
+
required: <T>(property: keyof T, message?: string) => (validator: BusinessRuleValidator<T>) => BusinessRuleValidator<T>;
|
|
39
|
+
minLength: <T>(property: keyof T, length: number, message?: string) => (validator: BusinessRuleValidator<T>) => BusinessRuleValidator<T>;
|
|
40
|
+
maxLength: <T>(property: keyof T, length: number, message?: string) => (validator: BusinessRuleValidator<T>) => BusinessRuleValidator<T>;
|
|
41
|
+
pattern: <T>(property: keyof T, regex: RegExp, message?: string) => (validator: BusinessRuleValidator<T>) => BusinessRuleValidator<T>;
|
|
42
|
+
range: <T>(property: keyof T, min: number, max: number, message?: string) => (validator: BusinessRuleValidator<T>) => BusinessRuleValidator<T>;
|
|
43
|
+
email: <T>(property: keyof T, message?: string) => (validator: BusinessRuleValidator<T>) => BusinessRuleValidator<T>;
|
|
44
|
+
satisfies: <T>(specification: ISpecification<T>, message: string) => (validator: BusinessRuleValidator<T>) => BusinessRuleValidator<T>;
|
|
45
|
+
propertySatisfies: <T, P>(property: keyof T & string, specification: ISpecification<P>, message: string, getValue: (obj: T) => P) => (validator: BusinessRuleValidator<T>) => BusinessRuleValidator<T>;
|
|
46
|
+
whenSatisfies: <T>(specification: ISpecification<T>, thenRules: (validator: BusinessRuleValidator<T>) => void) => (validator: BusinessRuleValidator<T>) => BusinessRuleValidator<T>;
|
|
47
|
+
otherwise: <T>(elseRules: (validator: BusinessRuleValidator<T>) => void) => (validator: BusinessRuleValidator<T>) => BusinessRuleValidator<T>;
|
|
48
|
+
when: <T>(condition: (value: T) => boolean, thenRules: (validator: BusinessRuleValidator<T>) => void) => (validator: BusinessRuleValidator<T>) => BusinessRuleValidator<T>;
|
|
49
|
+
}
|
|
50
|
+
export declare class RulesRegistry {
|
|
51
|
+
private static providers;
|
|
52
|
+
private static core;
|
|
53
|
+
/**
|
|
54
|
+
* Register a domain-specific rule provider
|
|
55
|
+
*/
|
|
56
|
+
static register(provider: IRulesProvider): void;
|
|
57
|
+
/**
|
|
58
|
+
* Get a specific rule provider by name
|
|
59
|
+
*/
|
|
60
|
+
static getProvider<T extends IRulesProvider>(name: string): T;
|
|
61
|
+
/**
|
|
62
|
+
* Get core rules
|
|
63
|
+
*/
|
|
64
|
+
static get Rules(): ICoreRules;
|
|
65
|
+
/**
|
|
66
|
+
* Access domain-specific rules
|
|
67
|
+
*/
|
|
68
|
+
static forDomain<T extends IRulesProvider>(domain: string): T;
|
|
69
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { IAsyncSpecification } from '../../../contracts/src/index.ts';
|
|
2
|
+
export declare abstract class AsyncCompositeSpecification<T> implements IAsyncSpecification<T> {
|
|
3
|
+
/**
|
|
4
|
+
* Check if the candidate satisfies this specification asynchronously
|
|
5
|
+
* @param candidate The entity to evaluate
|
|
6
|
+
* @param context Optional context for evaluation (e.g., user, tenant, environment)
|
|
7
|
+
*/
|
|
8
|
+
abstract isSatisfiedByAsync(candidate: T, context?: Record<string, unknown>): Promise<boolean>;
|
|
9
|
+
/**
|
|
10
|
+
* Optional name for debugging/logging
|
|
11
|
+
*/
|
|
12
|
+
readonly name?: string | undefined;
|
|
13
|
+
/**
|
|
14
|
+
* Optional description for debugging/logging
|
|
15
|
+
*/
|
|
16
|
+
readonly description?: string | undefined;
|
|
17
|
+
/**
|
|
18
|
+
* Combine with another async specification using AND logic
|
|
19
|
+
*/
|
|
20
|
+
and(other: IAsyncSpecification<T>): IAsyncSpecification<T>;
|
|
21
|
+
/**
|
|
22
|
+
* Combine with another async specification using OR logic
|
|
23
|
+
*/
|
|
24
|
+
or(other: IAsyncSpecification<T>): IAsyncSpecification<T>;
|
|
25
|
+
/**
|
|
26
|
+
* Negate this async specification
|
|
27
|
+
*/
|
|
28
|
+
not(): IAsyncSpecification<T>;
|
|
29
|
+
/**
|
|
30
|
+
* Create a specification from an async predicate function
|
|
31
|
+
*/
|
|
32
|
+
static create<T>(predicate: (candidate: T, context?: Record<string, unknown>) => Promise<boolean>, name?: string, description?: string): IAsyncSpecification<T>;
|
|
33
|
+
/**
|
|
34
|
+
* Optional method to explain why specification failed
|
|
35
|
+
*/
|
|
36
|
+
explainFailureAsync?(_candidate: T, _context?: Record<string, unknown>): Promise<string | null>;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* @since 0.4.2
|
|
40
|
+
*/
|
|
41
|
+
export declare class AndAsyncSpecification<T> extends AsyncCompositeSpecification<T> {
|
|
42
|
+
private readonly left;
|
|
43
|
+
private readonly right;
|
|
44
|
+
readonly name: string;
|
|
45
|
+
readonly description: string;
|
|
46
|
+
constructor(left: IAsyncSpecification<T>, right: IAsyncSpecification<T>);
|
|
47
|
+
isSatisfiedByAsync(candidate: T, context?: Record<string, unknown>): Promise<boolean>;
|
|
48
|
+
explainFailureAsync(candidate: T, context?: Record<string, unknown>): Promise<string | null>;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* @since 0.4.2
|
|
52
|
+
*/
|
|
53
|
+
export declare class OrAsyncSpecification<T> extends AsyncCompositeSpecification<T> {
|
|
54
|
+
private readonly left;
|
|
55
|
+
private readonly right;
|
|
56
|
+
readonly name: string;
|
|
57
|
+
readonly description: string;
|
|
58
|
+
constructor(left: IAsyncSpecification<T>, right: IAsyncSpecification<T>);
|
|
59
|
+
isSatisfiedByAsync(candidate: T, context?: Record<string, unknown>): Promise<boolean>;
|
|
60
|
+
explainFailureAsync(candidate: T, context?: Record<string, unknown>): Promise<string | null>;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* @since 0.4.2
|
|
64
|
+
*/
|
|
65
|
+
export declare class NotAsyncSpecification<T> extends AsyncCompositeSpecification<T> {
|
|
66
|
+
private readonly spec;
|
|
67
|
+
readonly name: string;
|
|
68
|
+
readonly description: string;
|
|
69
|
+
constructor(spec: IAsyncSpecification<T>);
|
|
70
|
+
isSatisfiedByAsync(candidate: T, context?: Record<string, unknown>): Promise<boolean>;
|
|
71
|
+
explainFailureAsync(candidate: T, context?: Record<string, unknown>): Promise<string | null>;
|
|
72
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { ISpecification } from '../../../contracts/src/index.ts';
|
|
2
|
+
export declare abstract class CompositeSpecification<T> implements ISpecification<T> {
|
|
3
|
+
abstract isSatisfiedBy(candidate: T): boolean;
|
|
4
|
+
and(other: ISpecification<T>): ISpecification<T>;
|
|
5
|
+
or(other: ISpecification<T>): ISpecification<T>;
|
|
6
|
+
not(): ISpecification<T>;
|
|
7
|
+
static create<T>(predicate: (candidate: T) => boolean): ISpecification<T>;
|
|
8
|
+
}
|
|
9
|
+
export declare class AndSpecification<T> extends CompositeSpecification<T> {
|
|
10
|
+
private readonly left;
|
|
11
|
+
private readonly right;
|
|
12
|
+
constructor(left: ISpecification<T>, right: ISpecification<T>);
|
|
13
|
+
isSatisfiedBy(candidate: T): boolean;
|
|
14
|
+
}
|
|
15
|
+
export declare class OrSpecification<T> extends CompositeSpecification<T> {
|
|
16
|
+
private readonly left;
|
|
17
|
+
private readonly right;
|
|
18
|
+
constructor(left: ISpecification<T>, right: ISpecification<T>);
|
|
19
|
+
isSatisfiedBy(candidate: T): boolean;
|
|
20
|
+
}
|
|
21
|
+
export declare class NotSpecification<T> extends CompositeSpecification<T> {
|
|
22
|
+
private readonly spec;
|
|
23
|
+
constructor(spec: ISpecification<T>);
|
|
24
|
+
isSatisfiedBy(candidate: T): boolean;
|
|
25
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { ISpecification } from '../../../contracts/src/index.ts';
|
|
2
|
+
import { CompositeSpecification } from './composite-specification';
|
|
3
|
+
/**
|
|
4
|
+
* Specification wrapper that memoizes `isSatisfiedBy` results per candidate.
|
|
5
|
+
*
|
|
6
|
+
* VP-002 (2026-05-09): cuts repeated evaluation cost when the same
|
|
7
|
+
* specification is checked multiple times against the same candidate
|
|
8
|
+
* during a single query — common in pipelines like "filter list by spec,
|
|
9
|
+
* then enrich with another spec, then sort by a third predicate that
|
|
10
|
+
* also calls the first spec." Without memoization, each `isSatisfiedBy`
|
|
11
|
+
* call walks the full predicate tree even when the answer is already
|
|
12
|
+
* known.
|
|
13
|
+
*
|
|
14
|
+
* **Memoization storage**: `WeakMap<object, boolean>` for object
|
|
15
|
+
* candidates — automatic GC when the candidate is unreachable. Primitive
|
|
16
|
+
* candidates (strings, numbers) bypass the cache entirely (WeakMap keys
|
|
17
|
+
* must be objects); for those the wrapper degrades to a pass-through.
|
|
18
|
+
*
|
|
19
|
+
* **When NOT to use this**: specs whose result depends on mutable
|
|
20
|
+
* external state (timestamps, counters, randomness, fetched data). The
|
|
21
|
+
* cache assumes "same candidate object → same answer." If your spec is
|
|
22
|
+
* pure (depends only on candidate fields), you're safe.
|
|
23
|
+
*
|
|
24
|
+
* **Lifecycle contract — NOT a singleton-safe Specification.** Per Evans
|
|
25
|
+
* (Blue Book, ch. 9), a canonical `Specification` is a *stateless predicate*:
|
|
26
|
+
* shareable, serializable, safe to cache as a singleton. `MemoizedSpecification`
|
|
27
|
+
* is intentionally stateful — its `WeakMap` cache makes results dependent
|
|
28
|
+
* on call history. Treat it as a *per-query* helper, not as something to
|
|
29
|
+
* stash in a module-level constant or DI singleton with a long lifetime.
|
|
30
|
+
* Construct fresh, use within a request, let it be collected.
|
|
31
|
+
*
|
|
32
|
+
* Composes correctly with `and`/`or`/`not` because it extends
|
|
33
|
+
* `CompositeSpecification` — but those operators wrap the *current*
|
|
34
|
+
* memoized result with new instances, so the composition itself isn't
|
|
35
|
+
* memoized. To memoize the whole tree, wrap the outer composite.
|
|
36
|
+
*
|
|
37
|
+
* @example Pure-function specification
|
|
38
|
+
* ```typescript
|
|
39
|
+
* import { MemoizedSpecification, CompositeSpecification } from '@vytches/ddd-validation';
|
|
40
|
+
*
|
|
41
|
+
* class HighValueOrder extends CompositeSpecification<Order> {
|
|
42
|
+
* isSatisfiedBy(o: Order) { return o.total > 1000; }
|
|
43
|
+
* }
|
|
44
|
+
*
|
|
45
|
+
* const spec = new MemoizedSpecification(new HighValueOrder());
|
|
46
|
+
* spec.isSatisfiedBy(order); // computes
|
|
47
|
+
* spec.isSatisfiedBy(order); // cached, no recomputation
|
|
48
|
+
* spec.isSatisfiedBy(otherOrder); // computes (different candidate)
|
|
49
|
+
*
|
|
50
|
+
* // After `order` is unreachable, its cache entry is GC'd automatically.
|
|
51
|
+
* ```
|
|
52
|
+
*
|
|
53
|
+
* @example Composing memoized + raw specs
|
|
54
|
+
* ```typescript
|
|
55
|
+
* const expensive = new MemoizedSpecification(new ComplexCustomerCheck());
|
|
56
|
+
* const cheap = new IsActiveOrder();
|
|
57
|
+
*
|
|
58
|
+
* // The composed spec re-runs `expensive` once per candidate (cache hit
|
|
59
|
+
* // on second call), and `cheap` directly each time.
|
|
60
|
+
* const both = expensive.and(cheap);
|
|
61
|
+
* ```
|
|
62
|
+
*
|
|
63
|
+
* @public
|
|
64
|
+
* @stable
|
|
65
|
+
* @since 0.25.0
|
|
66
|
+
*/
|
|
67
|
+
export declare class MemoizedSpecification<T extends object> extends CompositeSpecification<T> {
|
|
68
|
+
private readonly inner;
|
|
69
|
+
private readonly cache;
|
|
70
|
+
constructor(inner: ISpecification<T>);
|
|
71
|
+
isSatisfiedBy(candidate: T): boolean;
|
|
72
|
+
/**
|
|
73
|
+
* Manually evict a candidate from the cache. Use when you know the
|
|
74
|
+
* candidate's relevant fields have changed and you want fresh evaluation
|
|
75
|
+
* without discarding cache entries for other candidates.
|
|
76
|
+
*/
|
|
77
|
+
invalidate(candidate: T): void;
|
|
78
|
+
/**
|
|
79
|
+
* Forwarded to the inner spec, if it implements optional `explainFailure`.
|
|
80
|
+
* Skips the cache because failure explanations are typically only computed
|
|
81
|
+
* on-demand.
|
|
82
|
+
*/
|
|
83
|
+
explainFailure(candidate: T): string | null;
|
|
84
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { ISpecification } from '../../../contracts/src/index.ts';
|
|
2
|
+
import { CompositeSpecification } from './composite-specification';
|
|
3
|
+
export declare class AlwaysTrueSpecification<T> extends CompositeSpecification<T> {
|
|
4
|
+
isSatisfiedBy(_candidate: T): boolean;
|
|
5
|
+
}
|
|
6
|
+
export declare class AlwaysFalseSpecification<T> extends CompositeSpecification<T> {
|
|
7
|
+
isSatisfiedBy(_candidate: T): boolean;
|
|
8
|
+
}
|
|
9
|
+
export declare class PredicateSpecification<T> extends CompositeSpecification<T> {
|
|
10
|
+
private readonly predicate;
|
|
11
|
+
constructor(predicate: (candidate: T) => boolean);
|
|
12
|
+
isSatisfiedBy(candidate: T): boolean;
|
|
13
|
+
}
|
|
14
|
+
export declare class PropertyEqualsSpecification<T> extends CompositeSpecification<T> {
|
|
15
|
+
private readonly propertyName;
|
|
16
|
+
private readonly expectedValue;
|
|
17
|
+
constructor(propertyName: keyof T, expectedValue: T[keyof T]);
|
|
18
|
+
isSatisfiedBy(candidate: T): boolean;
|
|
19
|
+
}
|
|
20
|
+
export declare class PropertyInSpecification<T> extends CompositeSpecification<T> {
|
|
21
|
+
private readonly propertyName;
|
|
22
|
+
private readonly possibleValues;
|
|
23
|
+
constructor(propertyName: keyof T, possibleValues: T[keyof T][]);
|
|
24
|
+
isSatisfiedBy(candidate: T): boolean;
|
|
25
|
+
}
|
|
26
|
+
export declare class PropertyBetweenSpecification<T> extends CompositeSpecification<T> {
|
|
27
|
+
private readonly propertyName;
|
|
28
|
+
private readonly min;
|
|
29
|
+
private readonly max;
|
|
30
|
+
constructor(propertyName: keyof T, min: number, max: number);
|
|
31
|
+
isSatisfiedBy(candidate: T): boolean;
|
|
32
|
+
}
|
|
33
|
+
export declare const Specification: {
|
|
34
|
+
/**
|
|
35
|
+
* Tworzy specyfikację zawsze prawdziwą
|
|
36
|
+
*/
|
|
37
|
+
alwaysTrue<T>(): ISpecification<T>;
|
|
38
|
+
/**
|
|
39
|
+
* Tworzy specyfikację zawsze fałszywą
|
|
40
|
+
*/
|
|
41
|
+
alwaysFalse<T>(): ISpecification<T>;
|
|
42
|
+
/**
|
|
43
|
+
* Tworzy specyfikację opartą o predykat
|
|
44
|
+
*/
|
|
45
|
+
create<T>(predicate: (candidate: T) => boolean): ISpecification<T>;
|
|
46
|
+
/**
|
|
47
|
+
* Tworzy specyfikację sprawdzającą równość właściwości
|
|
48
|
+
*/
|
|
49
|
+
propertyEquals<T>(propertyName: keyof T, expectedValue: T[keyof T]): ISpecification<T>;
|
|
50
|
+
/**
|
|
51
|
+
* Tworzy specyfikację sprawdzającą zawieranie się właściwości w zbiorze
|
|
52
|
+
*/
|
|
53
|
+
propertyIn<T>(propertyName: keyof T, possibleValues: T[keyof T][]): ISpecification<T>;
|
|
54
|
+
/**
|
|
55
|
+
* Tworzy specyfikację sprawdzającą zakres wartości
|
|
56
|
+
*/
|
|
57
|
+
propertyBetween<T>(propertyName: keyof T, min: number, max: number): ISpecification<T>;
|
|
58
|
+
/**
|
|
59
|
+
* Łączy specyfikacje operatorem AND
|
|
60
|
+
*/
|
|
61
|
+
and<T>(...specifications: ISpecification<T>[]): ISpecification<T>;
|
|
62
|
+
/**
|
|
63
|
+
* Łączy specyfikacje operatorem OR
|
|
64
|
+
*/
|
|
65
|
+
or<T>(...specifications: ISpecification<T>[]): ISpecification<T>;
|
|
66
|
+
/**
|
|
67
|
+
* Neguje specyfikację
|
|
68
|
+
*/
|
|
69
|
+
not<T>(specification: ISpecification<T>): ISpecification<T>;
|
|
70
|
+
};
|
|
@@ -0,0 +1,26 @@
|
|
|
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 SpecificationValidator<T> implements IValidator<T> {
|
|
5
|
+
private validationRules;
|
|
6
|
+
/**
|
|
7
|
+
* Adds a validation rule based on a specification
|
|
8
|
+
*/
|
|
9
|
+
addRule(specification: ISpecification<T>, message: string, property?: string, context?: Record<string, unknown>): SpecificationValidator<T>;
|
|
10
|
+
/**
|
|
11
|
+
* Adds a rule for a specific object property
|
|
12
|
+
*/
|
|
13
|
+
addPropertyRule<P>(property: keyof T & string, specification: ISpecification<P>, message: string, getValue: (obj: T) => P, context?: Record<string, any>): SpecificationValidator<T>;
|
|
14
|
+
/**
|
|
15
|
+
* Performs validation based on all specifications
|
|
16
|
+
*/
|
|
17
|
+
validate(value: T): Result<T, ValidationErrors>;
|
|
18
|
+
/**
|
|
19
|
+
* Creates a validator with a single rule
|
|
20
|
+
*/
|
|
21
|
+
static fromSpecification<T>(specification: ISpecification<T>, message: string, property?: string, context?: Record<string, any>): SpecificationValidator<T>;
|
|
22
|
+
/**
|
|
23
|
+
* Creates an empty validator
|
|
24
|
+
*/
|
|
25
|
+
static create<T>(): SpecificationValidator<T>;
|
|
26
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { IValidationError, IValidationErrors } from '@vytches/ddd-contracts';
|
|
2
|
+
export declare class ValidationError implements IValidationError {
|
|
3
|
+
readonly property: string;
|
|
4
|
+
readonly message: string;
|
|
5
|
+
readonly context?: Record<string, unknown> | undefined;
|
|
6
|
+
constructor(property: string, message: string, context?: Record<string, unknown> | undefined);
|
|
7
|
+
toString(): string;
|
|
8
|
+
}
|
|
9
|
+
export declare class ValidationErrors extends Error implements IValidationErrors {
|
|
10
|
+
readonly errors: IValidationError[];
|
|
11
|
+
constructor(errors: ValidationError[]);
|
|
12
|
+
get length(): number;
|
|
13
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { ISpecification, IValidationErrors, IValidator } from '@vytches/ddd-contracts';
|
|
2
|
+
import { Result } from '@vytches/ddd-utils';
|
|
3
|
+
import { BusinessRuleValidator } from './business-rules/business-rule-validator';
|
|
4
|
+
import { ValidationErrors } from './validation-error';
|
|
5
|
+
export declare const Validation: {
|
|
6
|
+
/**
|
|
7
|
+
* Tworzy nowy walidator reguł biznesowych
|
|
8
|
+
*/
|
|
9
|
+
create<T>(): BusinessRuleValidator<T>;
|
|
10
|
+
/**
|
|
11
|
+
* Tworzy walidator oparty na specyfikacji
|
|
12
|
+
*/
|
|
13
|
+
fromSpecification<T>(specification: ISpecification<T>, message: string, property?: string): IValidator<T>;
|
|
14
|
+
/**
|
|
15
|
+
* Tworzy walidator łączący wiele walidatorów
|
|
16
|
+
*/
|
|
17
|
+
combine<T>(...validators: IValidator<T>[]): IValidator<T>;
|
|
18
|
+
/**
|
|
19
|
+
* Waliduje obiekt bezpośrednio za pomocą specyfikacji
|
|
20
|
+
*/
|
|
21
|
+
validateWithSpecification<T>(value: T, specification: ISpecification<T>, message: string): Result<T, IValidationErrors>;
|
|
22
|
+
/**
|
|
23
|
+
* Waliduje za pomocą wielu specyfikacji z własnymi komunikatami błędów
|
|
24
|
+
*/
|
|
25
|
+
validateWithRules<T>(value: T, rules: Array<{
|
|
26
|
+
specification: ISpecification<T>;
|
|
27
|
+
message: string;
|
|
28
|
+
property?: string;
|
|
29
|
+
}>): Result<T, ValidationErrors>;
|
|
30
|
+
/**
|
|
31
|
+
* Konwertuje specyfikację na walidator
|
|
32
|
+
*/
|
|
33
|
+
specificationToValidator<T>(specification: ISpecification<T>, message: string, property?: string): IValidator<T>;
|
|
34
|
+
/**
|
|
35
|
+
* Konwertuje walidator na specyfikację
|
|
36
|
+
*/
|
|
37
|
+
validatorToSpecification<T>(validator: IValidator<T>): ISpecification<T>;
|
|
38
|
+
/**
|
|
39
|
+
* Tworzy walidator dla głęboko zagnieżdżonej struktury obiektów
|
|
40
|
+
*/
|
|
41
|
+
forNestedPath<T>(path: string[], validator: IValidator<unknown>): IValidator<T>;
|
|
42
|
+
/**
|
|
43
|
+
* Waliduje głęboko zagnieżdżoną właściwość
|
|
44
|
+
*/
|
|
45
|
+
validatePath<T, P>(object: T, path: (string | number)[], valueValidator: IValidator<P>): Result<T, ValidationErrors>;
|
|
46
|
+
/**
|
|
47
|
+
* Używa zewnętrznego walidatora implementującego IValidator<T>
|
|
48
|
+
* Pozwala na integrację z bibliotekami takimi jak zod, class-validator, itp.
|
|
49
|
+
*/
|
|
50
|
+
useExternal<T>(validator: IValidator<T>): IValidator<T>;
|
|
51
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@vytches/ddd-validation",
|
|
3
|
+
"version": "0.26.0",
|
|
4
|
+
"description": "Business rules and specifications for domain validation",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.cjs",
|
|
7
|
+
"module": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.js",
|
|
13
|
+
"require": "./dist/index.cjs"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"sideEffects": false,
|
|
17
|
+
"files": [
|
|
18
|
+
"dist",
|
|
19
|
+
"README.md",
|
|
20
|
+
"LLMGUIDE.md"
|
|
21
|
+
],
|
|
22
|
+
"keywords": [
|
|
23
|
+
"ddd",
|
|
24
|
+
"domain-driven-design",
|
|
25
|
+
"typescript",
|
|
26
|
+
"validation"
|
|
27
|
+
],
|
|
28
|
+
"author": "VytchesDDD",
|
|
29
|
+
"license": "MIT",
|
|
30
|
+
"repository": {
|
|
31
|
+
"type": "git",
|
|
32
|
+
"url": "https://github.com/vytches/ddd.git",
|
|
33
|
+
"directory": "packages/validation"
|
|
34
|
+
},
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"typescript": "^5.0.0",
|
|
37
|
+
"vite": "^7.3.2",
|
|
38
|
+
"vite-plugin-dts": "^4.0.0",
|
|
39
|
+
"vitest": "^2.0.0",
|
|
40
|
+
"@vytches/ddd-testing": "0.26.0"
|
|
41
|
+
},
|
|
42
|
+
"nx": {
|
|
43
|
+
"tags": [
|
|
44
|
+
"scope:validation",
|
|
45
|
+
"type:lib",
|
|
46
|
+
"layer:patterns"
|
|
47
|
+
]
|
|
48
|
+
},
|
|
49
|
+
"publishConfig": {
|
|
50
|
+
"registry": "https://registry.npmjs.org",
|
|
51
|
+
"access": "public"
|
|
52
|
+
},
|
|
53
|
+
"dependencies": {
|
|
54
|
+
"@vytches/ddd-contracts": "0.26.0",
|
|
55
|
+
"@vytches/ddd-domain-primitives": "0.26.0",
|
|
56
|
+
"@vytches/ddd-logging": "0.26.0",
|
|
57
|
+
"@vytches/ddd-utils": "0.26.0"
|
|
58
|
+
},
|
|
59
|
+
"scripts": {
|
|
60
|
+
"build": "vite build",
|
|
61
|
+
"dev": "vite build --watch",
|
|
62
|
+
"clean": "rm -rf dist .tsbuildinfo",
|
|
63
|
+
"test": "vitest run",
|
|
64
|
+
"test:watch": "vitest",
|
|
65
|
+
"test:coverage": "vitest run --coverage",
|
|
66
|
+
"lint": "eslint src --ext .ts",
|
|
67
|
+
"type-check": "tsc --noEmit"
|
|
68
|
+
}
|
|
69
|
+
}
|