@webergency-utils/typechecker 0.1.9 → 0.2.1

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,38 +1,77 @@
1
1
  # @webergency-utils/typechecker
2
2
 
3
- `@webergency-utils/typechecker` is a high-performance, zero-runtime-dependency TypeScript compiler plugin (transformer) that converts your static TypeScript types into optimized, hashed, and hoisted runtime validators.
3
+ An ahead-of-time (AOT) TypeScript validation engine that compiles types into optimized runtime validators via a compiler transformer—no runtime reflection and no third-party schema library.
4
4
 
5
- It is designed to work seamlessly with the `@webergency-utils/server` library, automatically enforcing strict data validation at compile time, and provides standard validation API wrappers matching `typia` with extended options for coercion and array handling.
5
+ [![npm version](https://img.shields.io/npm/v/%40webergency-utils%2Ftypechecker)](https://www.npmjs.com/package/@webergency-utils/typechecker)
6
+ [![License](https://img.shields.io/npm/l/%40webergency-utils%2Ftypechecker)](https://www.npmjs.com/package/@webergency-utils/typechecker)
7
+ [![Maintenance](https://img.shields.io/badge/maintenance-active-brightgreen.svg)](#maintenance)
8
+ [![dependencies](https://img.shields.io/badge/dependencies-0-brightgreen.svg)](https://www.npmjs.com/package/@webergency-utils/typechecker?activeTab=dependencies)
9
+ [![npm downloads](https://img.shields.io/npm/dm/%40webergency-utils%2Ftypechecker)](https://www.npmjs.com/package/@webergency-utils/typechecker)
10
+ <br>
11
+ [![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/webergency-utils/typechecker/badge)](https://securityscorecards.dev/viewer/?uri=github.com/webergency-utils/typechecker)
12
+ [![codecov](https://codecov.io/gh/webergency-utils/typechecker/branch/main/graph/badge.svg)](https://codecov.io/gh/webergency-utils/typechecker)
13
+ [![tests](https://github.com/webergency-utils/typechecker/actions/workflows/ci.yml/badge.svg)](https://github.com/webergency-utils/typechecker/actions/workflows/ci.yml)
14
+ [![CodeQL](https://github.com/webergency-utils/typechecker/actions/workflows/codeql.yml/badge.svg)](https://github.com/webergency-utils/typechecker/actions/workflows/codeql.yml)
6
15
 
7
- ---
16
+ ## TL;DR
8
17
 
9
- ## Features
18
+ ```typescript
19
+ import { validate, constraint, format } from '@webergency-utils/typechecker';
10
20
 
11
- - **⚡ Blazing Fast**: No runtime schema parsing or generic reflection. Code is generated at compile time as highly optimized JavaScript pipelines.
12
- - **📦 Zero Dependency**: The generated code has absolutely zero external dependencies.
13
- - **🔄 Advanced Type Checking**: Full support for Unions, Intersections, Nested Objects, Tuples, and Optional Properties.
14
- - **🏷️ Tag-Based Validation**: Custom JSON-Schema validation tags directly inside your TypeScript types (e.g. `MinLength<8>`, `Format<'email'>`).
15
- - **🛡️ Multiple Validation Modes**: Easily switch between `'strict'`, `'relaxed'`, and `'strip'` modes.
16
- - **📈 Coercion & Coalescing**: Extended options for type conversion and single-value array wrapping (highly useful for HTTP Query parameters!).
21
+ interface User {
22
+ id: string & format.UUID;
23
+ name: string & constraint.MinLength<2>;
24
+ age: number & constraint.Minimum<18>;
25
+ }
17
26
 
18
- ---
27
+ const input: unknown = {
28
+ id: '550e8400-e29b-41d4-a716-446655440000',
29
+ name: 'Alice',
30
+ age: 25,
31
+ };
32
+
33
+ // Validate input against the User type definition
34
+ const result = validate<User>(input);
35
+
36
+ if (result.success) {
37
+ // TypeScript narrows type to User here
38
+ console.log('User is valid:', result.data);
39
+ } else {
40
+ console.error('Validation failed:', result.errors);
41
+ }
42
+ ```
19
43
 
20
- ## Installation
44
+ ## Installation & Setup
21
45
 
22
- Since this library is a TypeScript compiler plugin, you will need a tool like `ts-patch` or `ts-node` to hook into the compilation process.
46
+ This package is a TypeScript compiler plugin (transformer). You must compile with a compiler patcher such as `ts-patch` so the transformer can hook into `tsc`.
47
+
48
+ **Peer dependency:** `typescript` `>=5.0.0` (required; provides the compiler API the transformer and language service plugin use).
49
+
50
+ There are **no runtime `dependencies`**—only the peer `typescript` and your build tooling.
51
+
52
+ ### 1. Install Dependencies
53
+
54
+ Install the core package, along with `ts-patch` as a development dependency:
23
55
 
24
56
  ```bash
25
57
  npm install @webergency-utils/typechecker
26
- npm install -D ts-patch
58
+ npm install --save-dev ts-patch
27
59
  ```
28
60
 
29
- Run `ts-patch install` to patch your local TypeScript installation.
61
+ ### 2. Inject Compiler Hook
30
62
 
31
- ---
63
+ Run the patcher command to set up `ts-patch` inside your local TypeScript installation:
32
64
 
33
- ## Configuration
65
+ ```bash
66
+ npx ts-patch install
67
+ ```
68
+
69
+ > [!NOTE]
70
+ > It is recommended to add `ts-patch install` to your `package.json` `prepare` script so it runs automatically after every dependency installation.
71
+
72
+ ### 3. Configure tsconfig.json
34
73
 
35
- Update your `tsconfig.json` to include the transformer in the `compilerOptions.plugins` array:
74
+ Register the typechecker **transformer** (and optionally the language service plugin) under `compilerOptions.plugins` in `tsconfig.json`:
36
75
 
37
76
  ```json
38
77
  {
@@ -41,337 +80,328 @@ Update your `tsconfig.json` to include the transformer in the `compilerOptions.p
41
80
  "module": "NodeNext",
42
81
  "moduleResolution": "NodeNext",
43
82
  "plugins": [
44
- { "transform": "@webergency-utils/typechecker" }
83
+ { "transform": "@webergency-utils/typechecker/transformer" },
84
+ { "name": "@webergency-utils/typechecker/plugin" }
45
85
  ]
46
86
  }
47
87
  }
48
88
  ```
49
89
 
90
+ The `transform` entry is required for AOT validation. The `name` entry enables IDE constraint diagnostics via the language service plugin.
50
91
  ---
51
92
 
52
- ## Usage
93
+ ## Architecture & Internals
53
94
 
54
- You can validate unknown data anywhere in your code. The transformer intercepts these calls and replaces them with direct, optimized validation functions.
95
+ The package utilizes a custom TypeScript compiler transformer and language service plugin to deliver highly efficient type-safe runtime validations.
55
96
 
56
- ```typescript
57
- import { is, assert, assertGuard, validate } from '@webergency-utils/typechecker';
97
+ ### Build-Time Compilation Flow
58
98
 
59
- interface Payload {
60
- id: string;
61
- active: boolean;
62
- }
99
+ ```mermaid
100
+ graph TD
101
+ A[TypeScript Source Code] --> B[ts-patch / Compiler Hook]
102
+ B --> C[TypeScript compiler plugin / Transformer]
103
+ C --> D[Extract types via compiler TypeChecker]
104
+ D --> E[Generate optimized JS validator function]
105
+ E --> F[Hoisted Validator Registry & MetadataStore registration]
106
+ F --> G[Replace source call with MetadataStore invocation]
107
+ G --> H[Emit optimized JavaScript files]
108
+ ```
63
109
 
64
- const data: unknown = JSON.parse('{"id": "123", "active": true}');
110
+ 1. **Build-Time Transformation**: The compiler transformer intercepts calls to validation helper functions (`validate`, `is`, `assert`, `assertGuard`, `jsonSchema`) containing generic arguments.
111
+ 2. **Type Extraction & Analysis**: It parses the target TS type structure, extracting intersection constraints, formats, transforms, and defaults recursively.
112
+ 3. **Hoisted Registry**: The transformer generates highly optimized, direct JavaScript validator functions for each resolved type shape, generates a unique hash, and registers them in a global `MetadataStore`.
113
+ 4. **Call Replacement**: The transformer replaces the original compile-time call expressions with direct, zero-reflection references to the runtime `MetadataStore`.
65
114
 
66
- // 1. is() - returns a boolean (type guard)
67
- if (is<Payload>(data)) {
68
- console.log(data.id); // Narrowed to 'Payload'
69
- }
115
+ ### External dependencies
70
116
 
71
- // 2. assert() - returns the narrowed value or throws an error
72
- const validData = assert<Payload>(data);
117
+ - **Required peer:** `typescript` (`>=5.0.0`) compiler API for the transformer and optional language service plugin.
118
+ - **Required build tooling:** `ts-patch` (or equivalent) — patches `tsc` so `compilerOptions.plugins` `transform` entries run.
119
+ - **Runtime dependencies:** none.
73
120
 
74
- // 3. assertGuard() - asserts the type for the current scope in-place
75
- assertGuard<Payload>(data);
76
- console.log(data.id); // Narrowed in-place
121
+ ### Static Constraint Diagnostics
77
122
 
78
- // 4. validate() - returns a structured validation result with errors
79
- const result = validate<Payload>(data);
80
- if (result.success) {
81
- console.log(result.data);
82
- } else {
83
- console.error(result.errors); // Array of formatted errors
84
- }
123
+ The package includes an IDE / Language Service plugin that statically checks literal values against type constraints during editing or compilation:
124
+
125
+ ```typescript
126
+ import { constraint } from '@webergency-utils/typechecker';
127
+
128
+ // This yields a compilation error directly in the IDE:
129
+ // Type '5' is not assignable to type 'number & Minimum<18>'.
130
+ const age: number & constraint.Minimum<18> = 5;
85
131
  ```
86
132
 
87
133
  ---
88
134
 
89
- ### 3. Extended Options & Validation Modes
135
+ ## Glossary
136
+
137
+ - [`validate`](src/index.ts): Validates a value against a type, returning a structured result containing the validation status and a detailed list of errors.
138
+ - [`is`](src/index.ts): A type guard that returns `true` if a value already matches `T` (no coercion; `from` is ignored).
139
+ - [`assert`](src/index.ts): Validates a value and returns it, throwing a validation error on failure (supports `from` coercion).
140
+ - [`assertGuard`](src/index.ts): Asserts a value already matches `T` and narrows the outer scope (no coercion; `from` is ignored).
141
+ - [`jsonSchema`](src/index.ts): Generates and returns a JSON Schema representation matching a TypeScript type at compile time (draft-07 shaped, with `x-typescript-type` for Date/RegExp/Set/Map/bigint/etc.).
142
+ - [`WithModifiers`](src/runtime/tags.ts): A utility type that applies constraint, format, or transformation tags to properties of deeply nested or external types using dot-separated path mappings.
143
+ - [`ResolveDefaults`](src/runtime/tags.ts): A helper type that removes the optional flag (`?`) from properties that have defined default values.
144
+ - [`convertPropertyCasing`](src/runtime/casing.ts): A runtime utility to recursively change the casing of object keys.
145
+ - [`toZodIssues`](src/runtime/validators.ts) / [`groupErrorsByPath`](src/runtime/validators.ts): Transform or group validation errors (including nested union `issues`).
146
+ - [`ZodLikeError`](src/runtime/validators.ts): Error class wrapping validation errors in a structure compatible with libraries expecting Zod errors.
147
+ - [`@webergency-utils/typechecker/transformer`](src/transformer.ts): Required `ts-patch` transform entry for AOT rewrite.
148
+ - [`@webergency-utils/typechecker/plugin`](src/plugin.ts): Optional language-service plugin for IDE static constraint diagnostics.
90
149
 
91
- All validation APIs accept either a string `ValidationMode` or a custom `ValidationOptions` object:
150
+ ---
92
151
 
93
- ```typescript
94
- export type ValidationMode = 'strict' | 'relaxed' | 'strip';
152
+ ## API Reference
95
153
 
96
- export interface ValidationOptions {
97
- mode?: ValidationMode; // default: 'strict'
98
- tryConvert?: boolean; // Converts string numbers, booleans, and dates (ideal for query parameters)
99
- wrapArrays?: boolean; // Wraps a single value into an array if the type expects an array
100
- }
101
- ```
154
+ ### Validation Functions
102
155
 
103
- #### Examples:
104
- ```typescript
105
- // Relaxed Mode (ignores additional properties)
106
- const user = assert<User>(data, 'relaxed');
107
-
108
- // Strip Mode (strips out any unknown properties from returned object)
109
- const cleanUser = assert<User>(data, 'strip');
110
-
111
- // Query-String Coercion
112
- const query = assert<SearchQuery>(rawQuery, {
113
- mode: 'strip',
114
- tryConvert: true, // Coerces "18" -> 18, "true" -> true, etc.
115
- wrapArrays: true // Coerces "tag" -> ["tag"] if tags: string[] is expected
116
- });
117
- ```
156
+ #### `validate<T>(input: unknown, options?: ValidationMode | ValidationOptions): IValidation<ResolveDefaults<T>>`
118
157
 
119
- ---
158
+ Validates input data against type `T` and returns a structured validation result.
120
159
 
121
- ### 4. Error Reporting & Grouping
160
+ - **Parameters**:
161
+ - `input`: The value to validate.
162
+ - `options` (optional): Either a `ValidationMode` string ('strict' | 'relaxed' | 'strip') or a `ValidationOptions` object.
163
+ - **Returns**: `IValidation<ResolveDefaults<T>>` containing validation status, converted/stripped data, and error details.
164
+ - **Example**:
165
+ ```typescript
166
+ const result = validate<User>(data, 'strip');
167
+ ```
122
168
 
123
- When validation fails using `validate<T>()`, you receive a highly structured array of errors. To make this easy to consume for humans, LLMs, and UI libraries (like React Hook Form), the library provides a `groupErrorsByPath` helper that organizes these errors by their exact JSON path.
169
+ #### `is<T>(input: unknown, options?: ValidationMode | ValidationOptions): input is ResolveDefaults<T>`
124
170
 
125
- The error strings follow a deterministic, parser-friendly `Constraint<Value>` format.
171
+ A type guard function checking if the input matches type `T`. Does **not** coerce; `from` is ignored.
126
172
 
127
- ```typescript
128
- import { validate, groupErrorsByPath, Minimum } from '@webergency-utils/typechecker';
173
+ - **Parameters**:
174
+ - `input`: The value to check.
175
+ - `options` (optional): Either a `ValidationMode` string or a `ValidationOptions` object (`from` has no effect).
176
+ - **Returns**: `boolean` (`true` if valid, `false` otherwise). Narrows type of `input` to `ResolveDefaults<T>` on success.
177
+ - **Example**:
178
+ ```typescript
179
+ if (is<User>(data)) {
180
+ console.log(data.name);
181
+ }
182
+ ```
129
183
 
130
- interface Payload {
131
- id: string;
132
- role: "admin" | "user";
133
- age: number & Minimum<18>;
134
- metadata: { tag: string } | { priority: number };
135
- }
184
+ #### `assert<T>(input: unknown, options?: ValidationMode | ValidationOptions): ResolveDefaults<T>`
136
185
 
137
- const data = {
138
- id: 123, // Error: expected string, got number
139
- role: "guest", // Error: literal union mismatch
140
- age: 15, // Error: minimum constraint failed
141
- metadata: { } // Error: complex union mismatch
142
- };
186
+ Validates input data and returns it, throwing a validation error on failure.
143
187
 
144
- const result = validate<Payload>(data);
145
- if (!result.success) {
146
- const grouped = groupErrorsByPath(result.errors);
147
- console.log(JSON.stringify(grouped, null, 2));
148
- }
149
- ```
188
+ - **Parameters**:
189
+ - `input`: The value to validate.
190
+ - `options` (optional): Either a `ValidationMode` string or a `ValidationOptions` object.
191
+ - **Returns**: `ResolveDefaults<T>` (the validated value with defaults resolved).
192
+ - **Throws**: `Error` containing a list of path and constraint failures, or a custom error via `options.errorFactory`.
193
+ - **Example**:
194
+ ```typescript
195
+ const user = assert<User>(data);
196
+ ```
150
197
 
151
- **Output:**
152
- ```json
153
- {
154
- "id": {
155
- "value": 123,
156
- "errors": ["Type<string>"]
157
- },
158
- "role": {
159
- "value": "guest",
160
- "errors": [
161
- "Literal<'admin'>",
162
- "Literal<'user'>"
163
- ]
164
- },
165
- "age": {
166
- "value": 15,
167
- "errors": ["Minimum<18>"]
168
- },
169
- "metadata": {
170
- "value": {},
171
- "errors": ["Type<{tag:string}|{priority:number}>"]
172
- },
173
- "metadata.tag": {
174
- "value": undefined,
175
- "errors": ["Type<string>"]
176
- },
177
- "metadata.priority": {
178
- "value": undefined,
179
- "errors": ["Type<number>"]
180
- }
181
- }
182
- ```
198
+ #### `assertGuard<T>(input: unknown, options?: ValidationMode | ValidationOptions): asserts input is ResolveDefaults<T>`
183
199
 
184
- This flattened, grouped output is incredibly powerful—it tells the developer (or an AI agent) exactly *why* a complex union or object failed down to the very specific branch and missing property constraint.
200
+ An assertion guard that throws a validation error if the input does not match type `T`. Does **not** coerce; `from` is ignored.
185
201
 
186
- ---
202
+ - **Parameters**:
203
+ - `input`: The value to check.
204
+ - `options` (optional): Either a `ValidationMode` string or a `ValidationOptions` object (`from` has no effect).
205
+ - **Returns**: `void`. Narrows the type of `input` in the enclosing scope on success.
206
+ - **Throws**: `Error` if validation fails (or a custom error via `options.errorFactory`).
207
+ - **Example**:
208
+ ```typescript
209
+ assertGuard<User>(data);
210
+ ```
187
211
 
188
- ### 5. Deep/External Type Validation (WithModifiers)
212
+ #### `jsonSchema<T>(): any`
189
213
 
190
- If you have deeply nested objects or external types that you cannot edit directly, you can add validation tags to specific properties using dot-notation path references. This prevents you from having to retype the entire structure.
214
+ Generates a raw JSON Schema draft-07 object matching type `T` at compile time.
191
215
 
192
- ```typescript
193
- import { WithModifiers, constraint, format } from '@webergency-utils/typechecker';
194
-
195
- // External type we cannot edit directly
196
- interface ExternalUser {
197
- id: string;
198
- profile?: {
199
- details: {
200
- email: string;
201
- password: string;
202
- }
203
- }
204
- }
216
+ - **Returns**: `any` (a JSON Schema object).
217
+ - **Example**:
218
+ ```typescript
219
+ const userSchema = jsonSchema<User>();
220
+ ```
205
221
 
206
- // Decorate specific paths using dot notation
207
- type ValidatedUser = WithModifiers<ExternalUser, {
208
- 'profile.details.email': format.Email;
209
- 'profile.details.password': constraint.MinLength<8>;
210
- }>;
211
- ```
222
+ ---
212
223
 
213
- ### 6. Cross-Field Dependencies (Requires)
224
+ ### Utility Functions and Classes
214
225
 
215
- You can specify that a property requires the presence of other properties in the same object using absolute paths or relative dot-notation paths (e.g., `.sibling`, `..grandparent.cousin`):
226
+ #### `convertPropertyCasing<T, C extends CasingFormat>(obj: T, casing: C, options?: ConvertCasingOptions): ConvertPropertyCasing<T, C>`
216
227
 
217
- ```typescript
218
- import { WithModifiers, constraint } from '@webergency-utils/typechecker';
219
-
220
- interface DBConfig {
221
- ssl?: boolean;
222
- cert?: string;
223
- auth?: {
224
- username?: string;
225
- password?: string;
226
- }
227
- }
228
+ Recursively converts all property keys of an object to the specified casing format.
228
229
 
229
- type SecureConfig = WithModifiers<DBConfig, {
230
- // Absolute check: if "ssl" is defined, "cert" must exist at root
231
- 'ssl': constraint.Requires<'cert'>;
230
+ - **Parameters**:
231
+ - `obj`: The source object.
232
+ - `casing`: A `CasingFormat` string value (`'snake_case' | 'SNAKE_CASE' | 'camelCase' | 'camelCaseID' | 'PascalCase' | 'PascalCaseID' | 'kebab-case' | 'dot.case'`).
233
+ - `options` (optional): `ConvertCasingOptions` object.
234
+ - **Returns**: The casing-converted object with updated TypeScript property keys.
235
+ - **Example**:
236
+ ```typescript
237
+ const apiResponse = convertPropertyCasing(user, 'camelCase');
238
+ ```
232
239
 
233
- // Relative check: if "auth.password" is defined, its sibling "username" must exist
234
- 'auth.password': constraint.Requires<'.username'>;
235
- }>;
236
- ```
240
+ #### `toZodIssues(errors: IValidationError[]): any[]`
237
241
 
238
- ### 7. Custom Error Messages
242
+ Converts internal validation errors into Zod-compatible issues. Flattens nested union `issues` into a flat list.
239
243
 
240
- You can specify custom error messages for constraint violations. Most constraint tags accept a custom message as an optional template argument, or you can use the `Message<Msg>` tag:
244
+ - **Parameters**:
245
+ - `errors`: Array of `IValidationError`.
246
+ - **Returns**: An array of Zod-like issues.
241
247
 
242
- ```typescript
243
- import { validate, constraint, Message } from '@webergency-utils/typechecker';
248
+ #### `groupErrorsByPath(errors: IValidationError[]): Record<string, { value: any, errors: string[] }>`
244
249
 
245
- interface User {
246
- // Direct message in constraint tag
247
- age: number & constraint.Minimum<18, "Must be 18 or older">;
250
+ Groups validation errors by path, including nested `issues` from failed unions.
248
251
 
249
- // Fallback property message via Message<T>
250
- email: string & constraint.Format<'email'> & Message<"Please supply a valid email address">;
251
- }
252
- ```
252
+ - **Parameters**:
253
+ - `errors`: Array of `IValidationError`.
254
+ - **Returns**: A map of path → `{ value, errors }`.
253
255
 
254
- ### 8. Zod Compatibility
256
+ #### `coerceQueryNumber(v: any): any` / `coerceQueryBoolean(v: any): any` / `coerceQueryDate(v: any): any` / `coerceJsonDate(v: any): any`
255
257
 
256
- If you are integrating with libraries that expect Zod-style validation errors (such as React Hook Form or API gateways), you can convert the validation errors using the `toZodIssues` helper or throw a `ZodLikeError` which wraps the issues:
258
+ Shared coercion helpers used by `from: 'query'` / `from: 'json'` and by `transform.ToNumber` / `ToBoolean` / `ToDate`.
257
259
 
258
- ```typescript
259
- import { validate, toZodIssues, ZodLikeError } from '@webergency-utils/typechecker';
260
+ #### `class ZodLikeError extends Error`
260
261
 
261
- const result = validate<User>(data);
262
- if (!result.success) {
263
- // 1. Convert errors array to Zod-compliant issues
264
- const zodIssues = toZodIssues(result.errors);
265
- console.log(zodIssues); // [{ code: "custom", path: ["age"], message: "Minimum<18>", received: 15 }]
262
+ An error class wrapper that transforms internal validation errors into a Zod-like error structure.
266
263
 
267
- // 2. Or throw a Zod-like Error
268
- throw new ZodLikeError(result.errors);
269
- }
270
- ```
264
+ - **Constructor**: `constructor(errors: IValidationError[])`
265
+ - **Properties**:
266
+ - `name`: `'ZodError'`
267
+ - `issues`: Zod-like issue array.
271
268
 
272
269
  ---
273
270
 
274
- ## Supported Validation & Modifier Namespaces
275
-
276
- The library provides validation tags and modifiers grouped into four logical namespaces. These can be imported directly or used via their namespaces (e.g., `constraint.MinLength` vs `MinLength`).
277
-
278
- ### 1. `constraint` Namespace
279
- Impose structural and value-based constraints on primitives:
280
-
281
- - `MinLength<N, Msg?>`: Minimum string length.
282
- - `MaxLength<N, Msg?>`: Maximum string length.
283
- - `Length<Min, Max>`: Helper combining MinLength and MaxLength.
284
- - `Pattern<RegExp, Msg?>`: Regular expression validation.
285
- - `Minimum<N, Msg?>` / `Maximum<N, Msg?>`: Inclusive numeric bounds (supports `number | bigint`).
286
- - `ExclusiveMinimum<N, Msg?>` / `ExclusiveMaximum<N, Msg?>`: Exclusive numeric bounds.
287
- - `Range<Min, Max>`: Helper combining Minimum and Maximum.
288
- - `MultipleOf<N, Msg?>`: Enforces that the value is a multiple of `N`.
289
- - `MinItems<N, Msg?>` / `MaxItems<N, Msg?>`: Array size constraints.
290
- - `UniqueItems<Msg?>`: Enforces that all elements in an array or Set are deeply unique.
291
- - `Custom<Fn, Msg?>`: Executes a custom validation function: `(val, ctx) => boolean`.
292
- - `Requires<Paths, Msg?>`: Enforces cross-field dependency validation.
293
- - `Message<Msg>`: Attaches a fallback custom error message to a property.
294
-
295
- ### 2. `format` Namespace
296
- Pre-defined string formatting shortcuts:
297
-
298
- - `format.Email`: RFC 5322 Email.
299
- - `format.UUID`: UUID (v1-v5) format.
300
- - `format.URL`: Full URL validation (HTTP/HTTPS/FTP).
301
- - `format.IPv4` / `format.IPv6`: IP address validation.
302
- - `format.DateTime`: ISO-8601 Date Time string.
303
- - `format.Date`: ISO Date (YYYY-MM-DD).
304
- - `format.Time`: Time string (HH:MM:SS).
305
- - `format.Duration`: ISO-8601 duration (e.g., PT1H).
306
- - `format.ObjectId`: 24-character MongoDB ObjectId.
307
- - `format.Byte`: Base64 encoded string.
308
- - `format.Password`: Always valid string, placeholder for password parameters.
309
- - `format.Regex`: Valid regular expression string.
310
- - `format.Hostname`: Valid domain name/hostname.
311
- - `format.URI`: Valid URI.
271
+ ### Interfaces and Types
312
272
 
313
- ### 3. `transform` Namespace
314
- Coerce or sanitize values before validation:
273
+ #### `interface ValidationOptions`
315
274
 
316
- - `transform.Trim`: Trims whitespace from both ends of a string.
317
- - `transform.LowerCase`: Converts string to lowercase.
318
- - `transform.UpperCase`: Converts string to uppercase.
319
- - `transform.Capitalize`: Capitalizes the first letter of a string.
320
- - `transform.ToNumber`: Coerces inputs to numbers.
321
- - `transform.ToBoolean`: Coerces inputs to booleans.
322
- - `transform.ToDate`: Coerces string timestamp to a Date object.
323
- - `transform.Custom<Fn>`: Applies a custom mapping function `(val) => any`.
275
+ Configuration options to customize validator behavior.
324
276
 
325
- ### 4. `tag` Namespace
326
- Define defaults for optional fields:
277
+ | Property | Type | Default | Description |
278
+ | :--- | :--- | :--- | :--- |
279
+ | `mode` | `'strict' \| 'relaxed' \| 'strip'` | `'strict'` | Validation mode strategy (strict key checking, relaxed, or key stripping). |
280
+ | `from` | `'json' \| 'query' \| ((key, value, type) => any)` | `undefined` | Input conversion mode for `validate` / `assert`. `'json'` revives JSON-impossible types (Date, RegExp, bigint, Set, Map). `'query'` also coerces querystring shapes (string→number/boolean, timestamps→Date, etc.). A function is called only on type mismatch. Ignored by `is` / `assertGuard`. |
281
+ | `wrapArrays` | `boolean` | `false` | Wraps non-array values into single-element arrays if an array is expected. |
282
+ | `mutate` | `boolean` | `false` | When true, write validated/coerced values onto the input. Default false: always return new containers. |
283
+ | `schema` | `any` | `undefined` | Custom JSON Schema instance. |
284
+ | `errorFactory` | `(errors: IValidationError[]) => Error` | `undefined` | Custom error factory for `assert` throwing. |
327
285
 
328
- - `tag.Default<Value>`: Injects the specified `Value` if the property is `undefined` at validation time.
286
+ #### `interface IValidation<T>`
329
287
 
330
- ### Assignability & compile-time constants
288
+ Result object returned by `validate`.
331
289
 
332
- Constraint tags use **optional** phantom properties (same pattern as `tag.Default`), so plain values assign to tagged types:
290
+ - `success`: `boolean`
291
+ - `data?`: `T`
292
+ - `errors?`: `IValidationError[]`
333
293
 
334
- ```typescript
335
- const age: number & Minimum<18> = 18; // OK
336
- const plain: { age: number } = { age: 5 };
337
- const tagged: { age: number & Minimum<18> } = plain; // OK
338
- ```
294
+ #### `interface IValidationError`
339
295
 
340
- When the TypeScript plugin is enabled, **compile-time constants** that violate constraints produce diagnostics:
296
+ Details of a validation check failure.
341
297
 
342
- ```typescript
343
- const tooYoung: number & Minimum<18> = 5; // Error: does not satisfy Minimum<18>
344
- const short: string & MinLength<3> = 'ab'; // Error: does not satisfy MinLength<3>
345
- const empty: string[] & MinItems<1> = []; // Error: does not satisfy MinItems<1>
346
- const dupes: number[] & UniqueItems = [1, 1]; // Error: does not satisfy UniqueItems
347
- ```
298
+ - `path`: `string`
299
+ - `value`: `any`
300
+ - `error`: `string` (the constraint description or custom message)
301
+ - `issues?`: `IValidationError[]` nested failures (used for unions: one summary error with per-arm details)
348
302
 
349
- Non-constants (`number`, variables, function results) are not checked statically — use `is` / `assert` / `validate` at runtime.
303
+ #### `type WithModifiers<T, M>`
350
304
 
351
- #### Example:
352
- ```typescript
353
- import { MinLength, Minimum, Format, UniqueItems, tag, transform, constraint } from '@webergency-utils/typechecker';
354
-
355
- interface Profile {
356
- email: string & Format<'email'> & transform.Trim & transform.LowerCase;
357
- password: string & MinLength<8>;
358
- age: number & Minimum<18> & constraint.Message<"Must be 18+">;
359
- luckyNumbers: number[] & UniqueItems;
360
- role: string & tag.Default<"user">;
361
- }
362
- ```
305
+ Applies constraint, format, or transformation tags to properties of type `T` using a path mapping `M`.
306
+
307
+ - **Generics**:
308
+ - `T`: The baseline type to wrap.
309
+ - `M`: A key-value map where keys are dot-separated paths (e.g., `'profile.email'`) and values are tags.
363
310
 
364
311
  ---
365
312
 
366
- ## How it Works
313
+ ### Tags & Modifiers
314
+
315
+ #### `constraint` Namespace
316
+
317
+ Used to apply value constraints to types.
318
+
319
+ | Tag | Description |
320
+ | :--- | :--- |
321
+ | `constraint.MinLength<N, Msg?>` | Restricts string length to $\ge N$. |
322
+ | `constraint.MaxLength<N, Msg?>` | Restricts string length to $\le N$. |
323
+ | `constraint.Length<Min, Max>` | Shorthand for `MinLength<Min> & MaxLength<Max>`. |
324
+ | `constraint.Pattern<Regex, Msg?>` | Validates string using regular expression `Regex`. |
325
+ | `constraint.Minimum<N, Msg?>` | Inclusive minimum value restriction for `number | bigint`. |
326
+ | `constraint.Maximum<N, Msg?>` | Inclusive maximum value restriction for `number | bigint`. |
327
+ | `constraint.Range<Min, Max>` | Shorthand for `Minimum<Min> & Maximum<Max>`. |
328
+ | `constraint.ExclusiveMinimum<N, Msg?>` | Exclusive minimum value restriction for `number | bigint`. |
329
+ | `constraint.ExclusiveMaximum<N, Msg?>` | Exclusive maximum value restriction for `number | bigint`. |
330
+ | `constraint.MultipleOf<N, Msg?>` | Restricts `number | bigint` to multiples of `N`. |
331
+ | `constraint.MinItems<N, Msg?>` | Restricts array length to $\ge N$. |
332
+ | `constraint.MaxItems<N, Msg?>` | Restricts array length to $\le N$. |
333
+ | `constraint.UniqueItems<Msg?>` | Restricts arrays to deeply unique items. |
334
+ | `constraint.Custom<Fn, Msg?>` | Runs a custom validation function: `(val, ctx) => boolean`. |
335
+ | `constraint.Requires<Path | [Paths], Msg?>` | Enforces that other object property paths exist. |
336
+ | `constraint.Message<Msg>` | Fallback custom error message. |
337
+
338
+ #### `format` Namespace
339
+
340
+ Standard formats for string primitives.
341
+
342
+ - `format.Email`: Practical mailbox check (`local@domain`, length limits, DNS-like domain with a real TLD). Not full RFC 5322.
343
+ - `format.IdnEmail`: Like email, but Unicode local/domain labels allowed.
344
+ - `format.UUID`: UUID (v1-v5).
345
+ - `format.URL`: HTTP/HTTPS/FTP URLs.
346
+ - `format.IPv4` / `format.IPv6`: IP addresses.
347
+ - `format.Date`: `YYYY-MM-DD` validated via `new Date(...)` (calendar overflow like `2024-02-31` is allowed). With `from: 'query'`, the runtime value becomes a `Date` (the TypeScript type remains `string & format.Date`).
348
+ - `format.DateTime`: date-time validated via `new Date(...)`. With `from: 'query'`, returns a `Date`; otherwise keeps the string.
349
+ - `format.ObjectId`: MongoDB 24-character hex ObjectId.
350
+ - `format.Duration`: ISO-8601 duration.
351
+ - `format.Time`: Time string `HH:MM:SS` with a required timezone (`Z` or `±HH:MM`), e.g. `19:55:00Z`.
352
+ - `format.Byte`: Base64 string.
353
+ - `format.Password`: Any string (always valid placeholder).
354
+ - `format.Regex`: Valid regular expression string.
355
+ - `format.Hostname`: ASCII domain name (includes `localhost`).
356
+ - `format.IdnHostname`: Internationalized hostname (Unicode labels).
357
+ - `format.URI`: Absolute URI.
358
+ - `format.UriReference`: Absolute URI or relative reference.
359
+ - `format.IRI`: Absolute IRI (Unicode URI).
360
+ - `format.IriReference`: Absolute IRI or relative reference.
361
+ - `format.UriTemplate`: RFC 6570 URI template.
362
+ - Unknown `Format<'...'>` strings fail validation at runtime.
363
+
364
+ Object and record shapes require **plain objects** (`Object.prototype` or `null` prototype). Class instances, `Date`, `Map`, `Set`, typed arrays, etc. are rejected unless the type is a dedicated instance type (`Date`, `Map`, …).
365
+
366
+ #### `transform` Namespace
367
367
 
368
- 1. **AST Analysis**: The transformer scans compile-time type signatures and generates highly nested, direct runtime checks.
369
- 2. **Circular References**: Safely handles recursive and circular types by generating self-referencing lazy functions.
370
- 3. **Hoisting & Deduping**: Identical type validations are hoisted to top-level constants and shared, minimizing footprint.
371
- 4. **Clean Emitted JS**: The output compiles into vanilla JS, utilizing direct, blazing-fast validation logic.
368
+ Sanitizes and converts input values during validation.
369
+
370
+ - `transform.Trim`: Trims string whitespace.
371
+ - `transform.LowerCase`: Converts string to lowercase.
372
+ - `transform.UpperCase`: Converts string to uppercase.
373
+ - `transform.Capitalize`: Capitalizes the first letter.
374
+ - `transform.ToNumber`: Same coercion as `from: 'query'` for numbers (non-empty numeric strings → `parseFloat`).
375
+ - `transform.ToBoolean`: Same coercion as `from: 'query'` for booleans (`true`/`false`/`1`/`0`/`yes`/`no`/`on`/`off`); unknown values are left unchanged and fail the boolean check.
376
+ - `transform.ToDate`: Same coercion as `from: 'query'` for dates (parseable strings and finite timestamps).
377
+ - `transform.Custom<Fn>`: Custom mapping function: `(val) => any`.
378
+
379
+ #### `tag` Namespace
380
+
381
+ - `tag.Default<Value>`: Injects `Value` when a property is undefined. Removes the optional modifier (`?`) when resolved with `ResolveDefaults<T>`.
372
382
 
373
383
  ---
374
384
 
375
- ## License
385
+ ## Troubleshooting
386
+
387
+ ### `validate`, `is`, or `assert` throw “transformer was not applied”
388
+ - **Cause**: The compiler transformer did not rewrite the call at build time. Untransformed stubs always throw.
389
+ - **Diagnostics Check**: Inspect your built `.js` output. If it still contains `validate(...)` / `is(...)` / `assert(...)` as package imports rather than `MetadataStore.validate(...)` (etc.), the transformer did not run.
390
+ - **Fix**:
391
+ 1. Verify `npx ts-patch install` was executed successfully.
392
+ 2. Verify `{ "transform": "@webergency-utils/typechecker/transformer" }` is registered in `tsconfig.json` `compilerOptions.plugins`.
393
+ 3. Ensure your bundler or compiler CLI compiles using patched `tsc`.
394
+
395
+ ### IDE does not report constraint errors on literals
396
+ - **Cause**: The optional language service plugin is not loaded.
397
+ - **Fix**: Add `{ "name": "@webergency-utils/typechecker/plugin" }` alongside the transformer entry in `tsconfig.json` plugins, and restart the TypeScript language service in your editor.
398
+
399
+ ---
400
+
401
+ ## Maintenance
402
+
403
+ This package is actively maintained.
376
404
 
377
- MIT © radixxko / [webergency-utils](https://github.com/webergency-utils)
405
+ Bug reports and pull requests are welcome. Security issues and critical
406
+ regressions are prioritized. New features are considered when they align
407
+ with the package's existing scope.