Files
dbtype/docs/architecture/elements.md
glm-5.1 dd2ec9df3c Add SDD architecture docs for dbtype
Phase 0 architecture specification following the alkdev documentation
pattern from @alkdev/flowgraph. Documents the validated architecture
(UJSX elements → Type.Module → Drizzle hosts) based on e2e probe results.

Docs added:
- README: Project overview, architecture, current state
- architecture/README: Index, design decisions, relationships
- architecture/schema: Type.Module as bundle, construction, serialization
- architecture/hosts: HostConfig per dialect, column mapping, symbolic defaults
- architecture/elements: UJSX element types, props, function components
- architecture/module: Module mechanics, format registration, diffing
- architecture/repo-adapter: from-dbtype operations adapter (phase 2)
- architecture/build-distribution: Package structure, exports
- architecture/open-questions: 10 open questions across all topics
- ADRs 001-005: UJSX as IR, Type.Module, HostConfig, format, repo adapter
2026-05-22 11:34:58 +00:00

6.8 KiB

status, last_updated
status last_updated
draft 2026-05-22

Elements: UJSX Element Definitions

The UJSX element types, their props, function components, and how they compose.

Overview

dbtype elements are UJSX elements (h() calls or JSX) with a constrained set of tags: table, column, index, fk. Each element carries typed props that encode both validation metadata (for TypeBox) and database metadata (for Drizzle rendering).

Element Types

<table>

The top-level schema element. Contains <column>, <index>, and <fk> children.

Prop Type Required Description
name string yes Table name in the database

Children are column, index, and foreign key elements. Function component children that return column elements are transparent (their output is used, not the component itself).

<column>

A single column definition within a table.

Prop Type Required Description
name string yes Column name
type DbColumnType yes Column type vocabulary
notNull boolean no Column is NOT NULL
primaryKey boolean no Column is primary key
unique boolean no Column has UNIQUE constraint
default DbDefault | unknown no Symbolic default or literal value
references string no FK target table name
format string no TypeBox format annotation (uuid, email, etc.)
mode 'json' | 'text' no Storage mode for compound types
values string[] no Enum values (for type: 'enum')
length number no Max length (for varchar)
precision number no Numeric precision
scale number no Numeric scale
postgres PgColumnOpts no PG-specific overrides
sqlite SqliteColumnOpts no SQLite-specific overrides
mysql MySqlColumnOpts no MySQL-specific overrides

<index>

An index definition on a table.

Prop Type Required Description
name string yes Index name
columns string[] yes Column names in the index
unique boolean no Whether the index is unique

<fk>

A foreign key constraint.

Prop Type Required Description
columns string[] yes Local column names
references string yes Target table name
foreignColumns string[] yes Target column names
onDelete 'cascade' | 'set null' | 'restrict' | 'no action' no ON DELETE action
onUpdate 'cascade' | 'set null' | 'restrict' | 'no action' no ON UPDATE action

Column Type Vocabulary

DbColumnType is the cross-dialect type vocabulary. Each value maps to a specific Drizzle column builder per dialect:

type DbColumnType =
  | 'uuid'
  | 'string'
  | 'text'
  | 'varchar'
  | 'integer'
  | 'bigint'
  | 'boolean'
  | 'timestamp'
  | 'real'
  | 'numeric'
  | 'enum'
  | 'json'
  | 'array'
  | 'object'

The mapping to TypeBox and Drizzle types is defined in the host, but the element tree uses DbColumnType as the universal vocabulary.

Symbolic Defaults

type DbDefault =
  | 'now'            // Current timestamp (dialect-specific SQL)
  | 'uuid'           // UUID generation (dialect-specific mechanism)
  | 'autoincrement'  // Auto-incrementing integer
  | 'current_timestamp' // CURRENT_TIMESTAMP
  | unknown          // Literal value or SQL expression

Function Components

Reusable column groups defined as UJSX components:

// Common ID column
const IdColumn = createComponent('IdColumn', () =>
  h('column', { name: 'id', type: 'uuid', primaryKey: true, default: 'uuid' })
)

// Audit timestamp columns
const AuditColumns = createComponent('AuditColumns', () => [
  h('column', { name: 'createdAt', type: 'timestamp', notNull: true, default: 'now' }),
  h('column', { name: 'updatedAt', type: 'timestamp', notNull: true, default: 'now' }),
])

// Usage
const UsersEl = h('table', { name: 'users' },
  h(IdColumn, {}),
  h('column', { name: 'name', type: 'string', notNull: true }),
  h(AuditColumns, {}),
)

Function components are transparent — the host never sees them. The createInstance method only receives resolved intrinsic elements (column, table).

Tree Walking

The extractTable() function walks an element tree, resolving function components, and produces:

  1. TypeBox schema: Type.Object({ ... }) for the module entry
  2. Column metadata: Record<string, ColumnProps> for Drizzle rendering
  3. Table metadata: { name, columns, indexes, foreignKeys } for the host
function extractTable(el: UElement): {
  name: string
  schema: TObject           // TypeBox schema for Type.Module
  columns: Record<string, ColumnMeta>  // Props + computed metadata
  indexes: IndexMeta[]
  foreignKeys: FkMeta[]
}

For each column, extractTable:

  • Calls colToTypeBox(type, props) to produce the inner TypeBox schema
  • Stores the full props object as metadata for the host
  • Tracks primaryKey, default, notNull, references for schema derivation (insert, update)

Constraints

  • Column elements must have name and type props — these are required for both TypeBox schema construction and Drizzle rendering
  • Function components must return column elements (or arrays of column elements) — returning other element types inside a table is undefined
  • The references prop is metadata-only — it's not part of the TypeBox schema. It informs the host about FK constraints and the repo adapter about relation structure
  • default values can be symbolic strings or literals — symbolic defaults (now, uuid, autoincrement) are resolved by the host; literal values pass through directly

Open Questions

  1. Should column elements support inner TypeBox schemas? The research docs proposed DbType.String({ notNull: true, inner: Type.String({ format: 'email', maxLength: 255 }) }). With UJSX elements, this would be <column name="email" type="string" notNull format="email" maxLength={255} />. Is the flat props model sufficient, or do we need nested TypeBox schemas?

  2. Should <table> accept extraConfig props? Drizzle tables accept a third callback argument for indexes and unique constraints defined in terms of column references. How does this map to element props?

  3. Should we support JSX file extensions? The current probe uses h() calls. JSX syntax (.tsx files with jsxImportSource: '@alkdev/ujsx') would be more ergonomic for authoring but requires build configuration.

References

  • UJSX element factory: @alkdev/ujsx/src/core/h.ts
  • UJSX HostConfig: @alkdev/ujsx/src/host/config.ts
  • Probe element construction: scripts/probe-e2e.ts
  • Research: docs/research/architecture.md (DbTypeBuilder section)