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:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user