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
  }

}
Primary reactive value container class

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
  }
}
Gettable half holding value and lock

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
  }
}
Settable half with id and reverse callback

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
  }
}
Lock carrying an optional error

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
  }
}
Error message and optional retry flag

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
  }
}
Program with processes and persisted 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
  }
}
Process callback and root scope items

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
  }
}
Scope tree node with id and branches

ReactiveArray

class ReactiveArray {
    constructor(elements) {
        this.elements = elements;
    }
}
Array wrapper over an elements list

ReactiveArrayElement

class ReactiveArrayElement {
    constructor(key, value) {
        this.key = key;
        this.value = value;
    }
}
Keyed array element pair

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;
    }
}
Composable reverse helpers for side effects

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)
  }
}
Validation helpers for reactive values