@webergency-utils/typechecker 0.1.8 → 0.1.10
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 +256 -255
- package/dist/engine/resolver.js +40 -24
- package/dist/engine/staticAsserts.d.ts +34 -0
- package/dist/engine/staticAsserts.js +317 -0
- package/dist/plugin.d.ts +7 -0
- package/dist/plugin.js +30 -0
- package/dist/runtime/casing.d.ts +3 -3
- package/dist/runtime/casing.js +4 -4
- package/dist/runtime/tags/constraint.d.ts +15 -15
- package/dist/runtime/tags/tag.d.ts +1 -1
- package/dist/runtime/tags/transform.d.ts +8 -8
- package/dist/runtime/tags.d.ts +3 -6
- package/dist/transformer.js +2 -0
- package/package.json +10 -4
- package/LICENSE +0 -21
package/README.md
CHANGED
|
@@ -1,38 +1,64 @@
|
|
|
1
1
|
# @webergency-utils/typechecker
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/@webergency-utils/typechecker) [](#maintenance) [](https://www.npmjs.com/package/@webergency-utils/typechecker) [](https://www.npmjs.com/package/@webergency-utils/typechecker)
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
An ahead-of-time (AOT) type validation engine and TypeScript compiler plugin that compiles TypeScript types directly into optimized runtime validation functions. It intercepts type definitions at build time to enforce value constraints, formatting, and defaults with zero runtime reflection and no third-party schema dependencies.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
## TL;DR
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
```typescript
|
|
10
|
+
import { validate, constraint, format } from '@webergency-utils/typechecker';
|
|
10
11
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
- **📈 Coercion & Coalescing**: Extended options for type conversion and single-value array wrapping (highly useful for HTTP Query parameters!).
|
|
12
|
+
interface User {
|
|
13
|
+
id: string & format.UUID;
|
|
14
|
+
name: string & constraint.MinLength<2>;
|
|
15
|
+
age: number & constraint.Minimum<18>;
|
|
16
|
+
}
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
const input: unknown = {
|
|
19
|
+
id: '550e8400-e29b-41d4-a716-446655440000',
|
|
20
|
+
name: 'Alice',
|
|
21
|
+
age: 25,
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
// Validate input against the User type definition
|
|
25
|
+
const result = validate<User>(input);
|
|
26
|
+
|
|
27
|
+
if (result.success) {
|
|
28
|
+
// TypeScript narrows type to User here
|
|
29
|
+
console.log('User is valid:', result.data);
|
|
30
|
+
} else {
|
|
31
|
+
console.error('Validation failed:', result.errors);
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Installation & Setup
|
|
19
36
|
|
|
20
|
-
|
|
37
|
+
Since this package is a TypeScript compiler plugin (transformer), you must compile your project using a compiler patcher like `ts-patch` to hook into TS compilation.
|
|
21
38
|
|
|
22
|
-
|
|
39
|
+
### 1. Install Dependencies
|
|
40
|
+
|
|
41
|
+
Install the core package, along with `ts-patch` as a development dependency:
|
|
23
42
|
|
|
24
43
|
```bash
|
|
25
44
|
npm install @webergency-utils/typechecker
|
|
26
|
-
npm install -
|
|
45
|
+
npm install --save-dev ts-patch
|
|
27
46
|
```
|
|
28
47
|
|
|
29
|
-
|
|
48
|
+
### 2. Inject Compiler Hook
|
|
30
49
|
|
|
31
|
-
|
|
50
|
+
Run the patcher command to set up `ts-patch` inside your local TypeScript installation:
|
|
32
51
|
|
|
33
|
-
|
|
52
|
+
```bash
|
|
53
|
+
npx ts-patch install
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
> [!NOTE]
|
|
57
|
+
> It is recommended to add `ts-patch install` to your `package.json` `prepare` script so it runs automatically after every dependency installation.
|
|
34
58
|
|
|
35
|
-
|
|
59
|
+
### 3. Configure tsconfig.json
|
|
60
|
+
|
|
61
|
+
Register the typechecker transform plugin under the `plugins` array of `compilerOptions` in your `tsconfig.json`:
|
|
36
62
|
|
|
37
63
|
```json
|
|
38
64
|
{
|
|
@@ -49,308 +75,283 @@ Update your `tsconfig.json` to include the transformer in the `compilerOptions.p
|
|
|
49
75
|
|
|
50
76
|
---
|
|
51
77
|
|
|
52
|
-
##
|
|
78
|
+
## Architecture & Internals
|
|
53
79
|
|
|
54
|
-
|
|
80
|
+
The package utilizes a custom TypeScript compiler transformer and language service plugin to deliver highly efficient type-safe runtime validations.
|
|
55
81
|
|
|
56
|
-
|
|
57
|
-
import { is, assert, assertGuard, validate } from '@webergency-utils/typechecker';
|
|
82
|
+
### Build-Time Compilation Flow
|
|
58
83
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
84
|
+
```mermaid
|
|
85
|
+
graph TD
|
|
86
|
+
A[TypeScript Source Code] --> B[ts-patch / Compiler Hook]
|
|
87
|
+
B --> C[TypeScript compiler plugin / Transformer]
|
|
88
|
+
C --> D[Extract types via compiler TypeChecker]
|
|
89
|
+
D --> E[Generate optimized JS validator function]
|
|
90
|
+
E --> F[Hoisted Validator Registry & MetadataStore registration]
|
|
91
|
+
F --> G[Replace source call with MetadataStore invocation]
|
|
92
|
+
G --> H[Emit optimized JavaScript files]
|
|
93
|
+
```
|
|
63
94
|
|
|
64
|
-
|
|
95
|
+
1. **Build-Time Transformation**: The compiler transformer intercepts calls to validation helper functions (`validate`, `is`, `assert`, `assertGuard`, `jsonSchema`) containing generic arguments.
|
|
96
|
+
2. **Type Extraction & Analysis**: It parses the target TS type structure, extracting intersection constraints, formats, transforms, and defaults recursively.
|
|
97
|
+
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`.
|
|
98
|
+
4. **Call Replacement**: The transformer replaces the original compile-time call expressions with direct, zero-reflection references to the runtime `MetadataStore`.
|
|
65
99
|
|
|
66
|
-
|
|
67
|
-
if (is<Payload>(data)) {
|
|
68
|
-
console.log(data.id); // Narrowed to 'Payload'
|
|
69
|
-
}
|
|
100
|
+
### Static Constraint Diagnostics
|
|
70
101
|
|
|
71
|
-
|
|
72
|
-
const validData = assert<Payload>(data);
|
|
102
|
+
The package includes an IDE / Language Service plugin that statically checks literal values against type constraints during editing or compilation:
|
|
73
103
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
console.log(data.id); // Narrowed in-place
|
|
104
|
+
```typescript
|
|
105
|
+
import { constraint } from '@webergency-utils/typechecker';
|
|
77
106
|
|
|
78
|
-
//
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
console.log(result.data);
|
|
82
|
-
} else {
|
|
83
|
-
console.error(result.errors); // Array of formatted errors
|
|
84
|
-
}
|
|
107
|
+
// This yields a compilation error directly in the IDE:
|
|
108
|
+
// Type '5' is not assignable to type 'number & Minimum<18>'.
|
|
109
|
+
const age: number & constraint.Minimum<18> = 5;
|
|
85
110
|
```
|
|
86
111
|
|
|
87
112
|
---
|
|
88
113
|
|
|
89
|
-
|
|
114
|
+
## Glossary
|
|
90
115
|
|
|
91
|
-
|
|
116
|
+
- [validate](file:///Users/tomaskorenko/Projects/Github/webergency-utils/typechecker/src/index.ts#L22): Validates a value against a type, returning a structured result containing the validation status and a detailed list of errors.
|
|
117
|
+
- [is](file:///Users/tomaskorenko/Projects/Github/webergency-utils/typechecker/src/index.ts#L19): A type guard function that returns `true` if a value is valid, narrowing its type for TypeScript.
|
|
118
|
+
- [assert](file:///Users/tomaskorenko/Projects/Github/webergency-utils/typechecker/src/index.ts#L20): Validates a value and returns it, throwing a validation error on failure.
|
|
119
|
+
- [assertGuard](file:///Users/tomaskorenko/Projects/Github/webergency-utils/typechecker/src/index.ts#L21): A type assertion function that throws if a value does not match the target type, narrowing the type in the outer scope.
|
|
120
|
+
- [jsonSchema](file:///Users/tomaskorenko/Projects/Github/webergency-utils/typechecker/src/index.ts#L23): Generates and returns a raw JSON Schema draft-07 representation matching a TypeScript type at compile time.
|
|
121
|
+
- [WithModifiers](file:///Users/tomaskorenko/Projects/Github/webergency-utils/typechecker/src/runtime/tags.ts#L79): A utility type that applies constraint, format, or transformation tags to properties of deeply nested or external types using dot-separated path mappings.
|
|
122
|
+
- [ResolveDefaults](file:///Users/tomaskorenko/Projects/Github/webergency-utils/typechecker/src/runtime/tags.ts#L87): A helper type that removes the optional flag (`?`) from properties that have defined default values.
|
|
123
|
+
- [convertPropertyCasing](file:///Users/tomaskorenko/Projects/Github/webergency-utils/typechecker/src/runtime/casing.ts#L190): A runtime utility to recursively change the casing of object keys.
|
|
124
|
+
- [toZodIssues](file:///Users/tomaskorenko/Projects/Github/webergency-utils/typechecker/src/runtime/validators.ts#L991): Utility to transform internal typechecker validation errors into Zod-compatible issue structures.
|
|
125
|
+
- [ZodLikeError](file:///Users/tomaskorenko/Projects/Github/webergency-utils/typechecker/src/runtime/validators.ts#L1017): Error class wrapping validation errors in a structure compatible with libraries expecting Zod errors.
|
|
92
126
|
|
|
93
|
-
|
|
94
|
-
export type ValidationMode = 'strict' | 'relaxed' | 'strip';
|
|
127
|
+
---
|
|
95
128
|
|
|
96
|
-
|
|
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
|
-
```
|
|
129
|
+
## API Reference
|
|
102
130
|
|
|
103
|
-
|
|
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
|
-
```
|
|
131
|
+
### Validation Functions
|
|
118
132
|
|
|
119
|
-
|
|
133
|
+
#### `validate<T>(input: unknown, options?: ValidationMode | ValidationOptions): IValidation<ResolveDefaults<T>>`
|
|
120
134
|
|
|
121
|
-
|
|
135
|
+
Validates input data against type `T` and returns a structured validation result.
|
|
122
136
|
|
|
123
|
-
|
|
137
|
+
- **Parameters**:
|
|
138
|
+
- `input`: The value to validate.
|
|
139
|
+
- `options` (optional): Either a `ValidationMode` string ('strict' | 'relaxed' | 'strip') or a `ValidationOptions` object.
|
|
140
|
+
- **Returns**: `IValidation<ResolveDefaults<T>>` containing validation status, converted/stripped data, and error details.
|
|
141
|
+
- **Example**:
|
|
142
|
+
```typescript
|
|
143
|
+
const result = validate<User>(data, 'strip');
|
|
144
|
+
```
|
|
124
145
|
|
|
125
|
-
|
|
146
|
+
#### `is<T>(input: unknown, options?: ValidationMode | ValidationOptions): input is ResolveDefaults<T>`
|
|
126
147
|
|
|
127
|
-
|
|
128
|
-
import { validate, groupErrorsByPath, Minimum } from '@webergency-utils/typechecker';
|
|
148
|
+
A type guard function checking if the input matches type `T`.
|
|
129
149
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
150
|
+
- **Parameters**:
|
|
151
|
+
- `input`: The value to check.
|
|
152
|
+
- `options` (optional): Either a `ValidationMode` string or a `ValidationOptions` object.
|
|
153
|
+
- **Returns**: `boolean` (`true` if valid, `false` otherwise). Narrows type of `input` to `ResolveDefaults<T>` on success.
|
|
154
|
+
- **Example**:
|
|
155
|
+
```typescript
|
|
156
|
+
if (is<User>(data)) {
|
|
157
|
+
console.log(data.name);
|
|
158
|
+
}
|
|
159
|
+
```
|
|
136
160
|
|
|
137
|
-
|
|
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
|
-
};
|
|
161
|
+
#### `assert<T>(input: unknown, options?: ValidationMode | ValidationOptions): ResolveDefaults<T>`
|
|
143
162
|
|
|
144
|
-
|
|
145
|
-
if (!result.success) {
|
|
146
|
-
const grouped = groupErrorsByPath(result.errors);
|
|
147
|
-
console.log(JSON.stringify(grouped, null, 2));
|
|
148
|
-
}
|
|
149
|
-
```
|
|
163
|
+
Validates input data and returns it, throwing a validation error on failure.
|
|
150
164
|
|
|
151
|
-
**
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
"value": undefined,
|
|
175
|
-
"errors": ["Type<string>"]
|
|
176
|
-
},
|
|
177
|
-
"metadata.priority": {
|
|
178
|
-
"value": undefined,
|
|
179
|
-
"errors": ["Type<number>"]
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
```
|
|
165
|
+
- **Parameters**:
|
|
166
|
+
- `input`: The value to validate.
|
|
167
|
+
- `options` (optional): Either a `ValidationMode` string or a `ValidationOptions` object.
|
|
168
|
+
- **Returns**: `ResolveDefaults<T>` (the validated value with defaults resolved).
|
|
169
|
+
- **Throws**: `Error` containing a list of path and constraint failures, or a custom error via `options.errorFactory`.
|
|
170
|
+
- **Example**:
|
|
171
|
+
```typescript
|
|
172
|
+
const user = assert<User>(data);
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
#### `assertGuard<T>(input: unknown, options?: ValidationMode | ValidationOptions): asserts input is ResolveDefaults<T>`
|
|
176
|
+
|
|
177
|
+
An assertion guard that throws a validation error if the input does not match type `T`.
|
|
178
|
+
|
|
179
|
+
- **Parameters**:
|
|
180
|
+
- `input`: The value to check.
|
|
181
|
+
- `options` (optional): Either a `ValidationMode` string or a `ValidationOptions` object.
|
|
182
|
+
- **Returns**: `void`. Narrows the type of `input` in the enclosing scope on success.
|
|
183
|
+
- **Throws**: `Error` if validation fails.
|
|
184
|
+
- **Example**:
|
|
185
|
+
```typescript
|
|
186
|
+
assertGuard<User>(data);
|
|
187
|
+
```
|
|
183
188
|
|
|
184
|
-
|
|
189
|
+
#### `jsonSchema<T>(): any`
|
|
190
|
+
|
|
191
|
+
Generates a raw JSON Schema draft-07 object matching type `T` at compile time.
|
|
192
|
+
|
|
193
|
+
- **Returns**: `any` (a JSON Schema object).
|
|
194
|
+
- **Example**:
|
|
195
|
+
```typescript
|
|
196
|
+
const userSchema = jsonSchema<User>();
|
|
197
|
+
```
|
|
185
198
|
|
|
186
199
|
---
|
|
187
200
|
|
|
188
|
-
###
|
|
201
|
+
### Utility Functions and Classes
|
|
189
202
|
|
|
190
|
-
|
|
203
|
+
#### `convertPropertyCasing<T, C extends CasingFormat>(obj: T, casing: C, options?: ConvertCasingOptions): ConvertPropertyCasing<T, C>`
|
|
191
204
|
|
|
192
|
-
|
|
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
|
-
}
|
|
205
|
+
Recursively converts all property keys of an object to the specified casing format.
|
|
205
206
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
'
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
207
|
+
- **Parameters**:
|
|
208
|
+
- `obj`: The source object.
|
|
209
|
+
- `casing`: A `CasingFormat` string value (`'snake_case' | 'SNAKE_CASE' | 'camelCase' | 'camelCaseID' | 'PascalCase' | 'PascalCaseID' | 'kebab-case' | 'dot.case'`).
|
|
210
|
+
- `options` (optional): `ConvertCasingOptions` object.
|
|
211
|
+
- **Returns**: The casing-converted object with updated TypeScript property keys.
|
|
212
|
+
- **Example**:
|
|
213
|
+
```typescript
|
|
214
|
+
const apiResponse = convertPropertyCasing(user, 'camelCase');
|
|
215
|
+
```
|
|
212
216
|
|
|
213
|
-
|
|
217
|
+
#### `toZodIssues(errors: IValidationError[]): any[]`
|
|
214
218
|
|
|
215
|
-
|
|
219
|
+
Converts internal validation errors into Zod-compatible issues.
|
|
216
220
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
interface DBConfig {
|
|
221
|
-
ssl?: boolean;
|
|
222
|
-
cert?: string;
|
|
223
|
-
auth?: {
|
|
224
|
-
username?: string;
|
|
225
|
-
password?: string;
|
|
226
|
-
}
|
|
227
|
-
}
|
|
221
|
+
- **Parameters**:
|
|
222
|
+
- `errors`: Array of `IValidationError`.
|
|
223
|
+
- **Returns**: An array of Zod-like issues.
|
|
228
224
|
|
|
229
|
-
|
|
230
|
-
// Absolute check: if "ssl" is defined, "cert" must exist at root
|
|
231
|
-
'ssl': constraint.Requires<'cert'>;
|
|
225
|
+
#### `class ZodLikeError extends Error`
|
|
232
226
|
|
|
233
|
-
|
|
234
|
-
'auth.password': constraint.Requires<'.username'>;
|
|
235
|
-
}>;
|
|
236
|
-
```
|
|
227
|
+
An error class wrapper that transforms internal validation errors into a Zod-like error structure.
|
|
237
228
|
|
|
238
|
-
|
|
229
|
+
- **Constructor**: `constructor(errors: IValidationError[])`
|
|
230
|
+
- **Properties**:
|
|
231
|
+
- `name`: `'ZodError'`
|
|
232
|
+
- `issues`: Zod-like issue array.
|
|
239
233
|
|
|
240
|
-
|
|
234
|
+
---
|
|
241
235
|
|
|
242
|
-
|
|
243
|
-
import { validate, constraint, Message } from '@webergency-utils/typechecker';
|
|
236
|
+
### Interfaces and Types
|
|
244
237
|
|
|
245
|
-
interface
|
|
246
|
-
// Direct message in constraint tag
|
|
247
|
-
age: number & constraint.Minimum<18, "Must be 18 or older">;
|
|
238
|
+
#### `interface ValidationOptions`
|
|
248
239
|
|
|
249
|
-
|
|
250
|
-
email: string & constraint.Format<'email'> & Message<"Please supply a valid email address">;
|
|
251
|
-
}
|
|
252
|
-
```
|
|
240
|
+
Configuration options to customize validator behavior.
|
|
253
241
|
|
|
254
|
-
|
|
242
|
+
| Property | Type | Default | Description |
|
|
243
|
+
| :--- | :--- | :--- | :--- |
|
|
244
|
+
| `mode` | `'strict' \| 'relaxed' \| 'strip'` | `'strict'` | Validation mode strategy (strict key checking, relaxed, or key stripping). |
|
|
245
|
+
| `tryConvert` | `boolean` | `false` | Coerces compatible input primitives (e.g. numeric strings to numbers, ISO strings to Date). |
|
|
246
|
+
| `wrapArrays` | `boolean` | `false` | Wraps non-array values into single-element arrays if an array is expected. |
|
|
247
|
+
| `schema` | `any` | `undefined` | Custom JSON Schema instance. |
|
|
248
|
+
| `errorFactory` | `(errors: IValidationError[]) => Error` | `undefined` | Custom error factory for `assert` throwing. |
|
|
255
249
|
|
|
256
|
-
|
|
250
|
+
#### `interface IValidation<T>`
|
|
257
251
|
|
|
258
|
-
|
|
259
|
-
import { validate, toZodIssues, ZodLikeError } from '@webergency-utils/typechecker';
|
|
252
|
+
Result object returned by `validate`.
|
|
260
253
|
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
const zodIssues = toZodIssues(result.errors);
|
|
265
|
-
console.log(zodIssues); // [{ code: "custom", path: ["age"], message: "Minimum<18>", received: 15 }]
|
|
254
|
+
- `success`: `boolean`
|
|
255
|
+
- `data?`: `T`
|
|
256
|
+
- `errors?`: `IValidationError[]`
|
|
266
257
|
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
258
|
+
#### `interface IValidationError`
|
|
259
|
+
|
|
260
|
+
Details of a validation check failure.
|
|
261
|
+
|
|
262
|
+
- `path`: `string`
|
|
263
|
+
- `value`: `any`
|
|
264
|
+
- `error`: `string` (the constraint description or custom message)
|
|
265
|
+
|
|
266
|
+
#### `type WithModifiers<T, M>`
|
|
267
|
+
|
|
268
|
+
Applies constraint, format, or transformation tags to properties of type `T` using a path mapping `M`.
|
|
269
|
+
|
|
270
|
+
- **Generics**:
|
|
271
|
+
- `T`: The baseline type to wrap.
|
|
272
|
+
- `M`: A key-value map where keys are dot-separated paths (e.g., `'profile.email'`) and values are tags.
|
|
271
273
|
|
|
272
274
|
---
|
|
273
275
|
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
- `format.
|
|
304
|
-
- `format.
|
|
305
|
-
- `format.
|
|
306
|
-
- `format.
|
|
307
|
-
- `format.
|
|
308
|
-
- `format.
|
|
276
|
+
### Tags & Modifiers
|
|
277
|
+
|
|
278
|
+
#### `constraint` Namespace
|
|
279
|
+
|
|
280
|
+
Used to apply value constraints to types.
|
|
281
|
+
|
|
282
|
+
| Tag | Description |
|
|
283
|
+
| :--- | :--- |
|
|
284
|
+
| `constraint.MinLength<N, Msg?>` | Restricts string length to $\ge N$. |
|
|
285
|
+
| `constraint.MaxLength<N, Msg?>` | Restricts string length to $\le N$. |
|
|
286
|
+
| `constraint.Length<Min, Max>` | Shorthand for `MinLength<Min> & MaxLength<Max>`. |
|
|
287
|
+
| `constraint.Pattern<Regex, Msg?>` | Validates string using regular expression `Regex`. |
|
|
288
|
+
| `constraint.Minimum<N, Msg?>` | Inclusive minimum value restriction for `number | bigint`. |
|
|
289
|
+
| `constraint.Maximum<N, Msg?>` | Inclusive maximum value restriction for `number | bigint`. |
|
|
290
|
+
| `constraint.Range<Min, Max>` | Shorthand for `Minimum<Min> & Maximum<Max>`. |
|
|
291
|
+
| `constraint.ExclusiveMinimum<N, Msg?>` | Exclusive minimum value restriction for `number | bigint`. |
|
|
292
|
+
| `constraint.ExclusiveMaximum<N, Msg?>` | Exclusive maximum value restriction for `number | bigint`. |
|
|
293
|
+
| `constraint.MultipleOf<N, Msg?>` | Restricts `number | bigint` to multiples of `N`. |
|
|
294
|
+
| `constraint.MinItems<N, Msg?>` | Restricts array length to $\ge N$. |
|
|
295
|
+
| `constraint.MaxItems<N, Msg?>` | Restricts array length to $\le N$. |
|
|
296
|
+
| `constraint.UniqueItems<Msg?>` | Restricts arrays to deeply unique items. |
|
|
297
|
+
| `constraint.Custom<Fn, Msg?>` | Runs a custom validation function: `(val, ctx) => boolean`. |
|
|
298
|
+
| `constraint.Requires<Path | [Paths], Msg?>` | Enforces that other object property paths exist. |
|
|
299
|
+
| `constraint.Message<Msg>` | Fallback custom error message. |
|
|
300
|
+
|
|
301
|
+
#### `format` Namespace
|
|
302
|
+
|
|
303
|
+
Standard formats for string primitives.
|
|
304
|
+
|
|
305
|
+
- `format.Email`: RFC-5322 email.
|
|
306
|
+
- `format.UUID`: UUID (v1-v5).
|
|
307
|
+
- `format.URL`: HTTP/HTTPS/FTP URLs.
|
|
308
|
+
- `format.IPv4` / `format.IPv6`: IP addresses.
|
|
309
|
+
- `format.Date`: ISO date `YYYY-MM-DD`.
|
|
310
|
+
- `format.DateTime`: ISO date-time.
|
|
311
|
+
- `format.ObjectId`: MongoDB 24-character hex ObjectId.
|
|
312
|
+
- `format.Duration`: ISO-8601 duration.
|
|
313
|
+
- `format.Time`: Time string `HH:MM:SS`.
|
|
314
|
+
- `format.Byte`: Base64 string.
|
|
315
|
+
- `format.Password`: Any string (always valid placeholder).
|
|
309
316
|
- `format.Regex`: Valid regular expression string.
|
|
310
|
-
- `format.Hostname`: Valid domain name
|
|
317
|
+
- `format.Hostname`: Valid domain name.
|
|
311
318
|
- `format.URI`: Valid URI.
|
|
312
319
|
|
|
313
|
-
|
|
314
|
-
Coerce or sanitize values before validation:
|
|
320
|
+
#### `transform` Namespace
|
|
315
321
|
|
|
316
|
-
|
|
322
|
+
Sanitizes and converts input values during validation.
|
|
323
|
+
|
|
324
|
+
- `transform.Trim`: Trims string whitespace.
|
|
317
325
|
- `transform.LowerCase`: Converts string to lowercase.
|
|
318
326
|
- `transform.UpperCase`: Converts string to uppercase.
|
|
319
|
-
- `transform.Capitalize`: Capitalizes the first letter
|
|
327
|
+
- `transform.Capitalize`: Capitalizes the first letter.
|
|
320
328
|
- `transform.ToNumber`: Coerces inputs to numbers.
|
|
321
329
|
- `transform.ToBoolean`: Coerces inputs to booleans.
|
|
322
|
-
- `transform.ToDate`: Coerces
|
|
323
|
-
- `transform.Custom<Fn>`:
|
|
330
|
+
- `transform.ToDate`: Coerces inputs to Date objects.
|
|
331
|
+
- `transform.Custom<Fn>`: Custom mapping function: `(val) => any`.
|
|
324
332
|
|
|
325
|
-
|
|
326
|
-
Define defaults for optional fields:
|
|
333
|
+
#### `tag` Namespace
|
|
327
334
|
|
|
328
|
-
- `tag.Default<Value>`: Injects
|
|
329
|
-
|
|
330
|
-
#### Example:
|
|
331
|
-
```typescript
|
|
332
|
-
import { MinLength, Minimum, Format, UniqueItems, tag, transform, constraint } from '@webergency-utils/typechecker';
|
|
333
|
-
|
|
334
|
-
interface Profile {
|
|
335
|
-
email: string & Format<'email'> & transform.Trim & transform.LowerCase;
|
|
336
|
-
password: string & MinLength<8>;
|
|
337
|
-
age: number & Minimum<18> & constraint.Message<"Must be 18+">;
|
|
338
|
-
luckyNumbers: number[] & UniqueItems;
|
|
339
|
-
role: string & tag.Default<"user">;
|
|
340
|
-
}
|
|
341
|
-
```
|
|
335
|
+
- `tag.Default<Value>`: Injects `Value` when a property is undefined. Removes the optional modifier (`?`) when resolved with `ResolveDefaults<T>`.
|
|
342
336
|
|
|
343
337
|
---
|
|
344
338
|
|
|
345
|
-
##
|
|
339
|
+
## Troubleshooting
|
|
346
340
|
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
341
|
+
### `validate`, `is`, or `assert` calls return empty results or throw at runtime
|
|
342
|
+
- **Cause**: The compiler transformer did not execute during build.
|
|
343
|
+
- **Diagnostics Check**: Inspect your built `.js` code. If the output still contains `validate<User>(data)` or other generic validation calls as functions, compilation was bypassed.
|
|
344
|
+
- **Fix**:
|
|
345
|
+
1. Verify `npx ts-patch install` was executed successfully.
|
|
346
|
+
2. Verify the transformer plugin is registered in `tsconfig.json`.
|
|
347
|
+
3. Ensure your bundler or compiler CLI compiles using patched `tsc`.
|
|
351
348
|
|
|
352
349
|
---
|
|
353
350
|
|
|
354
|
-
##
|
|
351
|
+
## Maintenance
|
|
352
|
+
|
|
353
|
+
This package is actively maintained.
|
|
355
354
|
|
|
356
|
-
|
|
355
|
+
Bug reports and pull requests are welcome. Security issues and critical
|
|
356
|
+
regressions are prioritized. New features are considered when they align
|
|
357
|
+
with the package's existing scope.
|