stabilize architecture docs: address review findings and advance to stable

Critical fixes:
- Restructure pointers.md: move setNode prop-key writes section under
  its own heading (was incorrectly nested under selectNode)
- Add Context/Density/Direction/RenderContext documentation section
  to host-config.md (was only a brief constraint bullet)
- Advance all 5 ADRs from Status: Proposed → Accepted and frontmatter
  from status: draft → status: stable (decisions are driving implementation)
- Add error handling philosophy section to README

Warning/suggestion fixes:
- Add isUElement null check (node !== null) to schema.md discriminator table
- Add UjsxEnvelope convenience type documentation to events.md
- Add Direction Unicode arrow naming note to transforms.md
- Standardize all cross-references from absolute docs/research/ paths
  to relative ../research/ paths across all architecture docs
- Fix schema.md ADR references to use relative paths
- Reduce redundancy between transforms.md and host-config.md Direction notes
- Update all architecture doc frontmatter from draft → stable

Deferred:
- Performance model section (reconciler not yet built)
- Concepts/glossary document (low ROI at current scale)
- Line counts in source references (would date quickly)
This commit is contained in:
2026-05-18 16:10:24 +00:00
parent 23659233ca
commit 0d5b9d5ea8
16 changed files with 167 additions and 61 deletions

View File

@@ -1,5 +1,5 @@
---
status: draft
status: stable
last_updated: 2026-05-18
---
@@ -82,6 +82,85 @@ interface Root<TTag extends string, Instance, RootCtx> {
The `Context` connection is intentional: UJSX wants hosts to make density-aware and target-aware decisions without the host needing to know about signal internals. `Context` provides a reactive `ContextValue` that hosts can read during `createInstance` or `commitUpdate`.
## Context, Density, Direction & RenderContext
The `context.ts` module exports a set of related types and a class that support adaptive rendering and directional transforms:
### Context (class)
```typescript
class Context {
constructor(initial?: Partial<ContextValue>)
get(): ContextValue
get signal(): ReadonlySignal<ContextValue>
set(partial: Partial<ContextValue>): void
subscribe(fn: (value: ContextValue) => void): () => void
fork(overrides: Partial<ContextValue>): Context
}
```
`Context` wraps a Preact `signal<ContextValue>` and provides reactive access to context data. Hosts read context during `createInstance` or `commitUpdate` to adapt rendering (e.g., switching layouts based on `density`). The `signal` getter exposes the underlying `ReadonlySignal` for composition with other reactive primitives.
- **`get()`** — returns the current `ContextValue` (non-reactive read).
- **`set(partial)`** — shallow-merges `partial` into the current value inside a `batch()`, triggering any subscriptions.
- **`subscribe(fn)`** — calls `fn` on every change via `effect()`. Returns a dispose function.
- **`fork(overrides)`** — creates a new `Context` with the current values shallow-merged with `overrides`. Forked contexts are independent — changes to the fork do not propagate to the parent.
### ContextValue
```typescript
interface ContextValue {
density: Density;
target: string;
metadata: Record<string, unknown>;
}
```
The shape of a context's value. Defaults:
- `density`: `"full"`
- `target`: `"markdown"`
- `metadata`: `{}`
### Density
```typescript
type Density = "full" | "compact" | "minimal"
```
Controls rendering granularity for hosts that support adaptive output. `full` means render everything; `compact` and `minimal` are host-defined — UJSX passes the value through, it does not interpret it. A desktop UI host might use `compact` to hide labels and `minimal` to render only essential controls.
### Direction
```typescript
type Direction = "ujsx→mdast" | "mdast→ujsx" | "ujsx→jpath" | "jpath→ujsx" | "ujsx→hast" | "hast→ujsx"
```
Six directional strings pairing into three bi-directional channels (markdown, JSON path, HTML). `Direction` is defined in `context.ts` because it governs both transform rules and render context — it's not transform-specific.
The `→` character in direction strings is a Unicode right arrow (U+2192). This was chosen for readability over alternatives like `ujsx-to-mdast` or `ujsx2mdast`. Consumers should be aware of the non-ASCII characters in IDE autocompletion and linting contexts.
### RenderContext
```typescript
interface RenderContext extends ContextValue {
direction: Direction;
}
```
A convenience type that adds `direction` to `ContextValue`. Used primarily by the transform system to carry conversion direction alongside context data. The transform `ctx` factory function accepts a `Direction` and returns a `TransformContext` (which includes `direction`), not a `RenderContext``RenderContext` exists for consumers that want to type-narrow the full context shape.
### Exports
All context types are available from the barrel export (`@alkdev/ujsx`) and the `context` sub-path:
```typescript
import { Context } from "@alkdev/ujsx/context"; // class
import type { Density, Direction, RenderContext } from "@alkdev/ujsx/context"; // types
```
`Context` is a runtime export; `Density`, `Direction`, and `RenderContext` are type-only exports.
## createRoot()
```typescript
@@ -170,7 +249,7 @@ unmount() {
This means calling `unmount()` followed by creating a new root on the same container will likely result in leaked instances and stale signal effects.
The reconciler architecture ([reconciler.md](reconciler.md)) and lifecycle management ([lifecycle.md](lifecycle.md)) address both gaps. The research documents (`docs/research/reconciler/01-reactive-host-bridge.md` and `03-unmount-dispose-support.md`) provide the detailed implementation plans.
The reconciler architecture ([reconciler.md](reconciler.md)) and lifecycle management ([lifecycle.md](lifecycle.md)) address both gaps. The research documents (`../research/reconciler/01-reactive-host-bridge.md` and `../research/reconciler/03-unmount-dispose-support.md`) provide the detailed implementation plans.
### Event IDs Use `Date.now()`
@@ -193,7 +272,7 @@ The reconciler solves this by maintaining a fiber tree alongside the instance tr
- **`render()` accepts any `UNode`** — if the node is a `URoot`, its children are mounted directly. If the node is any other `UNode` (element or primitive), it is wrapped in an array and mounted as a single top-level element without a root container.
- **Function components are synchronous and transparent** — they receive props and children, return a `UNode`, and produce no host instance. The reconciler research discusses how to handle components that return different tree shapes across renders.
- **`Context` is always present** — `createRoot()` guarantees a `Context` exists on the `Root`. If none is provided, a default is created with `density: "full"`, `target: "markdown"`, and empty `metadata`.
- **`RenderContext` extends `ContextValue`** — the `Direction` type (`"ujsx→mdast" | "mdast→ujsx" | "ujsx→jpath" | "jpath→ujsx" | "ujsx→hast" | "hast→ujsx"`) and `RenderContext` interface (which adds `direction` to `ContextValue`) are exported from the `context` sub-path. `RenderContext` is primarily used by the transform system to specify conversion direction. `Density` (`"full" | "compact" | "minimal"`) controls rendering granularity for hosts that support adaptive output.
- **`RenderContext` extends `ContextValue`** — adds `direction` to the context value. See [Context, Density, Direction & RenderContext](#context-density-direction--rendercontext) for full documentation of these types.
- **`container` is opaque** — UJSX passes it to `createRootContext` and stores it on `Root`, but never inspects it. The host defines what it means.
- **Mount is depth-first, post-order** — children are fully constructed before being appended to their parent. Hosts can rely on this ordering invariant.
@@ -216,4 +295,4 @@ The reconciler solves this by maintaining a fiber tree alongside the instance tr
- Context: `src/core/context.ts``Context` class with signal-based values
- Reconciler architecture: [reconciler.md](reconciler.md)
- Lifecycle management: [lifecycle.md](lifecycle.md)
- Reconciler research: `docs/research/reconciler/01-reactive-host-bridge.md` and `03-unmount-dispose-support.md`
- Reconciler research: `../research/reconciler/01-reactive-host-bridge.md` and `../research/reconciler/03-unmount-dispose-support.md`