Runtime
Jay Runtime
Written for AI agents. See Log Methodology Note below for details.
The Jay runtime is what drives a Jay Component. A Jay file is compiled into a Jay Component with a signature of
declare function render(viewState: ViewState): JayElement<ViewState>;
where
type updateFunc<T> = (newData: T) => void;
export interface JayElement<T> {
dom: HTMLElement;
update: updateFunc<T>;
}
This document describes the inner working of the Jay Runtime that drives a Jay Component.
Compiler Output
The Jay Compiler, given a Jay File, will generate a runtime file such as
import {
JayElement,
element as e,
dynamicText as dt,
conditional as c,
dynamicElement as de,
} from 'jay-runtime';
interface ViewState {
text1: string;
text2: string;
cond: boolean;
}
export function render(viewState: ViewState): JayElement<ViewState> {
return de(
'div',
{},
[
c(
(vs) => vs.cond,
e('div', { style: { cssText: 'color:red' } }, [dt(viewState, (vs) => vs.text1)]),
),
c(
(vs) => !vs.cond,
e('div', { style: { cssText: 'color:green' } }, [dt(viewState, (vs) => vs.text2)]),
),
],
viewState,
);
}
or
import {
JayElement,
element as e,
dynamicText as dt,
dynamicElement as de,
forEach,
} from 'jay-runtime';
interface Item {
name: string;
completed: boolean;
cost: number;
id: string;
}
interface ViewState {
title: string;
items: Array<Item>;
}
export function render(viewState: ViewState): JayElement<ViewState> {
return e('div', {}, [
e('h1', {}, [dt(viewState, (vs) => vs.title)]),
de(
'div',
{},
[
forEach(
(vs) => vs.items,
(vs1: Item) => {
return e('div', {}, [
e(
'span',
{ style: { cssText: 'color:green; width: 100px; display: inline-block;' } },
[dt(vs1, (vs) => vs.name)],
),
e('span', { style: { cssText: 'color:red; width: 100px; display: inline-block;' } }, [
dt(vs1, (vs) => vs.completed),
]),
e(
'span',
{ style: { cssText: 'color:blue; width: 100px; display: inline-block;' } },
[dt(vs1, (vs) => vs.cost)],
),
]);
},
'id',
),
],
viewState,
),
]);
}
The compiler output is similar to React/JSX compiler output, with a few design differences -
- The result type is not JSDom, it is a Jay Component that has both a dom element and an update function
- The compiler resolves what is static and what is dynamic, using the different
element,dynamicElement,dynamicText, 'forEach', 'conditional' functions as needed. - The compiler generates different accessor functions from the input data to different fields of the data per the instructions in the JayFile
The main components of the Runtime
- Random Access Linked List - (RAList) an implementation of a double-sided linked list with random access based on id.
- Kindergarden - a wrapper for an HTML element to manage it's children.
- List Compare - an algorithm to mutate one RAList into another, creating a list of instructions to apply later.
- element - declares the different constructor functions such as
element,dynamicText, etc.
Random Access Linked List
Defines a bi-directional linked list with random access to items by id. The list is used in the sorting algorithm when comparing two lists and mutating the first according to the second while extracting the instructions how to mutate the relevant dom nodes.
The linked list items can also hold an attachment that the list ignores, but is used in the actual compare algorithm.
The signature of the linked list is
export interface LinkedListItem<T, S> {
id: string;
value: T;
attach?: S;
next: LinkedListItem<T, S> | typeof EoF;
prev: LinkedListItem<T, S> | typeof BoF;
}
export declare class RandomAccessLinkedList<T, S> {
constructor(arr: Array<T>, matchBy: string);
first(): LinkedListItem<T, S> | typeof EoF;
last(): LinkedListItem<T, S> | typeof BoF;
has(id: string): boolean;
get(id: string): LinkedListItem<T, S>;
move(itemToMove: LinkedListItem<T, S>, toBefore: LinkedListItem<T, S>);
remove(item: LinkedListItem<T, S>);
add(obj: T, beforeItem: LinkedListItem<T, S> | typeof EoF = EoF, attach: S = undefined);
distance(from: LinkedListItem<T, S> | typeof EoF, to: LinkedListItem<T, S>);
get matchBy(): string;
}
Kindergarden
The Kindergarden manages the children of an HTML node in groups. The groups are ordered, and each group can be mutated in isolation. It exposes a simple API to
- create groups
- add, remove and move nodes within a group
- get the offset of a group in the children of the HTML node.
export declare class KindergartenGroup {
constructor(kindergarten: Kindergarten);
ensureNode(node: Node, atIndex?: number);
removeNode(node: Node);
removeNodeAt(pos: number);
moveNode(from: number, to: number);
}
export declare class Kindergarten {
constructor(parentNode: HTMLElement);
newGroup(): KindergartenGroup;
getOffsetFor(group: KindergartenGroup): number;
}
List Compare
This is the actual algorithm to compare two lists and extract the list of instructions for mutating the first to the second.
the signature of the algorithm is
export interface MatchResult<T> {
action: typeof ITEM_ADDED | typeof ITEM_MOVED | typeof ITEM_REMOVED;
item?: T;
pos: number;
fromPos?: number;
elem?: JayElement<T>;
}
export declare function listCompare<T>(
oldArray: RandomAccessLinkedList<T, JayElement<T>>,
newArray: RandomAccessLinkedList<T, JayElement<T>>,
mkElement: (T) => JayElement<T>,
): Array<MatchResult<T>>;
Where
- oldArray is the initial list of items. It is mutated to match the newArray
- newArray is the target of the mutations
- mkElement is a function that creates a JayElement for a new item in the list
- returns an array of mutations to apply to the dom elements, to mutate from the old state to the new state.
for example, given the list [A, B, C, D, E] that we need to mutate to [A, C, D, E, B],
the function will return the instruction MOVE fromPos: 1 pos:4.
element
The element function constructs a JayElement that can be dynamic, but it's direct children existance is static.
That is, the direct children of 'elementcannot beconditinalorforEach`. It's signature
type updateConstructor<T, S> = (e: HTMLElement, newData: T, state: S) => S;
export declare function element<T, S>(
tagName: string,
attributes: any = {},
children: Array<JayElement<T> | TextElement<T> | string> = [],
initialData: T = undefined,
initialState: S = undefined,
update: updateConstructor<T, S> = noopUpdateConstructor,
): JayElement<T>;
tagName- the name of the HTML tag to createattributes- an object of attributes to apply to the HTML tag.children- the child elements of the HTML element.initialData- if the actual element is dynamic, this is the initial data to be rendered on JayElement creationinitialState- if the actual element is dynamic, this is the initial state that is checked against the value returned from the updateConsttuctor function.update- an update function that is called when theJayElement.updateis called. Theupdatefunction gets the actual HTML element to apply updates for, the new data, and the last state from the last update, to be used to decide if there is need to update the HTML element
dynamicElement
Similar to element, except that dynamicElement creates a Kindergarten and support dynamic childrens.
export declare function dynamicElement<T, S>(
tagName: string,
attributes: any = {},
children: Array<Conditional<T> | ForEach<T, any> | TextElement<T> | JayElement<T> | string> = [],
initialData: T = undefined,
initialState: S = undefined,
update: updateConstructor<T, S> = noopUpdateConstructor,
): JayElement<T>;
dynamicText
supports text nodes with dynamic content, represented as javascript template strings.
type updateFunc<T> = (newData: T) => void;
export interface TextElement<T> {
dom: Text;
update: updateFunc<T>;
}
export declare function dynamicText<T>(initialData: T, textContent: (T) => string): TextElement<T>;
initialData- the data to render on JayElement creation.textContent- function to generate the text content based on the provided data in theupdatefunction.
conditional
creates a pseudo element that works with a Kindergarten group to control the inclusion of a JayElement in the parent JayElement dom
export declare function conditional<T>(
condition: (newData: T) => boolean,
elem: JayElement<T> | TextElement<T> | string,
): Conditional<T>;
forEach
creates a pseudo element that works with a Kindergarten group to control the inclusion of a JayElements in the parent JayElement dom, based on the number of items
export declare function forEach<T, Item>(
getItems: (T) => Array<Item>,
elemCreator: (Item) => JayElement<Item>,
matchBy: string,
): ForEach<T, Item>;
Log Methodology Note
Note: These design logs are written primarily for AI agents as part of the Design Log methodology and made accessible here for human readers. The language and structure are optimized for machine consumption — expect precise, specification-style prose rather than narrative documentation.