reliant-type 1.0.0 → 2.1.4
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 +113 -688
- package/dist/cjs/core/schema/extensions/mods/typescript-generator.js +45 -16
- package/dist/cjs/core/schema/extensions/mods/typescript-generator.js.map +1 -1
- package/dist/cjs/core/schema/mode/interfaces/Interface.js +0 -12
- package/dist/cjs/core/schema/mode/interfaces/Interface.js.map +1 -1
- package/dist/cjs/core/schema/mode/interfaces/InterfaceSchema.js +34 -3
- package/dist/cjs/core/schema/mode/interfaces/InterfaceSchema.js.map +1 -1
- package/dist/cjs/core/schema/mode/interfaces/errors/SchemaValidationError.js +16 -0
- package/dist/cjs/core/schema/mode/interfaces/errors/SchemaValidationError.js.map +1 -0
- package/dist/cjs/core/schema/mode/interfaces/precompilation/FieldPrecompilers.js +119 -0
- package/dist/cjs/core/schema/mode/interfaces/precompilation/FieldPrecompilers.js.map +1 -1
- package/dist/cjs/core/schema/mode/interfaces/precompilation/SchemaPrecompiler.js +49 -0
- package/dist/cjs/core/schema/mode/interfaces/precompilation/SchemaPrecompiler.js.map +1 -1
- package/dist/cjs/core/schema/mode/interfaces/typescript/TypeInference.js.map +1 -1
- package/dist/cjs/core/schema/mode/interfaces/validators/ConstraintParser.js +6 -0
- package/dist/cjs/core/schema/mode/interfaces/validators/ConstraintParser.js.map +1 -1
- package/dist/cjs/core/schema/mode/interfaces/validators/TypeGuards.js +7 -0
- package/dist/cjs/core/schema/mode/interfaces/validators/TypeGuards.js.map +1 -1
- package/dist/cjs/core/schema/mode/interfaces/validators/mods/securityValidator.js +1 -1
- package/dist/cjs/core/schema/mode/interfaces/validators/mods/securityValidator.js.map +1 -1
- package/dist/cjs/core/utils/Mod.js +33 -4
- package/dist/cjs/core/utils/Mod.js.map +1 -1
- package/dist/cjs/index.js +74 -1
- package/dist/cjs/index.js.map +1 -1
- package/dist/esm/core/schema/extensions/mods/typescript-generator.js +45 -16
- package/dist/esm/core/schema/extensions/mods/typescript-generator.js.map +1 -1
- package/dist/esm/core/schema/mode/interfaces/Interface.js +1 -12
- package/dist/esm/core/schema/mode/interfaces/Interface.js.map +1 -1
- package/dist/esm/core/schema/mode/interfaces/InterfaceSchema.js +32 -1
- package/dist/esm/core/schema/mode/interfaces/InterfaceSchema.js.map +1 -1
- package/dist/esm/core/schema/mode/interfaces/errors/SchemaValidationError.js +14 -0
- package/dist/esm/core/schema/mode/interfaces/errors/SchemaValidationError.js.map +1 -0
- package/dist/esm/core/schema/mode/interfaces/precompilation/FieldPrecompilers.js +119 -0
- package/dist/esm/core/schema/mode/interfaces/precompilation/FieldPrecompilers.js.map +1 -1
- package/dist/esm/core/schema/mode/interfaces/precompilation/SchemaPrecompiler.js +49 -0
- package/dist/esm/core/schema/mode/interfaces/precompilation/SchemaPrecompiler.js.map +1 -1
- package/dist/esm/core/schema/mode/interfaces/typescript/TypeInference.js.map +1 -1
- package/dist/esm/core/schema/mode/interfaces/validators/ConstraintParser.js +6 -0
- package/dist/esm/core/schema/mode/interfaces/validators/ConstraintParser.js.map +1 -1
- package/dist/esm/core/schema/mode/interfaces/validators/TypeGuards.js +7 -0
- package/dist/esm/core/schema/mode/interfaces/validators/TypeGuards.js.map +1 -1
- package/dist/esm/core/schema/mode/interfaces/validators/mods/securityValidator.js +1 -1
- package/dist/esm/core/schema/mode/interfaces/validators/mods/securityValidator.js.map +1 -1
- package/dist/esm/core/utils/Mod.js +33 -4
- package/dist/esm/core/utils/Mod.js.map +1 -1
- package/dist/esm/index.js +78 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/schema.d.ts +44 -3
- package/docs/FUNCTION-TYPES.md +120 -0
- package/docs/GETTING-STARTED.md +56 -53
- package/docs/VSCODE-EXTENSION.md +58 -47
- package/package.json +4 -4
- package/src/core/schema/extensions/components/AutoDocumentation/Docs.ts +1 -1
- package/src/core/schema/extensions/mods/typescript-generator.ts +342 -295
- package/src/core/schema/mode/interfaces/Interface.ts +1 -13
- package/src/core/schema/mode/interfaces/InterfaceSchema.ts +41 -1
- package/src/core/schema/mode/interfaces/errors/SchemaValidationError.ts +13 -0
- package/src/core/schema/mode/interfaces/precompilation/FieldPrecompilers.ts +146 -0
- package/src/core/schema/mode/interfaces/precompilation/SchemaPrecompiler.ts +79 -0
- package/src/core/schema/mode/interfaces/typescript/TypeInference.ts +67 -16
- package/src/core/schema/mode/interfaces/validators/ConstraintParser.ts +8 -0
- package/src/core/schema/mode/interfaces/validators/TypeGuards.ts +19 -5
- package/src/core/schema/mode/interfaces/validators/mods/securityValidator.ts +1 -1
- package/src/core/types/SchemaValidator.type.ts +4 -0
- package/src/core/utils/Mod.ts +35 -4
- package/src/index.ts +9 -0
|
@@ -349,16 +349,4 @@ export { Mod } from "../../../utils/Mod";
|
|
|
349
349
|
*/
|
|
350
350
|
export { Make } from "../../../utils/Make";
|
|
351
351
|
|
|
352
|
-
|
|
353
|
-
* Custom error class for schema validation
|
|
354
|
-
*/
|
|
355
|
-
export class SchemaValidationError extends Error {
|
|
356
|
-
constructor(
|
|
357
|
-
message: string,
|
|
358
|
-
public errors: string[],
|
|
359
|
-
public warnings: string[]
|
|
360
|
-
) {
|
|
361
|
-
super(message);
|
|
362
|
-
this.name = "SchemaValidationError";
|
|
363
|
-
}
|
|
364
|
-
}
|
|
352
|
+
export { SchemaValidationError } from "./errors/SchemaValidationError";
|
|
@@ -35,7 +35,7 @@ import {
|
|
|
35
35
|
OptimizationLevel,
|
|
36
36
|
} from "./precompilation/SchemaPrecompiler";
|
|
37
37
|
import { MAX_OBJECT_DEPTH } from "../../../../constants/VALIDATION_CONSTANTS";
|
|
38
|
-
import { SchemaValidationError } from "./
|
|
38
|
+
import { SchemaValidationError } from "./errors/SchemaValidationError";
|
|
39
39
|
import { ErrorCode } from "./errors/types/errors.type";
|
|
40
40
|
|
|
41
41
|
/**
|
|
@@ -54,6 +54,46 @@ export class InterfaceSchema<T = any> {
|
|
|
54
54
|
private precompiledValidator?: PrecompiledValidator;
|
|
55
55
|
private optimizationLevel: OptimizationLevel = OptimizationLevel.NONE;
|
|
56
56
|
|
|
57
|
+
/**
|
|
58
|
+
* Static generator for TypeScript types
|
|
59
|
+
* This is set by the extension system to avoid circular dependencies
|
|
60
|
+
*/
|
|
61
|
+
public static tsGenerator?: (definition: SchemaInterface, options?: SchemaOptions) => string;
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Get the TypeScript type definition for this schema.
|
|
65
|
+
* At runtime, this returns the TypeScript interface/type as a string.
|
|
66
|
+
* For TypeScript, this property has the inferred type T.
|
|
67
|
+
*
|
|
68
|
+
* @example
|
|
69
|
+
* ```typescript
|
|
70
|
+
* const UserSchema = Interface({ name: "string" });
|
|
71
|
+
* type User = typeof UserSchema.types;
|
|
72
|
+
* console.log(UserSchema.types); // Outputs the TS interface string
|
|
73
|
+
* ```
|
|
74
|
+
*/
|
|
75
|
+
get types(): T {
|
|
76
|
+
if (InterfaceSchema.tsGenerator) {
|
|
77
|
+
return InterfaceSchema.tsGenerator(this.definition, this.options) as any;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Fallback: Generate a simple representation if generator not registered
|
|
81
|
+
let output = `export interface ${this.options.name || 'Schema'} {\n`;
|
|
82
|
+
for (const [key, value] of Object.entries(this.definition)) {
|
|
83
|
+
const isOptional = typeof value === 'string' && value.endsWith('?');
|
|
84
|
+
output += ` ${key}${isOptional ? '?' : ''}: any;\n`;
|
|
85
|
+
}
|
|
86
|
+
output += "}\n";
|
|
87
|
+
return output as any;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Alias for types property to support different coding styles
|
|
92
|
+
*/
|
|
93
|
+
public getTypes(): T {
|
|
94
|
+
return this.types;
|
|
95
|
+
}
|
|
96
|
+
|
|
57
97
|
constructor(
|
|
58
98
|
private definition: SchemaInterface,
|
|
59
99
|
private options: SchemaOptions = {}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Custom error class for schema validation
|
|
3
|
+
*/
|
|
4
|
+
export class SchemaValidationError extends Error {
|
|
5
|
+
constructor(
|
|
6
|
+
message: string,
|
|
7
|
+
public errors: string[],
|
|
8
|
+
public warnings: string[] = []
|
|
9
|
+
) {
|
|
10
|
+
super(message);
|
|
11
|
+
this.name = "SchemaValidationError";
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -703,6 +703,146 @@ export class FieldPrecompilers {
|
|
|
703
703
|
return validator as CompiledFieldValidator;
|
|
704
704
|
}
|
|
705
705
|
|
|
706
|
+
/**
|
|
707
|
+
* Precompile function field types (fn(() => void), etc.)
|
|
708
|
+
* ENHANCED: Implements function wrapping to validate arguments and return values at runtime.
|
|
709
|
+
*/
|
|
710
|
+
static precompileFunction(functionType: string): CompiledFieldValidator {
|
|
711
|
+
const validator = (value: any): SchemaValidationResult => {
|
|
712
|
+
if (typeof value !== "function") {
|
|
713
|
+
return {
|
|
714
|
+
success: false,
|
|
715
|
+
errors: [ErrorHandler.createTypeError([], "function", value)],
|
|
716
|
+
warnings: [],
|
|
717
|
+
data: undefined,
|
|
718
|
+
};
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
// Extract signature: fn((args) => returnType)
|
|
722
|
+
const type = functionType.endsWith("?")
|
|
723
|
+
? functionType.slice(0, -1)
|
|
724
|
+
: functionType;
|
|
725
|
+
const signature = type.slice(3, -1); // remove fn( and )
|
|
726
|
+
|
|
727
|
+
const arrowIndex = signature.lastIndexOf("=>");
|
|
728
|
+
if (arrowIndex === -1) {
|
|
729
|
+
return { success: true, errors: [], warnings: [], data: value };
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
const argsPart = signature.slice(0, arrowIndex).trim();
|
|
733
|
+
const returnPart = signature.slice(arrowIndex + 2).trim();
|
|
734
|
+
|
|
735
|
+
// Clean argsPart: remove outer parentheses if present
|
|
736
|
+
const cleanArgsPart =
|
|
737
|
+
argsPart.startsWith("(") && argsPart.endsWith(")")
|
|
738
|
+
? argsPart.slice(1, -1)
|
|
739
|
+
: argsPart;
|
|
740
|
+
|
|
741
|
+
// Create a wrapped function that validates on call
|
|
742
|
+
const ReliantFunction = (...args: any[]) => {
|
|
743
|
+
// 1. Validate Arguments
|
|
744
|
+
if (cleanArgsPart && cleanArgsPart !== "()") {
|
|
745
|
+
const argDefs = cleanArgsPart.split(",").map((s) => s.trim());
|
|
746
|
+
|
|
747
|
+
for (let i = 0; i < argDefs.length; i++) {
|
|
748
|
+
const def = argDefs[i];
|
|
749
|
+
// Parse definition: name?: type or ...name: type
|
|
750
|
+
const parts = def.split(":");
|
|
751
|
+
const namePart = parts[0].trim();
|
|
752
|
+
let typePart = parts.slice(1).join(":").trim() || "any";
|
|
753
|
+
|
|
754
|
+
// Handle TYPE placeholder as any
|
|
755
|
+
if (typePart === "TYPE") typePart = "any";
|
|
756
|
+
|
|
757
|
+
const isRest = namePart.startsWith("...");
|
|
758
|
+
const isOptional = namePart.endsWith("?") || typePart.endsWith("?");
|
|
759
|
+
|
|
760
|
+
// Check for missing required arguments
|
|
761
|
+
// We check if the index exists in args. explicitly passing undefined is allowed if the type allows it,
|
|
762
|
+
// but not passing it at all is a missing argument error for required params.
|
|
763
|
+
if (!isRest && !isOptional && i >= args.length) {
|
|
764
|
+
throw new Error(
|
|
765
|
+
`[ReliantType] Missing required argument at index ${i} ('${namePart}'). Expected ${typePart}.`
|
|
766
|
+
);
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
if (isRest) {
|
|
770
|
+
const restType = typePart.endsWith("[]")
|
|
771
|
+
? typePart.slice(0, -2)
|
|
772
|
+
: typePart;
|
|
773
|
+
const restArgs = args.slice(i);
|
|
774
|
+
const restValidator = FieldPrecompilers.parseAndCompile(restType);
|
|
775
|
+
for (const restArg of restArgs) {
|
|
776
|
+
const res = restValidator(restArg);
|
|
777
|
+
if (!res.success) {
|
|
778
|
+
throw new Error(
|
|
779
|
+
`[ReliantType] Argument validation failed for rest parameter at index ${i}: ${res.errors[0].message}`
|
|
780
|
+
);
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
break; // Rest is always last
|
|
784
|
+
} else {
|
|
785
|
+
// If argument is provided (or undefined but present), validate it
|
|
786
|
+
if (i < args.length) {
|
|
787
|
+
const argValidator =
|
|
788
|
+
FieldPrecompilers.parseAndCompile(typePart);
|
|
789
|
+
const res = argValidator(args[i]);
|
|
790
|
+
if (!res.success) {
|
|
791
|
+
throw new Error(
|
|
792
|
+
`[ReliantType] Argument validation failed for argument at index ${i} ('${namePart}'): ${res.errors[0].message}`
|
|
793
|
+
);
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
// 2. Execute original function
|
|
801
|
+
const result = value(...args);
|
|
802
|
+
|
|
803
|
+
// 3. Validate Return Value
|
|
804
|
+
if (returnPart && returnPart !== "void") {
|
|
805
|
+
// Handle TYPE placeholder as any
|
|
806
|
+
const returnType = returnPart === "TYPE" ? "any" : returnPart;
|
|
807
|
+
|
|
808
|
+
if (returnType !== "any") {
|
|
809
|
+
const returnValidator =
|
|
810
|
+
FieldPrecompilers.parseAndCompile(returnType);
|
|
811
|
+
const res = returnValidator(result);
|
|
812
|
+
if (!res.success) {
|
|
813
|
+
throw new Error(
|
|
814
|
+
`[ReliantType] Return value validation failed for ${functionType}. Expected ${returnType}, got ${typeof result}: ${res.errors[0].message}`
|
|
815
|
+
);
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
return result;
|
|
821
|
+
};
|
|
822
|
+
|
|
823
|
+
// Force function name for better debugging
|
|
824
|
+
try {
|
|
825
|
+
Object.defineProperty(ReliantFunction, "name", {
|
|
826
|
+
value: `ReliantFunction_${functionType.replace(/[^a-zA-Z0-9]/g, "_")}`,
|
|
827
|
+
configurable: true,
|
|
828
|
+
});
|
|
829
|
+
} catch (e) {
|
|
830
|
+
/* ignore */
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
return {
|
|
834
|
+
success: true,
|
|
835
|
+
errors: [],
|
|
836
|
+
warnings: [],
|
|
837
|
+
data: ReliantFunction,
|
|
838
|
+
};
|
|
839
|
+
};
|
|
840
|
+
|
|
841
|
+
(validator as any)._fieldType = functionType;
|
|
842
|
+
(validator as any)._isCompiled = true;
|
|
843
|
+
return validator as CompiledFieldValidator;
|
|
844
|
+
}
|
|
845
|
+
|
|
706
846
|
/**
|
|
707
847
|
* Precompile special field types (email, url, json, etc.)
|
|
708
848
|
*/
|
|
@@ -764,6 +904,12 @@ export class FieldPrecompilers {
|
|
|
764
904
|
return isOptional ? this.precompileOptional(validator) : validator;
|
|
765
905
|
}
|
|
766
906
|
|
|
907
|
+
// Handle function types (fn(() => void), etc.)
|
|
908
|
+
if (baseType.startsWith("fn(") && baseType.endsWith(")")) {
|
|
909
|
+
const validator = this.precompileFunction(baseType);
|
|
910
|
+
return isOptional ? this.precompileOptional(validator) : validator;
|
|
911
|
+
}
|
|
912
|
+
|
|
767
913
|
// Handle basic types with constraints (including URL args like url.https)
|
|
768
914
|
const constraintMatch = baseType.match(/^([\w.]+)(?:\(([^)]*)\))?$/);
|
|
769
915
|
if (constraintMatch) {
|
|
@@ -16,6 +16,7 @@ import { MAX_COMPILATION_DEPTH as IMPORTED_MAX_COMPILATION_DEPTH } from "../../.
|
|
|
16
16
|
import { ErrorHandler } from "../errors/ErrorHandler";
|
|
17
17
|
import { ErrorCode } from "../errors/types/errors.type";
|
|
18
18
|
import { SUPPORTED_VALIDATOR_TYPES } from "../../../../types/ValidatorTypes";
|
|
19
|
+
import { TypeGuards } from "../validators/TypeGuards";
|
|
19
20
|
|
|
20
21
|
// Precompiled validator function signature
|
|
21
22
|
export interface PrecompiledValidator {
|
|
@@ -208,6 +209,22 @@ export class SchemaPrecompiler {
|
|
|
208
209
|
// OPTIMIZED: Process simple fields first (fastest path)
|
|
209
210
|
for (const field of simpleFields) {
|
|
210
211
|
const value = data[field.fieldName];
|
|
212
|
+
|
|
213
|
+
// Handle optional fields
|
|
214
|
+
if (value === undefined) {
|
|
215
|
+
if (!field.isOptional) {
|
|
216
|
+
errors.push(
|
|
217
|
+
ErrorHandler.createMissingFieldError(
|
|
218
|
+
[field.fieldName],
|
|
219
|
+
field.fieldName
|
|
220
|
+
)
|
|
221
|
+
);
|
|
222
|
+
} else if (field.hasDefault) {
|
|
223
|
+
validatedData[field.fieldName] = field.defaultValue;
|
|
224
|
+
}
|
|
225
|
+
continue;
|
|
226
|
+
}
|
|
227
|
+
|
|
211
228
|
const result = field.validator(value);
|
|
212
229
|
|
|
213
230
|
if (!result.success) {
|
|
@@ -228,6 +245,22 @@ export class SchemaPrecompiler {
|
|
|
228
245
|
// OPTIMIZED: Process union fields with precompiled validators
|
|
229
246
|
for (const field of unionFields) {
|
|
230
247
|
const value = data[field.fieldName];
|
|
248
|
+
|
|
249
|
+
// Handle optional fields
|
|
250
|
+
if (value === undefined) {
|
|
251
|
+
if (!field.isOptional) {
|
|
252
|
+
errors.push(
|
|
253
|
+
ErrorHandler.createMissingFieldError(
|
|
254
|
+
[field.fieldName],
|
|
255
|
+
field.fieldName
|
|
256
|
+
)
|
|
257
|
+
);
|
|
258
|
+
} else if (field.hasDefault) {
|
|
259
|
+
validatedData[field.fieldName] = field.defaultValue;
|
|
260
|
+
}
|
|
261
|
+
continue;
|
|
262
|
+
}
|
|
263
|
+
|
|
231
264
|
const result = field.validator(value);
|
|
232
265
|
|
|
233
266
|
if (!result.success) {
|
|
@@ -248,6 +281,22 @@ export class SchemaPrecompiler {
|
|
|
248
281
|
// OPTIMIZED: Process array fields
|
|
249
282
|
for (const field of arrayFields) {
|
|
250
283
|
const value = data[field.fieldName];
|
|
284
|
+
|
|
285
|
+
// Handle optional fields
|
|
286
|
+
if (value === undefined) {
|
|
287
|
+
if (!field.isOptional) {
|
|
288
|
+
errors.push(
|
|
289
|
+
ErrorHandler.createMissingFieldError(
|
|
290
|
+
[field.fieldName],
|
|
291
|
+
field.fieldName
|
|
292
|
+
)
|
|
293
|
+
);
|
|
294
|
+
} else if (field.hasDefault) {
|
|
295
|
+
validatedData[field.fieldName] = field.defaultValue;
|
|
296
|
+
}
|
|
297
|
+
continue;
|
|
298
|
+
}
|
|
299
|
+
|
|
251
300
|
const result = field.validator(value);
|
|
252
301
|
|
|
253
302
|
if (!result.success) {
|
|
@@ -268,6 +317,22 @@ export class SchemaPrecompiler {
|
|
|
268
317
|
// OPTIMIZED: Process nested fields last
|
|
269
318
|
for (const field of nestedFields) {
|
|
270
319
|
const value = data[field.fieldName];
|
|
320
|
+
|
|
321
|
+
// Handle optional fields
|
|
322
|
+
if (value === undefined) {
|
|
323
|
+
if (!field.isOptional) {
|
|
324
|
+
errors.push(
|
|
325
|
+
ErrorHandler.createMissingFieldError(
|
|
326
|
+
[field.fieldName],
|
|
327
|
+
field.fieldName
|
|
328
|
+
)
|
|
329
|
+
);
|
|
330
|
+
} else if (field.hasDefault) {
|
|
331
|
+
validatedData[field.fieldName] = field.defaultValue;
|
|
332
|
+
}
|
|
333
|
+
continue;
|
|
334
|
+
}
|
|
335
|
+
|
|
271
336
|
const result = field.validator(value);
|
|
272
337
|
|
|
273
338
|
if (!result.success) {
|
|
@@ -391,6 +456,20 @@ export class SchemaPrecompiler {
|
|
|
391
456
|
options: SchemaOptions,
|
|
392
457
|
depth: number = 0
|
|
393
458
|
): FieldCompilation {
|
|
459
|
+
// Handle OptionalSchemaInterface wrapper (used by Mod.deepPartial and Mod.makeOptional)
|
|
460
|
+
if (TypeGuards.isOptionalSchemaInterface(fieldType)) {
|
|
461
|
+
const compilation = this.compileField(
|
|
462
|
+
fieldName,
|
|
463
|
+
fieldType.schema,
|
|
464
|
+
options,
|
|
465
|
+
depth
|
|
466
|
+
);
|
|
467
|
+
return {
|
|
468
|
+
...compilation,
|
|
469
|
+
isOptional: true,
|
|
470
|
+
};
|
|
471
|
+
}
|
|
472
|
+
|
|
394
473
|
// Handle nested objects properly
|
|
395
474
|
if (
|
|
396
475
|
typeof fieldType === "object" &&
|
|
@@ -53,16 +53,20 @@ export type CoreTypeMap = {
|
|
|
53
53
|
|
|
54
54
|
// Special types
|
|
55
55
|
unknown: unknown;
|
|
56
|
-
void:
|
|
56
|
+
void: void;
|
|
57
57
|
null: null;
|
|
58
58
|
undefined: undefined;
|
|
59
|
+
TYPE: any;
|
|
59
60
|
};
|
|
60
61
|
|
|
61
62
|
/**
|
|
62
63
|
* Extract base type from field type string (removes constraints and modifiers)
|
|
63
64
|
*/
|
|
64
|
-
export type ExtractBaseType<T extends string> =
|
|
65
|
-
|
|
65
|
+
export type ExtractBaseType<T extends string> = T extends
|
|
66
|
+
| `fn(${infer Sig})`
|
|
67
|
+
| `fn(${infer Sig})?`
|
|
68
|
+
? `fn(${Sig})`
|
|
69
|
+
: T extends `${infer Base}(${string})`
|
|
66
70
|
? Base
|
|
67
71
|
: T extends `${infer Base}?`
|
|
68
72
|
? Base
|
|
@@ -139,19 +143,22 @@ export type MapFieldType<T extends string> =
|
|
|
139
143
|
: // Handle conditional expressions (contains "when" and "*?")
|
|
140
144
|
T extends `when ${string} *? ${string}`
|
|
141
145
|
? InferConditionalType<T>
|
|
142
|
-
: // Handle
|
|
143
|
-
ExtractBaseType<T> extends
|
|
144
|
-
?
|
|
145
|
-
: // Handle
|
|
146
|
-
T extends
|
|
147
|
-
?
|
|
148
|
-
:
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
?
|
|
153
|
-
: //
|
|
154
|
-
|
|
146
|
+
: // Handle function types (fn((args) => void))
|
|
147
|
+
ExtractBaseType<T> extends `fn(${infer Sig})`
|
|
148
|
+
? ParseFunctionType<Sig>
|
|
149
|
+
: // Handle union types (contains |) - check after removing optional and parentheses
|
|
150
|
+
ExtractBaseType<T> extends `${string}|${string}`
|
|
151
|
+
? ParseUnionType<ExtractBaseType<T>>
|
|
152
|
+
: // Handle constant types (starts with =)
|
|
153
|
+
T extends `=${infer Value}?`
|
|
154
|
+
? Value | undefined
|
|
155
|
+
: T extends `=${infer Value}`
|
|
156
|
+
? Value
|
|
157
|
+
: // Handle core types
|
|
158
|
+
ExtractBaseType<T> extends keyof CoreTypeMap
|
|
159
|
+
? CoreTypeMap[ExtractBaseType<T>]
|
|
160
|
+
: // Fallback to any for unknown types
|
|
161
|
+
any;
|
|
155
162
|
|
|
156
163
|
/**
|
|
157
164
|
* Utility type to trim whitespace from string literal types
|
|
@@ -174,6 +181,50 @@ export type ParseUnionType<T extends string> =
|
|
|
174
181
|
? Trim<First> | ParseUnionType<Rest>
|
|
175
182
|
: Trim<T>;
|
|
176
183
|
|
|
184
|
+
/**
|
|
185
|
+
* Parse function signature string into a real TypeScript function type
|
|
186
|
+
*/
|
|
187
|
+
export type ParseFunctionType<S extends string> =
|
|
188
|
+
S extends `(${infer Args}) => ${infer Return}`
|
|
189
|
+
? (...args: ParseArgs<Args>) => ParseReturnType<Return>
|
|
190
|
+
: S extends `${infer Args} => ${infer Return}`
|
|
191
|
+
? (...args: ParseArgs<Args>) => ParseReturnType<Return>
|
|
192
|
+
: (...args: any[]) => any;
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Parse return type of a function
|
|
196
|
+
*/
|
|
197
|
+
export type ParseReturnType<T extends string> =
|
|
198
|
+
Trim<T> extends "void"
|
|
199
|
+
? void
|
|
200
|
+
: Trim<T> extends "TYPE"
|
|
201
|
+
? any
|
|
202
|
+
: MapFieldType<Trim<T>>;
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Parse arguments of a function into a tuple
|
|
206
|
+
*/
|
|
207
|
+
export type ParseArgs<T extends string> =
|
|
208
|
+
Trim<T> extends ""
|
|
209
|
+
? []
|
|
210
|
+
: Trim<T> extends `${infer First},${infer Rest}`
|
|
211
|
+
? [...ParseArgAsTuple<First>, ...ParseArgs<Rest>]
|
|
212
|
+
: ParseArgAsTuple<T>;
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Parse a single argument into a tuple element (handles rest parameters)
|
|
216
|
+
*/
|
|
217
|
+
export type ParseArgAsTuple<T extends string> =
|
|
218
|
+
Trim<T> extends `${infer Name}:${infer Type}`
|
|
219
|
+
? Name extends `...${infer RestName}`
|
|
220
|
+
? MapFieldType<Trim<Type>> extends (infer U)[]
|
|
221
|
+
? [...args: U[]]
|
|
222
|
+
: [...args: any[]]
|
|
223
|
+
: Name extends `${infer ArgName}?`
|
|
224
|
+
? [arg?: MapFieldType<Trim<Type>> | undefined]
|
|
225
|
+
: [arg: MapFieldType<Trim<Type>>]
|
|
226
|
+
: [any];
|
|
227
|
+
|
|
177
228
|
/**
|
|
178
229
|
* Handle optional fields
|
|
179
230
|
*/
|
|
@@ -80,6 +80,9 @@ export class ConstraintParser {
|
|
|
80
80
|
|
|
81
81
|
// Colon split for key-value pairs
|
|
82
82
|
colonSplit: /\s*:\s*/,
|
|
83
|
+
|
|
84
|
+
// Function type detection
|
|
85
|
+
functionType: /^fn\(.*\)$/,
|
|
83
86
|
} as const;
|
|
84
87
|
|
|
85
88
|
// Character codes for optimized checks
|
|
@@ -201,6 +204,11 @@ export class ConstraintParser {
|
|
|
201
204
|
return { type, constraints: {}, optional, required };
|
|
202
205
|
}
|
|
203
206
|
|
|
207
|
+
// Handle function types (fn(() => void), etc.)
|
|
208
|
+
if (this._patterns.functionType.test(type)) {
|
|
209
|
+
return { type, constraints: {}, optional, required };
|
|
210
|
+
}
|
|
211
|
+
|
|
204
212
|
// Enhanced constraint parsing with robust validation
|
|
205
213
|
const constraintMatch = this._parseBalancedConstraints(type);
|
|
206
214
|
if (constraintMatch) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Type Guards Module
|
|
3
|
-
*
|
|
3
|
+
*
|
|
4
4
|
* Contains all type guard functions extracted from InterfaceSchema
|
|
5
5
|
* to improve maintainability and reduce file size.
|
|
6
6
|
*/
|
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
OptionalSchemaInterface,
|
|
13
13
|
UnionValue,
|
|
14
14
|
} from "../../../../types/SchemaValidator.type";
|
|
15
|
-
|
|
15
|
+
|
|
16
16
|
/**
|
|
17
17
|
* Type guard functions for schema validation
|
|
18
18
|
*/
|
|
@@ -33,7 +33,9 @@ export class TypeGuards {
|
|
|
33
33
|
/**
|
|
34
34
|
* Check if value is a constant value
|
|
35
35
|
*/
|
|
36
|
-
static isConstantValue(
|
|
36
|
+
static isConstantValue(
|
|
37
|
+
value: any
|
|
38
|
+
): value is ConstantValue | OptionalConstantValue {
|
|
37
39
|
return (
|
|
38
40
|
typeof value === "object" &&
|
|
39
41
|
value !== null &&
|
|
@@ -45,7 +47,9 @@ export class TypeGuards {
|
|
|
45
47
|
/**
|
|
46
48
|
* Check if value is an optional schema interface
|
|
47
49
|
*/
|
|
48
|
-
static isOptionalSchemaInterface(
|
|
50
|
+
static isOptionalSchemaInterface(
|
|
51
|
+
value: any
|
|
52
|
+
): value is OptionalSchemaInterface {
|
|
49
53
|
return (
|
|
50
54
|
typeof value === "object" &&
|
|
51
55
|
value !== null &&
|
|
@@ -137,6 +141,14 @@ export class TypeGuards {
|
|
|
137
141
|
return fieldType.startsWith("record<") && fieldType.endsWith(">");
|
|
138
142
|
}
|
|
139
143
|
|
|
144
|
+
/**
|
|
145
|
+
* Check if string is a function type
|
|
146
|
+
*/
|
|
147
|
+
static isFunctionType(fieldType: string): boolean {
|
|
148
|
+
const type = fieldType.endsWith("?") ? fieldType.slice(0, -1) : fieldType;
|
|
149
|
+
return type.startsWith("fn(") && type.endsWith(")");
|
|
150
|
+
}
|
|
151
|
+
|
|
140
152
|
/**
|
|
141
153
|
* Check if string has constraints (contains parentheses)
|
|
142
154
|
*/
|
|
@@ -184,7 +196,9 @@ export class TypeGuards {
|
|
|
184
196
|
* Check if value is a valid UUID format
|
|
185
197
|
*/
|
|
186
198
|
static isValidUuid(value: string): boolean {
|
|
187
|
-
return /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(
|
|
199
|
+
return /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(
|
|
200
|
+
value
|
|
201
|
+
);
|
|
188
202
|
}
|
|
189
203
|
|
|
190
204
|
/**
|
|
@@ -44,7 +44,7 @@ export class SecurityValidators {
|
|
|
44
44
|
// security schema with more comprehensive protections
|
|
45
45
|
this.ajv.addSchema(
|
|
46
46
|
{
|
|
47
|
-
$id: "https://nehonix.
|
|
47
|
+
$id: "https://nehonix.com/lib/v/reliant-type",
|
|
48
48
|
type: ["object", "array", "string", "number", "boolean", "null"],
|
|
49
49
|
definitions: {
|
|
50
50
|
secureObject: {
|
|
@@ -189,6 +189,10 @@ export interface SchemaOptions {
|
|
|
189
189
|
enableOptimizations?: boolean;
|
|
190
190
|
cacheValidation?: boolean;
|
|
191
191
|
skipOptimization?: boolean; // Skip optimization to prevent circular dependency
|
|
192
|
+
|
|
193
|
+
// Metadata options
|
|
194
|
+
name?: string;
|
|
195
|
+
description?: string;
|
|
192
196
|
}
|
|
193
197
|
|
|
194
198
|
// Helper type for schemas that allow unknown properties
|
package/src/core/utils/Mod.ts
CHANGED
|
@@ -176,8 +176,22 @@ export class Mod {
|
|
|
176
176
|
|
|
177
177
|
const partialDefinition: SchemaInterface = {};
|
|
178
178
|
for (const [key, value] of Object.entries(definition)) {
|
|
179
|
-
if (typeof value === "string"
|
|
180
|
-
partialDefinition[key] = value + "?";
|
|
179
|
+
if (typeof value === "string") {
|
|
180
|
+
partialDefinition[key] = value.endsWith("?") ? value : value + "?";
|
|
181
|
+
} else if (
|
|
182
|
+
typeof value === "object" &&
|
|
183
|
+
value !== null &&
|
|
184
|
+
!Array.isArray(value)
|
|
185
|
+
) {
|
|
186
|
+
// For objects, wrap in optional schema interface if not already optional
|
|
187
|
+
if ("optional" in value && (value as any).optional === true) {
|
|
188
|
+
partialDefinition[key] = value;
|
|
189
|
+
} else {
|
|
190
|
+
partialDefinition[key] = {
|
|
191
|
+
schema: value,
|
|
192
|
+
optional: true,
|
|
193
|
+
};
|
|
194
|
+
}
|
|
181
195
|
} else {
|
|
182
196
|
partialDefinition[key] = value;
|
|
183
197
|
}
|
|
@@ -195,8 +209,25 @@ export class Mod {
|
|
|
195
209
|
|
|
196
210
|
const requiredDefinition: SchemaInterface = {};
|
|
197
211
|
for (const [key, value] of Object.entries(definition)) {
|
|
198
|
-
if (typeof value === "string"
|
|
199
|
-
requiredDefinition[key] = value.
|
|
212
|
+
if (typeof value === "string") {
|
|
213
|
+
requiredDefinition[key] = value.endsWith("?")
|
|
214
|
+
? value.slice(0, -1)
|
|
215
|
+
: value;
|
|
216
|
+
} else if (
|
|
217
|
+
typeof value === "object" &&
|
|
218
|
+
value !== null &&
|
|
219
|
+
!Array.isArray(value)
|
|
220
|
+
) {
|
|
221
|
+
// Unwrap optional schema interface if present
|
|
222
|
+
if (
|
|
223
|
+
"optional" in value &&
|
|
224
|
+
(value as any).optional === true &&
|
|
225
|
+
"schema" in value
|
|
226
|
+
) {
|
|
227
|
+
requiredDefinition[key] = (value as any).schema;
|
|
228
|
+
} else {
|
|
229
|
+
requiredDefinition[key] = value;
|
|
230
|
+
}
|
|
200
231
|
} else {
|
|
201
232
|
requiredDefinition[key] = value;
|
|
202
233
|
}
|
package/src/index.ts
CHANGED
|
@@ -122,3 +122,12 @@ export type {
|
|
|
122
122
|
SchemaType,
|
|
123
123
|
SchemaConfig,
|
|
124
124
|
} from "./core/types/types";
|
|
125
|
+
import { InterfaceSchema } from "./core/schema/mode/interfaces/InterfaceSchema";
|
|
126
|
+
import { TSGenerator } from "./core/schema/extensions/mods/typescript-generator";
|
|
127
|
+
|
|
128
|
+
// Register the TypeScript generator for InterfaceSchema to enable .types property
|
|
129
|
+
InterfaceSchema.tsGenerator = (definition, options) => {
|
|
130
|
+
return TSGenerator.generateInterface(definition, {
|
|
131
|
+
exportName: options?.name || "Schema"
|
|
132
|
+
});
|
|
133
|
+
};
|