import research docs from prior conversation and scattered sources
This commit is contained in:
119
docs/research/prior-poc-source-reference.md
Normal file
119
docs/research/prior-poc-source-reference.md
Normal file
@@ -0,0 +1,119 @@
|
||||
# Prior UJSX POC: Source Code Reference
|
||||
|
||||
Source: `/workspace/aui/ujsx/` (Deno/JSR package `@ade/ujsx`)
|
||||
Summary: `/workspace/aui/SUMMARY.md`
|
||||
|
||||
## What to Preserve
|
||||
|
||||
### Transform Registry (`transform/registry.ts`)
|
||||
|
||||
Priority-based transformation with continuation-passing style:
|
||||
|
||||
```typescript
|
||||
interface TransformRule<T, U, A> {
|
||||
name: string;
|
||||
match: (node: T) => boolean;
|
||||
transform: (node: T, ctx: TransformContext<A>, next: TransformFn<T, U, A>) => U;
|
||||
priority?: number;
|
||||
}
|
||||
```
|
||||
|
||||
Key pattern: `next` continuation allows recursive child transforms. Rules sorted by priority (higher = first). V2 extends this with `direction` and `schema` fields.
|
||||
|
||||
### HostConfig + Reconciler (`host/config.ts`)
|
||||
|
||||
React-reconciler-inspired host adapter:
|
||||
|
||||
```typescript
|
||||
interface HostConfig<TTag, Instance, RootCtx> {
|
||||
name: string;
|
||||
createRootContext(container, options?): RootCtx;
|
||||
createInstance(tag, props, ctx, parent?): Instance;
|
||||
createTextInstance(text, ctx, parent?): Instance;
|
||||
appendChild(parent, child, ctx): void;
|
||||
insertBefore?(parent, child, before, ctx): void;
|
||||
removeChild?(parent, child, ctx): void;
|
||||
prepareUpdate?(instance, tag, prevProps, nextProps, ctx): unknown | null;
|
||||
commitUpdate?(instance, payload, tag, prevProps, nextProps, ctx): void;
|
||||
}
|
||||
```
|
||||
|
||||
The `createRoot()` reconciler is mount-only (MVP). V2 needs full reconciliation with key-based diffing.
|
||||
|
||||
### Graphology Host (`host/graphology.ts`)
|
||||
|
||||
Dirty bitmask pattern on graph nodes:
|
||||
|
||||
```typescript
|
||||
const DIRTY = { Props: 1<<0, Content: 1<<1, Structure: 1<<2 } as const;
|
||||
```
|
||||
|
||||
Edges use `parent->child#order` keys. Version tracking on nodes. Issue: `createInstance` has commented-out append logic, `prepareUpdate` uses `JSON.stringify` diffing.
|
||||
|
||||
### Streaming Transformer (`streaming/transformer.ts`)
|
||||
|
||||
Chunk-based async iterable processor. V2 preserves this but flush should pass real ancestor context instead of empty arrays.
|
||||
|
||||
## What to Remove/Rewrite
|
||||
|
||||
### `UniversalProps` (`core/types.ts`)
|
||||
|
||||
HTML-specific props that don't belong in a universal IR:
|
||||
- `onClick`, `onSubmit`, `onInput`, `onChange` (event handlers)
|
||||
- `className`, `class` (HTML-specific)
|
||||
- `data-*`, `aria-*` template keys
|
||||
- `__html` (dangerouslySetInnerHTML)
|
||||
|
||||
V2 replaces with plain `Record<string, unknown>`.
|
||||
|
||||
### `genId()` (`core/jsx.ts`)
|
||||
|
||||
```typescript
|
||||
function genId(): string {
|
||||
return `e_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
|
||||
}
|
||||
```
|
||||
|
||||
Non-deterministic. V2 uses counter-based or injectable IDs.
|
||||
|
||||
### Metadata Injection (`core/jsx.ts`)
|
||||
|
||||
Every element gets `{ timestamp: new Date(), id: genId() }`. This is overhead without clear use in a universal IR. Move to host-specific concerns if needed.
|
||||
|
||||
## TypeBox Research Examples
|
||||
|
||||
Source: `/workspace/research/typebox_research/ujsx/`
|
||||
|
||||
### `unist.ts` - Unist schema as TypeBox Module
|
||||
|
||||
```typescript
|
||||
export const Unist = Type.Module({
|
||||
Data: Type.Object({},{additionalProperties: Type.Unknown()}),
|
||||
Point: Type.Object({ line: Type.Number(), column: Type.Number(), ... }),
|
||||
Position: Type.Object({ start: Type.Ref('Point'), end: Type.Ref("Point") }),
|
||||
Node: Type.Object({ type: Type.String(), data: Type.Optional(...), position: Type.Optional(...) }),
|
||||
Literal: Type.Composite([Type.Ref("Node"), Type.Object({ value: Type.Unknown() })]),
|
||||
Parent: Type.Composite([Type.Ref("Node"), Type.Object({ children: Type.Array(Type.Ref("Node")) })]),
|
||||
})
|
||||
```
|
||||
|
||||
### `ujsx.ts` - UJSX schema as TypeBox Module
|
||||
|
||||
```typescript
|
||||
export const UJSX = Type.Module({
|
||||
ElementMetadata: Type.Object({ id: Type.Optional(Type.String()), timestamp: Type.Optional(Type.Date()) }, { additionalProperties: Type.Unknown() }),
|
||||
Children: Type.Union([Type.Ref("UniversalNode"), Type.Array(Type.Ref("UniversalNode"))]),
|
||||
PropValue: Type.Union([Type.String(), Type.Number(), ..., Type.Function([...Type.Rest(Type.Array(Type.Unknown()))], Type.Void())]),
|
||||
UniversalProps: Type.Object({ id: Type.Optional(Type.String()), children: Type.Optional(Type.Ref("Children")) }, { additionalProperties: Type.Union([Type.Ref("PropValue"), Type.Undefined()]) }),
|
||||
RootElement: Type.Object({ type: Type.Literal("root"), props: Type.Intersect([...]), children: Type.Array(Type.Ref("UniversalNode")), ... }),
|
||||
UniversalElement: Type.Object({ type: Type.Union([Type.String(), Type.Function([Type.Ref("UniversalProps")], Type.Ref("UniversalNode"))]), props: Type.Ref("UniversalProps"), children: Type.Array(Type.Ref("UniversalNode")), ... }),
|
||||
})
|
||||
```
|
||||
|
||||
Key insight: `Type.Module` creates a `TModule` whose `$defs` is a live `string → TSchema` map. Access via `ValuePointer.Get(UJSX, "$defs/Children")`. Add at runtime via `ValuePointer.Set()`. Import resolved schemas via `UJSX.Import("Element")`.
|
||||
|
||||
Note: `ElementMetadata` is defined twice in the research file (duplicate). V2 should clean this up and likely drop metadata from the core schema entirely.
|
||||
|
||||
### ts2typebox
|
||||
|
||||
CLI tool `ts2typebox` can convert TypeScript types to TypeBox defs but has issues with complex types (JSDoc comments, linked references). Useful for basic types, unreliable for complex ones like unist/mdast type definitions.
|
||||
Reference in New Issue
Block a user