Data structures
Every data structure inside the Reactive module: the small classes the runtime moves around. Each is shown whole, exactly as it is in /src/reactive/. The runtime itself is on Source.
ReactiveValue
Primary reactive value container.
/**
* Primary reactive value container.
*/
class ReactiveValue {
/**
* @param {ReactiveGettable|undefined} optionalGettable - Optional gettable containing readable value and lock state.
* @param {ReactiveSettable|undefined} optionalSettable - Optional settable containing settable ID and reverse callback.
*/
constructor(optionalGettable, optionalSettable) {
/** @type {ReactiveGettable|undefined} Optional gettable. */
this.optionalGettable = optionalGettable
/** @type {ReactiveSettable|undefined} Optional settable. */
this.optionalSettable = optionalSettable
}
}
ReactiveGettable
Gettable container for a reactive value.
/**
* Gettable container for a reactive value.
*
* Present when the reactive value is gettable (has a value that can be read).
* Absent (undefined optionalGettable on ReactiveValue) when the value is settable-only.
*
* The value field holds the actual native value. It may itself be undefined
* if the encapsulated type is nullable, independent of whether the reactive value is gettable.
*/
class ReactiveGettable {
/**
* @param {ReactiveLock|undefined} optionalLock - When present, the value is locked (unavailable / in-flight).
* @param {*} value - The native value held by this gettable.
*/
constructor(optionalLock, value) {
/** @type {ReactiveLock|undefined} When present, the value is locked. */
this.optionalLock = optionalLock
/** @type {*} The native value. */
this.value = value
}
}
ReactiveSettable
Settable container for a reactive value.
/**
* Settable container for a reactive value.
*
* Present when the reactive value is settable (can be updated via Reactive.Set).
* Absent (undefined optionalSettable on ReactiveValue) when the value is gettable-only.
*/
class ReactiveSettable {
/**
* @param {number} id - Stable settable ID (non-optional; the settable itself is optional on ReactiveValue).
* @param {function|undefined} optionalNativeReverseCallback - Reverse callback for reverse-settable values.
*/
constructor(id, optionalNativeReverseCallback) {
/** @type {number} Stable settable ID. */
this.id = id
/** @type {function|undefined} Reverse callback. */
this.optionalNativeReverseCallback = optionalNativeReverseCallback
}
}
ReactiveLock
Lock data for reactive values.
/**
* Lock data for reactive values.
*
* When present on ReactiveValue (optionalLock), indicates the value is locked.
* Contains an optional error associated with this lock.
*/
class ReactiveLock {
/**
* @param {ReactiveError|undefined} optionalError - Optional error associated with this lock. Undefined when locked with no error (e.g. loading state).
*/
constructor(optionalError) {
/** @type {ReactiveError|undefined} Optional error associated with this lock. */
this.optionalError = optionalError
}
}
ReactiveError
Error data for reactive lock.
/**
* Error data for reactive lock.
*/
class ReactiveError {
/**
* @param {string} message - Error message.
* @param {number|undefined} optionalRetryFlagId - Optional retry flag ID. Use Reactive.Dereference to get settable.
*/
constructor(message, optionalRetryFlagId) {
/** @type {string} Error message. */
this.message = message
/** @type {number|undefined} Optional retry flag ID. */
this.optionalRetryFlagId = optionalRetryFlagId
}
}
ReactiveProgram
Represents a reactive program with its associated state and processes.
/**
* Represents a reactive program with its associated state and processes.
* Note: This is not a "Data" object because it contains callbacks.
*/
class ReactiveProgram {
/**
* @param {ReactiveProcess} forwardProcess - The forward process (main program execution).
* @param {Object<number, ReactiveProcess>} reverseProcesses - Map of settable IDs to reverse processes.
* @param {Object<number, ReactiveValue>} values - Persisted settable values map (stableId -> ReactiveValue) from the last run.
*/
constructor(forwardProcess, reverseProcesses, values) {
/** @type {ReactiveProcess} The forward process (main program execution). */
this.forwardProcess = forwardProcess
/** @type {Object<number, ReactiveProcess>} Map of settable IDs to reverse processes.
* Each process contains a callback and root scope. */
this.reverseProcesses = reverseProcesses
/** @type {Object<number, ReactiveValue>} Persisted settable values map (stableId -> ReactiveValue) from the last run.
* This is the "settable memory" for the whole program across runs. */
this.values = values
}
}
ReactiveProcess
Represents a reactive process (forward or reverse).
/**
* Represents a reactive process (forward or reverse).
*/
class ReactiveProcess {
/**
* @param {function} callback - The callback function to execute (no arguments, returns void).
* @param {ReactiveScopeItem[]} rootScopeItems - The root scope items list for stable IDs across runs.
*/
constructor(callback, rootScopeItems) {
/** @type {function} The callback function to execute (no arguments, returns void). */
this.callback = callback
/** @type {ReactiveScopeItem[]} The root scope items list for stable IDs across runs. */
this.rootScopeItems = rootScopeItems
}
}
ReactiveScopeItem
Represents a single item in the scope tree.
/**
* Represents a single item in the scope tree.
*
* A scope item stores only a stable ID and its named branch sub-scopes.
* It does NOT embed reactive values directly; persisted settable values live in `ReactiveProgram.values`.
*/
class ReactiveScopeItem {
/**
* @param {number} id - Stable ID for this call-site position.
* @param {Object<string, ReactiveScopeItem[]>} branches - Named branch scopes; each value is the branch scope's items list.
*/
constructor(id, branches) {
/** @type {number} Stable ID for this call-site position. */
this.id = id
/** @type {Object<string, ReactiveScopeItem[]>} Named branch scopes; each value is the branch scope's items list. */
this.branches = branches
}
}
ReactiveArray
class ReactiveArray {
constructor(elements) {
this.elements = elements;
}
}
ReactiveArrayElement
class ReactiveArrayElement {
constructor(key, value) {
this.key = key;
this.value = value;
}
}
ReactiveEvent
ReactiveEvent - Composable reverse helpers for side effects (navigation, listeners).
/**
* ReactiveEvent - Composable reverse helpers for side effects (navigation, listeners).
*
* Fully untyped at runtime: call sites choose the logical payload type; this module does not
* import or reference any domain types (e.g. Ui).
*/
class ReactiveEvent {
/**
* Returns a reverse whose callback does nothing (null-object settable).
* Generic in documentation only — payload type is whatever callers set on this settable.
*/
static Zero() {
return Reactive.Reverse((ignored) => {
ReactiveSecurity.Gettable(ignored);
});
}
/**
* Returns a reverse that, when set, calls Reactive.Set(settable, value).
* The value passed into the reverse is ignored.
*/
static Setter(settable, value) {
ReactiveSecurity.Settable(settable);
ReactiveSecurity.Gettable(value);
return Reactive.Reverse((ignored) => {
ReactiveSecurity.Gettable(ignored);
Reactive.Set(settable, value);
});
}
/**
* Returns a binding that reads from binding and, when set, updates the inner binding
* and sets reverseFlag to the same newValue (so listeners can inspect the payload).
*/
static Listen(binding, reverseFlag) {
ReactiveSecurity.Binding(binding);
ReactiveSecurity.Settable(reverseFlag);
const settable = Reactive.Reverse((newValue) => {
ReactiveSecurity.Gettable(newValue);
Reactive.Set(binding, newValue);
Reactive.Set(reverseFlag, newValue);
});
const result = Reactive.Binding(binding, settable);
return result;
}
}
ReactiveSecurity
Security validation functions for Reactive module.
/**
* Security validation functions for Reactive module.
*/
class ReactiveSecurity {
/**
* Checks if a ReactiveValue is settable (has `optionalSettable`).
*
* @param {ReactiveValue} reactiveValue - Reactive value to validate.
* @throws {Error} When `optionalSettable` is undefined.
*/
static Settable(reactiveValue) {
if (reactiveValue.optionalSettable == null) {
throw new Error('Value is not settable: optionalSettable is undefined')
}
}
/**
* Checks if a value is gettable (has an optionalGettable).
*
* @param {*} valueOrReactiveValue - Value or ReactiveValue to validate
* @throws {Error} When optionalGettable is undefined (settable-only value).
*/
static Gettable(valueOrReactiveValue) {
if (!(valueOrReactiveValue instanceof ReactiveValue)) {
return
}
if (valueOrReactiveValue.optionalGettable == null) {
throw new Error('Value is not gettable: optionalGettable is undefined (settable-only value)')
}
}
/**
* Checks if an optional value is gettable (only validates if not null/undefined).
*
* @param {*} optionalValueOrReactiveValue - Optional value or ReactiveValue to validate (null/undefined is allowed)
*/
static OptionalGettable(optionalValueOrReactiveValue) {
if (optionalValueOrReactiveValue == null) {
return
}
ReactiveSecurity.Gettable(optionalValueOrReactiveValue)
}
/**
* Checks if a ReactiveValue is reverse-settable (has `optionalSettable` AND a reverse callback).
*
* @param {ReactiveValue} reactiveValue - Reactive value to validate.
* @throws {Error} When `optionalSettable` is undefined, or when `optionalNativeReverseCallback` is undefined.
*/
static Reverse(reactiveValue) {
ReactiveSecurity.Settable(reactiveValue)
if (reactiveValue.optionalSettable.optionalNativeReverseCallback == null) {
throw new Error(
'Value is not reverse settable: optionalNativeReverseCallback is undefined'
)
}
}
/**
* Checks if a ReactiveValue is both gettable and settable (binding validation).
*
* @param {ReactiveValue} reactiveValue - Reactive value to validate.
* @throws {Error} When optionalGettable is undefined or optionalSettable is undefined.
*/
static Binding(reactiveValue) {
ReactiveSecurity.Gettable(reactiveValue)
ReactiveSecurity.Settable(reactiveValue)
}
}