@webergency-utils/typechecker 0.1.10 → 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/LICENSE +21 -0
- package/README.md +86 -36
- package/dist/engine/generators.d.ts +2 -1
- package/dist/engine/generators.js +37 -11
- package/dist/engine/resolver.js +279 -68
- package/dist/engine/staticAsserts.js +2 -1
- package/dist/index.d.ts +12 -6
- package/dist/index.js +19 -0
- package/dist/runtime/tags/format.d.ts +7 -0
- package/dist/runtime/tags/tag.d.ts +1 -1
- package/dist/runtime/tags.d.ts +1 -1
- package/dist/runtime/validators.d.ts +34 -10
- package/dist/runtime/validators.js +915 -212
- package/dist/transformer.js +5 -1
- package/package.json +12 -3
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2019-2026 Webergency s.r.o.
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,8 +1,17 @@
|
|
|
1
1
|
# @webergency-utils/typechecker
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
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
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/@webergency-utils/typechecker)
|
|
6
|
+
[](https://www.npmjs.com/package/@webergency-utils/typechecker)
|
|
7
|
+
[](#maintenance)
|
|
8
|
+
[](https://www.npmjs.com/package/@webergency-utils/typechecker?activeTab=dependencies)
|
|
9
|
+
[](https://www.npmjs.com/package/@webergency-utils/typechecker)
|
|
10
|
+
<br>
|
|
11
|
+
[](https://securityscorecards.dev/viewer/?uri=github.com/webergency-utils/typechecker)
|
|
12
|
+
[](https://codecov.io/gh/webergency-utils/typechecker)
|
|
13
|
+
[](https://github.com/webergency-utils/typechecker/actions/workflows/ci.yml)
|
|
14
|
+
[](https://github.com/webergency-utils/typechecker/actions/workflows/codeql.yml)
|
|
6
15
|
|
|
7
16
|
## TL;DR
|
|
8
17
|
|
|
@@ -34,7 +43,11 @@ if (result.success) {
|
|
|
34
43
|
|
|
35
44
|
## Installation & Setup
|
|
36
45
|
|
|
37
|
-
|
|
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.
|
|
38
51
|
|
|
39
52
|
### 1. Install Dependencies
|
|
40
53
|
|
|
@@ -58,7 +71,7 @@ npx ts-patch install
|
|
|
58
71
|
|
|
59
72
|
### 3. Configure tsconfig.json
|
|
60
73
|
|
|
61
|
-
Register the typechecker
|
|
74
|
+
Register the typechecker **transformer** (and optionally the language service plugin) under `compilerOptions.plugins` in `tsconfig.json`:
|
|
62
75
|
|
|
63
76
|
```json
|
|
64
77
|
{
|
|
@@ -67,12 +80,14 @@ Register the typechecker transform plugin under the `plugins` array of `compiler
|
|
|
67
80
|
"module": "NodeNext",
|
|
68
81
|
"moduleResolution": "NodeNext",
|
|
69
82
|
"plugins": [
|
|
70
|
-
{ "transform": "@webergency-utils/typechecker" }
|
|
83
|
+
{ "transform": "@webergency-utils/typechecker/transformer" },
|
|
84
|
+
{ "name": "@webergency-utils/typechecker/plugin" }
|
|
71
85
|
]
|
|
72
86
|
}
|
|
73
87
|
}
|
|
74
88
|
```
|
|
75
89
|
|
|
90
|
+
The `transform` entry is required for AOT validation. The `name` entry enables IDE constraint diagnostics via the language service plugin.
|
|
76
91
|
---
|
|
77
92
|
|
|
78
93
|
## Architecture & Internals
|
|
@@ -97,6 +112,12 @@ graph TD
|
|
|
97
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`.
|
|
98
113
|
4. **Call Replacement**: The transformer replaces the original compile-time call expressions with direct, zero-reflection references to the runtime `MetadataStore`.
|
|
99
114
|
|
|
115
|
+
### External dependencies
|
|
116
|
+
|
|
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.
|
|
120
|
+
|
|
100
121
|
### Static Constraint Diagnostics
|
|
101
122
|
|
|
102
123
|
The package includes an IDE / Language Service plugin that statically checks literal values against type constraints during editing or compilation:
|
|
@@ -113,16 +134,18 @@ const age: number & constraint.Minimum<18> = 5;
|
|
|
113
134
|
|
|
114
135
|
## Glossary
|
|
115
136
|
|
|
116
|
-
- [validate](
|
|
117
|
-
- [is](
|
|
118
|
-
- [assert](
|
|
119
|
-
- [assertGuard](
|
|
120
|
-
- [jsonSchema](
|
|
121
|
-
- [WithModifiers](
|
|
122
|
-
- [ResolveDefaults](
|
|
123
|
-
- [convertPropertyCasing](
|
|
124
|
-
- [toZodIssues](
|
|
125
|
-
- [ZodLikeError](
|
|
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.
|
|
126
149
|
|
|
127
150
|
---
|
|
128
151
|
|
|
@@ -145,11 +168,11 @@ Validates input data against type `T` and returns a structured validation result
|
|
|
145
168
|
|
|
146
169
|
#### `is<T>(input: unknown, options?: ValidationMode | ValidationOptions): input is ResolveDefaults<T>`
|
|
147
170
|
|
|
148
|
-
A type guard function checking if the input matches type `T`.
|
|
171
|
+
A type guard function checking if the input matches type `T`. Does **not** coerce; `from` is ignored.
|
|
149
172
|
|
|
150
173
|
- **Parameters**:
|
|
151
174
|
- `input`: The value to check.
|
|
152
|
-
- `options` (optional): Either a `ValidationMode` string or a `ValidationOptions` object.
|
|
175
|
+
- `options` (optional): Either a `ValidationMode` string or a `ValidationOptions` object (`from` has no effect).
|
|
153
176
|
- **Returns**: `boolean` (`true` if valid, `false` otherwise). Narrows type of `input` to `ResolveDefaults<T>` on success.
|
|
154
177
|
- **Example**:
|
|
155
178
|
```typescript
|
|
@@ -174,13 +197,13 @@ Validates input data and returns it, throwing a validation error on failure.
|
|
|
174
197
|
|
|
175
198
|
#### `assertGuard<T>(input: unknown, options?: ValidationMode | ValidationOptions): asserts input is ResolveDefaults<T>`
|
|
176
199
|
|
|
177
|
-
An assertion guard that throws a validation error if the input does not match type `T`.
|
|
200
|
+
An assertion guard that throws a validation error if the input does not match type `T`. Does **not** coerce; `from` is ignored.
|
|
178
201
|
|
|
179
202
|
- **Parameters**:
|
|
180
203
|
- `input`: The value to check.
|
|
181
|
-
- `options` (optional): Either a `ValidationMode` string or a `ValidationOptions` object.
|
|
204
|
+
- `options` (optional): Either a `ValidationMode` string or a `ValidationOptions` object (`from` has no effect).
|
|
182
205
|
- **Returns**: `void`. Narrows the type of `input` in the enclosing scope on success.
|
|
183
|
-
- **Throws**: `Error` if validation fails.
|
|
206
|
+
- **Throws**: `Error` if validation fails (or a custom error via `options.errorFactory`).
|
|
184
207
|
- **Example**:
|
|
185
208
|
```typescript
|
|
186
209
|
assertGuard<User>(data);
|
|
@@ -216,12 +239,24 @@ Recursively converts all property keys of an object to the specified casing form
|
|
|
216
239
|
|
|
217
240
|
#### `toZodIssues(errors: IValidationError[]): any[]`
|
|
218
241
|
|
|
219
|
-
Converts internal validation errors into Zod-compatible issues.
|
|
242
|
+
Converts internal validation errors into Zod-compatible issues. Flattens nested union `issues` into a flat list.
|
|
220
243
|
|
|
221
244
|
- **Parameters**:
|
|
222
245
|
- `errors`: Array of `IValidationError`.
|
|
223
246
|
- **Returns**: An array of Zod-like issues.
|
|
224
247
|
|
|
248
|
+
#### `groupErrorsByPath(errors: IValidationError[]): Record<string, { value: any, errors: string[] }>`
|
|
249
|
+
|
|
250
|
+
Groups validation errors by path, including nested `issues` from failed unions.
|
|
251
|
+
|
|
252
|
+
- **Parameters**:
|
|
253
|
+
- `errors`: Array of `IValidationError`.
|
|
254
|
+
- **Returns**: A map of path → `{ value, errors }`.
|
|
255
|
+
|
|
256
|
+
#### `coerceQueryNumber(v: any): any` / `coerceQueryBoolean(v: any): any` / `coerceQueryDate(v: any): any` / `coerceJsonDate(v: any): any`
|
|
257
|
+
|
|
258
|
+
Shared coercion helpers used by `from: 'query'` / `from: 'json'` and by `transform.ToNumber` / `ToBoolean` / `ToDate`.
|
|
259
|
+
|
|
225
260
|
#### `class ZodLikeError extends Error`
|
|
226
261
|
|
|
227
262
|
An error class wrapper that transforms internal validation errors into a Zod-like error structure.
|
|
@@ -242,8 +277,9 @@ Configuration options to customize validator behavior.
|
|
|
242
277
|
| Property | Type | Default | Description |
|
|
243
278
|
| :--- | :--- | :--- | :--- |
|
|
244
279
|
| `mode` | `'strict' \| 'relaxed' \| 'strip'` | `'strict'` | Validation mode strategy (strict key checking, relaxed, or key stripping). |
|
|
245
|
-
| `
|
|
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`. |
|
|
246
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. |
|
|
247
283
|
| `schema` | `any` | `undefined` | Custom JSON Schema instance. |
|
|
248
284
|
| `errorFactory` | `(errors: IValidationError[]) => Error` | `undefined` | Custom error factory for `assert` throwing. |
|
|
249
285
|
|
|
@@ -262,6 +298,7 @@ Details of a validation check failure.
|
|
|
262
298
|
- `path`: `string`
|
|
263
299
|
- `value`: `any`
|
|
264
300
|
- `error`: `string` (the constraint description or custom message)
|
|
301
|
+
- `issues?`: `IValidationError[]` — nested failures (used for unions: one summary error with per-arm details)
|
|
265
302
|
|
|
266
303
|
#### `type WithModifiers<T, M>`
|
|
267
304
|
|
|
@@ -302,20 +339,29 @@ Used to apply value constraints to types.
|
|
|
302
339
|
|
|
303
340
|
Standard formats for string primitives.
|
|
304
341
|
|
|
305
|
-
- `format.Email`:
|
|
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.
|
|
306
344
|
- `format.UUID`: UUID (v1-v5).
|
|
307
345
|
- `format.URL`: HTTP/HTTPS/FTP URLs.
|
|
308
346
|
- `format.IPv4` / `format.IPv6`: IP addresses.
|
|
309
|
-
- `format.Date`:
|
|
310
|
-
- `format.DateTime`:
|
|
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.
|
|
311
349
|
- `format.ObjectId`: MongoDB 24-character hex ObjectId.
|
|
312
350
|
- `format.Duration`: ISO-8601 duration.
|
|
313
|
-
- `format.Time`: Time string `HH:MM:SS`.
|
|
351
|
+
- `format.Time`: Time string `HH:MM:SS` with a required timezone (`Z` or `±HH:MM`), e.g. `19:55:00Z`.
|
|
314
352
|
- `format.Byte`: Base64 string.
|
|
315
353
|
- `format.Password`: Any string (always valid placeholder).
|
|
316
354
|
- `format.Regex`: Valid regular expression string.
|
|
317
|
-
- `format.Hostname`:
|
|
318
|
-
- `format.
|
|
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`, …).
|
|
319
365
|
|
|
320
366
|
#### `transform` Namespace
|
|
321
367
|
|
|
@@ -325,9 +371,9 @@ Sanitizes and converts input values during validation.
|
|
|
325
371
|
- `transform.LowerCase`: Converts string to lowercase.
|
|
326
372
|
- `transform.UpperCase`: Converts string to uppercase.
|
|
327
373
|
- `transform.Capitalize`: Capitalizes the first letter.
|
|
328
|
-
- `transform.ToNumber`:
|
|
329
|
-
- `transform.ToBoolean`:
|
|
330
|
-
- `transform.ToDate`:
|
|
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).
|
|
331
377
|
- `transform.Custom<Fn>`: Custom mapping function: `(val) => any`.
|
|
332
378
|
|
|
333
379
|
#### `tag` Namespace
|
|
@@ -338,14 +384,18 @@ Sanitizes and converts input values during validation.
|
|
|
338
384
|
|
|
339
385
|
## Troubleshooting
|
|
340
386
|
|
|
341
|
-
### `validate`, `is`, or `assert`
|
|
342
|
-
- **Cause**: The compiler transformer did not
|
|
343
|
-
- **Diagnostics Check**: Inspect your built `.js`
|
|
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.
|
|
344
390
|
- **Fix**:
|
|
345
391
|
1. Verify `npx ts-patch install` was executed successfully.
|
|
346
|
-
2. Verify
|
|
392
|
+
2. Verify `{ "transform": "@webergency-utils/typechecker/transformer" }` is registered in `tsconfig.json` `compilerOptions.plugins`.
|
|
347
393
|
3. Ensure your bundler or compiler CLI compiles using patched `tsc`.
|
|
348
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
|
+
|
|
349
399
|
---
|
|
350
400
|
|
|
351
401
|
## Maintenance
|
|
@@ -11,7 +11,7 @@ export declare function createLiteralCheck(value: string | number | boolean | ts
|
|
|
11
11
|
export declare function createArrayCheck(elementValidator: ts.Expression, requiredUtils: Set<string>): ts.Expression;
|
|
12
12
|
export declare function createTemplateLiteralCheck(regexStr: string, expected: string, requiredUtils: Set<string>): ts.Expression;
|
|
13
13
|
export declare function createUnionCheck(checks: ts.Expression[], requiredUtils: Set<string>, expected?: string): ts.Expression;
|
|
14
|
-
export declare function createObjectCheck(props: any[], requiredUtils: Set<string>, expected?: string): ts.Expression;
|
|
14
|
+
export declare function createObjectCheck(props: any[], requiredUtils: Set<string>, expected?: string, indexValidator?: ts.Expression): ts.Expression;
|
|
15
15
|
export declare function createRecordCheck(valueValidator: ts.Expression, requiredUtils: Set<string>): ts.Expression;
|
|
16
16
|
export declare function createTupleCheck(checks: ts.Expression[], requiredUtils: Set<string>): ts.Expression;
|
|
17
17
|
export declare function createDateCheck(requiredUtils: Set<string>): ts.Expression;
|
|
@@ -21,3 +21,4 @@ export declare function createUndefinedCheck(requiredUtils: Set<string>): ts.Exp
|
|
|
21
21
|
export declare function createIntersectionCheck(checks: ts.Expression[], requiredUtils: Set<string>): ts.Expression;
|
|
22
22
|
export declare function createSetCheck(elementValidator: ts.Expression, requiredUtils: Set<string>): ts.Expression;
|
|
23
23
|
export declare function createMapCheck(keyValidator: ts.Expression, valueValidator: ts.Expression, requiredUtils: Set<string>): ts.Expression;
|
|
24
|
+
export declare function createInstanceOfCheck(typeName: string, requiredUtils: Set<string>): ts.Expression;
|
|
@@ -79,7 +79,7 @@ export function createConstrainedPrimitiveCheck(baseType, constraints, requiredU
|
|
|
79
79
|
return `validators.pattern(v, path, ctx, new RegExp(${JSON.stringify(c.value)}), ${JSON.stringify('Pattern<' + c.value + '>')}${msgArg})`;
|
|
80
80
|
}
|
|
81
81
|
if (c.type === 'format') {
|
|
82
|
-
return `validators.format(v, path, ctx, ${JSON.stringify(c.value)}${msgArg})`;
|
|
82
|
+
return `v = validators.format(v, path, ctx, ${JSON.stringify(c.value)}${msgArg})`;
|
|
83
83
|
}
|
|
84
84
|
if (c.type === 'minItems') {
|
|
85
85
|
return `validators.minItems(v, path, ctx, ${valStr}${msgArg})`;
|
|
@@ -118,13 +118,13 @@ export function createConstrainedPrimitiveCheck(baseType, constraints, requiredU
|
|
|
118
118
|
return 'if (typeof v === \'string\' && v.length > 0) v = v.charAt(0).toUpperCase() + v.slice(1)';
|
|
119
119
|
}
|
|
120
120
|
if (tc.type === 'transform' && tc.value === 'tonumber') {
|
|
121
|
-
return 'v =
|
|
121
|
+
return 'v = validators.coerceQueryNumber(v)';
|
|
122
122
|
}
|
|
123
123
|
if (tc.type === 'transform' && tc.value === 'toboolean') {
|
|
124
|
-
return 'v = (v
|
|
124
|
+
return 'v = validators.coerceQueryBoolean(v)';
|
|
125
125
|
}
|
|
126
126
|
if (tc.type === 'transform' && tc.value === 'todate') {
|
|
127
|
-
return 'v =
|
|
127
|
+
return 'v = validators.coerceQueryDate(v)';
|
|
128
128
|
}
|
|
129
129
|
if (tc.type === 'transform_custom') {
|
|
130
130
|
return `v = ${tc.value}(v)`;
|
|
@@ -188,19 +188,39 @@ export function createUnionCheck(checks, requiredUtils, expected = 'Type<Union>'
|
|
|
188
188
|
ts.factory.createStringLiteral(expected)
|
|
189
189
|
]));
|
|
190
190
|
}
|
|
191
|
-
export function createObjectCheck(props, requiredUtils, expected = 'object') {
|
|
191
|
+
export function createObjectCheck(props, requiredUtils, expected = 'object', indexValidator) {
|
|
192
192
|
requiredUtils.add('validators');
|
|
193
|
-
const propDefinitions = props.map((p
|
|
193
|
+
const propDefinitions = props.map((p) => ts.factory.createArrayLiteralExpression([
|
|
194
194
|
ts.factory.createStringLiteral(p.name),
|
|
195
195
|
p.isOptional ? ts.factory.createTrue() : ts.factory.createFalse(),
|
|
196
196
|
p.validator
|
|
197
197
|
]));
|
|
198
198
|
const allowedKeys = props.map(p => ts.factory.createStringLiteral(p.name));
|
|
199
|
+
if (indexValidator) {
|
|
200
|
+
const tpl = `
|
|
201
|
+
(v, path, ctx) => {
|
|
202
|
+
const obj = validators.object(v, path, ctx, undefined, __EXPECTED__);
|
|
203
|
+
if (obj === false) return v;
|
|
204
|
+
const data = validators.objectShell(obj, ctx);
|
|
205
|
+
validators.props(obj, data, path, ctx, __PROPS__);
|
|
206
|
+
validators.additionalProps(obj, data, path, ctx, __KEYS__, __INDEX__);
|
|
207
|
+
return data;
|
|
208
|
+
}
|
|
209
|
+
`;
|
|
210
|
+
return injectNodes(templateToAst(tpl), {
|
|
211
|
+
'__KEYS__': ts.factory.createArrayLiteralExpression(allowedKeys),
|
|
212
|
+
'__EXPECTED__': ts.factory.createStringLiteral(expected),
|
|
213
|
+
'__PROPS__': ts.factory.createArrayLiteralExpression(propDefinitions, true),
|
|
214
|
+
'__INDEX__': indexValidator
|
|
215
|
+
});
|
|
216
|
+
}
|
|
199
217
|
const tpl = `
|
|
200
218
|
(v, path, ctx) => {
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
validators.
|
|
219
|
+
const obj = validators.object(v, path, ctx, __KEYS__, __EXPECTED__);
|
|
220
|
+
if (obj === false) return v;
|
|
221
|
+
const data = validators.objectShell(obj, ctx);
|
|
222
|
+
validators.props(obj, data, path, ctx, __PROPS__);
|
|
223
|
+
validators.stripExtras(data, ctx, __KEYS__);
|
|
204
224
|
return data;
|
|
205
225
|
}
|
|
206
226
|
`;
|
|
@@ -243,10 +263,11 @@ export function createIntersectionCheck(checks, requiredUtils) {
|
|
|
243
263
|
const tpl = `
|
|
244
264
|
(v, path, ctx) => {
|
|
245
265
|
const checks = __CHECKS__;
|
|
246
|
-
let data =
|
|
266
|
+
let data = validators.objectShell(v, ctx);
|
|
247
267
|
for (let i = 0; i < checks.length; i++) {
|
|
248
268
|
const val = checks[i](v, path, ctx);
|
|
249
|
-
if (
|
|
269
|
+
if (typeof val === "object" && val !== null && !Array.isArray(val) && typeof data === "object" && data !== null && !Array.isArray(data)) Object.assign(data, val);
|
|
270
|
+
else data = val;
|
|
250
271
|
}
|
|
251
272
|
return data;
|
|
252
273
|
}
|
|
@@ -265,3 +286,8 @@ export function createMapCheck(keyValidator, valueValidator, requiredUtils) {
|
|
|
265
286
|
const tpl = '(v, path, ctx) => validators.map(v, path, ctx, __KEY__, __VALUE__)';
|
|
266
287
|
return injectNodes(templateToAst(tpl), { '__KEY__': keyValidator, '__VALUE__': valueValidator });
|
|
267
288
|
}
|
|
289
|
+
export function createInstanceOfCheck(typeName, requiredUtils) {
|
|
290
|
+
requiredUtils.add('validators');
|
|
291
|
+
const tpl = `(v, path, ctx) => validators.instanceOf(v, path, ctx, ${JSON.stringify(typeName)})`;
|
|
292
|
+
return templateToAst(tpl);
|
|
293
|
+
}
|