# TypeBox Module & ValuePointer: Core Patterns for UJSX Source: `/workspace/@alkdev/typebox` (npm: `@alkdev/typebox`) ## TModule: The Schema Registry `Type.Module()` creates a `TModule` whose `$defs` property is a live `string → TSchema` map. This IS the type registry UJSX needs — no separate registry required. ### Core Operations ```typescript const UJSX = Type.Module({ Props: Type.Object({ id: Type.Optional(Type.String()) }, { additionalProperties: Type.Unknown() }), Element: Type.Object({ type: Type.String(), props: Type.Ref("Props"), children: Type.Array(Type.Ref("Node")), }), Node: Type.Union([Type.String(), Type.Number(), Type.Null(), Type.Ref("Element")]), }) ``` ### Read schemas by name ```typescript import { ValuePointer } from "@alkdev/typebox/value"; ValuePointer.Get(UJSX, "$defs/Props") // TSchema with $id: "Props" ValuePointer.Get(UJSX, "$defs/Element") // TSchema with $id: "Element" ``` ### Add schemas at runtime ```typescript ValuePointer.Set(UJSX, "$defs/Heading", Type.Object({ type: Type.Literal("heading"), depth: Type.Number(), children: Type.Array(Type.Ref("Node")), })) // Now UJSX.$defs.Heading exists and Type.Ref("Heading") resolves ``` ### Import: Resolved schemas for validation ```typescript const Element = UJSX.Import("Element") // Creates TImport with: { [Kind]: 'Import', $defs: { all module defs inlined }, $ref: "Element" } Value.Check(Element, someData) // Works! All $refs resolved via inlined $defs ``` ### Static type inference ```typescript type UE = Static // UE = { type: string; props: { id?: string; [k: string]: unknown }; children: UE[] } ``` The Module's type-level inference handles cycles through `TRef` — no `Type.Recursive` needed. ## How Import Works (from source) Module source: `/workspace/@alkdev/typebox/src/type/module/index.ts` ```typescript public Import(key: Key, options?: SchemaOptions): TImport { const $defs = { ...this.$defs, [key]: CreateType(this.$defs[key], options) } return CreateType({ [Kind]: 'Import', $defs, $ref: key }) } ``` Creates a `TImport` schema with: - `[Kind]: 'Import'` — uniquely identifies as module import - `$defs` — ALL module definitions copied in (so $refs resolve) - `$ref: key` — which definition to use as root ## ComputeModuleProperties Called during Module construction. Walks every property and: 1. Resolves `Type.Ref("X")` → dereferences to actual type at `moduleProperties[X]` 2. Recursively processes: `Type.Array(Type.Ref("Node"))` → `Type.Array(ResolvedNode)` 3. Handles all TypeBox combinators: Object, Union, Intersect, Function, Record, etc. 4. Adds `$id` to each definition via `WithIdentifiers` ## Function Types in Modules ```typescript Type.Function([Type.Ref("Props")], Type.Ref("Node")) ``` Creates a JavaScript-extended schema type `{ type: "Function", parameters: [...], returns: {...} }`. Not standard JSON Schema but valid TypeBox. `Value.Check` validates that the value is a function at runtime. This means component functions CAN be represented in the schema. For serialization, resolve function components to string identifiers before going to mdast/other targets. ## Implications for UJSX v2 1. **No separate type registry needed** — `TModule` IS the registry 2. **Schemas ARE types AND tool parameter schemas** — one definition, triple duty 3. **Bi-directional transforms can be schema-driven** — `Value.Check(ruleSchema, node)` instead of string matching 4. **Node definitions can be added at runtime** — plugins can extend the IR via `ValuePointer.Set()` 5. **`Import` creates self-contained schemas** — perfect for tool definitions or validation contexts 6. **Function components are first-class in schema** — separate runtime vs serializable representations 7. **No `Type.Recursive` needed in Module** — cycles handle through `TRef` + type-level inference