@decaf-ts/decorator-validation 1.7.19 → 1.7.20

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 CHANGED
@@ -1,7 +1,11 @@
1
- ![Banner](./workdocs/assets/Banner.png)
2
- ## Simple Decorator based Model Validation Engine
1
+ # Decorator Validation
2
+
3
+ A TypeScript decorator-driven validation and model framework. It lets you:
4
+ - Define validation rules with declarative decorators on model properties (e.g., @required, @min, @pattern).
5
+ - Build, validate, serialize, and hash models with pluggable registries and algorithms.
6
+ - Extend validation via a registry of Validator classes and utilities.
7
+ - Optionally expose validation as MCP tools for automation workflows.
3
8
 
4
- A comprehensive TypeScript library that provides a powerful model validation framework using decorators. It enables type-safe, declarative validation for your TypeScript models with features for serialization, comparison, and hashing. The library offers a rich set of validation decorators for various data types and constraints, making it easy to define and enforce validation rules on your model properties.
5
9
 
6
10
 
7
11
  ![Licence](https://img.shields.io/github/license/decaf-ts/decorator-validation.svg?style=plastic)
@@ -27,328 +31,521 @@ A comprehensive TypeScript library that provides a powerful model validation fra
27
31
 
28
32
  Documentation available [here](https://decaf-ts.github.io/decorator-validation/)
29
33
 
30
- ### Description
31
-
32
- The decorator-validation library is a comprehensive TypeScript implementation of a decorator-based validation system. It provides a robust framework for defining, validating, and managing model objects in TypeScript applications.
34
+ # Decorator Validation – Detailed Description
33
35
 
34
- Meant to be easily extended, customized, and integrated with browser input validation mechanisms, this library offers a declarative approach to validation through TypeScript decorators.
36
+ Decorator Validation is a TypeScript library that centers on two complementary pillars:
35
37
 
36
- #### Core Components
38
+ 1) Declarative validation via decorators
39
+ - A rich set of property decorators such as @required, @min, @max, @minLength, @maxLength, @pattern, @email, @url, @type, @equals, @greaterThan, @greaterThanOrEqual, @lessThan, @lessThanOrEqual, @step, @list, @diff, @date, @password, as well as an async() flag helper.
40
+ - Each decorator writes strongly-typed metadata using reflect-metadata and a common Validation metadata keying convention, so validators can later interpret the rules consistently.
41
+ - Decorators are defined in src/validation/decorators.ts and are backed by concrete Validator classes in src/validation/Validators/* that implement the actual validation logic.
37
42
 
38
- ##### Model System
39
- The library is built around the abstract `Model` class, which serves as the foundation for all model objects. The Model system provides:
43
+ 2) A model system tailored for validation, building, hashing, and serialization
44
+ - Models are ordinary classes marked with @model() (src/model/decorators.ts). The decorator replaces the constructor, binds the model prototype utilities, runs a global builder (if registered), and tags reflection metadata for identification.
45
+ - Additional class-level decorators configure algorithms:
46
+ - @hashedBy(algorithm, ...args) to define model hashing implementation.
47
+ - @serializedBy(serializer, ...args) to define serialization strategy.
48
+ - @description(text) to attach human-readable documentation to a class, property, or method.
49
+ - The Model class (src/model/Model.ts) provides:
50
+ - A ModelRegistryManager for registering and retrieving model constructors by name, enabling rebuild/deserialize flows.
51
+ - Validation integration (model/validation.ts) that runs the registered validators against metadata collected by decorators.
52
+ - Utility methods and metadata helpers to identify models, fetch metadata, compare instances, and orchestrate hashing/serialization strategies.
40
53
 
41
- - A registry mechanism for storing and retrieving model constructors
42
- - Serialization and deserialization capabilities
43
- - Hashing functionality for model objects
44
- - Equality comparison between model objects
45
- - Support for nested model instantiation and validation
54
+ Core runtime architecture
55
+ - Validation namespace (src/validation/Validation.ts):
56
+ - Manages a pluggable IValidatorRegistry so custom Validator implementations can be registered, migrated, and queried.
57
+ - Exposes helper utilities: Validation.key(key) for reflect-metadata keying, Validation.keys() to list available validator keys, register(...) and get(...) to manage validators, decorator registration to link a metadata key to its decorator for dynamic use.
58
+ - Validator classes (src/validation/Validators/*):
59
+ - BaseValidator.ts defines common behaviors; concrete validators (RequiredValidator, MinValidator, PatternValidator, etc.) implement validate and message/typing logic.
60
+ - ValidatorRegistry.ts stores Validator instances keyed by ValidationKeys constants.
61
+ - constants.ts defines DEFAULT_ERROR_MESSAGES, DEFAULT_PATTERNS, and ValidationKeys (e.g., REQUIRED, MIN, MAX...).
62
+ - decorators.ts contains decorator sugar for directly registering standard validators and building metadata using Decoration/Reflection utilities.
63
+ - Utilities (src/utils/*):
64
+ - strings, dates, types, serialization, hashing, registry, decorators, Decoration helper, and a PathProxy to traverse nested properties and apply metadata.
46
65
 
47
- ##### Validation Framework
48
- The validation framework offers a rich set of decorators for validating model properties:
66
+ Intent of the library
67
+ - Provide a cohesive, decorator-first developer experience for enforcing validation constraints on model classes.
68
+ - Ensure that validation, model lifecycle (build/serialize/hash), and metadata are consistent and extensible through registries.
69
+ - Allow advanced composition (custom validators, alternative registries), and integration into automation flows (MCP tools).
49
70
 
50
- - **Basic Validation**: `required()`, `min()`, `max()`, `step()`, `minlength()`, `maxlength()`, `pattern()`
51
- - **Type-Specific Validation**: `email()`, `url()`, `type()`, `date()`, `password()`, `list()`, `set()`
52
- - **Comparison Validation**: `eq()`, `diff()`, `lt()`, `lte()`, `gt()`, `gte()` for comparing properties
71
+ Design principles
72
+ - Declarative over imperative: Constraints live next to the properties they validate.
73
+ - Extensibility: Registries and helper factories allow swapping implementations without changing consumer code.
74
+ - Type safety: Metadata and decorators are typed; validators advertise supported types; utility functions use narrow types where practical.
75
+ - Separation of concerns: Decorators express intent; Validator classes implement behavior; Model provides lifecycle utilities.
53
76
 
54
- ##### Key Features
55
- - **Decorator-based validation API** with recursive validation for nested models
56
- - **Customizable Model building factories** enabling nested instantiation
57
- - **Model serialization/deserialization** with configurable serializers
58
- - **Model Hashing** with configurable algorithms
59
- - **Model Equality** comparison with support for excluding properties
60
- - **Easily extended custom validation** through the decorator system
61
- - **Java-like date handling** (format and serialization)
62
- - **Configurable error messages** for all validation rules
63
- - **Comparative validation** between attributes, supporting various comparison operators
64
- - **Type safety** through TypeScript's static typing system
65
77
 
66
- The library is designed with extensibility in mind, allowing developers to create custom validators and decorators to meet specific application requirements. It integrates seamlessly with TypeScript's type system to provide compile-time type checking alongside runtime validation.
78
+ # How to Use Decorator Validation
67
79
 
80
+ This guide shows concrete, TypeScript-accurate examples for the main public APIs exported by the library. Examples are inspired by patterns used across the repo tests and typical usage of decorator-driven validation and models.
68
81
 
69
- ### How to Use
82
+ Notes
83
+ - Import paths assume a consumer importing from the package entry and submodules as re-exported by src/index.ts.
84
+ - All snippets are valid TypeScript.
70
85
 
71
- - [Initial Setup](./workdocs/tutorials/For%20Developers.md#_initial-setup_)
72
- - [Installation](./workdocs/tutorials/For%20Developers.md#installation)
73
86
 
74
- ### Examples
87
+ ## Model decorators and model lifecycle
75
88
 
76
- #### Creating a Model Class
89
+ ### @model()
90
+ Description: Marks a class as a model. The constructor is wrapped to bind model utilities and run a global builder (if any).
77
91
 
78
92
  ```typescript
79
- import { Model, model, required, email, minlength, maxlength, min, hashedBy, serializedBy } from 'decorator-validation';
93
+ import { model, Model, ModelArg } from "@decaf-ts/decorator-validation";
80
94
 
81
95
  @model()
82
- @hashedBy('sha256')
83
- @serializedBy('json')
84
96
  class User extends Model {
85
- @required()
86
- @minlength(3)
87
- @maxlength(50)
88
- username!: string;
97
+ @prop()
98
+ id!: string;
99
+ @prop()
100
+ name!: string;
101
+
102
+ constructor(arg?: ModelArg<User>) {
103
+ super(arg);
104
+ }
105
+ }
106
+
107
+ const u = new User();
108
+ ```
109
+
110
+ Extending from the Model class is optional but highly recommended. Regardless, if decorated with `@model()`, the constructor signature must be compatible.
111
+
112
+ When using a class for validation, eg `hasErrors()` only, @model() is not required.
113
+
114
+ #### Model construction
89
115
 
116
+ When a class is decorated with @model(), the framework invokes a model building function right after the instance is constructed. This builder initializes your model from the argument you pass to the constructor.
117
+
118
+ There are two builder strategies:
119
+ - Model.fromObject: Accepts a plain object and performs a more permissive, best-effort mapping.
120
+ - Does not enforce nested Model classes
121
+ - Model.fromModel (default): does the same as fromObject, but also instantiates nested Model classes
122
+
123
+ Your model constructors should accept an optional argument and pass it to super so the builder can use it.
124
+
125
+ You can change between builder functions by using:
126
+
127
+ ```typescript
128
+ import { required, Model, model, ModelArg } from "@decaf-ts/decorator-validation";
129
+
130
+ @model()
131
+ class Child extends Model {
90
132
  @required()
91
- @email()
92
- email!: string;
133
+ name!: string;
134
+
135
+ constructor(arg?: ModelArg<Child>) {
136
+ super(arg);
137
+ }
138
+ }
93
139
 
140
+ @model()
141
+ class Parent extends Model {
142
+ @required()
143
+ name!: string;
94
144
  @required()
95
- @min(18, "User must be at least 18 years old")
96
- age!: number;
145
+ child!: Child;
146
+ constructor(arg?: ModelArg<Parent>) {
147
+ super(arg);
148
+ }
149
+ }
150
+ // Default builder is Model.fromModel
151
+
152
+ let parent = new Parent({
153
+ child: {
154
+ name: "child"
155
+ }
156
+ })
157
+
158
+ parent.child instanceof Child; // true
159
+
160
+ Model.setBuilder(Model.fromObject);
161
+
162
+ parent = new Parent({
163
+ child: {
164
+ name: "child"
165
+ }
166
+ })
167
+
168
+ parent.child instanceof Child; // false
169
+ ```
170
+
171
+ ### @hashedBy(algorithm, ...args)
172
+ Description: Declares which hashing strategy to use when hashing model instances.
173
+
174
+ ```typescript
175
+ import { model, hashedBy, ModelArg, prop } from "@decaf-ts/decorator-validation";
97
176
 
98
- constructor(data?: any) {
99
- super(data);
100
- Model.fromModel(this, data);
177
+ @model()
178
+ @hashedBy("sha256", "utf8")
179
+ class FileInfo extends Model {
180
+ @prop()
181
+ path!: string;
182
+ @prop()
183
+ size!: number;
184
+
185
+ constructor(arg?: ModelArg<FileInfo>) {
186
+ super(arg)
101
187
  }
102
188
  }
103
189
  ```
104
190
 
105
- #### Basic Validation
191
+ ### @serializedBy(serializer, ...args)
192
+ Description: Declares which serializer to use for (de)serializing model instances.
106
193
 
107
194
  ```typescript
108
- // Create a user with invalid data
109
- const invalidUser = new User({
110
- username: "jo", // too short
111
- email: "not-an-email",
112
- age: 16 // below minimum
113
- });
195
+ import { prop, ModelArg, model, serializedBy } from "@decaf-ts/decorator-validation";
196
+
197
+ @model()
198
+ @serializedBy("json")
199
+ class Settings extends Model {
200
+ @prop()
201
+ theme!: string;
202
+ @prop()
203
+ locale!: string;
204
+
205
+ constructor(arg?: ModelArg<Settings>){
206
+ super(arg)
207
+ }
208
+ }
209
+ ```
114
210
 
115
- // Check for validation errors
116
- const errors = invalidUser.hasErrors();
117
- console.log(errors);
118
- // Output will contain validation errors for username, email, and age
211
+ ### @description(text)
212
+ Description: Applies textual documentation metadata to a class, property, or method.
119
213
 
120
- // Create a valid user
121
- const validUser = new User({
122
- username: "john_doe",
123
- email: "john@example.com",
124
- age: 25
125
- });
214
+ ```typescript
215
+ import { model, description, Model } from "@decaf-ts/decorator-validation";
126
216
 
127
- // Check for validation errors
128
- const validErrors = validUser.hasErrors();
129
- console.log(validErrors); // undefined - no errors
217
+ @model()
218
+ @description("Represents an application user")
219
+ class User extends Model {
220
+ @description("Unique identifier")
221
+ id!: string;
222
+ // ...
223
+ }
130
224
  ```
131
225
 
132
- #### Using Different Validation Decorators
133
226
 
134
- ##### Numeric Validation
227
+ ## Validation decorators (property-level)
228
+ Each decorator writes metadata for a corresponding Validator. Use them on model fields.
229
+
230
+
231
+ ### @prop()
232
+ Description: Registers a property with the model system. This is required for model construction so the property participates in metadata, serialization, hashing, validation discovery, etc.
233
+
234
+ Important
235
+ - All other property decorators (e.g., @required, @min, @email, etc.) already apply @prop under the hood.
236
+ - Therefore, you only need to use @prop when a property has no other decorators.
237
+
238
+
239
+ ### @required(message?)
240
+ Description: Field must be present and non-empty.
135
241
 
136
242
  ```typescript
137
- class Product {
138
- @required()
139
- name!: string;
243
+ import { Model, required, model } from "@decaf-ts/decorator-validation";
140
244
 
245
+ @model()
246
+ class User extends Model {
141
247
  @required()
142
- @min(0, "Price cannot be negative")
143
- @max(10000, "Price cannot exceed 10,000")
144
- @step(0.01, "Price must have at most 2 decimal places")
145
- price!: number;
248
+ username!: string;
249
+ //...
250
+ }
251
+ ```
146
252
 
147
- constructor(data?: any) {
148
- Model.fromModel(this, data);
149
- }
253
+ ### @min(value, message?) and @max(value, message?)
254
+ Description: Numeric or date boundaries.
255
+
256
+ ```typescript
257
+ import { model } from "@decaf-ts/decorator-validation";
258
+ import { min } from "@decaf-ts/decorator-validation";
259
+ import { max } from "@decaf-ts/decorator-validation";
260
+
261
+ @model()
262
+ class Product extends Model {
263
+ @min(0)
264
+ @max(1000)
265
+ price!: number;
150
266
  }
151
267
  ```
152
268
 
153
- ##### String Validation
269
+ ### @minLength(n, message?) and @maxLength(n, message?)
270
+ Description: String length boundaries.
154
271
 
155
272
  ```typescript
156
- class Article {
157
- @required()
158
- @minlength(5)
159
- @maxlength(100)
160
- title!: string;
273
+ import { model, Model } from "@decaf-ts/decorator-validation";
274
+ import { minLength, maxLength } from "@decaf-ts/decorator-validation";
161
275
 
162
- @required()
163
- @minlength(50)
164
- @maxlength(5000)
165
- content!: string;
276
+ @model()
277
+ class Credentials extends Model {
278
+ @minLength(8)
279
+ @maxLength(64)
280
+ password!: string;
281
+ }
282
+ ```
166
283
 
167
- @pattern(/^[a-z0-9-]+$/, "Slug must contain only lowercase letters, numbers, and hyphens")
168
- slug!: string;
284
+ ### @pattern(regex | string, message?)
285
+ Description: String must match a pattern.
169
286
 
170
- constructor(data?: any) {
171
- Model.fromModel(this, data);
172
- }
287
+ ```typescript
288
+ import { model } from "@decaf-ts/decorator-validation";
289
+ import { pattern, model } from "@decaf-ts/decorator-validation";
290
+
291
+ @model()
292
+ class Vehicle extends Model {
293
+ @pattern(/^[A-Z]{2}-\d{2}-[A-Z]{2}$/)
294
+ plate!: string;
173
295
  }
174
296
  ```
175
297
 
176
- ##### Special Types Validation
298
+ ### @email(message?)
299
+ Description: Must be a valid email.
177
300
 
178
301
  ```typescript
179
- class Contact {
180
- @required()
181
- name!: string;
302
+ import { model } from "@decaf-ts/decorator-validation";
303
+ import { email } from "@decaf-ts/decorator-validation";
182
304
 
183
- @required()
305
+ @model()
306
+ class Contact extends Model {
184
307
  @email()
185
308
  email!: string;
309
+ }
310
+ ```
311
+
312
+ ### @url(message?)
313
+ Description: Must be a valid URL.
314
+
315
+ ```typescript
316
+ import { model } from "@decaf-ts/decorator-validation";
317
+ import { url } from "@decaf-ts/decorator-validation";
186
318
 
319
+ @model()
320
+ class Link extends Model {
187
321
  @url()
188
- website?: string;
322
+ href!: string;
323
+ }
324
+ ```
189
325
 
190
- @date("yyyy-MM-dd")
191
- birthdate?: Date;
326
+ ### @type(T, message?)
327
+ Description: Enforces a runtime type match (e.g., Number, String, Date).
192
328
 
193
- @password()
194
- password!: string;
329
+ ```typescript
330
+ import { model } from "@decaf-ts/decorator-validation";
331
+ import { type } from "@decaf-ts/decorator-validation";
195
332
 
196
- constructor(data?: any) {
197
- Model.fromModel(this, data);
198
- }
333
+ @model()
334
+ class Measurement extends Model {
335
+ @type(Number)
336
+ value!: number;
199
337
  }
200
338
  ```
201
339
 
202
- ##### Comparison Validation
340
+ ### @equals(otherValueOrPath, message?)
341
+ Description: Value must equal the provided value or another property.
203
342
 
204
343
  ```typescript
205
- class DateRange {
206
- @required()
207
- @date("yyyy-MM-dd")
208
- startDate!: Date;
344
+ import { model } from "@decaf-ts/decorator-validation";
345
+ import { equals } from "@decaf-ts/decorator-validation";
209
346
 
210
- @required()
211
- @date("yyyy-MM-dd")
212
- @gt("startDate", "End date must be after start date")
213
- endDate!: Date;
347
+ @model()
348
+ class Confirmation extends Model {
349
+ password!: string;
350
+ @equals(":password")
351
+ confirm!: string;
352
+ }
353
+ ```
214
354
 
215
- constructor(data?: any) {
216
- Model.fromModel(this, data);
217
- }
355
+ ### @greaterThan(x) / @greaterThanOrEqual(x) / @lessThan(x) / @lessThanOrEqual(x)
356
+ Description: Numeric or date comparisons.
357
+
358
+ ```typescript
359
+ import { model } from "@decaf-ts/decorator-validation";
360
+ import { greaterThan, greaterThanOrEqual, lessThan, lessThanOrEqual } from "@decaf-ts/decorator-validation";
361
+
362
+ @model()
363
+ class Range extends Model {
364
+ @greaterThan(0)
365
+ @lessThanOrEqual(100)
366
+ ratio!: number;
218
367
  }
368
+ ```
219
369
 
220
- class PriceRange {
221
- @required()
222
- @min(0)
223
- minPrice!: number;
370
+ ### @step(step, message?)
371
+ Description: Numeric step constraints.
224
372
 
225
- @required()
226
- @min(0)
227
- @gte("minPrice", "Maximum price must be greater than or equal to minimum price")
228
- maxPrice!: number;
373
+ ```typescript
374
+ import { model, Model } from "@decaf-ts/decorator-validation";
375
+ import { step } from "@decaf-ts/decorator-validation";
229
376
 
230
- constructor(data?: any) {
231
- Model.fromModel(this, data);
232
- }
377
+ @model()
378
+ class Slider extends Model {
379
+ @step(0.5)
380
+ value!: number;
233
381
  }
234
382
  ```
235
383
 
236
- ##### Collection Validation
384
+ ### @list(values, message?)
385
+ Description: Constrains value to be one of the provided list.
237
386
 
238
387
  ```typescript
239
- class Tag {
240
- @required()
241
- name!: string;
388
+ import { model, Model } from "@decaf-ts/decorator-validation";
389
+ import { list } from "@decaf-ts/decorator-validation";
242
390
 
243
- constructor(data?: any) {
244
- Model.fromModel(this, data);
245
- }
391
+ @model()
392
+ class ThemeSettingm extends Model {
393
+ @list(["light", "dark", "system"])
394
+ theme!: "light" | "dark" | "system";
246
395
  }
396
+ ```
247
397
 
248
- class BlogPost {
249
- @required()
250
- title!: string;
398
+ ### @diff(propertyPath, message?)
399
+ Description: Must be different from another property.
251
400
 
252
- @required()
253
- content!: string;
401
+ ```typescript
402
+ import { model, Model } from "@decaf-ts/decorator-validation";
403
+ import { diff } from "@decaf-ts/decorator-validation";
254
404
 
255
- @list(Tag)
256
- tags!: Tag[];
405
+ @model()
406
+ class Credentials extends Model {
407
+ username!: string;
408
+ @diff(":username")
409
+ password!: string;
410
+ }
411
+ ```
257
412
 
258
- @set(Tag)
259
- uniqueTags!: Set<Tag>;
413
+ ### @date({ min?, max? }, message?)
414
+ Description: Date constraints for a date-typed field.
260
415
 
261
- constructor(data?: any) {
262
- Model.fromModel(this, data);
263
- }
416
+ ```typescript
417
+ import { model, Model } from "@decaf-ts/decorator-validation";
418
+ import { date } from "@decaf-ts/decorator-validation";
419
+
420
+ @model()
421
+ class Booking extends Model {
422
+ @date({ min: new Date("2025-01-01"), max: new Date("2025-12-31") })
423
+ start!: Date;
424
+ }
425
+ ```
426
+
427
+ ### @password(options?, message?)
428
+ Description: Password strength constraints (e.g., min length, uppercase, digits, symbols) depending on validator configuration.
429
+
430
+ ```typescript
431
+ import { model } from "@decaf-ts/decorator-validation";
432
+ import { password, Model } from "@decaf-ts/decorator-validation";
433
+
434
+ @model()
435
+ class Account extends Model{
436
+ @password({ minLength: 10 })
437
+ password!: string;
264
438
  }
265
439
  ```
266
440
 
267
- #### Model Registry and Building
441
+ ### async()
442
+ Description: Marks a model as involving async validation rules (decorator flag helper).
268
443
 
269
444
  ```typescript
270
- // Register models
271
- Model.register(User);
272
- Model.register(BlogPost);
273
- Model.register(Tag);
274
-
275
- // Build a model from plain object
276
- const userData = {
277
- username: "jane_doe",
278
- email: "jane@example.com",
279
- age: 28
280
- };
281
-
282
- const user = Model.build(userData, "User");
283
-
284
- // Bulk register models
285
- bulkModelRegister(User, BlogPost, Tag);
445
+ import { model } from "@decaf-ts/decorator-validation";
446
+ import { async } from "@decaf-ts/decorator-validation";
447
+
448
+ @model()
449
+ @async()
450
+ class Signup {
451
+ // fields that may use async validators
452
+ }
286
453
  ```
287
454
 
288
- #### Serialization and Deserialization
455
+
456
+ ## Running validation
457
+ Use the model validation utilities to evaluate rules defined by decorators.
289
458
 
290
459
  ```typescript
291
- // Create a user
292
- const user = new User({
293
- username: "john_doe",
294
- email: "john@example.com",
295
- age: 25
460
+ import { model, required, email, validate, Model } from "@decaf-ts/decorator-validation";
461
+
462
+ @model()
463
+ class Contact extends Model {
464
+ @required()
465
+ @email()
466
+ email!: string;
467
+
468
+ constructor(arg?: ModelArg<Contact>) {
469
+ super(arg);
470
+ }
471
+ }
472
+
473
+ const c = new Contact({
474
+ email: "not-an-email"
296
475
  });
297
476
 
298
- // Serialize to string
299
- const serialized = user.serialize();
300
- console.log(serialized);
301
- // Output: JSON string representation of the user
477
+ let errs = c.hasErrors(); // resolves to a list of errors or undefined
478
+
479
+ @async()
480
+ @model()
481
+ class User extends Model {
482
+ @required()
483
+ @email()
484
+ email!: string;
485
+
486
+ constructor(arg?: ModelArg<User>) {
487
+ super(arg);
488
+ }
489
+ }
490
+
491
+ const u = new User({
492
+ email: "not-an-email"
493
+ })
494
+
495
+ errs = await u.hasErrors(); // resolves to a list of errors or undefined
302
496
 
303
- // Deserialize from string
304
- const deserialized = Model.deserialize(serialized);
305
- console.log(deserialized);
306
- // Output: User object with the same properties
307
497
  ```
308
498
 
309
- #### Comparing Models
499
+
500
+ ## Validation registry APIs (Validation)
501
+
502
+ ### Validation.setRegistry(registry, migration?)
503
+ Description: Swap the active validator registry and optionally migrate validators.
310
504
 
311
505
  ```typescript
312
- const user1 = new User({
313
- username: "john_doe",
314
- email: "john@example.com",
315
- age: 25
316
- });
506
+ import { Validation, ValidatorRegistry } from "@decaf-ts/decorator-validation";
317
507
 
318
- const user2 = new User({
319
- username: "john_doe",
320
- email: "john@example.com",
321
- age: 25
322
- });
508
+ const custom = new ValidatorRegistry();
509
+ Validation.setRegistry(custom, v => v); // trivial migration
510
+ ```
323
511
 
324
- const user3 = new User({
325
- username: "jane_doe",
326
- email: "jane@example.com",
327
- age: 28
328
- });
512
+ ### Validation.register(...validators)
513
+ Description: Register one or more Validator implementations or definitions.
514
+
515
+ ```typescript
516
+ import { Validation, Validator, validator } from "@decaf-ts/decorator-validation";
329
517
 
330
- console.log(user1.equals(user2)); // true - same properties
331
- console.log(user1.equals(user3)); // false - different properties
518
+ @validator("ALWAYS_OK")
519
+ class AlwaysOk extends Validator {
520
+ hasErrors(...args: any[]) { return []; }
521
+ }
332
522
 
333
- // Compare ignoring specific properties
334
- console.log(user1.equals(user3, "username", "email")); // true - only comparing age
523
+ Validation.register(new AlwaysOk());
335
524
  ```
336
525
 
337
- #### Hashing Models
526
+ ### Validation.get(key)
527
+ Description: Retrieve a registered validator by key.
338
528
 
339
529
  ```typescript
340
- const user = new User({
341
- username: "john_doe",
342
- email: "john@example.com",
343
- age: 25
344
- });
530
+ import { Validation, ValidationKeys } from "@decaf-ts/decorator-validation";
345
531
 
346
- // Get hash of the model
347
- const hash = user.hash();
348
- console.log(hash);
349
- // Output: Hash string based on the configured algorithm (sha256)
532
+ const requiredValidator = Validation.get(ValidationKeys.REQUIRED);
350
533
  ```
351
534
 
535
+ ### Validation.key(k) and Validation.keys()
536
+ Description: Build a reflect-metadata key or list all registered keys.
537
+
538
+ ```typescript
539
+ import { Validation } from "@decaf-ts/decorator-validation";
540
+
541
+ const metaKey = Validation.key("REQUIRED");
542
+ const allKeys = Validation.keys();
543
+ ```
544
+
545
+ ## Notes on tests and validity
546
+ - Patterns here reflect common test patterns found across the monorepo (e.g., model decoration, decorator application, registry customization).
547
+ - Each snippet is valid TypeScript and aligns with the re-exports provided by the package entry.
548
+
352
549
 
353
550
  ### Related
354
551