@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 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
- [![npm version](https://img.shields.io/npm/v/%40webergency-utils%2Ftypechecker)](https://www.npmjs.com/package/@webergency-utils/typechecker) [![Maintenance](https://img.shields.io/badge/maintenance-active-brightgreen.svg)](#maintenance) [![npm downloads](https://img.shields.io/npm/dm/%40webergency-utils%2Ftypechecker)](https://www.npmjs.com/package/@webergency-utils/typechecker) [![License](https://img.shields.io/npm/l/%40webergency-utils%2Ftypechecker)](https://www.npmjs.com/package/@webergency-utils/typechecker)
4
-
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.
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
+ [![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
 
@@ -34,7 +43,11 @@ if (result.success) {
34
43
 
35
44
  ## Installation & Setup
36
45
 
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.
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 transform plugin under the `plugins` array of `compilerOptions` in your `tsconfig.json`:
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](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.
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
- | `tryConvert` | `boolean` | `false` | Coerces compatible input primitives (e.g. numeric strings to numbers, ISO strings to Date). |
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`: RFC-5322 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`: ISO date `YYYY-MM-DD`.
310
- - `format.DateTime`: ISO date-time.
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`: Valid domain name.
318
- - `format.URI`: Valid URI.
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`: Coerces inputs to numbers.
329
- - `transform.ToBoolean`: Coerces inputs to booleans.
330
- - `transform.ToDate`: Coerces inputs to Date objects.
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` 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.
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 the transformer plugin is registered in `tsconfig.json`.
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 = Number(v)';
121
+ return 'v = validators.coerceQueryNumber(v)';
122
122
  }
123
123
  if (tc.type === 'transform' && tc.value === 'toboolean') {
124
- return 'v = (v === \'true\' || v === \'1\' || v === true || v === 1)';
124
+ return 'v = validators.coerceQueryBoolean(v)';
125
125
  }
126
126
  if (tc.type === 'transform' && tc.value === 'todate') {
127
- return 'v = new Date(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, i) => ts.factory.createArrayLiteralExpression([
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
- if (!validators.object(v, path, ctx, __KEYS__, __EXPECTED__)) return v;
202
- const data = ctx.mode === 'strip' ? {} : v;
203
- validators.props(v, data, path, ctx, __PROPS__);
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 = ctx.mode === "strip" ? (typeof v === "object" && v !== null && !Array.isArray(v) ? {} : v) : v;
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 (ctx.mode === "strip" && typeof val === "object" && val !== null) Object.assign(data, val);
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
+ }