/** * dbtype probe: End-to-end validation of core architecture * * Validates: * 1. UJSX elements construct schema trees * 2. Function components compose (IdCol, AuditCols) * 3. Element trees → Type.Module (with Type.Ref for relations) * 4. Value.Check on module-derived schemas (with format registration) * 5. Drizzle table rendering from column metadata * 6. Schema derivation (select, insert, update) from module * 7. Serialization roundtrip (JSON Schema with $defs) * 8. Migration diff via Value.Diff * 9. Incremental module construction (defs map) */ import { Type, FormatRegistry } from '@alkdev/typebox' import { Value } from '@alkdev/typebox/value' import { h, createComponent, isUElement } from '@alkdev/ujsx' import { sqliteTable, text, integer } from 'drizzle-orm/sqlite-core' import { sql } from 'drizzle-orm' let passed = 0 let failed = 0 function assert(label: string, condition: boolean) { if (condition) { passed++; console.log(` ✓ ${label}`) } else { failed++; console.log(` ✗ ${label}`) } } // ── Format registration ── FormatRegistry.Set('uuid', (v) => /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(v)) FormatRegistry.Set('email', (v) => /^[^@]+@[^@]+\.[^@]+$/.test(v)) const UUID = '550e8400-e29b-41d4-a716-446655440000' // ── 1. UJSX element construction ── console.log('\n=== 1. UJSX element construction ===') const col = h('column', { name: 'id', type: 'uuid', primaryKey: true }) assert('column element type', col.type === 'column') assert('column props', col.props.name === 'id' && col.props.type === 'uuid') assert('column primaryKey', col.props.primaryKey === true) const tbl = h('table', { name: 'users' }, col) assert('table element type', tbl.type === 'table') assert('table has child', tbl.children.length === 1) // ── 2. Function components ── console.log('\n=== 2. Function components ===') const IdCol = createComponent('IdCol', () => h('column', { name: 'id', type: 'uuid', primaryKey: true, default: 'uuid' }) ) const AuditCols = createComponent('AuditCols', () => [ h('column', { name: 'createdAt', type: 'timestamp', notNull: true, default: 'now' }), h('column', { name: 'updatedAt', type: 'timestamp', notNull: true, default: 'now' }), ]) const audit = AuditCols({}) assert('AuditCols returns array', Array.isArray(audit) && audit.length === 2) assert('AuditCols first child type', audit[0].type === 'column') // ── 3. Element tree → Type.Module ── console.log('\n=== 3. Element → Type.Module ===') function colToTypeBox(type: string, props: Record) { switch (type) { case 'uuid': return Type.String({ format: 'uuid' }) case 'string': return Type.String() case 'integer': return Type.Integer() case 'boolean': return Type.Boolean() case 'timestamp': return Type.Number() case 'enum': return Type.Union((props.values || []).map((v: string) => Type.Literal(v))) default: return Type.String() } } function extractTable(el: any): { name: string; schema: any; columns: Record } { const props: Record = {} const meta: Record = {} function walk(node: any) { if (!node) return if (Array.isArray(node)) { node.forEach(walk); return } if (!isUElement(node)) return if (typeof node.type === 'function') { walk(node.type({ ...node.props, children: node.children })) return } if (node.type === 'column') { props[node.props.name] = colToTypeBox(node.props.type, node.props) meta[node.props.name] = { ...node.props } } if (node.children) walk(node.children) } walk(el) return { name: el.props.name, schema: Type.Object(props), columns: meta } } const UsersEl = h('table', { name: 'users' }, h(IdCol, {}), h('column', { name: 'name', type: 'string', notNull: true }), h('column', { name: 'email', type: 'string', notNull: true }), h(AuditCols, {}), ) const TasksEl = h('table', { name: 'tasks' }, h(IdCol, {}), h('column', { name: 'title', type: 'string', notNull: true }), h('column', { name: 'userId', type: 'uuid', notNull: true, references: 'users' }), h(AuditCols, {}), ) const users = extractTable(UsersEl) const tasks = extractTable(TasksEl) assert('Users has 5 columns', Object.keys(users.columns).length === 5) assert('Users.id is uuid PK', users.columns.id.type === 'uuid' && users.columns.id.primaryKey) assert('Users.createdAt has default now', users.columns.createdAt.default === 'now') assert('Tasks.userId references users', tasks.columns.userId.references === 'users') // Build module const defs: Record = { Users: users.schema, Tasks: tasks.schema, UsersRelations: Type.Object({ tasks: Type.Array(Type.Ref('Tasks')) }), TasksRelations: Type.Object({ user: Type.Ref('Users') }), } const M = Type.Module(defs as any) const MUsers = M.Import('Users') const MTasks = M.Import('Tasks') const MRel = M.Import('UsersRelations') const validUser = { id: UUID, name: 'alice', email: 'a@b.com', createdAt: 1, updatedAt: 1 } const validTask = { id: UUID, title: 'build', userId: UUID, createdAt: 1, updatedAt: 1 } assert('Module User validates', Value.Check(MUsers, validUser)) assert('Module Task validates', Value.Check(MTasks, validTask)) assert('Module Relations validates', Value.Check(MRel, { tasks: [validTask] })) assert('Module rejects invalid user', !Value.Check(MUsers, { id: 'bad', name: '', email: '', createdAt: 0, updatedAt: 0 })) // ── 4. Schema derivation from module ── console.log('\n=== 4. Schema derivation ===') defs.InsertUsers = Type.Object({ name: Type.String(), email: Type.String(), }) defs.UpdateUsers = Type.Partial(Type.Ref('Users')) const M2 = Type.Module(defs as any) const InsertUser = M2.Import('InsertUsers') const UpdateUser = M2.Import('UpdateUsers') assert('Insert validates correct', Value.Check(InsertUser, { name: 'alice', email: 'a@b.com' })) assert('Insert rejects empty', !Value.Check(InsertUser, {})) assert('Update validates partial', Value.Check(UpdateUser, { name: 'bob' })) // ── 5. Drizzle rendering ── console.log('\n=== 5. Drizzle rendering ===') function renderSqliteTable(meta: { name: string; columns: Record }) { const cols: Record = {} for (const [name, props] of Object.entries(meta.columns)) { let builder: any switch (props.type) { case 'uuid': builder = text(name) if (props.primaryKey) builder = builder.primaryKey() if (props.default === 'uuid') builder = builder.$defaultFn(() => crypto.randomUUID()) break case 'string': builder = text(name) if (props.notNull) builder = builder.notNull() break case 'timestamp': builder = integer(name, { mode: 'timestamp' as const }) if (props.notNull) builder = builder.notNull() if (props.default === 'now') builder = builder.default(sql`(strftime('%s', 'now'))`) break } if (builder) cols[name] = builder } return sqliteTable(meta.name, cols) } const drizzleUsers = renderSqliteTable(users) const drizzleTasks = renderSqliteTable(tasks) const uCols = drizzleUsers[Symbol.for('drizzle:Columns')] assert('Drizzle users has 5 columns', Object.keys(uCols).length === 5) assert('Drizzle users.id is PK', uCols.id?.primaryKey) assert('Drizzle users.id notNull', uCols.id?.notNull) // ── 6. Serialization ── console.log('\n=== 6. Serialization ===') const ser = JSON.parse(JSON.stringify(M2.Import('Users'))) assert('Serialized has $defs', !!ser.$defs) assert('$defs has Users', 'Users' in (ser.$defs || {})) assert('$defs has UsersRelations', 'UsersRelations' in (ser.$defs || {})) assert('UsersRelations.tasks refs Tasks', ser.$defs?.UsersRelations?.properties?.tasks?.items?.$ref === 'Tasks') assert('TasksRelations.user refs Users', ser.$defs?.TasksRelations?.properties?.user?.$ref === 'Users') // ── 7. Migration diff ── console.log('\n=== 7. Migration diff ===') const v1 = JSON.parse(JSON.stringify(M2.Import('Users'))) defs.Users = Type.Object({ id: Type.String({ format: 'uuid' }), name: Type.String(), email: Type.String(), role: Type.String(), createdAt: Type.Number(), updatedAt: Type.Number(), }) const M3 = Type.Module(defs as any) const v2 = JSON.parse(JSON.stringify(M3.Import('Users'))) const edits = Value.Diff(v1, v2) const roleEdits = edits.filter((e: any) => e.path?.includes('role')) assert('Migration detects new column', roleEdits.length > 0) assert('Migration edit path includes role', roleEdits.some((e: any) => e.path?.includes('role'))) // ── 8. Incremental module construction ── console.log('\n=== 8. Incremental defs ===') const incrDefs: Record = {} incrDefs.Users = Type.Object({ id: Type.String(), name: Type.String() }) incrDefs.Tasks = Type.Object({ id: Type.String(), userId: Type.String(), title: Type.String() }) // Mutate: add email to Users after initial construction incrDefs.Users = Type.Object({ id: Type.String(), name: Type.String(), email: Type.String() }) const IM = Type.Module(incrDefs as any) assert('Incremental module validates', Value.Check(IM.Import('Users'), { id: '1', name: 'a', email: 'b' })) // ── Summary ── console.log(`\n=== Results: ${passed} passed, ${failed} failed ===`) process.exit(failed > 0 ? 1 : 0)