reliant-type 1.0.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 +21 -0
- package/README.md +1305 -0
- package/dist/cjs/constants/SECURITY_CONSTANTS.js +49 -0
- package/dist/cjs/constants/SECURITY_CONSTANTS.js.map +1 -0
- package/dist/cjs/constants/VALIDATION_CONSTANTS.js +103 -0
- package/dist/cjs/constants/VALIDATION_CONSTANTS.js.map +1 -0
- package/dist/cjs/core/schema/extensions/SmartInference.js +200 -0
- package/dist/cjs/core/schema/extensions/SmartInference.js.map +1 -0
- package/dist/cjs/core/schema/extensions/components/AutoDocumentation/Docs.js +100 -0
- package/dist/cjs/core/schema/extensions/components/AutoDocumentation/Docs.js.map +1 -0
- package/dist/cjs/core/schema/extensions/components/AutoDocumentation/DocumentationGenerator.js +192 -0
- package/dist/cjs/core/schema/extensions/components/AutoDocumentation/DocumentationGenerator.js.map +1 -0
- package/dist/cjs/core/schema/extensions/components/AutoDocumentation/InteractiveDocumentationGenerator.js +174 -0
- package/dist/cjs/core/schema/extensions/components/AutoDocumentation/InteractiveDocumentationGenerator.js.map +1 -0
- package/dist/cjs/core/schema/extensions/components/AutoDocumentation/SchemaAnalyzer.js +54 -0
- package/dist/cjs/core/schema/extensions/components/AutoDocumentation/SchemaAnalyzer.js.map +1 -0
- package/dist/cjs/core/schema/extensions/components/AutoDocumentation/TypeScriptGenerator.js +50 -0
- package/dist/cjs/core/schema/extensions/components/AutoDocumentation/TypeScriptGenerator.js.map +1 -0
- package/dist/cjs/core/schema/extensions/components/AutoDocumentation/index.js +15 -0
- package/dist/cjs/core/schema/extensions/components/AutoDocumentation/index.js.map +1 -0
- package/dist/cjs/core/schema/extensions/components/ConditionalValidation/ConditionalBuilder.js +70 -0
- package/dist/cjs/core/schema/extensions/components/ConditionalValidation/ConditionalBuilder.js.map +1 -0
- package/dist/cjs/core/schema/extensions/components/ConditionalValidation/ConditionalElse.js +40 -0
- package/dist/cjs/core/schema/extensions/components/ConditionalValidation/ConditionalElse.js.map +1 -0
- package/dist/cjs/core/schema/extensions/components/ConditionalValidation/ConditionalThen.js +19 -0
- package/dist/cjs/core/schema/extensions/components/ConditionalValidation/ConditionalThen.js.map +1 -0
- package/dist/cjs/core/schema/extensions/components/ConditionalValidation/MultiConditionalBuilder.js +20 -0
- package/dist/cjs/core/schema/extensions/components/ConditionalValidation/MultiConditionalBuilder.js.map +1 -0
- package/dist/cjs/core/schema/extensions/components/ConditionalValidation/MultiConditionalThen.js +49 -0
- package/dist/cjs/core/schema/extensions/components/ConditionalValidation/MultiConditionalThen.js.map +1 -0
- package/dist/cjs/core/schema/extensions/components/ConditionalValidation/index.js +98 -0
- package/dist/cjs/core/schema/extensions/components/ConditionalValidation/index.js.map +1 -0
- package/dist/cjs/core/schema/extensions/components/RealtimeValidation/FormValidator.js +85 -0
- package/dist/cjs/core/schema/extensions/components/RealtimeValidation/FormValidator.js.map +1 -0
- package/dist/cjs/core/schema/extensions/components/RealtimeValidation/LiveValidator.js +133 -0
- package/dist/cjs/core/schema/extensions/components/RealtimeValidation/LiveValidator.js.map +1 -0
- package/dist/cjs/core/schema/extensions/components/RealtimeValidation/StreamValidator.js +351 -0
- package/dist/cjs/core/schema/extensions/components/RealtimeValidation/StreamValidator.js.map +1 -0
- package/dist/cjs/core/schema/extensions/components/RealtimeValidation/index.js +109 -0
- package/dist/cjs/core/schema/extensions/components/RealtimeValidation/index.js.map +1 -0
- package/dist/cjs/core/schema/extensions/index.js +61 -0
- package/dist/cjs/core/schema/extensions/index.js.map +1 -0
- package/dist/cjs/core/schema/extensions/mods/index.js +51 -0
- package/dist/cjs/core/schema/extensions/mods/index.js.map +1 -0
- package/dist/cjs/core/schema/extensions/mods/openapi-converter.js +227 -0
- package/dist/cjs/core/schema/extensions/mods/openapi-converter.js.map +1 -0
- package/dist/cjs/core/schema/extensions/mods/typescript-generator.js +287 -0
- package/dist/cjs/core/schema/extensions/mods/typescript-generator.js.map +1 -0
- package/dist/cjs/core/schema/extensions/mods/validation-engine.js +224 -0
- package/dist/cjs/core/schema/extensions/mods/validation-engine.js.map +1 -0
- package/dist/cjs/core/schema/mode/interfaces/Interface.js +277 -0
- package/dist/cjs/core/schema/mode/interfaces/Interface.js.map +1 -0
- package/dist/cjs/core/schema/mode/interfaces/InterfaceSchema.js +1431 -0
- package/dist/cjs/core/schema/mode/interfaces/InterfaceSchema.js.map +1 -0
- package/dist/cjs/core/schema/mode/interfaces/conditional/evaluator/ConditionalEvaluator.js +520 -0
- package/dist/cjs/core/schema/mode/interfaces/conditional/evaluator/ConditionalEvaluator.js.map +1 -0
- package/dist/cjs/core/schema/mode/interfaces/conditional/parser/ConditionalAST.js +624 -0
- package/dist/cjs/core/schema/mode/interfaces/conditional/parser/ConditionalAST.js.map +1 -0
- package/dist/cjs/core/schema/mode/interfaces/conditional/parser/ConditionalLexer.js +811 -0
- package/dist/cjs/core/schema/mode/interfaces/conditional/parser/ConditionalLexer.js.map +1 -0
- package/dist/cjs/core/schema/mode/interfaces/conditional/parser/ConditionalParser.js +599 -0
- package/dist/cjs/core/schema/mode/interfaces/conditional/parser/ConditionalParser.js.map +1 -0
- package/dist/cjs/core/schema/mode/interfaces/conditional/types/ConditionalTypes.js +89 -0
- package/dist/cjs/core/schema/mode/interfaces/conditional/types/ConditionalTypes.js.map +1 -0
- package/dist/cjs/core/schema/mode/interfaces/errors/ErrorHandler.js +356 -0
- package/dist/cjs/core/schema/mode/interfaces/errors/ErrorHandler.js.map +1 -0
- package/dist/cjs/core/schema/mode/interfaces/errors/types/errors.type.js +80 -0
- package/dist/cjs/core/schema/mode/interfaces/errors/types/errors.type.js.map +1 -0
- package/dist/cjs/core/schema/mode/interfaces/precompilation/FieldPrecompilers.js +778 -0
- package/dist/cjs/core/schema/mode/interfaces/precompilation/FieldPrecompilers.js.map +1 -0
- package/dist/cjs/core/schema/mode/interfaces/precompilation/SchemaPrecompiler.js +523 -0
- package/dist/cjs/core/schema/mode/interfaces/precompilation/SchemaPrecompiler.js.map +1 -0
- package/dist/cjs/core/schema/mode/interfaces/typescript/ConditionalTypes.js +681 -0
- package/dist/cjs/core/schema/mode/interfaces/typescript/ConditionalTypes.js.map +1 -0
- package/dist/cjs/core/schema/mode/interfaces/typescript/IDESupport.js +430 -0
- package/dist/cjs/core/schema/mode/interfaces/typescript/IDESupport.js.map +1 -0
- package/dist/cjs/core/schema/mode/interfaces/typescript/TypeInference.js +225 -0
- package/dist/cjs/core/schema/mode/interfaces/typescript/TypeInference.js.map +1 -0
- package/dist/cjs/core/schema/mode/interfaces/typescript/index.js +44 -0
- package/dist/cjs/core/schema/mode/interfaces/typescript/index.js.map +1 -0
- package/dist/cjs/core/schema/mode/interfaces/validators/ConstraintParser.js +1134 -0
- package/dist/cjs/core/schema/mode/interfaces/validators/ConstraintParser.js.map +1 -0
- package/dist/cjs/core/schema/mode/interfaces/validators/TypeGuards.js +256 -0
- package/dist/cjs/core/schema/mode/interfaces/validators/TypeGuards.js.map +1 -0
- package/dist/cjs/core/schema/mode/interfaces/validators/TypeValidators.js +429 -0
- package/dist/cjs/core/schema/mode/interfaces/validators/TypeValidators.js.map +1 -0
- package/dist/cjs/core/schema/mode/interfaces/validators/UnionCache.js +404 -0
- package/dist/cjs/core/schema/mode/interfaces/validators/UnionCache.js.map +1 -0
- package/dist/cjs/core/schema/mode/interfaces/validators/ValidationHelpers.js +851 -0
- package/dist/cjs/core/schema/mode/interfaces/validators/ValidationHelpers.js.map +1 -0
- package/dist/cjs/core/schema/mode/interfaces/validators/mods/passValidator.js +262 -0
- package/dist/cjs/core/schema/mode/interfaces/validators/mods/passValidator.js.map +1 -0
- package/dist/cjs/core/schema/mode/interfaces/validators/mods/securityValidator.js +887 -0
- package/dist/cjs/core/schema/mode/interfaces/validators/mods/securityValidator.js.map +1 -0
- package/dist/cjs/core/schema/mode/interfaces/validators/mods/urlValidation.js +191 -0
- package/dist/cjs/core/schema/mode/interfaces/validators/mods/urlValidation.js.map +1 -0
- package/dist/cjs/core/schema/optimization/ObjectValidationCache.js +462 -0
- package/dist/cjs/core/schema/optimization/ObjectValidationCache.js.map +1 -0
- package/dist/cjs/core/schema/optimization/PerformanceMonitor.js +773 -0
- package/dist/cjs/core/schema/optimization/PerformanceMonitor.js.map +1 -0
- package/dist/cjs/core/schema/optimization/SchemaCompiler.js +600 -0
- package/dist/cjs/core/schema/optimization/SchemaCompiler.js.map +1 -0
- package/dist/cjs/core/types/ValidatorTypes.js +70 -0
- package/dist/cjs/core/types/ValidatorTypes.js.map +1 -0
- package/dist/cjs/core/types/parser.type.js +12 -0
- package/dist/cjs/core/types/parser.type.js.map +1 -0
- package/dist/cjs/core/utils/Make.js +61 -0
- package/dist/cjs/core/utils/Make.js.map +1 -0
- package/dist/cjs/core/utils/Mod.js +1033 -0
- package/dist/cjs/core/utils/Mod.js.map +1 -0
- package/dist/cjs/core/utils/UrlArgs.js +102 -0
- package/dist/cjs/core/utils/UrlArgs.js.map +1 -0
- package/dist/cjs/core/utils/arrayToEnum.js +18 -0
- package/dist/cjs/core/utils/arrayToEnum.js.map +1 -0
- package/dist/cjs/core/utils/createUrlArgsEnumFArray.js +13 -0
- package/dist/cjs/core/utils/createUrlArgsEnumFArray.js.map +1 -0
- package/dist/cjs/core/utils/securityHelpers.js +215 -0
- package/dist/cjs/core/utils/securityHelpers.js.map +1 -0
- package/dist/cjs/core/utils/securityValidatorHelpers.js +65 -0
- package/dist/cjs/core/utils/securityValidatorHelpers.js.map +1 -0
- package/dist/cjs/index.js +31 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/cjs/package.json +3 -0
- package/dist/esm/constants/SECURITY_CONSTANTS.js +47 -0
- package/dist/esm/constants/SECURITY_CONSTANTS.js.map +1 -0
- package/dist/esm/constants/VALIDATION_CONSTANTS.js +98 -0
- package/dist/esm/constants/VALIDATION_CONSTANTS.js.map +1 -0
- package/dist/esm/core/schema/extensions/SmartInference.js +197 -0
- package/dist/esm/core/schema/extensions/SmartInference.js.map +1 -0
- package/dist/esm/core/schema/extensions/components/AutoDocumentation/Docs.js +98 -0
- package/dist/esm/core/schema/extensions/components/AutoDocumentation/Docs.js.map +1 -0
- package/dist/esm/core/schema/extensions/components/AutoDocumentation/DocumentationGenerator.js +190 -0
- package/dist/esm/core/schema/extensions/components/AutoDocumentation/DocumentationGenerator.js.map +1 -0
- package/dist/esm/core/schema/extensions/components/AutoDocumentation/InteractiveDocumentationGenerator.js +172 -0
- package/dist/esm/core/schema/extensions/components/AutoDocumentation/InteractiveDocumentationGenerator.js.map +1 -0
- package/dist/esm/core/schema/extensions/components/AutoDocumentation/SchemaAnalyzer.js +52 -0
- package/dist/esm/core/schema/extensions/components/AutoDocumentation/SchemaAnalyzer.js.map +1 -0
- package/dist/esm/core/schema/extensions/components/AutoDocumentation/TypeScriptGenerator.js +48 -0
- package/dist/esm/core/schema/extensions/components/AutoDocumentation/TypeScriptGenerator.js.map +1 -0
- package/dist/esm/core/schema/extensions/components/AutoDocumentation/index.js +12 -0
- package/dist/esm/core/schema/extensions/components/AutoDocumentation/index.js.map +1 -0
- package/dist/esm/core/schema/extensions/components/ConditionalValidation/ConditionalBuilder.js +68 -0
- package/dist/esm/core/schema/extensions/components/ConditionalValidation/ConditionalBuilder.js.map +1 -0
- package/dist/esm/core/schema/extensions/components/ConditionalValidation/ConditionalElse.js +38 -0
- package/dist/esm/core/schema/extensions/components/ConditionalValidation/ConditionalElse.js.map +1 -0
- package/dist/esm/core/schema/extensions/components/ConditionalValidation/ConditionalThen.js +17 -0
- package/dist/esm/core/schema/extensions/components/ConditionalValidation/ConditionalThen.js.map +1 -0
- package/dist/esm/core/schema/extensions/components/ConditionalValidation/MultiConditionalBuilder.js +18 -0
- package/dist/esm/core/schema/extensions/components/ConditionalValidation/MultiConditionalBuilder.js.map +1 -0
- package/dist/esm/core/schema/extensions/components/ConditionalValidation/MultiConditionalThen.js +45 -0
- package/dist/esm/core/schema/extensions/components/ConditionalValidation/MultiConditionalThen.js.map +1 -0
- package/dist/esm/core/schema/extensions/components/ConditionalValidation/index.js +92 -0
- package/dist/esm/core/schema/extensions/components/ConditionalValidation/index.js.map +1 -0
- package/dist/esm/core/schema/extensions/components/RealtimeValidation/FormValidator.js +83 -0
- package/dist/esm/core/schema/extensions/components/RealtimeValidation/FormValidator.js.map +1 -0
- package/dist/esm/core/schema/extensions/components/RealtimeValidation/LiveValidator.js +131 -0
- package/dist/esm/core/schema/extensions/components/RealtimeValidation/LiveValidator.js.map +1 -0
- package/dist/esm/core/schema/extensions/components/RealtimeValidation/StreamValidator.js +349 -0
- package/dist/esm/core/schema/extensions/components/RealtimeValidation/StreamValidator.js.map +1 -0
- package/dist/esm/core/schema/extensions/components/RealtimeValidation/index.js +103 -0
- package/dist/esm/core/schema/extensions/components/RealtimeValidation/index.js.map +1 -0
- package/dist/esm/core/schema/extensions/index.js +53 -0
- package/dist/esm/core/schema/extensions/index.js.map +1 -0
- package/dist/esm/core/schema/extensions/mods/index.js +47 -0
- package/dist/esm/core/schema/extensions/mods/index.js.map +1 -0
- package/dist/esm/core/schema/extensions/mods/openapi-converter.js +225 -0
- package/dist/esm/core/schema/extensions/mods/openapi-converter.js.map +1 -0
- package/dist/esm/core/schema/extensions/mods/typescript-generator.js +284 -0
- package/dist/esm/core/schema/extensions/mods/typescript-generator.js.map +1 -0
- package/dist/esm/core/schema/extensions/mods/validation-engine.js +222 -0
- package/dist/esm/core/schema/extensions/mods/validation-engine.js.map +1 -0
- package/dist/esm/core/schema/mode/interfaces/Interface.js +269 -0
- package/dist/esm/core/schema/mode/interfaces/Interface.js.map +1 -0
- package/dist/esm/core/schema/mode/interfaces/InterfaceSchema.js +1429 -0
- package/dist/esm/core/schema/mode/interfaces/InterfaceSchema.js.map +1 -0
- package/dist/esm/core/schema/mode/interfaces/conditional/evaluator/ConditionalEvaluator.js +518 -0
- package/dist/esm/core/schema/mode/interfaces/conditional/evaluator/ConditionalEvaluator.js.map +1 -0
- package/dist/esm/core/schema/mode/interfaces/conditional/parser/ConditionalAST.js +620 -0
- package/dist/esm/core/schema/mode/interfaces/conditional/parser/ConditionalAST.js.map +1 -0
- package/dist/esm/core/schema/mode/interfaces/conditional/parser/ConditionalLexer.js +809 -0
- package/dist/esm/core/schema/mode/interfaces/conditional/parser/ConditionalLexer.js.map +1 -0
- package/dist/esm/core/schema/mode/interfaces/conditional/parser/ConditionalParser.js +597 -0
- package/dist/esm/core/schema/mode/interfaces/conditional/parser/ConditionalParser.js.map +1 -0
- package/dist/esm/core/schema/mode/interfaces/conditional/types/ConditionalTypes.js +89 -0
- package/dist/esm/core/schema/mode/interfaces/conditional/types/ConditionalTypes.js.map +1 -0
- package/dist/esm/core/schema/mode/interfaces/errors/ErrorHandler.js +354 -0
- package/dist/esm/core/schema/mode/interfaces/errors/ErrorHandler.js.map +1 -0
- package/dist/esm/core/schema/mode/interfaces/errors/types/errors.type.js +80 -0
- package/dist/esm/core/schema/mode/interfaces/errors/types/errors.type.js.map +1 -0
- package/dist/esm/core/schema/mode/interfaces/precompilation/FieldPrecompilers.js +776 -0
- package/dist/esm/core/schema/mode/interfaces/precompilation/FieldPrecompilers.js.map +1 -0
- package/dist/esm/core/schema/mode/interfaces/precompilation/SchemaPrecompiler.js +521 -0
- package/dist/esm/core/schema/mode/interfaces/precompilation/SchemaPrecompiler.js.map +1 -0
- package/dist/esm/core/schema/mode/interfaces/typescript/ConditionalTypes.js +681 -0
- package/dist/esm/core/schema/mode/interfaces/typescript/ConditionalTypes.js.map +1 -0
- package/dist/esm/core/schema/mode/interfaces/typescript/IDESupport.js +428 -0
- package/dist/esm/core/schema/mode/interfaces/typescript/IDESupport.js.map +1 -0
- package/dist/esm/core/schema/mode/interfaces/typescript/TypeInference.js +223 -0
- package/dist/esm/core/schema/mode/interfaces/typescript/TypeInference.js.map +1 -0
- package/dist/esm/core/schema/mode/interfaces/typescript/index.js +35 -0
- package/dist/esm/core/schema/mode/interfaces/typescript/index.js.map +1 -0
- package/dist/esm/core/schema/mode/interfaces/validators/ConstraintParser.js +1132 -0
- package/dist/esm/core/schema/mode/interfaces/validators/ConstraintParser.js.map +1 -0
- package/dist/esm/core/schema/mode/interfaces/validators/TypeGuards.js +254 -0
- package/dist/esm/core/schema/mode/interfaces/validators/TypeGuards.js.map +1 -0
- package/dist/esm/core/schema/mode/interfaces/validators/TypeValidators.js +427 -0
- package/dist/esm/core/schema/mode/interfaces/validators/TypeValidators.js.map +1 -0
- package/dist/esm/core/schema/mode/interfaces/validators/UnionCache.js +400 -0
- package/dist/esm/core/schema/mode/interfaces/validators/UnionCache.js.map +1 -0
- package/dist/esm/core/schema/mode/interfaces/validators/ValidationHelpers.js +849 -0
- package/dist/esm/core/schema/mode/interfaces/validators/ValidationHelpers.js.map +1 -0
- package/dist/esm/core/schema/mode/interfaces/validators/mods/passValidator.js +260 -0
- package/dist/esm/core/schema/mode/interfaces/validators/mods/passValidator.js.map +1 -0
- package/dist/esm/core/schema/mode/interfaces/validators/mods/securityValidator.js +881 -0
- package/dist/esm/core/schema/mode/interfaces/validators/mods/securityValidator.js.map +1 -0
- package/dist/esm/core/schema/mode/interfaces/validators/mods/urlValidation.js +189 -0
- package/dist/esm/core/schema/mode/interfaces/validators/mods/urlValidation.js.map +1 -0
- package/dist/esm/core/schema/optimization/ObjectValidationCache.js +460 -0
- package/dist/esm/core/schema/optimization/ObjectValidationCache.js.map +1 -0
- package/dist/esm/core/schema/optimization/PerformanceMonitor.js +771 -0
- package/dist/esm/core/schema/optimization/PerformanceMonitor.js.map +1 -0
- package/dist/esm/core/schema/optimization/SchemaCompiler.js +598 -0
- package/dist/esm/core/schema/optimization/SchemaCompiler.js.map +1 -0
- package/dist/esm/core/types/ValidatorTypes.js +65 -0
- package/dist/esm/core/types/ValidatorTypes.js.map +1 -0
- package/dist/esm/core/types/parser.type.js +12 -0
- package/dist/esm/core/types/parser.type.js.map +1 -0
- package/dist/esm/core/utils/Make.js +59 -0
- package/dist/esm/core/utils/Make.js.map +1 -0
- package/dist/esm/core/utils/Mod.js +1031 -0
- package/dist/esm/core/utils/Mod.js.map +1 -0
- package/dist/esm/core/utils/UrlArgs.js +98 -0
- package/dist/esm/core/utils/UrlArgs.js.map +1 -0
- package/dist/esm/core/utils/arrayToEnum.js +16 -0
- package/dist/esm/core/utils/arrayToEnum.js.map +1 -0
- package/dist/esm/core/utils/createUrlArgsEnumFArray.js +11 -0
- package/dist/esm/core/utils/createUrlArgsEnumFArray.js.map +1 -0
- package/dist/esm/core/utils/securityHelpers.js +207 -0
- package/dist/esm/core/utils/securityHelpers.js.map +1 -0
- package/dist/esm/core/utils/securityValidatorHelpers.js +62 -0
- package/dist/esm/core/utils/securityValidatorHelpers.js.map +1 -0
- package/dist/esm/index.js +12 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/package.json +3 -0
- package/dist/schema.d.ts +2595 -0
- package/docs/ALL_TYPES.md +120 -0
- package/docs/API-STABILITY.md +336 -0
- package/docs/CONDITIONAL-VALIDATION.md +637 -0
- package/docs/EXAMPLES.md +876 -0
- package/docs/FIELD-TYPES.md +829 -0
- package/docs/GETTING-STARTED.md +394 -0
- package/docs/LIVE-UTILITY.md +1137 -0
- package/docs/QUICK-REFERENCE.md +346 -0
- package/docs/README.md +205 -0
- package/docs/VSCODE-EXTENSION.md +458 -0
- package/package.json +219 -0
- package/src/bench/BENCHMARK-RESULTS.md +211 -0
- package/src/bench/benchmark-results.json +148 -0
- package/src/bench/performance-comparison.ts +218 -0
- package/src/bench/precompilation-benchmark.ts +218 -0
- package/src/constants/SECURITY_CONSTANTS.ts +44 -0
- package/src/constants/VALIDATION_CONSTANTS.ts +176 -0
- package/src/core/README.md +395 -0
- package/src/core/compiler/SchemaTransformer.ts +279 -0
- package/src/core/compiler/TypeAnalyzer.ts +378 -0
- package/src/core/compiler/TypeScriptCompilerIntegration.ts +220 -0
- package/src/core/compiler/TypeToSchemaConverter.ts +288 -0
- package/src/core/index.ts +70 -0
- package/src/core/schema/extensions/AutoDocumentation.ts +572 -0
- package/src/core/schema/extensions/ConditionalValidation.ts +330 -0
- package/src/core/schema/extensions/README.md +171 -0
- package/src/core/schema/extensions/RealtimeValidation.ts +656 -0
- package/src/core/schema/extensions/SmartInference.ts +224 -0
- package/src/core/schema/extensions/components/AutoDocumentation/Docs.ts +98 -0
- package/src/core/schema/extensions/components/AutoDocumentation/DocumentationGenerator.ts +201 -0
- package/src/core/schema/extensions/components/AutoDocumentation/InteractiveDocumentationGenerator.ts +176 -0
- package/src/core/schema/extensions/components/AutoDocumentation/OpenAPIGenerator.ts +175 -0
- package/src/core/schema/extensions/components/AutoDocumentation/SchemaAnalyzer.ts +49 -0
- package/src/core/schema/extensions/components/AutoDocumentation/TypeScriptGenerator.ts +54 -0
- package/src/core/schema/extensions/components/AutoDocumentation/index.ts +17 -0
- package/src/core/schema/extensions/components/ConditionalValidation/ConditionalBuilder.ts +101 -0
- package/src/core/schema/extensions/components/ConditionalValidation/ConditionalElse.ts +65 -0
- package/src/core/schema/extensions/components/ConditionalValidation/ConditionalThen.ts +33 -0
- package/src/core/schema/extensions/components/ConditionalValidation/Extend.ts +75 -0
- package/src/core/schema/extensions/components/ConditionalValidation/MultiConditionalBuilder.ts +16 -0
- package/src/core/schema/extensions/components/ConditionalValidation/MultiConditionalThen.ts +50 -0
- package/src/core/schema/extensions/components/ConditionalValidation/index.ts +104 -0
- package/src/core/schema/extensions/components/RealtimeValidation/FormValidator.ts +88 -0
- package/src/core/schema/extensions/components/RealtimeValidation/LiveValidator.ts +171 -0
- package/src/core/schema/extensions/components/RealtimeValidation/StreamValidator.ts +397 -0
- package/src/core/schema/extensions/components/RealtimeValidation/index.ts +114 -0
- package/src/core/schema/extensions/index.ts +76 -0
- package/src/core/schema/extensions/mods/index.ts +131 -0
- package/src/core/schema/extensions/mods/openapi-converter.ts +338 -0
- package/src/core/schema/extensions/mods/typescript-generator.ts +379 -0
- package/src/core/schema/extensions/mods/validation-engine.ts +295 -0
- package/src/core/schema/mode/interfaces/Interface.ts +364 -0
- package/src/core/schema/mode/interfaces/InterfaceSchema.ts +1838 -0
- package/src/core/schema/mode/interfaces/README.md +278 -0
- package/src/core/schema/mode/interfaces/conditional/evaluator/ConditionalEvaluator.ts +657 -0
- package/src/core/schema/mode/interfaces/conditional/parser/ConditionalAST.ts +826 -0
- package/src/core/schema/mode/interfaces/conditional/parser/ConditionalLexer.ts +992 -0
- package/src/core/schema/mode/interfaces/conditional/parser/ConditionalParser.ts +803 -0
- package/src/core/schema/mode/interfaces/conditional/parser/readme.md +406 -0
- package/src/core/schema/mode/interfaces/conditional/types/ConditionalTypes.ts +273 -0
- package/src/core/schema/mode/interfaces/errors/ErrorHandler.ts +624 -0
- package/src/core/schema/mode/interfaces/errors/types/errors.type.ts +102 -0
- package/src/core/schema/mode/interfaces/precompilation/FieldPrecompilers.ts +962 -0
- package/src/core/schema/mode/interfaces/precompilation/SchemaPrecompiler.ts +667 -0
- package/src/core/schema/mode/interfaces/typescript/ConditionalTypes.ts +1534 -0
- package/src/core/schema/mode/interfaces/typescript/IDESupport.ts +534 -0
- package/src/core/schema/mode/interfaces/typescript/TypeInference.ts +737 -0
- package/src/core/schema/mode/interfaces/typescript/index.ts +92 -0
- package/src/core/schema/mode/interfaces/validators/ConstraintParser.ts +1438 -0
- package/src/core/schema/mode/interfaces/validators/EnhancedErrorReporting.ts +227 -0
- package/src/core/schema/mode/interfaces/validators/TypeGuards.ts +288 -0
- package/src/core/schema/mode/interfaces/validators/TypeValidators.ts +660 -0
- package/src/core/schema/mode/interfaces/validators/UnionCache.ts +508 -0
- package/src/core/schema/mode/interfaces/validators/ValidationHelpers.ts +1257 -0
- package/src/core/schema/mode/interfaces/validators/index.ts +21 -0
- package/src/core/schema/mode/interfaces/validators/mods/passValidator.ts +424 -0
- package/src/core/schema/mode/interfaces/validators/mods/securityValidator.ts +1634 -0
- package/src/core/schema/mode/interfaces/validators/mods/urlValidation.ts +333 -0
- package/src/core/schema/optimization/ObjectValidationCache.ts +560 -0
- package/src/core/schema/optimization/PerformanceInitializer.ts +188 -0
- package/src/core/schema/optimization/PerformanceMonitor.ts +898 -0
- package/src/core/schema/optimization/SchemaCompiler.ts +730 -0
- package/src/core/testing/TestDataGenerator.ts +590 -0
- package/src/core/types/SchemaValidator.type.ts +210 -0
- package/src/core/types/ValidatorTypes.ts +93 -0
- package/src/core/types/extension.type.ts +109 -0
- package/src/core/types/objValidationCache.ts +17 -0
- package/src/core/types/parser.type.ts +15 -0
- package/src/core/types/perfoMonitor.ts +37 -0
- package/src/core/types/scompiler.ts +22 -0
- package/src/core/types/securityValidator.type.ts +10 -0
- package/src/core/types/types.ts +154 -0
- package/src/core/utils/Make.ts +97 -0
- package/src/core/utils/Mod.ts +1168 -0
- package/src/core/utils/UrlArgs.ts +124 -0
- package/src/core/utils/arrayToEnum.ts +89 -0
- package/src/core/utils/createUrlArgsEnumFArray.ts +11 -0
- package/src/core/utils/securityHelpers.ts +341 -0
- package/src/core/utils/securityValidatorHelpers.ts +76 -0
- package/src/index.ts +124 -0
|
@@ -0,0 +1,1429 @@
|
|
|
1
|
+
import './validators/TypeValidators.js';
|
|
2
|
+
import { ConstraintParser } from './validators/ConstraintParser.js';
|
|
3
|
+
import { TypeGuards } from './validators/TypeGuards.js';
|
|
4
|
+
import { ValidationHelpers } from './validators/ValidationHelpers.js';
|
|
5
|
+
import { ErrorHandler } from './errors/ErrorHandler.js';
|
|
6
|
+
import { ConditionalParser } from './conditional/parser/ConditionalParser.js';
|
|
7
|
+
import { ConditionalEvaluator } from './conditional/evaluator/ConditionalEvaluator.js';
|
|
8
|
+
import { SchemaCompiler } from '../../optimization/SchemaCompiler.js';
|
|
9
|
+
import { ObjectValidationCache } from '../../optimization/ObjectValidationCache.js';
|
|
10
|
+
import { PerformanceMonitor } from '../../optimization/PerformanceMonitor.js';
|
|
11
|
+
import { OptimizationLevel, SchemaPrecompiler } from './precompilation/SchemaPrecompiler.js';
|
|
12
|
+
import { MAX_OBJECT_DEPTH } from '../../../../constants/VALIDATION_CONSTANTS.js';
|
|
13
|
+
import { SchemaValidationError } from './Interface.js';
|
|
14
|
+
import { ErrorCode } from './errors/types/errors.type.js';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* TypeScript Interface-like Schema Definition System
|
|
18
|
+
*
|
|
19
|
+
* Allows defining schemas using TypeScript-like syntax with string literals
|
|
20
|
+
* that feel natural and are much easier to read and write.
|
|
21
|
+
*/
|
|
22
|
+
/**
|
|
23
|
+
* Interface Schema class for TypeScript-like schema definitions
|
|
24
|
+
*/
|
|
25
|
+
class InterfaceSchema {
|
|
26
|
+
constructor(definition, options = {}) {
|
|
27
|
+
this.definition = definition;
|
|
28
|
+
this.options = options;
|
|
29
|
+
this.compiledFields = [];
|
|
30
|
+
this.schemaKeys = [];
|
|
31
|
+
this.schemaComplexity = 0;
|
|
32
|
+
this.isOptimized = false;
|
|
33
|
+
this.optimizationLevel = OptimizationLevel.NONE;
|
|
34
|
+
// Initialize conditional parser
|
|
35
|
+
this.ConditionalParser = new ConditionalParser({
|
|
36
|
+
allowNestedConditionals: true,
|
|
37
|
+
maxNestingDepth: MAX_OBJECT_DEPTH,
|
|
38
|
+
strictMode: false,
|
|
39
|
+
enableDebug: false,
|
|
40
|
+
});
|
|
41
|
+
// ULTRA-OPTIMIZED: Pre-compile schema with advanced optimization
|
|
42
|
+
this.precompileSchema();
|
|
43
|
+
// Apply performance optimizations (skip if requested to prevent circular dependency)
|
|
44
|
+
if (!this.options.skipOptimization) {
|
|
45
|
+
this.applyOptimizations();
|
|
46
|
+
// Create precompiled validator for maximum speed
|
|
47
|
+
this.createPrecompiledValidator();
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Check if a field type uses conditional syntax using secure regex pattern
|
|
52
|
+
*/
|
|
53
|
+
isConditionalSyntax(fieldType) {
|
|
54
|
+
// Secure regex pattern to match: when <condition> *? <thenValue> [: <elseValue>]
|
|
55
|
+
const conditionalPattern = /^\s*when\s+.+?\s*\*\?\s*.+/;
|
|
56
|
+
return conditionalPattern.test(fieldType);
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Apply performance optimizations based on schema characteristics
|
|
60
|
+
*/
|
|
61
|
+
applyOptimizations() {
|
|
62
|
+
// Calculate schema complexity
|
|
63
|
+
this.schemaComplexity = this.calculateComplexity();
|
|
64
|
+
// Check if schema has conditional fields
|
|
65
|
+
const hasConditionalFields = this.compiledFields.some((field) => field.isConditional);
|
|
66
|
+
// Check nesting depth to avoid optimization bugs with deep nested objects
|
|
67
|
+
const maxNestingDepth = this.calculateMaxNestingDepth();
|
|
68
|
+
// debugging optimization decisions:
|
|
69
|
+
//// console.log(`[DEBUG] Schema optimization analysis:
|
|
70
|
+
// - Complexity: ${this.schemaComplexity}
|
|
71
|
+
// - Has conditionals: ${hasConditionalFields}
|
|
72
|
+
// - Max nesting depth: ${maxNestingDepth}
|
|
73
|
+
// - Will use advanced optimization: ${this.schemaComplexity > 15 && !hasConditionalFields && maxNestingDepth <= 3}
|
|
74
|
+
// - Will use caching: ${this.schemaComplexity > 5 && !hasConditionalFields && maxNestingDepth <= 3}`);
|
|
75
|
+
// Apply optimizations based on complexity, but avoid advanced optimizations for conditional fields or deep nesting
|
|
76
|
+
if (this.schemaComplexity > 15 &&
|
|
77
|
+
!hasConditionalFields &&
|
|
78
|
+
maxNestingDepth <= 3) {
|
|
79
|
+
// High complexity, no conditionals, shallow nesting - use advanced optimizations
|
|
80
|
+
this.compiledValidator = SchemaCompiler.compileSchema(this.definition, this.options);
|
|
81
|
+
this.isOptimized = true;
|
|
82
|
+
}
|
|
83
|
+
else if (this.schemaComplexity > 5 &&
|
|
84
|
+
!hasConditionalFields &&
|
|
85
|
+
maxNestingDepth <= 3) {
|
|
86
|
+
// Medium complexity, no conditionals, shallow nesting - use caching
|
|
87
|
+
this.isOptimized = true;
|
|
88
|
+
}
|
|
89
|
+
// Note: Conditional fields or deep nesting use the standard validation path for reliability
|
|
90
|
+
// Start performance monitoring if enabled
|
|
91
|
+
if (this.options.enablePerformanceMonitoring) {
|
|
92
|
+
PerformanceMonitor.startMonitoring();
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Create precompiled validator for maximum speed
|
|
97
|
+
* SAFETY: Now includes recursion protection and cycle detection
|
|
98
|
+
*/
|
|
99
|
+
createPrecompiledValidator() {
|
|
100
|
+
// Only create precompiled validator for non-conditional schemas and non-loose mode
|
|
101
|
+
const hasConditionalFields = this.compiledFields.some((field) => field.isConditional);
|
|
102
|
+
// Check nesting depth to avoid precompilation bugs with deep nested objects
|
|
103
|
+
const maxNestingDepth = this.calculateMaxNestingDepth();
|
|
104
|
+
// CRITICAL FIX: Also check for nested conditional fields
|
|
105
|
+
const hasNestedConditionalFields = this.hasNestedConditionalFields();
|
|
106
|
+
// Skip precompilation if loose mode is enabled (needs type coercion support), deep nesting, or nested conditionals
|
|
107
|
+
if (!hasConditionalFields &&
|
|
108
|
+
!hasNestedConditionalFields &&
|
|
109
|
+
!this.options.loose &&
|
|
110
|
+
maxNestingDepth <= 3) {
|
|
111
|
+
try {
|
|
112
|
+
this.precompiledValidator = SchemaPrecompiler.precompileSchema(this.definition, this.options);
|
|
113
|
+
this.optimizationLevel = this.precompiledValidator._optimizationLevel;
|
|
114
|
+
}
|
|
115
|
+
catch (error) {
|
|
116
|
+
// Fallback to standard validation if precompilation fails
|
|
117
|
+
// console.warn(
|
|
118
|
+
// "Schema precompilation failed, falling back to standard validation:",
|
|
119
|
+
// error
|
|
120
|
+
// );
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Check if schema has nested conditional fields
|
|
126
|
+
* CRITICAL FIX: This prevents precompilation for schemas with nested conditionals
|
|
127
|
+
*/
|
|
128
|
+
hasNestedConditionalFields() {
|
|
129
|
+
const checkObject = (obj) => {
|
|
130
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
131
|
+
if (typeof value === "string") {
|
|
132
|
+
// Check if this field has conditional syntax
|
|
133
|
+
if (this.isConditionalSyntax(value)) {
|
|
134
|
+
return true;
|
|
135
|
+
}
|
|
136
|
+
// Note: Precompilation issues have been fixed for double, positive, negative, and regex patterns
|
|
137
|
+
}
|
|
138
|
+
else if (typeof value === "object" &&
|
|
139
|
+
value !== null &&
|
|
140
|
+
!Array.isArray(value)) {
|
|
141
|
+
// Recursively check nested objects
|
|
142
|
+
if (checkObject(value)) {
|
|
143
|
+
return true;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return false;
|
|
148
|
+
};
|
|
149
|
+
return checkObject(this.definition);
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Calculate schema complexity score
|
|
153
|
+
*/
|
|
154
|
+
calculateComplexity() {
|
|
155
|
+
let complexity = this.compiledFields.length;
|
|
156
|
+
for (const field of this.compiledFields) {
|
|
157
|
+
if (field.isConditional)
|
|
158
|
+
complexity += 5;
|
|
159
|
+
if (field.isArray)
|
|
160
|
+
complexity += 2;
|
|
161
|
+
if (typeof field.originalType === "object")
|
|
162
|
+
complexity += 3;
|
|
163
|
+
}
|
|
164
|
+
return complexity;
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Calculate maximum nesting depth to avoid optimization bugs
|
|
168
|
+
*/
|
|
169
|
+
calculateMaxNestingDepth() {
|
|
170
|
+
const calculateDepth = (obj, currentDepth = 0) => {
|
|
171
|
+
if (typeof obj !== "object" || obj === null || Array.isArray(obj)) {
|
|
172
|
+
return currentDepth;
|
|
173
|
+
}
|
|
174
|
+
let maxDepth = currentDepth;
|
|
175
|
+
for (const value of Object.values(obj)) {
|
|
176
|
+
if (typeof value === "object" &&
|
|
177
|
+
value !== null &&
|
|
178
|
+
!Array.isArray(value)) {
|
|
179
|
+
const depth = calculateDepth(value, currentDepth + 1);
|
|
180
|
+
maxDepth = Math.max(maxDepth, depth);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
return maxDepth;
|
|
184
|
+
};
|
|
185
|
+
return calculateDepth(this.definition);
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Pre-compile schema for faster validation
|
|
189
|
+
*/
|
|
190
|
+
precompileSchema() {
|
|
191
|
+
const entries = Object.entries(this.definition);
|
|
192
|
+
this.schemaKeys = entries.map(([key]) => key);
|
|
193
|
+
this.compiledFields = [];
|
|
194
|
+
for (const [key, fieldType] of entries) {
|
|
195
|
+
const compiled = {
|
|
196
|
+
key,
|
|
197
|
+
originalType: fieldType,
|
|
198
|
+
isString: typeof fieldType === "string",
|
|
199
|
+
isConditional: false,
|
|
200
|
+
};
|
|
201
|
+
if (typeof fieldType === "string") {
|
|
202
|
+
// Check for conditional syntax (when ... *? ... : ...)
|
|
203
|
+
if (this.isConditionalSyntax(fieldType)) {
|
|
204
|
+
compiled.isConditional = true;
|
|
205
|
+
// Parse with parser
|
|
206
|
+
const { ast, errors } = this.ConditionalParser.parse(fieldType);
|
|
207
|
+
if (ast && errors.length === 0) {
|
|
208
|
+
compiled.ConditionalAST = ast;
|
|
209
|
+
}
|
|
210
|
+
else {
|
|
211
|
+
// If parsing fails, treat as regular field type
|
|
212
|
+
// console.warn(
|
|
213
|
+
// `Failed to parse conditional expression: ${fieldType}`,
|
|
214
|
+
// errors
|
|
215
|
+
// );
|
|
216
|
+
const parsed = ConstraintParser.parseConstraints(fieldType);
|
|
217
|
+
compiled.parsedConstraints = parsed;
|
|
218
|
+
compiled.isOptional = parsed.optional;
|
|
219
|
+
// Secure regex pattern to check for array type
|
|
220
|
+
const arrayPattern = /\[\]$/;
|
|
221
|
+
compiled.isArray = arrayPattern.test(parsed.type);
|
|
222
|
+
compiled.elementType = compiled.isArray
|
|
223
|
+
? parsed.type.replace(/\[\]$/, "")
|
|
224
|
+
: parsed.type;
|
|
225
|
+
compiled.isConditional = false;
|
|
226
|
+
compiled.isConditional = false;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
else {
|
|
230
|
+
// Pre-parse constraints for regular field types
|
|
231
|
+
const parsed = ConstraintParser.parseConstraints(fieldType);
|
|
232
|
+
compiled.parsedConstraints = parsed;
|
|
233
|
+
compiled.isOptional = parsed.optional;
|
|
234
|
+
// Secure regex pattern to check for array type
|
|
235
|
+
const arrayPattern = /\[\]$/;
|
|
236
|
+
compiled.isArray = arrayPattern.test(parsed.type);
|
|
237
|
+
compiled.elementType = compiled.isArray
|
|
238
|
+
? parsed.type.replace(/\[\]$/, "")
|
|
239
|
+
: parsed.type;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
else if (TypeGuards.isConditionalValidation(fieldType)) {
|
|
243
|
+
// Object-based conditional validation (keep for backward compatibility)
|
|
244
|
+
compiled.isConditional = true;
|
|
245
|
+
compiled.conditionalConfig = fieldType;
|
|
246
|
+
}
|
|
247
|
+
this.compiledFields.push(compiled);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Validate data against the interface schema - ULTRA-OPTIMIZED version
|
|
252
|
+
*/
|
|
253
|
+
validate(data) {
|
|
254
|
+
const startTime = performance.now();
|
|
255
|
+
const operationId = `schema-${this.schemaComplexity}`;
|
|
256
|
+
let result;
|
|
257
|
+
// Check if schema has conditional fields - if so, force standard validation
|
|
258
|
+
const hasConditionalFields = this.compiledFields.some((field) => field.isConditional);
|
|
259
|
+
// Check for required fields (need standard validation for proper required field handling)
|
|
260
|
+
const hasRequiredFields = this.compiledFields.some((field) => field.parsedConstraints?.required === true);
|
|
261
|
+
// Use precompiled validator first (fastest path)
|
|
262
|
+
// BUT: Skip precompiled validator if loose mode is enabled (needs type coercion)
|
|
263
|
+
// ALSO: Skip ALL optimizations if schema has conditional fields (they need special handling)
|
|
264
|
+
// ALSO: Skip precompiled validator if schema has required fields (they need proper validation)
|
|
265
|
+
if (this.precompiledValidator &&
|
|
266
|
+
!this.options.loose &&
|
|
267
|
+
!hasConditionalFields &&
|
|
268
|
+
!hasRequiredFields) {
|
|
269
|
+
// console.log("using precompiled validator");
|
|
270
|
+
result = this.precompiledValidator(data);
|
|
271
|
+
}
|
|
272
|
+
else if (this.isOptimized &&
|
|
273
|
+
this.compiledValidator &&
|
|
274
|
+
!hasConditionalFields) {
|
|
275
|
+
// console.log("using compiled validator");
|
|
276
|
+
// Use compiled validator (second fastest) - but not for conditional fields
|
|
277
|
+
result = this.compiledValidator.validate(data);
|
|
278
|
+
}
|
|
279
|
+
else if (this.isOptimized &&
|
|
280
|
+
this.schemaComplexity > 5 &&
|
|
281
|
+
!hasConditionalFields) {
|
|
282
|
+
// console.log("using cached validation for medium complexity");
|
|
283
|
+
// Use cached validation for medium complexity - but not for conditional fields
|
|
284
|
+
result = ObjectValidationCache.getCachedValidation(data, (value) => this.validateStandard(value), []);
|
|
285
|
+
}
|
|
286
|
+
else {
|
|
287
|
+
// console.log(
|
|
288
|
+
// "using standard validation for simple schemas or conditional schemas"
|
|
289
|
+
// );
|
|
290
|
+
// Standard validation for simple schemas or conditional schemas
|
|
291
|
+
result = this.validateStandard(data);
|
|
292
|
+
}
|
|
293
|
+
// Record performance metrics
|
|
294
|
+
const duration = performance.now() - startTime;
|
|
295
|
+
PerformanceMonitor.recordOperation(operationId, duration, this.schemaComplexity, this.isOptimized || !!this.precompiledValidator);
|
|
296
|
+
return result;
|
|
297
|
+
}
|
|
298
|
+
/**
|
|
299
|
+
* Standard validation method (original implementation)
|
|
300
|
+
*/
|
|
301
|
+
validateStandard(data) {
|
|
302
|
+
// console.log("validating standard");
|
|
303
|
+
// Fast path for non-objects
|
|
304
|
+
if (typeof data !== "object" || data === null || Array.isArray(data)) {
|
|
305
|
+
return ValidationHelpers.createErrorResult("Expected object", data);
|
|
306
|
+
}
|
|
307
|
+
const validatedData = {};
|
|
308
|
+
const errors = [];
|
|
309
|
+
const warnings = [];
|
|
310
|
+
let hasErrors = false;
|
|
311
|
+
// Apply default values if they exist
|
|
312
|
+
const defaults = this.options?.defaults;
|
|
313
|
+
if (defaults) {
|
|
314
|
+
for (const [key, defaultValue] of Object.entries(defaults)) {
|
|
315
|
+
if (!(key in data) || data[key] === undefined) {
|
|
316
|
+
data = { ...data, [key]: defaultValue };
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
// Use pre-compiled fields for faster validation
|
|
321
|
+
for (let i = 0; i < this.compiledFields.length; i++) {
|
|
322
|
+
const field = this.compiledFields[i];
|
|
323
|
+
const value = data[field.key];
|
|
324
|
+
let fieldResult;
|
|
325
|
+
// Use pre-compiled information to skip parsing
|
|
326
|
+
if (field.isConditional) {
|
|
327
|
+
// console.log("validating conditional field");
|
|
328
|
+
if (field.isConditional && field.ConditionalAST) {
|
|
329
|
+
// FIXED: Use conditional validation with proper nested context
|
|
330
|
+
// Pass the current data object as nested context for field resolution
|
|
331
|
+
fieldResult = this.validateEnhancedConditionalField(field.ConditionalAST, value, data, // Full data for fallback
|
|
332
|
+
data // Nested context (same as data at this level)
|
|
333
|
+
);
|
|
334
|
+
}
|
|
335
|
+
else {
|
|
336
|
+
// console.log("validating legacy conditional field");
|
|
337
|
+
// Use legacy conditional validation
|
|
338
|
+
fieldResult = this.validateConditionalFieldWithContext(field.conditionalConfig, value, data);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
else if (field.isString && field.parsedConstraints) {
|
|
342
|
+
// console.log("validating precompiled string field");
|
|
343
|
+
// Use pre-parsed constraints for string fields
|
|
344
|
+
fieldResult = this.validatePrecompiledStringField(field, value);
|
|
345
|
+
}
|
|
346
|
+
else {
|
|
347
|
+
// console.log("fallback to original validation for complex types");
|
|
348
|
+
// Fallback to original validation for complex types
|
|
349
|
+
fieldResult = this.validateField(field.key, field.originalType, value, data);
|
|
350
|
+
}
|
|
351
|
+
// Process field result
|
|
352
|
+
if (!fieldResult.success) {
|
|
353
|
+
hasErrors = true;
|
|
354
|
+
// Batch error processing with proper path tracking
|
|
355
|
+
for (let j = 0; j < fieldResult.errors.length; j++) {
|
|
356
|
+
const error = fieldResult.errors[j];
|
|
357
|
+
// Convert string errors to ValidationError objects and add field path
|
|
358
|
+
if (typeof error === "string") {
|
|
359
|
+
errors.push(ErrorHandler.createSimpleError(error, [field.key]));
|
|
360
|
+
}
|
|
361
|
+
else if (error && typeof error === "object" && "message" in error) {
|
|
362
|
+
// This is already a ValidationError object, add field to path and enhance message
|
|
363
|
+
const validationError = error;
|
|
364
|
+
const fullPath = [field.key, ...validationError.path];
|
|
365
|
+
// Always use the full path for field context, replacing any existing field context
|
|
366
|
+
let message = validationError.message;
|
|
367
|
+
// Remove any existing field context to avoid duplication
|
|
368
|
+
const fieldContextRegex = / in field "[^"]*"$/;
|
|
369
|
+
message = message.replace(fieldContextRegex, "");
|
|
370
|
+
// Add the complete field path context
|
|
371
|
+
const fieldContext = fullPath.length > 0 ? ` in field "${fullPath.join(".")}"` : "";
|
|
372
|
+
errors.push({
|
|
373
|
+
...validationError,
|
|
374
|
+
path: fullPath,
|
|
375
|
+
message: `${message}${fieldContext}`,
|
|
376
|
+
});
|
|
377
|
+
}
|
|
378
|
+
else {
|
|
379
|
+
// Fallback for unknown error format
|
|
380
|
+
errors.push(ErrorHandler.createSimpleError(JSON.stringify(error), [field.key]));
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
else if (fieldResult.data !== undefined) {
|
|
385
|
+
validatedData[field.key] = fieldResult.data;
|
|
386
|
+
}
|
|
387
|
+
// Batch warning processing
|
|
388
|
+
for (let j = 0; j < fieldResult.warnings.length; j++) {
|
|
389
|
+
warnings.push(`${field.key}: ${fieldResult.warnings[j]}`);
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
// Handle extra properties efficiently using pre-computed schema keys
|
|
393
|
+
const inputKeys = Object.keys(data);
|
|
394
|
+
const omittedFields = this.options._omittedFields || [];
|
|
395
|
+
// Check for strict mode or additionalProperties setting
|
|
396
|
+
const isStrict = this.options.strict === true ||
|
|
397
|
+
this.options.additionalProperties === false;
|
|
398
|
+
const allowAdditional = this.options.allowUnknown === true ||
|
|
399
|
+
this.options.additionalProperties === true;
|
|
400
|
+
if (allowAdditional && !isStrict) {
|
|
401
|
+
// Allow unknown properties
|
|
402
|
+
for (let i = 0; i < inputKeys.length; i++) {
|
|
403
|
+
const key = inputKeys[i];
|
|
404
|
+
// Secure check using indexOf instead of includes for security
|
|
405
|
+
if (this.schemaKeys.indexOf(key) === -1 &&
|
|
406
|
+
omittedFields.indexOf(key) === -1) {
|
|
407
|
+
validatedData[key] = data[key];
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
else {
|
|
412
|
+
// Check for extra keys in strict mode or when additional properties are not allowed
|
|
413
|
+
const extraKeys = [];
|
|
414
|
+
for (let i = 0; i < inputKeys.length; i++) {
|
|
415
|
+
const key = inputKeys[i];
|
|
416
|
+
// Secure check using indexOf instead of includes for security
|
|
417
|
+
if (this.schemaKeys.indexOf(key) === -1 &&
|
|
418
|
+
omittedFields.indexOf(key) === -1) {
|
|
419
|
+
extraKeys.push(key);
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
if (extraKeys.length > 0) {
|
|
423
|
+
if (isStrict) {
|
|
424
|
+
// In strict mode, reject extra properties
|
|
425
|
+
hasErrors = true;
|
|
426
|
+
errors.push(ErrorHandler.createSimpleError(`Unexpected properties: ${extraKeys.join(", ")}`, []));
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
// console.log("validation error: ", validatedData);
|
|
431
|
+
return {
|
|
432
|
+
success: !hasErrors,
|
|
433
|
+
errors: errors,
|
|
434
|
+
warnings,
|
|
435
|
+
data: hasErrors ? undefined : validatedData,
|
|
436
|
+
};
|
|
437
|
+
}
|
|
438
|
+
/**
|
|
439
|
+
* Validate pre-compiled string field for maximum performance
|
|
440
|
+
*/
|
|
441
|
+
validatePrecompiledStringField(field, value) {
|
|
442
|
+
const { parsedConstraints } = field;
|
|
443
|
+
const { type, constraints, optional: isOptional, required: isRequired, } = parsedConstraints;
|
|
444
|
+
// Fast path for undefined/null values
|
|
445
|
+
if (value === undefined) {
|
|
446
|
+
return isOptional
|
|
447
|
+
? {
|
|
448
|
+
success: true,
|
|
449
|
+
errors: [],
|
|
450
|
+
warnings: [],
|
|
451
|
+
data: this.options.default,
|
|
452
|
+
}
|
|
453
|
+
: {
|
|
454
|
+
success: false,
|
|
455
|
+
errors: [ErrorHandler.createMissingFieldError([], field.key)],
|
|
456
|
+
warnings: [],
|
|
457
|
+
data: value,
|
|
458
|
+
};
|
|
459
|
+
}
|
|
460
|
+
if (value === null) {
|
|
461
|
+
return isOptional
|
|
462
|
+
? { success: true, errors: [], warnings: [], data: null }
|
|
463
|
+
: {
|
|
464
|
+
success: false,
|
|
465
|
+
errors: [ErrorHandler.createTypeError([], "null", value)],
|
|
466
|
+
warnings: [],
|
|
467
|
+
data: value,
|
|
468
|
+
};
|
|
469
|
+
}
|
|
470
|
+
// Handle array types
|
|
471
|
+
if (field.isArray) {
|
|
472
|
+
if (!Array.isArray(value)) {
|
|
473
|
+
return {
|
|
474
|
+
success: false,
|
|
475
|
+
errors: [ErrorHandler.createTypeError([], "array", value)],
|
|
476
|
+
warnings: [],
|
|
477
|
+
data: value,
|
|
478
|
+
};
|
|
479
|
+
}
|
|
480
|
+
// Check array constraints
|
|
481
|
+
if (constraints.minItems !== undefined &&
|
|
482
|
+
value.length < constraints.minItems) {
|
|
483
|
+
return {
|
|
484
|
+
success: false,
|
|
485
|
+
errors: [
|
|
486
|
+
ErrorHandler.createArrayError([], `must have at least ${constraints.minItems} items, got ${value.length}`, value, ErrorCode.ARRAY_TOO_SHORT),
|
|
487
|
+
],
|
|
488
|
+
warnings: [],
|
|
489
|
+
data: value,
|
|
490
|
+
};
|
|
491
|
+
}
|
|
492
|
+
if (constraints.maxItems !== undefined &&
|
|
493
|
+
value.length > constraints.maxItems) {
|
|
494
|
+
return {
|
|
495
|
+
success: false,
|
|
496
|
+
errors: [
|
|
497
|
+
ErrorHandler.createArrayError([], `must have at most ${constraints.maxItems} items, got ${value.length}`, value, ErrorCode.ARRAY_TOO_LONG),
|
|
498
|
+
],
|
|
499
|
+
warnings: [],
|
|
500
|
+
data: value,
|
|
501
|
+
};
|
|
502
|
+
}
|
|
503
|
+
// Validate array elements
|
|
504
|
+
const validatedArray = [];
|
|
505
|
+
const errors = [];
|
|
506
|
+
for (let i = 0; i < value.length; i++) {
|
|
507
|
+
// Use validateStringFieldType to handle union types properly
|
|
508
|
+
const elementResult = this.validateStringFieldType(field.elementType, value[i]);
|
|
509
|
+
if (!elementResult.success) {
|
|
510
|
+
errors.push(...elementResult.errors.map((error) => ({
|
|
511
|
+
...error,
|
|
512
|
+
path: [i.toString(), ...error.path],
|
|
513
|
+
})));
|
|
514
|
+
}
|
|
515
|
+
else {
|
|
516
|
+
validatedArray.push(elementResult.data);
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
if (errors.length > 0) {
|
|
520
|
+
return { success: false, errors, warnings: [], data: value };
|
|
521
|
+
}
|
|
522
|
+
// Check uniqueness if required
|
|
523
|
+
if (constraints.unique) {
|
|
524
|
+
const uniqueValues = new Set(validatedArray);
|
|
525
|
+
if (uniqueValues.size !== validatedArray.length) {
|
|
526
|
+
return {
|
|
527
|
+
success: false,
|
|
528
|
+
errors: [
|
|
529
|
+
ErrorHandler.createArrayError([], "values must be unique", value, ErrorCode.ARRAY_VALUES_NOT_UNIQUE),
|
|
530
|
+
],
|
|
531
|
+
warnings: [],
|
|
532
|
+
data: value,
|
|
533
|
+
};
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
return { success: true, errors: [], warnings: [], data: validatedArray };
|
|
537
|
+
}
|
|
538
|
+
// Handle constant values using secure regex
|
|
539
|
+
const constantPattern = /^=/;
|
|
540
|
+
if (constantPattern.test(type)) {
|
|
541
|
+
// Validate constant value
|
|
542
|
+
return ValidationHelpers.validateConstantType(type.replace(/^=/, ""), value);
|
|
543
|
+
}
|
|
544
|
+
// Handle union types using secure regex
|
|
545
|
+
const unionPattern = /\|/;
|
|
546
|
+
if (unionPattern.test(type)) {
|
|
547
|
+
return ValidationHelpers.validateUnionType(type, value);
|
|
548
|
+
}
|
|
549
|
+
// Handle basic types using pre-parsed constraints
|
|
550
|
+
return ValidationHelpers.routeTypeValidation(type, value, { ...constraints, ...this.options }, constraints, isRequired // FIXED: Pass the required parameter
|
|
551
|
+
);
|
|
552
|
+
}
|
|
553
|
+
/**
|
|
554
|
+
* Validate individual field
|
|
555
|
+
*/
|
|
556
|
+
validateField(_key, fieldType, value, fullData // NEW: Add full data context for nested validation
|
|
557
|
+
) {
|
|
558
|
+
const result = {
|
|
559
|
+
success: true,
|
|
560
|
+
errors: [],
|
|
561
|
+
warnings: [],
|
|
562
|
+
data: value,
|
|
563
|
+
};
|
|
564
|
+
// console.log("checking for union types");
|
|
565
|
+
// Handle union values
|
|
566
|
+
if (TypeGuards.isUnionValue(fieldType)) {
|
|
567
|
+
const allowedValues = fieldType.union;
|
|
568
|
+
// Secure check using indexOf instead of includes
|
|
569
|
+
if (allowedValues.indexOf(value) === -1) {
|
|
570
|
+
result.success = false;
|
|
571
|
+
result.errors.push(ErrorHandler.createUnionError([], allowedValues, value));
|
|
572
|
+
}
|
|
573
|
+
return result;
|
|
574
|
+
}
|
|
575
|
+
// console.log("checking for constant types");
|
|
576
|
+
// Handle constant values
|
|
577
|
+
if (TypeGuards.isConstantValue(fieldType)) {
|
|
578
|
+
const expectedValue = fieldType.const;
|
|
579
|
+
const isOptional = "optional" in fieldType && fieldType.optional;
|
|
580
|
+
if (value === undefined && isOptional) {
|
|
581
|
+
result.data = this.options.default;
|
|
582
|
+
return result;
|
|
583
|
+
}
|
|
584
|
+
if (value !== expectedValue) {
|
|
585
|
+
result.success = false;
|
|
586
|
+
result.errors.push(ErrorHandler.createConstantError([], expectedValue, value, expectedValue));
|
|
587
|
+
}
|
|
588
|
+
return result;
|
|
589
|
+
}
|
|
590
|
+
// console.log("checking for optional constant types");
|
|
591
|
+
// Handle optional nested schemas
|
|
592
|
+
if (TypeGuards.isOptionalSchemaInterface(fieldType)) {
|
|
593
|
+
// console.log("validating optional schema interface");
|
|
594
|
+
if (value === undefined) {
|
|
595
|
+
result.data = this.options.default;
|
|
596
|
+
return result;
|
|
597
|
+
}
|
|
598
|
+
const nestedSchema = new InterfaceSchema(fieldType.schema, this.options);
|
|
599
|
+
return nestedSchema.validate(value);
|
|
600
|
+
}
|
|
601
|
+
// console.log("checking for conditional validation objects");
|
|
602
|
+
// Handle conditional validation objects
|
|
603
|
+
if (TypeGuards.isConditionalValidation(fieldType)) {
|
|
604
|
+
return this.validateConditionalField(fieldType, value);
|
|
605
|
+
}
|
|
606
|
+
// console.log("checking for nested objects");
|
|
607
|
+
// Handle nested objects
|
|
608
|
+
if (TypeGuards.isSchemaInterface(fieldType)) {
|
|
609
|
+
const nestedSchema = new InterfaceSchema(fieldType, this.options);
|
|
610
|
+
// CRITICAL FIX: For nested objects, we need to pass the full data context
|
|
611
|
+
// so that conditional validation can access parent fields
|
|
612
|
+
const nestedResult = this.validateNestedObjectWithContext(nestedSchema, value, fullData);
|
|
613
|
+
// Path is already handled in the main validation loop, no need to add it here
|
|
614
|
+
// The main validation loop will add the field key to the path
|
|
615
|
+
return nestedResult;
|
|
616
|
+
}
|
|
617
|
+
// console.log("checking for array of schemas");
|
|
618
|
+
// Handle array of schemas
|
|
619
|
+
if (Array.isArray(fieldType) && fieldType.length === 1) {
|
|
620
|
+
if (!Array.isArray(value)) {
|
|
621
|
+
result.success = false;
|
|
622
|
+
result.errors.push(ErrorHandler.createTypeError([], "array", value));
|
|
623
|
+
return result;
|
|
624
|
+
}
|
|
625
|
+
const validatedArray = [];
|
|
626
|
+
const itemSchema = fieldType[0];
|
|
627
|
+
for (let i = 0; i < value.length; i++) {
|
|
628
|
+
const elementResult = this.validateField(`[${i}]`, itemSchema, value[i]);
|
|
629
|
+
if (!elementResult.success) {
|
|
630
|
+
result.success = false;
|
|
631
|
+
result.errors.push(...elementResult.errors.map((error) => ({
|
|
632
|
+
...error,
|
|
633
|
+
path: [i.toString(), ...error.path],
|
|
634
|
+
})));
|
|
635
|
+
}
|
|
636
|
+
else {
|
|
637
|
+
validatedArray.push(elementResult.data);
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
if (result.success) {
|
|
641
|
+
result.data = validatedArray;
|
|
642
|
+
}
|
|
643
|
+
return result;
|
|
644
|
+
}
|
|
645
|
+
// Handle string field types
|
|
646
|
+
if (typeof fieldType === "string") {
|
|
647
|
+
// console.log("validating string field type");
|
|
648
|
+
// conditional validation is handled in the main validation loop
|
|
649
|
+
// This method is only for direct field type validation
|
|
650
|
+
return this.validateStringFieldType(fieldType, value);
|
|
651
|
+
}
|
|
652
|
+
// console.log("val/donex");
|
|
653
|
+
result.success = false;
|
|
654
|
+
result.errors.push(ErrorHandler.createUnknownFieldError([], fieldType));
|
|
655
|
+
return result;
|
|
656
|
+
}
|
|
657
|
+
/**
|
|
658
|
+
* Validate string-based field types - optimized version
|
|
659
|
+
*/
|
|
660
|
+
validateStringFieldType(fieldType, value) {
|
|
661
|
+
// Check for conditional expressions first - route to conditional validation
|
|
662
|
+
if (this.isConditionalSyntax(fieldType)) {
|
|
663
|
+
// Parse conditional expression
|
|
664
|
+
const { ast, errors } = this.ConditionalParser.parse(fieldType);
|
|
665
|
+
if (ast && errors.length === 0) {
|
|
666
|
+
// Use conditional validation (without full data context)
|
|
667
|
+
return this.validateEnhancedConditionalField(ast, value, {}, {});
|
|
668
|
+
}
|
|
669
|
+
else {
|
|
670
|
+
// If parsing fails, return error
|
|
671
|
+
return {
|
|
672
|
+
success: false,
|
|
673
|
+
errors: [
|
|
674
|
+
ErrorHandler.createValidationError([], `Invalid conditional expression: ${fieldType}`, value),
|
|
675
|
+
],
|
|
676
|
+
warnings: [],
|
|
677
|
+
data: value,
|
|
678
|
+
};
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
// Parse constraints once
|
|
682
|
+
const { type: parsedType, constraints, optional: isOptional, required: isRequired, } = ConstraintParser.parseConstraints(fieldType);
|
|
683
|
+
// Fast path for undefined/null values
|
|
684
|
+
if (value === undefined) {
|
|
685
|
+
return isOptional
|
|
686
|
+
? {
|
|
687
|
+
success: true,
|
|
688
|
+
errors: [],
|
|
689
|
+
warnings: [],
|
|
690
|
+
data: this.options.default,
|
|
691
|
+
}
|
|
692
|
+
: {
|
|
693
|
+
success: false,
|
|
694
|
+
errors: [
|
|
695
|
+
{
|
|
696
|
+
path: [],
|
|
697
|
+
message: "Missing required field",
|
|
698
|
+
code: ErrorCode.MISSING_REQUIRED_FIELD,
|
|
699
|
+
expected: "required field",
|
|
700
|
+
received: undefined,
|
|
701
|
+
receivedType: "undefined",
|
|
702
|
+
},
|
|
703
|
+
],
|
|
704
|
+
warnings: [],
|
|
705
|
+
data: value,
|
|
706
|
+
};
|
|
707
|
+
}
|
|
708
|
+
if (value === null) {
|
|
709
|
+
return isOptional
|
|
710
|
+
? { success: true, errors: [], warnings: [], data: null }
|
|
711
|
+
: {
|
|
712
|
+
success: false,
|
|
713
|
+
errors: [ErrorHandler.createTypeError([], "null", value)],
|
|
714
|
+
warnings: [],
|
|
715
|
+
data: value,
|
|
716
|
+
};
|
|
717
|
+
}
|
|
718
|
+
// Secure regex pattern to check for array type
|
|
719
|
+
const arrayPattern = /\[\]$/;
|
|
720
|
+
const isArray = arrayPattern.test(parsedType);
|
|
721
|
+
const elementType = isArray ? parsedType.replace(/\[\]$/, "") : parsedType;
|
|
722
|
+
// Handle array types
|
|
723
|
+
if (isArray) {
|
|
724
|
+
if (!Array.isArray(value)) {
|
|
725
|
+
return {
|
|
726
|
+
success: false,
|
|
727
|
+
errors: [ErrorHandler.createTypeError([], "array", value)],
|
|
728
|
+
warnings: [],
|
|
729
|
+
data: value,
|
|
730
|
+
};
|
|
731
|
+
}
|
|
732
|
+
// Apply parsed constraints to options, but preserve important options like loose
|
|
733
|
+
const Options = { ...constraints, ...this.options };
|
|
734
|
+
// Check array constraints
|
|
735
|
+
if (Options.minItems !== undefined && value.length < Options.minItems) {
|
|
736
|
+
return {
|
|
737
|
+
success: false,
|
|
738
|
+
errors: [
|
|
739
|
+
ErrorHandler.createArrayError([], `must have at least ${Options.minItems} items, got ${value.length}`, value, ErrorCode.ARRAY_TOO_SHORT),
|
|
740
|
+
],
|
|
741
|
+
warnings: [],
|
|
742
|
+
data: value,
|
|
743
|
+
};
|
|
744
|
+
}
|
|
745
|
+
if (Options.maxItems !== undefined && value.length > Options.maxItems) {
|
|
746
|
+
return {
|
|
747
|
+
success: false,
|
|
748
|
+
errors: [
|
|
749
|
+
ErrorHandler.createArrayError([], `must have at most ${Options.maxItems} items, got ${value.length}`, value, ErrorCode.ARRAY_TOO_LONG),
|
|
750
|
+
],
|
|
751
|
+
warnings: [],
|
|
752
|
+
data: value,
|
|
753
|
+
};
|
|
754
|
+
}
|
|
755
|
+
// Validate array elements
|
|
756
|
+
const validatedArray = [];
|
|
757
|
+
const errors = [];
|
|
758
|
+
for (let i = 0; i < value.length; i++) {
|
|
759
|
+
const elementResult = this.validateStringFieldType(elementType, value[i]);
|
|
760
|
+
if (!elementResult.success) {
|
|
761
|
+
errors.push(...elementResult.errors.map((error) => ({
|
|
762
|
+
...error,
|
|
763
|
+
path: [i.toString(), ...error.path],
|
|
764
|
+
})));
|
|
765
|
+
}
|
|
766
|
+
else {
|
|
767
|
+
validatedArray.push(elementResult.data);
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
if (errors.length > 0) {
|
|
771
|
+
return { success: false, errors, warnings: [], data: value };
|
|
772
|
+
}
|
|
773
|
+
// Check uniqueness if required
|
|
774
|
+
if (Options.unique) {
|
|
775
|
+
const uniqueValues = new Set(validatedArray);
|
|
776
|
+
if (uniqueValues.size !== validatedArray.length) {
|
|
777
|
+
return {
|
|
778
|
+
success: false,
|
|
779
|
+
errors: [
|
|
780
|
+
ErrorHandler.createArrayError([], "values must be unique", value, ErrorCode.ARRAY_VALUES_NOT_UNIQUE),
|
|
781
|
+
],
|
|
782
|
+
warnings: [],
|
|
783
|
+
data: value,
|
|
784
|
+
};
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
return { success: true, errors: [], warnings: [], data: validatedArray };
|
|
788
|
+
}
|
|
789
|
+
// Note: Conditional "when" syntax is handled at the field level, not here
|
|
790
|
+
// Handle constant values (e.g., "=admin", "=user") using secure regex
|
|
791
|
+
const constantPattern = /^=/;
|
|
792
|
+
if (constantPattern.test(elementType)) {
|
|
793
|
+
return ValidationHelpers.validateConstantType(elementType.replace(/^=/, ""), value);
|
|
794
|
+
}
|
|
795
|
+
// Handle union types (e.g., "pending|accepted|rejected" or "(user|admin|guest)") using secure regex
|
|
796
|
+
const unionPattern = /\|/;
|
|
797
|
+
if (unionPattern.test(elementType)) {
|
|
798
|
+
return ValidationHelpers.validateUnionType(elementType, value);
|
|
799
|
+
}
|
|
800
|
+
// Handle basic types - pass the original fieldType to preserve constraints
|
|
801
|
+
return this.validateBasicType(fieldType, value);
|
|
802
|
+
}
|
|
803
|
+
/**
|
|
804
|
+
* Validate basic types with constraints
|
|
805
|
+
*/
|
|
806
|
+
validateBasicType(fieldType, value) {
|
|
807
|
+
// Handle union types before constraint parsing (e.g., "(user|admin|guest)") using secure regex
|
|
808
|
+
const unionPattern = /\|/;
|
|
809
|
+
if (unionPattern.test(fieldType)) {
|
|
810
|
+
return ValidationHelpers.validateUnionType(fieldType, value);
|
|
811
|
+
}
|
|
812
|
+
// Parse constraints from field type (include required field)
|
|
813
|
+
const { type, constraints, required: fieldIsRequired, } = ConstraintParser.parseConstraints(fieldType);
|
|
814
|
+
// Apply parsed constraints to options, but preserve important options like loose
|
|
815
|
+
const Options = { ...constraints, ...this.options };
|
|
816
|
+
// Check for Record types first (both lowercase and TypeScript-style uppercase) using secure regex
|
|
817
|
+
const recordPattern = /^(record|Record)<.*>$/;
|
|
818
|
+
if (recordPattern.test(type)) {
|
|
819
|
+
// Normalize to lowercase for the validator using secure regex
|
|
820
|
+
const uppercaseRecordPattern = /^Record</;
|
|
821
|
+
const normalizedType = uppercaseRecordPattern.test(type)
|
|
822
|
+
? type.replace(/^Record/, "record")
|
|
823
|
+
: type;
|
|
824
|
+
return ValidationHelpers.validateRecordType(normalizedType, value, (fieldType, value) => this.validateStringFieldType(fieldType, value));
|
|
825
|
+
}
|
|
826
|
+
// Route to appropriate type validator
|
|
827
|
+
const result = ValidationHelpers.routeTypeValidation(type, // Use the type from the constraint parsing above
|
|
828
|
+
value, Options, constraints, fieldIsRequired // Use the required field from constraint parsing
|
|
829
|
+
);
|
|
830
|
+
return result;
|
|
831
|
+
}
|
|
832
|
+
/**
|
|
833
|
+
* Validate nested object with full data context for conditional field resolution
|
|
834
|
+
* CRITICAL FIX: This method ensures nested conditional validation has access to parent context
|
|
835
|
+
*/
|
|
836
|
+
validateNestedObjectWithContext(nestedSchema, nestedValue, fullDataContext) {
|
|
837
|
+
// If we don't have full data context, fall back to standard validation
|
|
838
|
+
if (!fullDataContext) {
|
|
839
|
+
// console.log("no full data context, falling back to standard validation");
|
|
840
|
+
return nestedSchema.validate(nestedValue);
|
|
841
|
+
}
|
|
842
|
+
// console.log("validating nested object with full data context");
|
|
843
|
+
// CRITICAL FIX: Temporarily store the full context in the nested schema
|
|
844
|
+
// so that conditional validation can access parent fields
|
|
845
|
+
const originalValidateEnhancedConditionalField = nestedSchema["validateEnhancedConditionalField"];
|
|
846
|
+
// Override the conditional validation method to pass parent context
|
|
847
|
+
nestedSchema["validateEnhancedConditionalField"] = function (ast, value, localData, nestedContext) {
|
|
848
|
+
// console.log("validating enhanced conditional field with context");
|
|
849
|
+
return originalValidateEnhancedConditionalField.call(this, ast, value, fullDataContext, localData);
|
|
850
|
+
};
|
|
851
|
+
try {
|
|
852
|
+
// Perform the validation with the modified context
|
|
853
|
+
const result = nestedSchema.validate(nestedValue);
|
|
854
|
+
// console.log("nested validation result:", result);
|
|
855
|
+
return result;
|
|
856
|
+
}
|
|
857
|
+
finally {
|
|
858
|
+
// console.log("restoring original conditional validation method");
|
|
859
|
+
// Restore the original method
|
|
860
|
+
nestedSchema["validateEnhancedConditionalField"] =
|
|
861
|
+
originalValidateEnhancedConditionalField;
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
/**
|
|
865
|
+
* Validate enhanced conditional field using our new AST-based system
|
|
866
|
+
* FIXED: Now properly handles nested context for field resolution
|
|
867
|
+
*/
|
|
868
|
+
validateEnhancedConditionalField(ast, value, fullData, nestedContext // Add nested context parameter
|
|
869
|
+
) {
|
|
870
|
+
try {
|
|
871
|
+
// CRITICAL FIX: For nested conditional validation, we need to provide both
|
|
872
|
+
// the local context (nested object) and the full context (root object)
|
|
873
|
+
// This allows field resolution to work correctly for both local and parent references
|
|
874
|
+
// Create enhanced context that supports both local and parent field resolution
|
|
875
|
+
const contextData = nestedContext || fullData;
|
|
876
|
+
// FIXED: Pass both contexts to the evaluator for proper field resolution
|
|
877
|
+
const evaluationResult = ConditionalEvaluator.evaluate(ast, contextData, {
|
|
878
|
+
strict: this.options.strict || false,
|
|
879
|
+
debug: true, // Enable debug to get condition result
|
|
880
|
+
schema: this.definition,
|
|
881
|
+
validatePaths: true,
|
|
882
|
+
// NEW: Add parent context for nested field resolution
|
|
883
|
+
parentContext: fullData !== contextData ? fullData : undefined,
|
|
884
|
+
});
|
|
885
|
+
if (!evaluationResult.success) {
|
|
886
|
+
return {
|
|
887
|
+
success: false,
|
|
888
|
+
errors: evaluationResult.errors,
|
|
889
|
+
warnings: [],
|
|
890
|
+
data: value,
|
|
891
|
+
};
|
|
892
|
+
}
|
|
893
|
+
// Get the expected schema and condition result
|
|
894
|
+
const expectedSchema = evaluationResult.value;
|
|
895
|
+
const conditionIsTrue = evaluationResult.debugInfo?.finalCondition;
|
|
896
|
+
if (expectedSchema === undefined) {
|
|
897
|
+
// No schema constraint, accept the value
|
|
898
|
+
return {
|
|
899
|
+
success: true,
|
|
900
|
+
errors: [],
|
|
901
|
+
warnings: [],
|
|
902
|
+
data: value,
|
|
903
|
+
};
|
|
904
|
+
}
|
|
905
|
+
// CRITICAL FIX: Handle constant values - VALIDATE user input against expected constant
|
|
906
|
+
if (typeof expectedSchema === "string" &&
|
|
907
|
+
expectedSchema.startsWith("=")) {
|
|
908
|
+
const expectedValue = expectedSchema.slice(1); // Remove the = prefix
|
|
909
|
+
// Handle special constant values
|
|
910
|
+
let actualExpectedValue = expectedValue;
|
|
911
|
+
if (expectedValue === "null") {
|
|
912
|
+
actualExpectedValue = null;
|
|
913
|
+
}
|
|
914
|
+
else if (expectedValue === "true") {
|
|
915
|
+
actualExpectedValue = true;
|
|
916
|
+
}
|
|
917
|
+
else if (expectedValue === "false") {
|
|
918
|
+
actualExpectedValue = false;
|
|
919
|
+
}
|
|
920
|
+
else if (/^\d+(\.\d+)?$/.test(expectedValue)) {
|
|
921
|
+
actualExpectedValue = parseFloat(expectedValue);
|
|
922
|
+
}
|
|
923
|
+
else if (expectedValue.startsWith("[") &&
|
|
924
|
+
expectedValue.endsWith("]")) {
|
|
925
|
+
// Handle array constants like ["USD"] or [1,2,3]
|
|
926
|
+
try {
|
|
927
|
+
actualExpectedValue = JSON.parse(expectedValue);
|
|
928
|
+
}
|
|
929
|
+
catch (error) {
|
|
930
|
+
// If JSON parsing fails, treat as string
|
|
931
|
+
actualExpectedValue = expectedValue;
|
|
932
|
+
}
|
|
933
|
+
}
|
|
934
|
+
else if (expectedValue.startsWith("{") &&
|
|
935
|
+
expectedValue.endsWith("}")) {
|
|
936
|
+
// Handle object constants like {"key": "value"}
|
|
937
|
+
try {
|
|
938
|
+
actualExpectedValue = JSON.parse(expectedValue);
|
|
939
|
+
}
|
|
940
|
+
catch (error) {
|
|
941
|
+
// If JSON parsing fails, treat as string
|
|
942
|
+
actualExpectedValue = expectedValue;
|
|
943
|
+
}
|
|
944
|
+
}
|
|
945
|
+
// FIXED: Validate user input against expected constant value
|
|
946
|
+
// Do NOT override user data - validate it!
|
|
947
|
+
if (!ValidationHelpers.deepEquals(value, actualExpectedValue)) {
|
|
948
|
+
return {
|
|
949
|
+
success: false,
|
|
950
|
+
errors: [
|
|
951
|
+
ErrorHandler.createConstantError([], actualExpectedValue, value, expectedValue),
|
|
952
|
+
],
|
|
953
|
+
warnings: [],
|
|
954
|
+
data: value, // Return original user input, not the expected value
|
|
955
|
+
};
|
|
956
|
+
}
|
|
957
|
+
// User input matches expected constant - validation passes
|
|
958
|
+
return {
|
|
959
|
+
success: true,
|
|
960
|
+
errors: [],
|
|
961
|
+
warnings: [],
|
|
962
|
+
data: value, // Return user's input, not the expected value
|
|
963
|
+
};
|
|
964
|
+
}
|
|
965
|
+
// Handle non-constant string schemas (like "boolean", "string", etc.)
|
|
966
|
+
// For conditionals, validate user input against the expected type
|
|
967
|
+
if (typeof expectedSchema === "string") {
|
|
968
|
+
if (expectedSchema === "boolean") {
|
|
969
|
+
// Validate that user provided a boolean
|
|
970
|
+
if (typeof value !== "boolean") {
|
|
971
|
+
return {
|
|
972
|
+
success: false,
|
|
973
|
+
errors: [ErrorHandler.createTypeError([], "boolean", value)],
|
|
974
|
+
warnings: [],
|
|
975
|
+
data: value,
|
|
976
|
+
};
|
|
977
|
+
}
|
|
978
|
+
// Keep user's boolean value
|
|
979
|
+
return {
|
|
980
|
+
success: true,
|
|
981
|
+
errors: [],
|
|
982
|
+
warnings: [],
|
|
983
|
+
data: value,
|
|
984
|
+
};
|
|
985
|
+
}
|
|
986
|
+
if (expectedSchema === "string") {
|
|
987
|
+
// Validate that user provided a string
|
|
988
|
+
if (typeof value !== "string") {
|
|
989
|
+
return {
|
|
990
|
+
success: false,
|
|
991
|
+
errors: [ErrorHandler.createTypeError([], "string", value)],
|
|
992
|
+
warnings: [],
|
|
993
|
+
data: value,
|
|
994
|
+
};
|
|
995
|
+
}
|
|
996
|
+
// Keep user's string value
|
|
997
|
+
return {
|
|
998
|
+
success: true,
|
|
999
|
+
errors: [],
|
|
1000
|
+
warnings: [],
|
|
1001
|
+
data: value,
|
|
1002
|
+
};
|
|
1003
|
+
}
|
|
1004
|
+
if (expectedSchema === "number" || expectedSchema === "int") {
|
|
1005
|
+
if (typeof value !== "number") {
|
|
1006
|
+
return {
|
|
1007
|
+
success: false,
|
|
1008
|
+
errors: [ErrorHandler.createTypeError([], "number", value)],
|
|
1009
|
+
warnings: [],
|
|
1010
|
+
data: value,
|
|
1011
|
+
};
|
|
1012
|
+
}
|
|
1013
|
+
if (expectedSchema === "int" && !Number.isInteger(value)) {
|
|
1014
|
+
return {
|
|
1015
|
+
success: false,
|
|
1016
|
+
errors: [ErrorHandler.createTypeError([], "integer", value)],
|
|
1017
|
+
warnings: [],
|
|
1018
|
+
data: value,
|
|
1019
|
+
};
|
|
1020
|
+
}
|
|
1021
|
+
return {
|
|
1022
|
+
success: true,
|
|
1023
|
+
errors: [],
|
|
1024
|
+
warnings: [],
|
|
1025
|
+
data: value,
|
|
1026
|
+
};
|
|
1027
|
+
}
|
|
1028
|
+
// Handle array types specially
|
|
1029
|
+
if (expectedSchema.endsWith("[]") || expectedSchema.endsWith("[]?")) {
|
|
1030
|
+
const isOptional = expectedSchema.endsWith("[]?");
|
|
1031
|
+
if (value === null || value === undefined) {
|
|
1032
|
+
if (isOptional) {
|
|
1033
|
+
return {
|
|
1034
|
+
success: true,
|
|
1035
|
+
errors: [],
|
|
1036
|
+
warnings: [],
|
|
1037
|
+
data: value,
|
|
1038
|
+
};
|
|
1039
|
+
}
|
|
1040
|
+
else {
|
|
1041
|
+
return {
|
|
1042
|
+
success: false,
|
|
1043
|
+
errors: [ErrorHandler.createTypeError([], "null", value)],
|
|
1044
|
+
warnings: [],
|
|
1045
|
+
data: value,
|
|
1046
|
+
};
|
|
1047
|
+
}
|
|
1048
|
+
}
|
|
1049
|
+
if (!Array.isArray(value)) {
|
|
1050
|
+
return {
|
|
1051
|
+
success: false,
|
|
1052
|
+
errors: [ErrorHandler.createTypeError([], "array", value)],
|
|
1053
|
+
warnings: [],
|
|
1054
|
+
data: value,
|
|
1055
|
+
};
|
|
1056
|
+
}
|
|
1057
|
+
// FIXED: Validate array elements against the expected type
|
|
1058
|
+
// Extract the element type from the array type (e.g., "number[]" -> "number")
|
|
1059
|
+
const elementType = expectedSchema.replace(/\[\]\??$/, "");
|
|
1060
|
+
// Validate each array element
|
|
1061
|
+
const validatedArray = [];
|
|
1062
|
+
const errors = [];
|
|
1063
|
+
for (let i = 0; i < value.length; i++) {
|
|
1064
|
+
const elementResult = this.validateStringFieldType(elementType, value[i]);
|
|
1065
|
+
if (!elementResult.success) {
|
|
1066
|
+
errors.push(...elementResult.errors.map((error) => ({
|
|
1067
|
+
...error,
|
|
1068
|
+
path: [i.toString(), ...error.path],
|
|
1069
|
+
})));
|
|
1070
|
+
}
|
|
1071
|
+
else {
|
|
1072
|
+
validatedArray.push(elementResult.data);
|
|
1073
|
+
}
|
|
1074
|
+
}
|
|
1075
|
+
if (errors.length > 0) {
|
|
1076
|
+
return {
|
|
1077
|
+
success: false,
|
|
1078
|
+
errors,
|
|
1079
|
+
warnings: [],
|
|
1080
|
+
data: value,
|
|
1081
|
+
};
|
|
1082
|
+
}
|
|
1083
|
+
return {
|
|
1084
|
+
success: true,
|
|
1085
|
+
errors: [],
|
|
1086
|
+
warnings: [],
|
|
1087
|
+
data: validatedArray,
|
|
1088
|
+
};
|
|
1089
|
+
}
|
|
1090
|
+
return this.validateStringFieldType(expectedSchema, value);
|
|
1091
|
+
}
|
|
1092
|
+
// Accept the value if we can't determine the schema
|
|
1093
|
+
return {
|
|
1094
|
+
success: true,
|
|
1095
|
+
errors: [],
|
|
1096
|
+
warnings: [],
|
|
1097
|
+
data: value,
|
|
1098
|
+
};
|
|
1099
|
+
}
|
|
1100
|
+
catch (error) {
|
|
1101
|
+
// Better error message extraction
|
|
1102
|
+
let errorMessage = "Unknown error";
|
|
1103
|
+
if (error instanceof Error) {
|
|
1104
|
+
errorMessage = error.message;
|
|
1105
|
+
}
|
|
1106
|
+
else if (error && typeof error === "object" && "message" in error) {
|
|
1107
|
+
errorMessage = error.message;
|
|
1108
|
+
}
|
|
1109
|
+
else if (error && typeof error === "object") {
|
|
1110
|
+
errorMessage = JSON.stringify(error);
|
|
1111
|
+
}
|
|
1112
|
+
else {
|
|
1113
|
+
errorMessage = String(error);
|
|
1114
|
+
}
|
|
1115
|
+
return {
|
|
1116
|
+
success: false,
|
|
1117
|
+
errors: [
|
|
1118
|
+
ErrorHandler.createConditionalError([], `Conditional validation error: ${errorMessage}`, value),
|
|
1119
|
+
],
|
|
1120
|
+
warnings: [],
|
|
1121
|
+
data: value,
|
|
1122
|
+
};
|
|
1123
|
+
}
|
|
1124
|
+
}
|
|
1125
|
+
/**
|
|
1126
|
+
* Validate conditional field with full data context
|
|
1127
|
+
*/
|
|
1128
|
+
validateConditionalFieldWithContext(conditionalDef, value, fullData) {
|
|
1129
|
+
const result = {
|
|
1130
|
+
success: true,
|
|
1131
|
+
errors: [],
|
|
1132
|
+
warnings: [],
|
|
1133
|
+
data: value,
|
|
1134
|
+
};
|
|
1135
|
+
// Get the field this condition depends on
|
|
1136
|
+
const fieldName = conditionalDef.fieldName;
|
|
1137
|
+
const conditions = conditionalDef.conditions || [];
|
|
1138
|
+
const defaultSchema = conditionalDef.default;
|
|
1139
|
+
// Get the value of the dependent field
|
|
1140
|
+
const dependentFieldValue = fullData[fieldName];
|
|
1141
|
+
// Find the matching condition
|
|
1142
|
+
let schemaToUse = defaultSchema;
|
|
1143
|
+
for (const condition of conditions) {
|
|
1144
|
+
if (this.evaluateCondition(condition, dependentFieldValue)) {
|
|
1145
|
+
schemaToUse = condition.schema;
|
|
1146
|
+
break;
|
|
1147
|
+
}
|
|
1148
|
+
}
|
|
1149
|
+
// If we have a schema to validate against, use it
|
|
1150
|
+
if (schemaToUse) {
|
|
1151
|
+
if (typeof schemaToUse === "string") {
|
|
1152
|
+
return this.validateStringFieldType(schemaToUse, value);
|
|
1153
|
+
}
|
|
1154
|
+
else if (typeof schemaToUse === "object") {
|
|
1155
|
+
return this.validateField("conditional", schemaToUse, value);
|
|
1156
|
+
}
|
|
1157
|
+
}
|
|
1158
|
+
// If no schema found, accept the value
|
|
1159
|
+
return result;
|
|
1160
|
+
}
|
|
1161
|
+
/**
|
|
1162
|
+
* Evaluate a condition against a field value
|
|
1163
|
+
*/
|
|
1164
|
+
evaluateCondition(condition, fieldValue) {
|
|
1165
|
+
if (!condition.condition) {
|
|
1166
|
+
return false;
|
|
1167
|
+
}
|
|
1168
|
+
return condition.condition(fieldValue);
|
|
1169
|
+
}
|
|
1170
|
+
/**
|
|
1171
|
+
* Validate conditional field based on other field values (legacy method)
|
|
1172
|
+
*
|
|
1173
|
+
* Note: This method is used when conditional validation is called without
|
|
1174
|
+
* full data context. It provides a fallback validation approach.
|
|
1175
|
+
*/
|
|
1176
|
+
validateConditionalField(conditionalDef, value) {
|
|
1177
|
+
const result = {
|
|
1178
|
+
success: true,
|
|
1179
|
+
errors: [],
|
|
1180
|
+
warnings: [],
|
|
1181
|
+
data: value,
|
|
1182
|
+
};
|
|
1183
|
+
// Get the field this condition depends on
|
|
1184
|
+
const conditions = conditionalDef.conditions || [];
|
|
1185
|
+
const defaultSchema = conditionalDef.default;
|
|
1186
|
+
// Since we don't have access to the full data object in this context,
|
|
1187
|
+
// we'll validate against all possible schemas and accept if any pass
|
|
1188
|
+
let validationPassed = false;
|
|
1189
|
+
let lastError = [];
|
|
1190
|
+
// Try to validate against each condition's schema
|
|
1191
|
+
for (const condition of conditions) {
|
|
1192
|
+
if (condition.schema) {
|
|
1193
|
+
try {
|
|
1194
|
+
const conditionResult = this.validateSchemaType(condition.schema, value);
|
|
1195
|
+
if (conditionResult.success) {
|
|
1196
|
+
validationPassed = true;
|
|
1197
|
+
result.data = conditionResult.data;
|
|
1198
|
+
result.warnings.push(...conditionResult.warnings);
|
|
1199
|
+
break; // Found a valid schema, use it
|
|
1200
|
+
}
|
|
1201
|
+
else {
|
|
1202
|
+
lastError = conditionResult.errors;
|
|
1203
|
+
}
|
|
1204
|
+
}
|
|
1205
|
+
catch (error) {
|
|
1206
|
+
// Continue to next condition if this one fails
|
|
1207
|
+
lastError = [
|
|
1208
|
+
ErrorHandler.createConditionalError([], `Conditional validation error: ${error instanceof Error ? error.message : String(error)}`, value),
|
|
1209
|
+
];
|
|
1210
|
+
}
|
|
1211
|
+
}
|
|
1212
|
+
}
|
|
1213
|
+
// If no condition schema worked, try the default schema
|
|
1214
|
+
if (!validationPassed && defaultSchema) {
|
|
1215
|
+
try {
|
|
1216
|
+
const defaultResult = this.validateSchemaType(defaultSchema, value);
|
|
1217
|
+
if (defaultResult.success) {
|
|
1218
|
+
validationPassed = true;
|
|
1219
|
+
result.data = defaultResult.data;
|
|
1220
|
+
result.warnings.push(...defaultResult.warnings);
|
|
1221
|
+
}
|
|
1222
|
+
else {
|
|
1223
|
+
lastError = defaultResult.errors;
|
|
1224
|
+
}
|
|
1225
|
+
}
|
|
1226
|
+
catch (error) {
|
|
1227
|
+
lastError = [
|
|
1228
|
+
ErrorHandler.createConditionalError([], `Default schema validation error: ${error instanceof Error ? error.message : String(error)}`, value),
|
|
1229
|
+
];
|
|
1230
|
+
}
|
|
1231
|
+
}
|
|
1232
|
+
// If no schema validation passed, report the error
|
|
1233
|
+
if (!validationPassed) {
|
|
1234
|
+
result.success = false;
|
|
1235
|
+
result.errors =
|
|
1236
|
+
lastError.length > 0
|
|
1237
|
+
? lastError
|
|
1238
|
+
: [
|
|
1239
|
+
ErrorHandler.createConditionalError([], "No valid conditional schema found", value),
|
|
1240
|
+
];
|
|
1241
|
+
result.warnings.push("Conditional validation performed without full data context");
|
|
1242
|
+
}
|
|
1243
|
+
return result;
|
|
1244
|
+
}
|
|
1245
|
+
/**
|
|
1246
|
+
* Helper method to validate a value against a schema type
|
|
1247
|
+
*/
|
|
1248
|
+
validateSchemaType(schema, value) {
|
|
1249
|
+
if (typeof schema === "string") {
|
|
1250
|
+
return this.validateStringFieldType(schema, value);
|
|
1251
|
+
}
|
|
1252
|
+
else if (typeof schema === "object" && schema !== null) {
|
|
1253
|
+
return this.validateField("conditional", schema, value);
|
|
1254
|
+
}
|
|
1255
|
+
else {
|
|
1256
|
+
return ValidationHelpers.createErrorResult(`Invalid schema type: ${typeof schema}`, value);
|
|
1257
|
+
}
|
|
1258
|
+
}
|
|
1259
|
+
/**
|
|
1260
|
+
* Parse and validate (throws on error)
|
|
1261
|
+
*/
|
|
1262
|
+
parse(data) {
|
|
1263
|
+
const result = this.validate(data);
|
|
1264
|
+
if (!result.success) {
|
|
1265
|
+
result.errors.forEach((error) => {
|
|
1266
|
+
throw new SchemaValidationError(error.message, [error.context?.suggestion || error.code], result.warnings);
|
|
1267
|
+
});
|
|
1268
|
+
}
|
|
1269
|
+
return result.data;
|
|
1270
|
+
}
|
|
1271
|
+
/**
|
|
1272
|
+
* Safe parse (returns result object) - strictly typed input
|
|
1273
|
+
*/
|
|
1274
|
+
safeParse(data) {
|
|
1275
|
+
return this.validate(data);
|
|
1276
|
+
}
|
|
1277
|
+
/**
|
|
1278
|
+
* Safe parse with unknown data (for testing invalid inputs)
|
|
1279
|
+
* Use this when you need to test data that might not match the schema
|
|
1280
|
+
*/
|
|
1281
|
+
safeParseUnknown(data) {
|
|
1282
|
+
return this.validate(data);
|
|
1283
|
+
}
|
|
1284
|
+
/**
|
|
1285
|
+
* Set schema options
|
|
1286
|
+
*/
|
|
1287
|
+
withOptions(opts) {
|
|
1288
|
+
return new InterfaceSchema(this.definition, {
|
|
1289
|
+
...this.options,
|
|
1290
|
+
...opts,
|
|
1291
|
+
});
|
|
1292
|
+
}
|
|
1293
|
+
/**
|
|
1294
|
+
* Async validation - returns a promise with validation result
|
|
1295
|
+
*/
|
|
1296
|
+
async parseAsync(data) {
|
|
1297
|
+
return new Promise((resolve, reject) => {
|
|
1298
|
+
try {
|
|
1299
|
+
// Use setTimeout to make it truly async
|
|
1300
|
+
setTimeout(() => {
|
|
1301
|
+
try {
|
|
1302
|
+
const result = this.validate(data);
|
|
1303
|
+
if (!result.success) {
|
|
1304
|
+
reject(result.errors.forEach((error) => {
|
|
1305
|
+
throw new SchemaValidationError(error.message, [error.context?.suggestion || error.code], result.warnings);
|
|
1306
|
+
}));
|
|
1307
|
+
}
|
|
1308
|
+
else {
|
|
1309
|
+
resolve(result.data);
|
|
1310
|
+
}
|
|
1311
|
+
}
|
|
1312
|
+
catch (error) {
|
|
1313
|
+
reject(error);
|
|
1314
|
+
}
|
|
1315
|
+
}, 0);
|
|
1316
|
+
}
|
|
1317
|
+
catch (error) {
|
|
1318
|
+
reject(error);
|
|
1319
|
+
}
|
|
1320
|
+
});
|
|
1321
|
+
}
|
|
1322
|
+
/**
|
|
1323
|
+
* Async safe parse - returns a promise with validation result object
|
|
1324
|
+
*/
|
|
1325
|
+
async safeParseAsync(data) {
|
|
1326
|
+
return new Promise((resolve) => {
|
|
1327
|
+
// Use setTimeout to make it truly async
|
|
1328
|
+
setTimeout(() => {
|
|
1329
|
+
try {
|
|
1330
|
+
const result = this.validate(data);
|
|
1331
|
+
resolve(result);
|
|
1332
|
+
}
|
|
1333
|
+
catch (error) {
|
|
1334
|
+
resolve({
|
|
1335
|
+
success: false,
|
|
1336
|
+
errors: [
|
|
1337
|
+
ErrorHandler.createSimpleError(`Unexpected validation error: ${error}`),
|
|
1338
|
+
],
|
|
1339
|
+
warnings: [],
|
|
1340
|
+
data: undefined,
|
|
1341
|
+
});
|
|
1342
|
+
}
|
|
1343
|
+
}, 0);
|
|
1344
|
+
});
|
|
1345
|
+
}
|
|
1346
|
+
/**
|
|
1347
|
+
* Async safe parse with unknown data
|
|
1348
|
+
*/
|
|
1349
|
+
async safeParseUnknownAsync(data) {
|
|
1350
|
+
return new Promise((resolve) => {
|
|
1351
|
+
setTimeout(() => {
|
|
1352
|
+
try {
|
|
1353
|
+
const result = this.validate(data);
|
|
1354
|
+
resolve(result);
|
|
1355
|
+
}
|
|
1356
|
+
catch (error) {
|
|
1357
|
+
resolve({
|
|
1358
|
+
success: false,
|
|
1359
|
+
errors: [
|
|
1360
|
+
ErrorHandler.createSimpleError(`Unexpected validation error: ${error}`),
|
|
1361
|
+
],
|
|
1362
|
+
warnings: [],
|
|
1363
|
+
data: undefined,
|
|
1364
|
+
});
|
|
1365
|
+
}
|
|
1366
|
+
}, 0);
|
|
1367
|
+
});
|
|
1368
|
+
}
|
|
1369
|
+
/**
|
|
1370
|
+
* Enable strict mode (no unknown properties allowed)
|
|
1371
|
+
*/
|
|
1372
|
+
strict() {
|
|
1373
|
+
return this.withOptions({ strict: true });
|
|
1374
|
+
}
|
|
1375
|
+
/**
|
|
1376
|
+
* Enable loose mode (allow type coercion)
|
|
1377
|
+
*/
|
|
1378
|
+
loose() {
|
|
1379
|
+
return this.withOptions({ loose: true });
|
|
1380
|
+
}
|
|
1381
|
+
/**
|
|
1382
|
+
* Allow unknown properties (not strict about extra fields)
|
|
1383
|
+
* Returns a schema that accepts extra properties beyond the defined interface
|
|
1384
|
+
*/
|
|
1385
|
+
allowUnknown() {
|
|
1386
|
+
return this.withOptions({ allowUnknown: true });
|
|
1387
|
+
}
|
|
1388
|
+
/**
|
|
1389
|
+
* Set minimum constraints
|
|
1390
|
+
*/
|
|
1391
|
+
min(value) {
|
|
1392
|
+
return this.withOptions({
|
|
1393
|
+
min: value,
|
|
1394
|
+
minLength: value,
|
|
1395
|
+
minItems: value,
|
|
1396
|
+
});
|
|
1397
|
+
}
|
|
1398
|
+
/**
|
|
1399
|
+
* Set maximum constraints
|
|
1400
|
+
*/
|
|
1401
|
+
max(value) {
|
|
1402
|
+
return this.withOptions({
|
|
1403
|
+
max: value,
|
|
1404
|
+
maxLength: value,
|
|
1405
|
+
maxItems: value,
|
|
1406
|
+
});
|
|
1407
|
+
}
|
|
1408
|
+
/**
|
|
1409
|
+
* Require unique array values
|
|
1410
|
+
*/
|
|
1411
|
+
unique() {
|
|
1412
|
+
return this.withOptions({ unique: true });
|
|
1413
|
+
}
|
|
1414
|
+
/**
|
|
1415
|
+
* Set pattern for string validation
|
|
1416
|
+
*/
|
|
1417
|
+
pattern(regex) {
|
|
1418
|
+
return this.withOptions({ pattern: regex });
|
|
1419
|
+
}
|
|
1420
|
+
/**
|
|
1421
|
+
* Set default value
|
|
1422
|
+
*/
|
|
1423
|
+
default(value) {
|
|
1424
|
+
return this.withOptions({ default: value });
|
|
1425
|
+
}
|
|
1426
|
+
}
|
|
1427
|
+
|
|
1428
|
+
export { InterfaceSchema };
|
|
1429
|
+
//# sourceMappingURL=InterfaceSchema.js.map
|