@webergency-utils/typechecker 0.1.0

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) 2026 webergency-utils
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 ADDED
@@ -0,0 +1,263 @@
1
+ # @webergency-utils/typechecker
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.
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.
6
+
7
+ ---
8
+
9
+ ## Features
10
+
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!).
17
+
18
+ ---
19
+
20
+ ## Installation
21
+
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.
23
+
24
+ ```bash
25
+ npm install @webergency-utils/typechecker
26
+ npm install -D ts-patch
27
+ ```
28
+
29
+ Run `ts-patch install` to patch your local TypeScript installation.
30
+
31
+ ---
32
+
33
+ ## Configuration
34
+
35
+ Update your `tsconfig.json` to include the transformer in the `compilerOptions.plugins` array:
36
+
37
+ ```json
38
+ {
39
+ "compilerOptions": {
40
+ "target": "ES2022",
41
+ "module": "NodeNext",
42
+ "moduleResolution": "NodeNext",
43
+ "plugins": [
44
+ { "transform": "@webergency-utils/typechecker" }
45
+ ]
46
+ }
47
+ }
48
+ ```
49
+
50
+ ---
51
+
52
+ ## Usage
53
+
54
+ ### 1. Decorator-Based Validation (Server Endpoints)
55
+
56
+ The transformer automatically intercepts decorators imported from `@webergency-utils/server` (`@Body`, `@Query`, `@Param`). It hashes the underlying TypeScript type, generates a highly optimized validation function, and hoists it to the top of the file using the `MetadataStore`.
57
+
58
+ ```typescript
59
+ import { Controller, Post, Body } from '@webergency-utils/server';
60
+
61
+ interface UserDTO {
62
+ id: string;
63
+ name: string;
64
+ age?: number;
65
+ }
66
+
67
+ @Controller('/users')
68
+ export class UserController {
69
+ // @webergency-utils/typechecker automatically generates a validator for UserDTO,
70
+ // registers it, and transforms this to @Body("hash_id", "strict")
71
+ @Post('/')
72
+ createUser(@Body() user: UserDTO) {
73
+ return { success: true, user };
74
+ }
75
+ }
76
+ ```
77
+
78
+ ---
79
+
80
+ ### 2. Manual Runtime Validation APIs
81
+
82
+ You can manually validate unknown data anywhere in your code. The transformer intercepts these calls and replaces them with direct, optimized validation functions.
83
+
84
+ ```typescript
85
+ import { is, assert, assertGuard, validate } from '@webergency-utils/typechecker';
86
+
87
+ interface Payload {
88
+ id: string;
89
+ active: boolean;
90
+ }
91
+
92
+ const data: unknown = JSON.parse('{"id": "123", "active": true}');
93
+
94
+ // 1. is() - returns a boolean (type guard)
95
+ if (is<Payload>(data)) {
96
+ console.log(data.id); // Narrowed to 'Payload'
97
+ }
98
+
99
+ // 2. assert() - returns the narrowed value or throws an error
100
+ const validData = assert<Payload>(data);
101
+
102
+ // 3. assertGuard() - asserts the type for the current scope in-place
103
+ assertGuard<Payload>(data);
104
+ console.log(data.id); // Narrowed in-place
105
+
106
+ // 4. validate() - returns a structured validation result with errors
107
+ const result = validate<Payload>(data);
108
+ if (result.success) {
109
+ console.log(result.data);
110
+ } else {
111
+ console.error(result.errors); // Array of formatted errors
112
+ }
113
+ ```
114
+
115
+ ---
116
+
117
+ ### 3. Extended Options & Validation Modes
118
+
119
+ All validation APIs accept either a string `ValidationMode` or a custom `ValidationOptions` object:
120
+
121
+ ```typescript
122
+ export type ValidationMode = 'strict' | 'relaxed' | 'strip';
123
+
124
+ export interface ValidationOptions {
125
+ mode?: ValidationMode; // default: 'strict'
126
+ tryConvert?: boolean; // Converts string numbers, booleans, and dates (ideal for query parameters)
127
+ wrapArrays?: boolean; // Wraps a single value into an array if the type expects an array
128
+ }
129
+ ```
130
+
131
+ #### Examples:
132
+ ```typescript
133
+ // Relaxed Mode (ignores additional properties)
134
+ const user = assert<User>(data, 'relaxed');
135
+
136
+ // Strip Mode (strips out any unknown properties from returned object)
137
+ const cleanUser = assert<User>(data, 'strip');
138
+
139
+ // Query-String Coercion
140
+ const query = assert<SearchQuery>(rawQuery, {
141
+ mode: 'strip',
142
+ tryConvert: true, // Coerces "18" -> 18, "true" -> true, etc.
143
+ wrapArrays: true // Coerces "tag" -> ["tag"] if tags: string[] is expected
144
+ });
145
+ ```
146
+
147
+ ---
148
+
149
+ ### 4. Error Reporting & Grouping
150
+
151
+ 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.
152
+
153
+ The error strings follow a deterministic, parser-friendly `Constraint<Value>` format.
154
+
155
+ ```typescript
156
+ import { validate, groupErrorsByPath, Minimum } from '@webergency-utils/typechecker';
157
+
158
+ interface Payload {
159
+ id: string;
160
+ role: "admin" | "user";
161
+ age: number & Minimum<18>;
162
+ metadata: { tag: string } | { priority: number };
163
+ }
164
+
165
+ const data = {
166
+ id: 123, // Error: expected string, got number
167
+ role: "guest", // Error: literal union mismatch
168
+ age: 15, // Error: minimum constraint failed
169
+ metadata: { } // Error: complex union mismatch
170
+ };
171
+
172
+ const result = validate<Payload>(data);
173
+ if (!result.success) {
174
+ const grouped = groupErrorsByPath(result.errors);
175
+ console.log(JSON.stringify(grouped, null, 2));
176
+ }
177
+ ```
178
+
179
+ **Output:**
180
+ ```json
181
+ {
182
+ "id": {
183
+ "value": 123,
184
+ "errors": ["Type<string>"]
185
+ },
186
+ "role": {
187
+ "value": "guest",
188
+ "errors": [
189
+ "Literal<'admin'>",
190
+ "Literal<'user'>"
191
+ ]
192
+ },
193
+ "age": {
194
+ "value": 15,
195
+ "errors": ["Minimum<18>"]
196
+ },
197
+ "metadata": {
198
+ "value": {},
199
+ "errors": ["Type<{tag:string}|{priority:number}>"]
200
+ },
201
+ "metadata.tag": {
202
+ "value": undefined,
203
+ "errors": ["Type<string>"]
204
+ },
205
+ "metadata.priority": {
206
+ "value": undefined,
207
+ "errors": ["Type<number>"]
208
+ }
209
+ }
210
+ ```
211
+
212
+ 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.
213
+
214
+ ---
215
+
216
+ ## Supported JSON-Schema Validation Tags
217
+
218
+ Add strict runtime metadata to your TypeScript primitives using standard intersection types:
219
+
220
+ ### String Tags
221
+ - `MinLength<N>`: Minimum string length.
222
+ - `MaxLength<N>`: Maximum string length.
223
+ - `Pattern<RegExp>`: Regular expression validation.
224
+ - `Format<T>`: Structural formats: `'email'`, `'uuid'`, `'date'`, `'date-time'`, `'url'`, `'ipv4'`, `'ipv6'`.
225
+
226
+ ### Number Tags
227
+ - `Minimum<N>`: Minimum numeric value (inclusive).
228
+ - `Maximum<N>`: Maximum numeric value (inclusive).
229
+ - `ExclusiveMinimum<N>`: Greater than `N`.
230
+ - `ExclusiveMaximum<N>`: Less than `N`.
231
+ - `MultipleOf<N>`: Must be a multiple of `N`.
232
+
233
+ ### Array Tags
234
+ - `MinItems<N>`: Minimum array items count.
235
+ - `MaxItems<N>`: Maximum array items count.
236
+ - `UniqueItems`: Enforces all elements in the array to be deeply unique.
237
+
238
+ #### Example:
239
+ ```typescript
240
+ import { MinLength, Minimum, Format, UniqueItems } from '@webergency-utils/typechecker';
241
+
242
+ interface Profile {
243
+ email: string & Format<'email'>;
244
+ password: string & MinLength<8>;
245
+ age: number & Minimum<18>;
246
+ luckyNumbers: number[] & UniqueItems;
247
+ }
248
+ ```
249
+
250
+ ---
251
+
252
+ ## How it Works
253
+
254
+ 1. **AST Analysis**: The transformer scans compile-time type signatures and generates highly nested, direct runtime checks.
255
+ 2. **Circular References**: Safely handles recursive and circular types by generating self-referencing lazy functions.
256
+ 3. **Hoisting & Deduping**: Identical type validations are hoisted to top-level constants and shared, minimizing footprint.
257
+ 4. **Clean Emitted JS**: The output compiles into vanilla JS, utilizing direct, blazing-fast validation logic.
258
+
259
+ ---
260
+
261
+ ## License
262
+
263
+ MIT © radixxko / [webergency-utils](https://github.com/webergency-utils)
@@ -0,0 +1,23 @@
1
+ import * as ts from 'typescript';
2
+ export interface IValidationRegistry {
3
+ validators: Map<string, ts.Expression>;
4
+ }
5
+ export declare function createRegistry(): IValidationRegistry;
6
+ export declare function templateToAst(template: string): ts.Expression;
7
+ export declare function injectNodes(expr: ts.Expression, replacements: Record<string, ts.Expression>): ts.Expression;
8
+ export declare function createPrimitiveCheck(type: string, requiredUtils: Set<string>): ts.Expression;
9
+ export declare function createConstrainedPrimitiveCheck(baseType: string, constraints: any[], requiredUtils: Set<string>, baseValidator?: ts.Expression): ts.Expression;
10
+ export declare function createLiteralCheck(value: string | number | boolean | ts.PseudoBigInt, requiredUtils: Set<string>): ts.Expression;
11
+ export declare function createArrayCheck(elementValidator: ts.Expression, requiredUtils: Set<string>): ts.Expression;
12
+ export declare function createTemplateLiteralCheck(regexStr: string, expected: string, requiredUtils: Set<string>): ts.Expression;
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;
15
+ export declare function createRecordCheck(valueValidator: ts.Expression, requiredUtils: Set<string>): ts.Expression;
16
+ export declare function createTupleCheck(checks: ts.Expression[], requiredUtils: Set<string>): ts.Expression;
17
+ export declare function createDateCheck(requiredUtils: Set<string>): ts.Expression;
18
+ export declare function createRegExpCheck(requiredUtils: Set<string>): ts.Expression;
19
+ export declare function createNullCheck(requiredUtils: Set<string>): ts.Expression;
20
+ export declare function createUndefinedCheck(requiredUtils: Set<string>): ts.Expression;
21
+ export declare function createIntersectionCheck(checks: ts.Expression[], requiredUtils: Set<string>): ts.Expression;
22
+ export declare function createSetCheck(elementValidator: ts.Expression, requiredUtils: Set<string>): ts.Expression;
23
+ export declare function createMapCheck(keyValidator: ts.Expression, valueValidator: ts.Expression, requiredUtils: Set<string>): ts.Expression;
@@ -0,0 +1,283 @@
1
+ import * as ts from 'typescript';
2
+ export function createRegistry() {
3
+ return {
4
+ validators: new Map()
5
+ };
6
+ }
7
+ export function templateToAst(template) {
8
+ const source = ts.createSourceFile('template.ts', `const x = ${template};`, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);
9
+ const statement = source.statements[0];
10
+ if (ts.isVariableStatement(statement)) {
11
+ return statement.declarationList.declarations[0].initializer;
12
+ }
13
+ if (ts.isExpressionStatement(statement)) {
14
+ return statement.expression;
15
+ }
16
+ throw new Error('Template must be an expression or variable declaration');
17
+ }
18
+ function stripPositions(node) {
19
+ const visitor = (n) => {
20
+ const cloned = ts.visitEachChild(n, visitor, undefined);
21
+ const res = { ...cloned, pos: -1, end: -1 };
22
+ Object.setPrototypeOf(res, Object.getPrototypeOf(cloned));
23
+ return res;
24
+ };
25
+ return ts.visitNode(node, visitor);
26
+ }
27
+ export function injectNodes(expr, replacements) {
28
+ const transformer = (context) => {
29
+ return (rootNode) => {
30
+ function visit(node) {
31
+ if (ts.isIdentifier(node) && replacements[node.text]) {
32
+ return stripPositions(replacements[node.text]);
33
+ }
34
+ return ts.visitEachChild(node, visit, context);
35
+ }
36
+ return ts.visitNode(rootNode, visit);
37
+ };
38
+ };
39
+ const result = ts.transform(expr, [transformer]);
40
+ return stripPositions(result.transformed[0]);
41
+ }
42
+ export function createPrimitiveCheck(type, requiredUtils) {
43
+ requiredUtils.add('validators');
44
+ return ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('validators'), ts.factory.createIdentifier(type));
45
+ }
46
+ export function createConstrainedPrimitiveCheck(baseType, constraints, requiredUtils, baseValidator) {
47
+ requiredUtils.add('validators');
48
+ const defaultConstraint = constraints.find(c => c.type === 'default');
49
+ const transformConstraints = constraints.filter(c => c.type === 'transform' || c.type === 'transform_custom');
50
+ const messageConstraint = constraints.find(c => c.type === 'message');
51
+ const remainingConstraints = constraints.filter(c => c.type !== 'default' && c.type !== 'transform' && c.type !== 'transform_custom' && c.type !== 'message');
52
+ const fallbackMsg = messageConstraint?.value;
53
+ const constraintCode = remainingConstraints.map(c => {
54
+ const valStr = typeof c.value === 'bigint' ? `${c.value}n` : (typeof c.value === 'string' ? `"${c.value}"` : `${c.value}`);
55
+ const activeMsg = c.message !== undefined ? c.message : fallbackMsg;
56
+ const msgArg = activeMsg !== undefined ? `, ${JSON.stringify(activeMsg)}` : '';
57
+ if (c.type === 'minLength') {
58
+ return `validators.minLength(v, path, ctx, ${valStr}${msgArg})`;
59
+ }
60
+ if (c.type === 'maxLength') {
61
+ return `validators.maxLength(v, path, ctx, ${valStr}${msgArg})`;
62
+ }
63
+ if (c.type === 'minimum') {
64
+ return `validators.minimum(v, path, ctx, ${valStr}${msgArg})`;
65
+ }
66
+ if (c.type === 'maximum') {
67
+ return `validators.maximum(v, path, ctx, ${valStr}${msgArg})`;
68
+ }
69
+ if (c.type === 'exclusiveMinimum') {
70
+ return `validators.exclusiveMinimum(v, path, ctx, ${valStr}${msgArg})`;
71
+ }
72
+ if (c.type === 'exclusiveMaximum') {
73
+ return `validators.exclusiveMaximum(v, path, ctx, ${valStr}${msgArg})`;
74
+ }
75
+ if (c.type === 'multipleOf') {
76
+ return `validators.multipleOf(v, path, ctx, ${valStr}${msgArg})`;
77
+ }
78
+ if (c.type === 'pattern') {
79
+ return `validators.pattern(v, path, ctx, new RegExp(${JSON.stringify(c.value)}), ${JSON.stringify('Pattern<' + c.value + '>')}${msgArg})`;
80
+ }
81
+ if (c.type === 'format') {
82
+ return `validators.format(v, path, ctx, ${JSON.stringify(c.value)}${msgArg})`;
83
+ }
84
+ if (c.type === 'minItems') {
85
+ return `validators.minItems(v, path, ctx, ${valStr}${msgArg})`;
86
+ }
87
+ if (c.type === 'maxItems') {
88
+ return `validators.maxItems(v, path, ctx, ${valStr}${msgArg})`;
89
+ }
90
+ if (c.type === 'uniqueItems') {
91
+ return `validators.uniqueItems(v, path, ctx${msgArg})`;
92
+ }
93
+ if (c.type === 'custom') {
94
+ return `validators.custom(v, path, ctx, ${c.value}${msgArg})`;
95
+ }
96
+ if (c.type === 'requires') {
97
+ return `validators.requires(v, path, ctx, ${JSON.stringify(Array.isArray(c.value) ? c.value : [c.value])}${msgArg})`;
98
+ }
99
+ return '';
100
+ }).filter(c => c !== '').join(';\n ');
101
+ let defaultInit = '';
102
+ if (defaultConstraint) {
103
+ defaultInit = `if (v === undefined) v = ${JSON.stringify(defaultConstraint.value)};\n `;
104
+ }
105
+ let transformInit = '';
106
+ if (transformConstraints.length > 0) {
107
+ const statements = transformConstraints.map(tc => {
108
+ if (tc.type === 'transform' && tc.value === 'lowercase') {
109
+ return 'if (typeof v === \'string\') v = v.toLowerCase()';
110
+ }
111
+ if (tc.type === 'transform' && tc.value === 'uppercase') {
112
+ return 'if (typeof v === \'string\') v = v.toUpperCase()';
113
+ }
114
+ if (tc.type === 'transform' && tc.value === 'trim') {
115
+ return 'if (typeof v === \'string\') v = v.trim()';
116
+ }
117
+ if (tc.type === 'transform' && tc.value === 'capitalize') {
118
+ return 'if (typeof v === \'string\' && v.length > 0) v = v.charAt(0).toUpperCase() + v.slice(1)';
119
+ }
120
+ if (tc.type === 'transform' && tc.value === 'tonumber') {
121
+ return 'v = Number(v)';
122
+ }
123
+ if (tc.type === 'transform' && tc.value === 'toboolean') {
124
+ return 'v = (v === \'true\' || v === \'1\' || v === true || v === 1)';
125
+ }
126
+ if (tc.type === 'transform' && tc.value === 'todate') {
127
+ return 'v = new Date(v)';
128
+ }
129
+ if (tc.type === 'transform_custom') {
130
+ return `v = ${tc.value}(v)`;
131
+ }
132
+ return '';
133
+ }).filter(s => s !== '').join(';\n ');
134
+ transformInit = `if (v !== undefined && v !== null) {\n ${statements};\n }\n `;
135
+ }
136
+ const tpl = `
137
+ (v, path, ctx) => {
138
+ const _s = ctx.success;
139
+ ctx.success = true;
140
+ ${defaultInit}${transformInit}v = __BASE_CHECK__;
141
+ if (ctx.success && v !== undefined && v !== null) {
142
+ ${constraintCode};
143
+ }
144
+ if (_s === false) ctx.success = false;
145
+ return v;
146
+ }
147
+ `;
148
+ const baseCheck = baseValidator ? ts.factory.createCallExpression(baseValidator, undefined, [ts.factory.createIdentifier('v'), ts.factory.createIdentifier('path'), ts.factory.createIdentifier('ctx')]) : ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('validators'), ts.factory.createIdentifier(baseType)), undefined, [ts.factory.createIdentifier('v'), ts.factory.createIdentifier('path'), ts.factory.createIdentifier('ctx')]);
149
+ return injectNodes(templateToAst(tpl), { '__BASE_CHECK__': baseCheck });
150
+ }
151
+ export function createLiteralCheck(value, requiredUtils) {
152
+ requiredUtils.add('validators');
153
+ return ts.factory.createArrowFunction(undefined, undefined, [
154
+ ts.factory.createParameterDeclaration(undefined, undefined, ts.factory.createIdentifier('v')),
155
+ ts.factory.createParameterDeclaration(undefined, undefined, ts.factory.createIdentifier('path')),
156
+ ts.factory.createParameterDeclaration(undefined, undefined, ts.factory.createIdentifier('ctx'))
157
+ ], undefined, undefined, ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('validators'), ts.factory.createIdentifier('literal')), undefined, [
158
+ ts.factory.createIdentifier('v'),
159
+ ts.factory.createIdentifier('path'),
160
+ ts.factory.createIdentifier('ctx'),
161
+ typeof value === 'string' ? ts.factory.createStringLiteral(value) :
162
+ typeof value === 'number' ? ts.factory.createNumericLiteral(value.toString()) :
163
+ typeof value === 'boolean' ? (value ? ts.factory.createTrue() : ts.factory.createFalse()) :
164
+ ts.factory.createBigIntLiteral(value.base10Value + 'n')
165
+ ]));
166
+ }
167
+ export function createArrayCheck(elementValidator, requiredUtils) {
168
+ requiredUtils.add('validators');
169
+ const tpl = '(v, path, ctx) => validators.array(v, path, ctx, __CHILD__)';
170
+ return injectNodes(templateToAst(tpl), { '__CHILD__': elementValidator });
171
+ }
172
+ export function createTemplateLiteralCheck(regexStr, expected, requiredUtils) {
173
+ requiredUtils.add('validators');
174
+ const tpl = `(v, path, ctx) => validators.templateLiteral(v, path, ctx, new RegExp(${JSON.stringify(regexStr)}), ${JSON.stringify(expected)})`;
175
+ return stripPositions(templateToAst(tpl));
176
+ }
177
+ export function createUnionCheck(checks, requiredUtils, expected = 'Type<Union>') {
178
+ requiredUtils.add('validators');
179
+ return ts.factory.createArrowFunction(undefined, undefined, [
180
+ ts.factory.createParameterDeclaration(undefined, undefined, ts.factory.createIdentifier('v')),
181
+ ts.factory.createParameterDeclaration(undefined, undefined, ts.factory.createIdentifier('path')),
182
+ ts.factory.createParameterDeclaration(undefined, undefined, ts.factory.createIdentifier('ctx'))
183
+ ], undefined, undefined, ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('validators'), ts.factory.createIdentifier('union')), undefined, [
184
+ ts.factory.createIdentifier('v'),
185
+ ts.factory.createIdentifier('path'),
186
+ ts.factory.createIdentifier('ctx'),
187
+ ts.factory.createArrayLiteralExpression(checks),
188
+ ts.factory.createStringLiteral(expected)
189
+ ]));
190
+ }
191
+ export function createObjectCheck(props, requiredUtils, expected = 'object') {
192
+ requiredUtils.add('validators');
193
+ const propDefinitions = props.map((p, i) => ts.factory.createArrayLiteralExpression([
194
+ ts.factory.createStringLiteral(p.name),
195
+ p.isOptional ? ts.factory.createTrue() : ts.factory.createFalse(),
196
+ p.validator
197
+ ]));
198
+ const allowedKeys = props.map(p => ts.factory.createStringLiteral(p.name));
199
+ const tpl = `
200
+ (v, path, ctx) => {
201
+ if (!validators.object(v, path, ctx, __KEYS__, __EXPECTED__)) return v;
202
+ let data = v;
203
+ if (ctx.mode === 'strip') {
204
+ let hasAdditional = false;
205
+ const keys = Object.keys(v);
206
+ const allowed = __KEYS__;
207
+ if (keys.length > allowed.length) {
208
+ hasAdditional = true;
209
+ } else {
210
+ for (let i = 0; i < keys.length; i++) {
211
+ if (!allowed.includes(keys[i])) {
212
+ hasAdditional = true;
213
+ break;
214
+ }
215
+ }
216
+ }
217
+ if (hasAdditional) data = {};
218
+ }
219
+ validators.props(v, data, path, ctx, __PROPS__);
220
+ return data;
221
+ }
222
+ `;
223
+ return injectNodes(templateToAst(tpl), {
224
+ '__KEYS__': ts.factory.createArrayLiteralExpression(allowedKeys),
225
+ '__EXPECTED__': ts.factory.createStringLiteral(expected),
226
+ '__PROPS__': ts.factory.createArrayLiteralExpression(propDefinitions, true)
227
+ });
228
+ }
229
+ export function createRecordCheck(valueValidator, requiredUtils) {
230
+ requiredUtils.add('validators');
231
+ const tpl = '(v, path, ctx) => validators.record(v, path, ctx, __CHILD__)';
232
+ return injectNodes(templateToAst(tpl), { '__CHILD__': valueValidator });
233
+ }
234
+ export function createTupleCheck(checks, requiredUtils) {
235
+ requiredUtils.add('validators');
236
+ const arrayElements = checks.map((_, i) => `__CHECK_${i}__`).join(', ');
237
+ const tpl = `(v, path, ctx) => validators.tuple(v, path, ctx, [${arrayElements}])`;
238
+ const replacements = {};
239
+ checks.forEach((c, i) => replacements[`__CHECK_${i}__`] = c);
240
+ return injectNodes(templateToAst(tpl), replacements);
241
+ }
242
+ export function createDateCheck(requiredUtils) {
243
+ requiredUtils.add('validators');
244
+ return ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('validators'), ts.factory.createIdentifier('date'));
245
+ }
246
+ export function createRegExpCheck(requiredUtils) {
247
+ requiredUtils.add('validators');
248
+ return ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('validators'), ts.factory.createIdentifier('regexp'));
249
+ }
250
+ export function createNullCheck(requiredUtils) {
251
+ requiredUtils.add('validators');
252
+ return ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('validators'), ts.factory.createIdentifier('null'));
253
+ }
254
+ export function createUndefinedCheck(requiredUtils) {
255
+ requiredUtils.add('validators');
256
+ return ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('validators'), ts.factory.createIdentifier('undefined'));
257
+ }
258
+ export function createIntersectionCheck(checks, requiredUtils) {
259
+ const tpl = `
260
+ (v, path, ctx) => {
261
+ const checks = __CHECKS__;
262
+ let data = ctx.mode === "strip" ? (typeof v === "object" && v !== null && !Array.isArray(v) ? {} : v) : v;
263
+ for (let i = 0; i < checks.length; i++) {
264
+ const val = checks[i](v, path, ctx);
265
+ if (ctx.mode === "strip" && typeof val === "object" && val !== null) Object.assign(data, val);
266
+ }
267
+ return data;
268
+ }
269
+ `;
270
+ return injectNodes(templateToAst(tpl), {
271
+ '__CHECKS__': ts.factory.createArrayLiteralExpression(checks)
272
+ });
273
+ }
274
+ export function createSetCheck(elementValidator, requiredUtils) {
275
+ requiredUtils.add('validators');
276
+ const tpl = '(v, path, ctx) => validators.set(v, path, ctx, __CHILD__)';
277
+ return injectNodes(templateToAst(tpl), { '__CHILD__': elementValidator });
278
+ }
279
+ export function createMapCheck(keyValidator, valueValidator, requiredUtils) {
280
+ requiredUtils.add('validators');
281
+ const tpl = '(v, path, ctx) => validators.map(v, path, ctx, __KEY__, __VALUE__)';
282
+ return injectNodes(templateToAst(tpl), { '__KEY__': keyValidator, '__VALUE__': valueValidator });
283
+ }
@@ -0,0 +1,2 @@
1
+ import ts from 'typescript';
2
+ export declare function hoistRegistrations(sourceFile: ts.SourceFile, cache: Map<string, ts.Expression>, requiredUtils: Set<string>, schemasMap?: Map<string, ts.Expression>): ts.SourceFile;