fortify-schema
Version:
A modern TypeScript validation library designed around familiar interface syntax and powerful conditional validation. Experience schema validation that feels natural to TypeScript developers while unlocking advanced runtime validation capabilities.
392 lines (352 loc) โข 17.9 kB
HTML
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Bug Fixes - Fortify Schema</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/github-dark.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
<link rel="stylesheet" href="styles.css">
</head>
<body class="bg-gray-50">
<!-- Header -->
<header class="gradient-bg text-white shadow-lg">
<div class="container mx-auto px-6 py-8">
<h1 class="text-4xl font-bold mb-2">๐ Bug Fixes</h1>
<p class="text-xl text-purple-100">Recent fixes and improvements</p>
</div>
</header>
<!-- Navigation -->
<nav class="bg-white shadow-md sticky top-0 z-50">
<div class="container mx-auto px-6">
<div class="flex space-x-8 overflow-x-auto py-4">
<a href="index.html" class="nav-link">๐ Home</a>
<a href="bug-fixes.html" class="nav-link active">๐ Bug Fixes</a>
<a href="make-utility.html" class="nav-link">๐ง Make</a>
<a href="mod-utility.html" class="nav-link">โ๏ธ Mod</a>
<a href="examples.html" class="nav-link">๐ Examples</a>
<a href="api-reference.html" class="nav-link">๐ API</a>
</div>
</div>
</nav>
<!-- Main Content -->
<main class="container mx-auto px-6 py-12">
<!-- Summary -->
<section class="mb-12">
<div class="alert alert-success">
<h3 class="text-xl font-bold mb-2">โ
Critical Bugs Fixed</h3>
<p class="mb-2">We've resolved two major issues affecting array-of-objects validation:</p>
<ul class="list-disc list-inside space-y-1">
<li><strong>TypeScript Type Inference:</strong> Arrays now correctly infer as <code>Array<T></code> instead of tuple types</li>
<li><strong>Runtime Validation:</strong> Array elements are now properly validated against their schema</li>
</ul>
</div>
</section>
<!-- Bug Fix 1: Type Inference -->
<section class="mb-12">
<div class="section-card">
<div class="flex items-start justify-between mb-6">
<h2 class="text-3xl font-bold text-gray-800">Array-of-Objects Type Inference</h2>
<span class="badge badge-success">FIXED</span>
</div>
<!-- Problem -->
<div class="mb-8">
<h3 class="text-2xl font-semibold text-gray-700 mb-4 flex items-center">
<span class="text-3xl mr-3">โ</span>
Problem
</h3>
<p class="text-gray-600 mb-4">
When defining array-of-objects using the syntax <code>videos: [{ ... }]</code>,
TypeScript was incorrectly inferring it as a <strong>tuple type</strong> requiring exactly one element,
instead of <code>Array<T></code>.
</p>
<div class="bg-red-50 border-l-4 border-red-500 p-4 mb-4">
<p class="font-semibold text-red-800 mb-2">TypeScript Error:</p>
<pre class="code-block"><code class="language-typescript">// Before fix - TypeScript error
const schema = Interface({
videos: [{
id: "string",
title: "string"
}]
});
const data = {
videos: [] // โ Error: Property '0' is missing in type '[]'
};</code></pre>
</div>
</div>
<!-- Root Cause -->
<div class="mb-8">
<h3 class="text-2xl font-semibold text-gray-700 mb-4 flex items-center">
<span class="text-3xl mr-3">๐</span>
Root Cause
</h3>
<p class="text-gray-600 mb-4">
In <code>src/core/schema/mode/interfaces/typescript/TypeInference.ts</code>,
the <code>InferFieldType</code> type didn't check for <code>readonly [T]</code>
syntax before checking <code>Array<T></code>. TypeScript treats <code>[{...}]</code>
as a tuple type by default.
</p>
<pre class="code-block"><code class="language-typescript">// Before - Missing tuple check
export type InferFieldType<T> =
T extends string
? HandleOptional<MapFieldType<T>, IsOptional<T>>
: T extends Array<infer U> // โ Doesn't match tuple types
? Array<InferSchemaType<U>>
: // ... other types</code></pre>
</div>
<!-- Solution -->
<div class="mb-8">
<h3 class="text-2xl font-semibold text-gray-700 mb-4 flex items-center">
<span class="text-3xl mr-3">โ
</span>
Solution
</h3>
<p class="text-gray-600 mb-4">
Added check for <code>T extends readonly [infer U]</code> before the <code>Array</code> check.
This ensures tuple syntax is properly detected and converted to array type.
</p>
<pre class="code-block"><code class="language-typescript">// After - Tuple check added
export type InferFieldType<T> =
T extends string
? HandleOptional<MapFieldType<T>, IsOptional<T>>
: T extends readonly [infer U] // โ
Check tuple first
? Array<InferSchemaType<U>>
: T extends Array<infer U>
? Array<InferSchemaType<U>>
: // ... other types</code></pre>
</div>
<!-- Result -->
<div>
<h3 class="text-2xl font-semibold text-gray-700 mb-4 flex items-center">
<span class="text-3xl mr-3">๐</span>
Result
</h3>
<div class="bg-green-50 border-l-4 border-green-500 p-4">
<pre class="code-block"><code class="language-typescript">// After fix - Works correctly
const schema = Interface({
videos: [{
id: "string",
title: "string"
}]
});
type InferredType = InferType<typeof schema>;
// โ
{ videos: Array<{ id: string; title: string }> }
const data1 = { videos: [] }; // โ
Valid
const data2 = { videos: [{ id: "1", title: "Video" }] }; // โ
Valid</code></pre>
</div>
</div>
</div>
</section>
<!-- Bug Fix 2: Runtime Validation -->
<section class="mb-12">
<div class="section-card">
<div class="flex items-start justify-between mb-6">
<h2 class="text-3xl font-bold text-gray-800">Array-of-Objects Runtime Validation</h2>
<span class="badge badge-success">FIXED</span>
</div>
<!-- Problem -->
<div class="mb-8">
<h3 class="text-2xl font-semibold text-gray-700 mb-4 flex items-center">
<span class="text-3xl mr-3">โ</span>
Problem
</h3>
<p class="text-gray-600 mb-4">
Array-of-objects validation was <strong>completely broken</strong>. Invalid data was passing through without any validation:
</p>
<div class="grid md:grid-cols-3 gap-4 mb-4">
<div class="bg-red-50 border border-red-200 rounded-lg p-4">
<p class="font-semibold text-red-800 mb-2">โ Missing Fields</p>
<p class="text-sm text-red-700">Required fields were not validated</p>
</div>
<div class="bg-red-50 border border-red-200 rounded-lg p-4">
<p class="font-semibold text-red-800 mb-2">โ Wrong Types</p>
<p class="text-sm text-red-700">Type mismatches were ignored</p>
</div>
<div class="bg-red-50 border border-red-200 rounded-lg p-4">
<p class="font-semibold text-red-800 mb-2">โ Not Even Array</p>
<p class="text-sm text-red-700">Objects passed as arrays</p>
</div>
</div>
<pre class="code-block"><code class="language-typescript">// Before fix - All these incorrectly passed โ
const schema = Interface({
videos: [{
id: "string",
title: "string",
duration: "number"
}]
});
// Missing required fields - should FAIL but PASSED
schema.parse({ videos: [{ id: "1" }] }); // โ
// Wrong types - should FAIL but PASSED
schema.parse({ videos: [{ id: 123, title: "Test", duration: "wrong" }] }); // โ
// Not even an array - should FAIL but PASSED
schema.parse({ videos: { id: "1", title: "Test" } }); // โ</code></pre>
</div>
<!-- Root Cause -->
<div class="mb-8">
<h3 class="text-2xl font-semibold text-gray-700 mb-4 flex items-center">
<span class="text-3xl mr-3">๐</span>
Root Cause
</h3>
<p class="text-gray-600 mb-4">
The precompiled validator was being used for schemas with array-of-objects, but the precompiler
doesn't handle them. It was calling <code>String(fieldType)</code> on the array <code>[{ ... }]</code>,
converting it to <code>"[object Object]"</code>, and creating a broken validator that did nothing.
</p>
<pre class="code-block"><code class="language-typescript">// In SchemaPrecompiler.ts - The bug
private static compileField(fieldName: string, fieldType: any) {
if (typeof fieldType === "object" && !Array.isArray(fieldType)) {
// Handle nested objects
}
// โ BUG: Arrays fall through to here
const fieldTypeStr = String(fieldType); // "[object Object]"
const validator = FieldPrecompilers.parseAndCompile(fieldTypeStr);
// Creates broken validator!
}</code></pre>
</div>
<!-- Solution -->
<div class="mb-8">
<h3 class="text-2xl font-semibold text-gray-700 mb-4 flex items-center">
<span class="text-3xl mr-3">โ
</span>
Solution
</h3>
<p class="text-gray-600 mb-4">
Added check in <code>InterfaceSchema.ts</code> to detect array-of-objects fields and skip precompilation,
forcing the use of standard validation which correctly handles arrays.
</p>
<pre class="code-block"><code class="language-typescript">// In InterfaceSchema.ts - The fix
private createPrecompiledValidator(): void {
const hasConditionalFields = this.compiledFields.some(
(field) => field.isConditional
);
// โ
NEW: Check for array-of-objects fields
const hasArrayOfObjects = this.compiledFields.some(
(field) => Array.isArray(field.originalType)
);
// Skip precompilation if array-of-objects present
if (!hasConditionalFields && !hasArrayOfObjects && /* other checks */) {
this.precompiledValidator = SchemaPrecompiler.precompileSchema(...);
}
// Otherwise use standard validation (which works correctly)
}</code></pre>
</div>
<!-- Result -->
<div>
<h3 class="text-2xl font-semibold text-gray-700 mb-4 flex items-center">
<span class="text-3xl mr-3">๐</span>
Result
</h3>
<div class="bg-green-50 border-l-4 border-green-500 p-4">
<pre class="code-block"><code class="language-typescript">// After fix - Validation works correctly โ
const schema = Interface({
videos: [{
id: "string",
title: "string",
duration: "number"
}]
});
// Empty array - PASSES (correct, no minimum constraint)
schema.parse({ videos: [] }); // โ
// Valid data - PASSES
schema.parse({
videos: [{ id: "1", title: "Video", duration: 120 }]
}); // โ
// Missing fields - FAILS (correct)
schema.parse({ videos: [{ id: "1" }] });
// โ Error: Missing required field: title
// Wrong types - FAILS (correct)
schema.parse({ videos: [{ id: 123, title: "Test", duration: "wrong" }] });
// โ Error: Expected String, but received Number
// Not an array - FAILS (correct)
schema.parse({ videos: { id: "1" } });
// โ Error: Expected Array, but received Object</code></pre>
</div>
</div>
</div>
</section>
<!-- Test Results -->
<section class="mb-12">
<div class="section-card">
<h2 class="text-3xl font-bold text-gray-800 mb-6">โ
Test Results</h2>
<div class="overflow-x-auto">
<table>
<thead>
<tr>
<th>Test Case</th>
<th>Before</th>
<th>After</th>
<th>Status</th>
</tr>
</thead>
<tbody>
<tr>
<td>Empty array</td>
<td><span class="badge badge-success">PASS</span></td>
<td><span class="badge badge-success">PASS</span></td>
<td>โ
Correct</td>
</tr>
<tr>
<td>Valid array elements</td>
<td><span class="badge badge-success">PASS</span></td>
<td><span class="badge badge-success">PASS</span></td>
<td>โ
Correct</td>
</tr>
<tr>
<td>Missing required fields</td>
<td><span class="badge badge-success">PASS</span></td>
<td><span class="badge badge-error">FAIL</span></td>
<td>โ
Fixed</td>
</tr>
<tr>
<td>Wrong types</td>
<td><span class="badge badge-success">PASS</span></td>
<td><span class="badge badge-error">FAIL</span></td>
<td>โ
Fixed</td>
</tr>
<tr>
<td>Not an array</td>
<td><span class="badge badge-success">PASS</span></td>
<td><span class="badge badge-error">FAIL</span></td>
<td>โ
Fixed</td>
</tr>
<tr>
<td>TypeScript type inference</td>
<td><span class="badge badge-error">Tuple</span></td>
<td><span class="badge badge-success">Array</span></td>
<td>โ
Fixed</td>
</tr>
</tbody>
</table>
</div>
</div>
</section>
<!-- Migration Guide -->
<section>
<div class="section-card bg-blue-50 border-l-4 border-blue-500">
<h2 class="text-2xl font-bold text-gray-800 mb-4">๐ Migration Guide</h2>
<p class="text-gray-700 mb-4">
<strong>Good news!</strong> These fixes are backward compatible. No changes needed to your existing code.
</p>
<ul class="list-disc list-inside space-y-2 text-gray-700">
<li>Empty arrays remain valid by default (use array length constraints if you need minimums)</li>
<li>TypeScript types are now more accurate</li>
<li>Validation is now stricter and catches errors that were previously missed</li>
</ul>
</div>
</section>
</main>
<!-- Footer -->
<footer class="bg-gray-800 text-white mt-16 py-8">
<div class="container mx-auto px-6 text-center">
<p class="mb-2">ยฉ 2025 Nehonix Team. All rights reserved.</p>
<p class="text-gray-400 text-sm">
<a href="https://github.com/Nehonix-Team/fortify-schema" class="hover:text-white">GitHub</a> ยท
<a href="https://www.npmjs.com/package/fortify-schema" class="hover:text-white">NPM</a>
</p>
</div>
</footer>
<script>
hljs.highlightAll();
</script>
</body>
</html>