docs: resolve architecture open questions, add type definitions, consolidate docs

Architecture review session resolving all high-priority open questions and
filling documentation gaps identified during review:

Decisions resolved:
- OQ-04: Flat props with inner escape hatch for column validation (ADR-007)
- OQ-05: PG enum pre-declaration returns enums and tables (ADR-008)
- OQ-06: Render results accumulate in root.ctx (resolved in hosts.md)
- Column references vs fk: references is shorthand, explicit fk takes
  precedence (ADR-006)
- ADR-001, 002, 003 promoted from Proposed to Accepted (probe-validated)

Documentation improvements:
- Complete DbColumnType mapping tables for all 14 types across 3 dialects
- Define ColumnMeta, TableMeta, IndexMeta, FkMeta types in elements.md
- Document inner prop, mode prop, and default prop semantics
- Add PgRootCtx, SqliteRootCtx, MySqlRootCtx context types
- Consolidate schema.md and module.md (remove duplication)
- Add end-to-end pipeline walkthrough to README
- Add glossary with 13 terms
- Add error handling strategy
- Remove duplicate content from hosts.md (cross-ref elements.md)
This commit is contained in:
2026-05-23 12:06:51 +00:00
parent 4644e1b362
commit d4fd67f4d2
12 changed files with 476 additions and 221 deletions

View File

@@ -1,17 +1,11 @@
---
status: draft
last_updated: 2026-05-22
status: stable
last_updated: 2026-05-23
---
# Module: Type.Module as the Schema Bundle
# Module: Type.Module Mechanical Reference
Technical details on how dbtype uses `Type.Module` for schema construction, validation, serialization, and migration.
## Overview
dbtype uses `@alkdev/typebox`'s `Type.Module` as the schema storage and resolution mechanism. A module holds all table schemas, their relations, and derived schemas (insert, update, partial) in a single flat namespace. `Type.Ref` resolves cross-table references — including circular ones — without import ordering issues.
This document covers the mechanics, constraints, and patterns discovered during architecture probing.
How `Type.Module` works mechanically — construction, validation, serialization, migration diffing, cross-module references, and constraints. For the domain-specific content (what goes into a dbtype module, schema derivation semantics, relations), see [schema.md](schema.md).
## Construction Patterns
@@ -27,6 +21,27 @@ const M = Type.Module(defs)
const Users = M.Import('Users')
```
### Incremental Construction
The defs map is a plain `Record<string, TSchema>` — it can be built incrementally, mutated, and extended before compilation:
```typescript
const defs: Record<string, any> = {}
defs.Users = extractTableSchema(UsersElement)
defs.Tasks = extractTableSchema(TasksElement)
// Add a column later
defs.Users = Type.Object({ ...defs.Users.properties, role: Type.String() })
// Add relations
defs.UsersRelations = Type.Object({ tasks: Type.Array(Type.Ref('Tasks')) })
// Compile when ready
const M = Type.Module(defs)
```
Once compiled, mutations to the original defs map don't affect the compiled module.
### With Relations
```typescript
@@ -43,24 +58,6 @@ defs.InsertUsers = Type.Object({ name: Type.String(), email: Type.String() }) /
defs.UpdateUsers = Type.Partial(Type.Ref('Users')) // computed
```
### Incremental Construction
```typescript
// Build defs incrementally
const defs: Record<string, any> = {}
defs.Users = extractTableSchema(UsersElement)
defs.Tasks = extractTableSchema(TasksElement)
// Add a column later
defs.Users = Type.Object({ ...defs.Users.properties, role: Type.String() })
// Add relations
defs.UsersRelations = Type.Object({ tasks: Type.Array(Type.Ref('Tasks')) })
// Compile when ready
const M = Type.Module(defs)
```
## Validation
### Format Registration Required
@@ -107,13 +104,13 @@ for (const err of Value.Errors(Users, badData)) { ... }
Key properties:
- Each `$defs` entry has an `$id` matching its key
- `Type.Ref` remains as `{ "$ref": "Key" }` — not inlined
- `Type.Ref` remains as `{ "$ref": "Key" }` — not inlined; consumers must resolve them
- The entire structure is valid JSON Schema
- All entries in the module are present in `$defs` (even if only one was imported)
### Roundtrip
The serialized form can be parsed back into a schema-like structure. `Value.Diff` works on these serialized objects to produce structural edit lists.
The serialized form can be parsed back into a schema-like structure. `Value.Diff` works on these serialized objects to produce structural edit lists. Note that Symbol properties (`[Kind]`, `[Hint]`, etc.) are stripped by `JSON.stringify` — the serialized form is JSON Schema, not TypeBox schema. Roundtripping requires `FromSchema` or reconstructed TypeBox objects.
## Migration Diffing
@@ -160,7 +157,10 @@ However, this nests `$defs` within `$defs` (the User's `$defs` contains CommonUu
- **Module entries are computed at construction time** — `Type.Partial(Type.Ref('Users'))` is resolved when the module is built, producing a concrete optional-property object
- **`Type.Ref` outside a module has `static: unknown`** — always use `M.Import(key)` for proper type inference
- **Module keys are a flat namespace** — no nested paths like `"tables/Users"`. Table names must be unique within the module.
- **`Type.Ref` resolves within the module only** — no cross-module references without `Module.Import`
- **`Module.Import` embeds all `$defs`** — every import carries the full module. This is correct for validation but increases JSON Schema size.
- **Defs map is mutable until compiled** — once passed to `Type.Module`, mutations to the original map don't affect the compiled module
- **Format validation requires `FormatRegistry.Set`** — `uuid`, `email`, and other custom formats must be registered before `Value.Check` will enforce them
- **Symbol properties are lost in `JSON.stringify`** — `[Kind]`, `[Hint]`, etc. are stripped. The serialized form is JSON Schema, not TypeBox schema. Roundtripping requires `FromSchema` or reconstructed TypeBox objects.
## References