Problem
fields() currently returns FieldMeta which mixes three concerns:
- Database metadata (
primaryKey, autoIncrement, inserted, etc.) - from .db.*() methods
- Form interpretation (
type: "email" | "textarea", required, etc.) - computed/inferred
- Zod constraints (
min, max, minLength, maxLength, options) - extracted from schema internals
This creates problems:
- Form logic doesn't belong in a database library
- HTML input types (
FieldType) are opinionated and leak into core
- Consumers can't easily access raw db metadata without the "cooking"
- The flattened structure loses information
Solution
1. Simplify fields() to return raw data
interface FieldInfo {
name: string;
schema: ZodType; // raw Zod schema
db: FieldDBMeta; // raw db metadata from .db.*()
}
interface Relation {
table: Table;
fields(): TableFields; // navigate to related table
}
type TableFields = Record<string, FieldInfo | Relation>;
2. Remove from Zen
FieldType (HTML input types)
FieldMeta (the cooked/flattened type)
extractFieldMeta() (the cooking function)
- Computed properties:
type, required, min, max, minLength, maxLength, options
3. Create @b9g/forms (separate package)
Headless schema introspection for form generation:
- Zod type inference → field types
- Constraint extraction → validation hints
- Db-awareness → skip auto-generated fields, handle relations
- Works with any form library (TanStack Form, RHF, etc.)
Breaking Changes
fields() return type changes from FieldMeta to FieldInfo
FieldType and FieldMeta exports removed
- Consumers needing form metadata should use
@b9g/forms (or access schema.meta() directly)
Migration
// Before
const fields = Users.fields();
fields.email.type; // "email"
fields.email.required; // true
// After
const fields = Users.fields();
fields.email.schema; // ZodString (use Zod APIs)
fields.email.db.autoIncrement; // boolean
fields.email.schema.isOptional(); // use Zod directly
// Or use @b9g/forms for form-specific metadata
import { formFields } from "@b9g/forms";
const form = formFields(Users);
form.email.type; // "email"
form.email.required; // true
Related
Problem
fields()currently returnsFieldMetawhich mixes three concerns:primaryKey,autoIncrement,inserted, etc.) - from.db.*()methodstype: "email" | "textarea",required, etc.) - computed/inferredmin,max,minLength,maxLength,options) - extracted from schema internalsThis creates problems:
FieldType) are opinionated and leak into coreSolution
1. Simplify
fields()to return raw data2. Remove from Zen
FieldType(HTML input types)FieldMeta(the cooked/flattened type)extractFieldMeta()(the cooking function)type,required,min,max,minLength,maxLength,options3. Create
@b9g/forms(separate package)Headless schema introspection for form generation:
Breaking Changes
fields()return type changes fromFieldMetatoFieldInfoFieldTypeandFieldMetaexports removed@b9g/forms(or accessschema.meta()directly)Migration
Related
fields()introspection aligns with the principle that validation should happen on writes, not reads/introspection