README

marianoguerra/tutuca/core does not have a README file

#
Ctx

pub(open) trait Ctx {
fn path(Self) -> DispatchPath = _
fn target_path(Self) -> DispatchPath = _
fn name(Self) -> String? = _
fn send(Self, name : String, args : Array[Value]) -> Unit = _
fn bubble(Self, name : String, args : Array[Value]) -> Unit = _
fn send_at_path(Self, path : DispatchPath, name : String, args : Array[Value]) -> Unit = _
fn bubble_at_path(Self, path : DispatchPath, name : String, args : Array[Value]) -> Unit = _
fn request(Self, name : String, args : Array[Value], opts : RequestOpts) -> Unit = _
fn stop_propagation(Self) -> Unit = _
fn at(Self) -> PathChanges = _
fn walk_path(Self, callback : (Int, Value) -> Bool) -> Unit = _
}

What a handler can do besides transforming its leaf (JS EventContext). Defined here so Handler can carry it without depending on the transactor; every method defaults to a no-op so tests and Path::update can run handlers without a dispatcher. The transactor implements the real thing.

#
Fields

pub(open) trait Fields {
fn schema() -> SchemaInfo
fn encode(Self) -> Map[String, Value]
fn decode(Map[String, Value]) -> Self?
}

What a typed state struct states about itself: its declared schema and its conversion to and from the fields map the render and path layers read.

A generic BOUND, not a trait object — schema and decode are facts about the TYPE, and Value::Obj already carries the object-safe half of the protocol (Obj). Nothing ever needs a &Fields.

Three methods, all of them things only the author (or the generator reading the author's WIT) knows. There is no fourth: every dynamic operation OVER the result — typed reads, defaults, sizes, item lookup, method calls, field enumeration — is a free function in this package, written once rather than implemented per type.

#
Obj

pub(open) trait Obj {
fn component_id(Self) -> Int? = _
fn obj_field(Self, name : String) -> Value? = _
fn obj_with_field(Self, name : String, v : Value) -> Value? = _
fn obj_item(Self, key : PathKey) -> Value? = _
fn obj_with_item(Self, key : PathKey, v : Value) -> Value? = _
fn obj_seq_entries(Self) -> Array[(PathKey, Value)]? = _
fn obj_callable(Self, name : String, ns : HandlerNamespace) -> Value? = _
fn obj_handler(Self, bucket : HandlerBucket, name : String) -> Handler? = _
fn obj_schema(Self) -> SchemaInfo? = _
fn obj_size(Self) -> Int? = _
fn obj_identity(Self) -> ObjId? = _
fn obj_eq(Self, other : &Obj) -> Bool = _
fn obj_debug(Self) -> String = _
fn obj_persist_id(Self) -> String? = _
}

Component-instance protocol (the port of JS's runtime Records tagged with a hidden component symbol). ONE trait serves both hybrid representations: the Value-backed Instance (component package) and user-defined typed structs. Instances live INSIDE Value so they flow through every Value-typed seam (binds, handler args, dynamic lookups, iterated seqs). Fns returned by obj_field / obj_callable and the Handler returned by obj_handler are SELF-PRE-BOUND (they close over the concrete Self — the no-downcast analogue of JS late this) and ignore args element 0. Every method defaults to an empty/None answer.

#
Stack

pub(open) trait Stack {
fn lookup_name(Self, String) -> Value = _
fn lookup_bind(Self, String) -> Value = _
fn lookup_dynamic(Self, String) -> Value = _
fn lookup_field_raw(Self, String) -> Value = _
fn lookup_method(Self, String) -> Value = _
fn get_handler_for(Self, String, HandlerNamespace) -> Value = _
}

Evaluation environment (JS stack). Every method defaults to Null so implementations (and test mocks) provide only the lookups they use. lookup_method returns the RESULT of the no-arg call — the stack invokes. get_handler_for returns Null when no handler is registered.

#
Builtin

pub struct Builtin {
name : String
arity : Int
apply : (Array[Value]) -> Value
}

One name an application may head: its arity, and what it does to already-evaluated arguments.

The table lives in value_builtin.mbt and is reachable only through builtin / builtin_names, so a caller sees a vocabulary rather than an enum it would have to match exhaustively. apply is only ever called with exactly arity arguments — the parser refuses anything else, which is what lets the implementations index positionally.

#
DispatchPath

pub(all) struct DispatchPath {
items : Array[DispatchStep]
} derive(Eq,
Debug
)

A path that still knows which component contributed each hop. Bubbling pops one hop per component; converting to a transaction Path teleports every Dyn marker so mutations land on the data's real location.

#
DispatchPath::compact

fn DispatchPath::compact(self : DispatchPath) -> DispatchPath

#
DispatchPath::concat

fn DispatchPath::concat(self : DispatchPath, steps : Array[Step]) -> DispatchPath

#
DispatchPath::new

#
DispatchPath::of_steps

fn DispatchPath::of_steps(steps : Array[Step]) -> DispatchPath

#
DispatchPath::pop_step

fn DispatchPath::pop_step(self : DispatchPath) -> DispatchPath

#
DispatchPath::to_transaction_path

fn DispatchPath::to_transaction_path(self : DispatchPath) -> Path

#
DispatchStep

pub(all) enum DispatchStep {
Plain(step~ : Step, origin~ : Int?)
Dyn(producer~ : Int, steps~ : Array[Step], interior~ : Array[Int], key~ : PathKey?)
} derive(Eq,
Debug
)

One hop of a dispatch path — the component-boundary projection of an event path. Plain wraps an addressing step with its origin (the id of the component that contributed it — teleport provenance); Dyn marks a dynamic variable (*dyn) used as a render target: the rendered data lives at the PRODUCER component (where the dynamic was defined), not at the consumer that wrote <x render="*dyn">, so interior lists the component ids crossed between producer and consumer, and steps is the producer's own path to the data. key is set when the dynamic is ITERATED (the item lives at the producer's sequence field under that key) — the one thing that used to be a whole second variant, DynEach, which differed in nothing else and so was matched alongside Dyn everywhere except the one line that reads the key.

#
FieldInfo

pub(all) struct FieldInfo {
name : String
ty : TyInfo
}

One declared field of a component's state.

Two parts, where there were six. The WIT spelling, the runtime kind, a collection's element type, a slot's component and a closed set's members are all FUNCTIONS of the type (ty_info.mbt), so they are asked rather than stored — which is what stops them from contradicting each other, and what lets a reader descend into list<card> instead of receiving the string "card" and having nowhere to take it.

#
FieldInfo::default

fn FieldInfo::default(self : FieldInfo) -> Value

#
FieldInfo::kind

fn FieldInfo::kind(self : FieldInfo) -> FieldKind

The runtime kind this field carries — a projection of its type, never a second statement about it.

#
FieldInfo::new

fn FieldInfo::new(name : String, ty : TyInfo) -> FieldInfo

#
FieldKind

pub(all) enum FieldKind {
FBool
FText
FInt
FFloat
FList
FMap
FAny
FComp(comp~ : String, args~ : Map[String, Value])
FSet
FOMap
} derive(
Debug
)

What a field carries at runtime. DECLARED, never inferred: reading the kind off a seed value is what used to make FInt vs FFloat depend on whether a double happened to be integral.

FComp fields hold a child component instance created through the registration scope; FSet/FOMap are the Map-backed set / ordered-map refinements.

#
Handler

pub(all) struct Handler((Array[Value], &Ctx) -> Value?)

A leaf-update handler, resolved ON the leaf so self is pre-bound (the JS handler.apply(instance, args) binds this at call time; binding at resolution time is equivalent — same instance either way). Receives its args and the dispatch Ctx (JS appends ctx as the last handler argument). Returns the new leaf, or None for "no change" (the port of the JS newLeaf !== curLeaf identity check — answering None lets the traversal skip the spine rebuild without comparing anything).

#
HandlerBucket

pub(all) enum HandlerBucket {
Receive
Input
Bubble
Response
} derive(Eq,
Debug
)

Dispatch buckets a handler can live in (semantics.md §6). Alter is render-time only and never dispatched, so it is not a bucket here.

#
HandlerNamespace

pub(all) enum HandlerNamespace {
Input
Alter
} derive(Eq,
Debug
)

Handler namespaces are a fixed enumeration (JS passes "input"/"alter" strings)

#
Lit

pub(all) enum Lit {
LNull
LBool(Bool)
LNum(Double)
LStr(String)
} derive(Eq,
Debug
)

Literal constant payload (JS ConstVal.val: string | number | boolean | null)

#
NullCtx

pub(all) struct NullCtx {
}

A Ctx that dispatches nothing (JS eval(null) analogue for handlers).
impl Ctx for NullCtx

#
NullStack

pub(all) struct NullStack {
}

Stand-in for JS eval(null) on values that touch no stack
impl Stack for NullStack

#
ObjId

pub(all) struct ObjId {
origin : UInt64
rev : Int
} derive(Eq,
Debug
)

A render-cache bucket for one instance: a structural fingerprint fixed when the instance was created, and a revision bumped by each successor.

NOT an identity, and deliberately not unique. Two unrelated instances can land on the same pair — two bundles loaded at runtime declaring the same component, or two structurally equal siblings of one — and that is fine: a bucket collision costs a MISS, never a wrong subtree, because the cache validates its stored value by physical identity before returning it.

Giving up on uniqueness is what removes the need for a process-global counter, and with it the question of how two independently loaded components avoid clashing: they do not have to.

#
ObjId::next

fn ObjId::next(self : ObjId) -> ObjId

#
ObjId::of

fn ObjId::of(fingerprint : String, fields : Map[String, Value]) -> ObjId

#
ObjId::to_hex

fn ObjId::to_hex(self : ObjId) -> String

#
Path

pub(all) struct Path {
steps : Array[Step]
} derive(Eq,
Debug
)

An address into the component/state tree: a sequence of steps root→leaf.
impl Show for Path

#
Path::concat

fn Path::concat(self : Path, steps : Array[Step]) -> Path

#
Path::field

fn Path::field(self : Path, name : String) -> Path

Path::new().field("rows").index("rows", 2) — the absolute twin of the relative PathChanges builder, which delegates to these so the two spell a step the same way. Each returns a fresh Path, so a base is reusable.

#
Path::index

fn Path::index(self : Path, name : String, i : Int) -> Path

A positional item of a sequence FIELD. There is no bare-index step: a Path addresses field-then-key, so a nested .a[0][1] is not expressible and the value-level item/index are what read one.

#
Path::key

fn Path::key(self : Path, name : String, k : String) -> Path

A keyed item of a sequence field.

#
Path::lookup

fn Path::lookup(self : Path, root : Value) -> Value?

#
Path::new

fn Path::new(steps? : Array[Step]) -> Path

#
Path::pin_keys

fn Path::pin_keys(self : Path, root : Value) -> Path

#
Path::pop_step

fn Path::pop_step(self : Path) -> Path

#
Path::resolve_chain

fn Path::resolve_chain(self : Path, root : Value) -> Array[Value]

#
Path::set_value

fn Path::set_value(self : Path, root : Value, v : Value) -> Value

#
Path::to_keys

fn Path::to_keys(self : Path) -> Array[StepKey]

#
Path::to_label

fn Path::to_label(self : Path) -> String

The addressing steps as readable text.

Over to_keys, so the frame-only steps (which address nothing) are absent and a seq-access step shows as its field.

#
Path::update

fn Path::update(self : Path, root : Value, bucket : HandlerBucket, name : String, args : Array[Value]) -> Value

#
PathChanges

pub struct PathChanges {
ctx : &Ctx
steps : Array[Step]
}

#
PathChanges::bubble

fn PathChanges::bubble(self : PathChanges, name : String, args : Array[Value]) -> Unit

Bubble name from the built path (JS PathChanges.bubble: skipSelf + bubbles).

#
PathChanges::build_path

fn PathChanges::build_path(self : PathChanges) -> DispatchPath

The absolute dispatch path this builder addresses: the ctx's path with the accumulated steps appended (JS PathChanges.buildPath).

#
PathChanges::field

fn PathChanges::field(self : PathChanges, name : String) -> PathChanges

Descend into field name (JS PathBuilder.field).

#
PathChanges::index

fn PathChanges::index(self : PathChanges, name : String, i : Int) -> PathChanges

Descend into element i of sequence field name (JS PathBuilder.index).

#
PathChanges::key

fn PathChanges::key(self : PathChanges, name : String, key : String) -> PathChanges

Descend into keyed element key of map field name (JS PathBuilder.key).

#
PathChanges::send

fn PathChanges::send(self : PathChanges, name : String, args : Array[Value]) -> Unit

Send name at the built path (JS PathChanges.send).

#
PathKey

pub(all) enum PathKey {
KInt(Int)
KStr(String)
} derive(Eq,
Debug
)

A concrete sequence key: list index or map key (JS SeqStep.key is int|string)

#
PathKey::to_label

fn PathKey::to_label(self : PathKey) -> String

A key as its addressing text: 2 or title.

#
PathKey::to_value

fn PathKey::to_value(self : PathKey) -> Value

A key as a Value — what @key binds to inside a loop, and what a seq-access read compares against.

#
Refusal

pub(all) struct Refusal {
code : RefusalCode
asked : String
rule : String
sentence : String
state : Value
path : Path
} derive(Eq,
Debug
)

One refusal: where it happened, what was asked for, which rule said no, the sentence that rule produced, and the state that was rejected.

#
Refusal::to_line

fn Refusal::to_line(self : Refusal) -> String

A one-line rendering, for a host that wants text rather than the record.

The sentence the author wrote comes LAST and unquoted, because it is the half a reader is meant to read: everything before it is provenance.

#
RefusalCode

pub(all) enum RefusalCode {
PathUnresolved
NoHandler
Precondition
Postcondition
Invariant
} derive(Eq,
Debug
)

Why a dispatch produced nothing.

Each case names a producer that exists in this repo. The design this comes from has a longer vocabulary — NO_REQUEST_FN, DECODE_FAILED, COERCED_TO_DEFAULT, OUT_OF_RANGE, TELEPORT_MISSING — and none of those is here, because a code nothing raises is a promise the runtime does not keep: a host filtering on it would conclude the failure never happens. A new case arrives with the site that raises it, in the same change.

(@emit_mbt.Refusal is a compile-time namesake and unrelated: that one is a declaration the MoonBit backend will not compile, and it is answered to the generator rather than to a running page.)

#
RefusalCode::word

fn RefusalCode::word(self : RefusalCode) -> String

The screaming-snake spelling — the one an author reads in a test failure.

One vocabulary learned once: a build-time hint about a missing arm and the runtime record for the same failure say the same word.

#
RequestOpts

pub(all) struct RequestOpts {
on_ok_name : String?
on_error_name : String?
on_res_name : String?
live_path : Bool
} derive(Eq,
Debug
)

Options for Ctx::request (JS pushRequest opts): response-handler routing (on_ok_name / on_error_name / on_res_name) and live_path, which opts out of pinning field-resolved keys at request time.

#
RequestOpts::new

fn RequestOpts::new(on_ok_name? : String, on_error_name? : String, on_res_name? : String, live_path? : Bool) -> RequestOpts

#
SchemaInfo

pub(all) struct SchemaInfo {
name : String
fingerprint : String
fields : Array[FieldInfo]
inputs : Array[String]
receives : Array[String]
bubbles : Array[String]
responses : Array[String]
methods : Array[String]
ids : Array[String]
view_names : Array[String]
init_names : Array[String]
}

What a component declares, as static metadata.

inputs is the one that matters most: the names update answers are UNKNOWABLE at runtime (it is one opaque pattern match), so introspection could only ever report THAT an update exists. The generator knows them.

#
SchemaInfo::field

fn SchemaInfo::field(self : SchemaInfo, name : String) -> FieldInfo?

Linear over fields, which is the right shape at these sizes: a component has a handful of them and the array preserves declaration order, which the callers below read as much as they read the entries themselves.

#
SchemaInfo::field_names

fn SchemaInfo::field_names(self : SchemaInfo) -> Array[String]

#
SchemaInfo::new

fn SchemaInfo::new(name? : String, fingerprint~ : String, fields? : Array[FieldInfo], inputs? : Array[String], receives? : Array[String], bubbles? : Array[String], responses? : Array[String], methods? : Array[String], ids? : Array[String], view_names? : Array[String], init_names? : Array[String]) -> SchemaInfo

#
SchemaInfo::shape_fingerprint

fn SchemaInfo::shape_fingerprint(self : SchemaInfo) -> String

A structural fingerprint of a schema that arrives at RUNTIME, for a caller with no generated one to carry: the dynamic-component host, which receives a guest's declared shape over the tutuca:component boundary and needs the same thing <v>_schema_fingerprint gives a compiled component — a value that changes when the shape does, and not otherwise (it seeds the render cache's bucket, and says when stored state can no longer be read back).

The twin of @statedef.fingerprint, which hashes the same shape one step earlier — from the SOURCE schema, where a variant's payload types and the user types are still visible. The two hash different inputs and do not produce the same string for the same component; neither is compared against the other, and each is stable for its own producer, which is all either one is for.

#
Step

pub(all) enum Step {
FieldStep(String)
SeqStep(field~ : String, key~ : PathKey)
SeqAccessStep(seq_field~ : String, key_field~ : String)
EachRenderItStep(field~ : String, key~ : PathKey)
BindStep(binds~ : Map[String, Value])
ScopeBindStep(val~ : Val)
EachBindStep(val~ : Val, when_val~ : Val?, enrich_with_val~ : Val?, loop_with_val~ : Val?, key~ : PathKey)
} derive(Eq,
Debug
)

Path steps (src/path.js). FieldStep/SeqAccessStep are what values address (Val::to_path_item); SeqStep is a field + concrete key (also the pinned form of SeqAccessStep); EachRenderItStep is an iterated render target that compacts to a SeqStep; Bind/ScopeBind/EachBind are frame-only (stack rebuild). Dynamic-var markers live on DispatchStep in the path package.

#
StepKey

pub(all) struct StepKey {
field : String
key : PathKey?
} derive(Eq,
Debug
)

A generic {field, key?} descriptor of an addressing step (JS Step.toKey), so tooling can introspect a path without matching on Step.

#
TyInfo

pub(all) enum TyInfo {
TyBool
TyInt
TyFloat
TyText
TyList(TyInfo)
TyTuple(Array[TyInfo])
TyOption(TyInfo)
TyRecord(String)
TyEnum(String)
TyVariant(String)
TyFlags(String, Array[String])
TySet
TyOMap(TyInfo)
TyComp(String?)
TyTable
TyAny
} derive(Eq,
Debug
)

What a declared field carries.

#
TyInfo::elem

fn TyInfo::elem(self : TyInfo) -> TyInfo?

What this type CONTAINS, or None when it contains nothing.

Strict, and deliberately not the same question @statedef.StateTy::elem answers. That one asks "may a view @each over this?", where any and a child slot must both say yes — a dynamic value may turn out to be a list and a component iterates its own entries, neither knowable at generation time. Answering the two with one method put a meaningless elem: any on 117 fields of the corpus and showed it in the inspector, where the question is the other one: what is in here?

#
TyInfo::kind

fn TyInfo::kind(self : TyInfo) -> FieldKind

The runtime kind: how a value of this type is REPRESENTED, once the shape that distinguishes it no longer matters.

A projection rather than a stored field, which is the point — the kind used to be written beside the spelling and could contradict it.

#
TyInfo::members

fn TyInfo::members(self : TyInfo) -> Array[String]

A closed set's declared members. Empty for every other type, and for a set with open membership — which is a real answer, not a missing one.

#
TyInfo::show_source

fn TyInfo::show_source(self : TyInfo) -> String

How the author wrote it. The WIT spelling, because that is what is in the file and what they will edit.

#
TyInfo::slot

fn TyInfo::slot(self : TyInfo) -> String?

The component a child slot holds, or None for a data field. Some("") is a slot whose component the schema did not name.

#
TyInfo::zero

fn TyInfo::zero(self : TyInfo) -> Value

The empty value of this type, as the runtime carries it.

Type-directed, like the generated <T>State::zero() — which is the point: a default is not an independent fact about a field, so storing one beside the type would be a second thing to keep true. The one default a TYPE cannot give is a child slot's construction arguments, and those are a value the author chooses rather than anything the schema knows.

This is what an inspector shows in a field's "default" column, reachable from a bare Value — it used to come off Component.specs, which a holder of an instance and no registry could not get to.

#
Val

pub(all) enum Val {
Const(lit~ : Lit, from_macro~ : Bool)
StrTpl(Array[Val?])
App(name~ : String, args~ : Array[Val])
Name(String)
HandlerName(name~ : String, ns~ : HandlerNamespace)
TypeName(String)
Bind(String)
BindMember(name~ : String, prop~ : String)
Dyn(String)
Field(String)
Method(String)
SeqAccess(seq~ : String, key~ : String)
} derive(Eq,
Debug
)

Parsed value AST. One variant per JS BaseVal subclass. from_macro ports ConstVal.fromMacroVar: set when a ^name macro var resolves to a constant, which makes an enclosing template non-literal. StrTpl parts alternate text constants and placeholder expressions; a None part is a placeholder whose inner expression failed to parse (JS keeps null there).
impl Show for Val

#
Val::eval

fn Val::eval(self : Val, stack : &Stack) -> Value

#
Val::eval_as_handler

fn Val::eval_as_handler(self : Val, stack : &Stack) -> Value

#
Val::is_literal

fn Val::is_literal(self : Val) -> Bool

#
Val::to_path_item

fn Val::to_path_item(self : Val) -> Step?

#
Value

pub(all) enum Value {
Null
Bool(Bool)
Num(Double)
Str(String)
List(Array[Value])
Map(Map[String, Value])
Fn((Array[Value]) -> Value)
Obj(&Obj)
}

Dynamic runtime value: what eval(stack) reads and returns.
impl Eq for Value
impl ToJson for Value
impl FromJson for Value

#
Value::as_key

fn Value::as_key(self : Value) -> PathKey?

This value AS a sequence key: a string keys a map, an integral number indexes a list. None for everything else — including a non-integral number, which is not an index and must not be rounded into one.

The bridge between the value world and the addressing world: it is how a .seq[.key] read turns the key FIELD into the key it looks up, and how the renderer names an iterated item in its §Each§ breadcrumb.

#
Value::at

fn Value::at(self : Value, p : Path) -> Value

The value at a path, or Null at the first step that does not resolve.

This is the composition, not a new primitive: one-hop reads stay field/item, because building a one-element Path to replace a match costs three allocations. Reach for a Path when the ADDRESS is the thing being passed around — a transaction target, an observed change, a test's assertion site.

#
Value::at_opt

fn Value::at_opt(self : Value, p : Path) -> Value?

The value at a path, distinguishing "no such place" from a Null stored there — the one distinction at cannot express and no coercer can recover.

#
Value::bool

fn Value::bool(self : Value, default? : Bool) -> Bool

#
Value::bool_opt

fn Value::bool_opt(self : Value) -> Bool?

#
Value::call

fn Value::call(self : Value, args : Array[Value], this? : Value) -> Value

Call a function value under the Fn convention: element 0 of the argument array is the this slot, which self-pre-bound callees ignore. Null for a non-function.

The convention was hand-rolled at every call site — match v { Fn(f) =>f([Null, x]) ... } — with the dummy slot spelled out each time.

#
Value::call_field

fn Value::call_field(self : Value, name : String, args : Array[Value]) -> Value

Call a method OF this value, with this value in the this slot: the $name read followed by the invocation, which is how a view calls one and how a handler filters a list of instances it cannot downcast.

#
Value::entries

fn Value::entries(self : Value) -> Array[(PathKey, Value)]

The ordered (key, value) entries of any container: a list keyed by index, a map by name, a custom collection by whatever obj_seq_entries says. Empty for anything that is not one (the JS unkIter default).

TOOLING and one-off use: this materializes. The renderer's @each keeps indexing List and Map directly, because allocating a tuple per item on every render is not what a convenience is for.

#
Value::field

fn Value::field(self : Value, name : String) -> Value

Field access that works on Map values and Obj instances alike; Null for anything else (including missing fields).

#
Value::field_info

fn Value::field_info(self : Value, name : String) -> FieldInfo?

The declared description of one field.

#
Value::field_names

fn Value::field_names(self : Value) -> Array[String]

Every field name: declaration order for a described instance, key order for a Map, empty for anything else (including an instance that declares nothing — "no schema" is not "no fields", and guessing is what the schema work removed).

#
Value::field_opt

fn Value::field_opt(self : Value, name : String) -> Value?

A field, or None when there is no such field. Map and Obj answer, anything else has no fields.

#
Value::from_json

fn Value::from_json(j : Json) -> Value

Build a Value from parsed JSON. Lossless: every JSON shape has a Value.

#
Value::handler

fn Value::handler(self : Value, bucket : HandlerBucket, name : String) -> Handler?

The handlers a value carries: component instances answer through the Obj trait, pure data has none.

#
Value::identity

fn Value::identity(self : Value) -> ObjId?

This value's render-cache bucket, when it is an instance that tracks one.

None for every plain value — a Map render site, a component-less embedding — and that is the answer the render cache reads as "decline": there is nothing here whose generations can be told apart, so keying on it would only build a key and miss.

#
Value::index

fn Value::index(self : Value, i : Int, default? : Value) -> Value

v.index(2) — an item by position.

#
Value::int

fn Value::int(self : Value, default? : Int) -> Int

#
Value::int_opt

fn Value::int_opt(self : Value) -> Int?

#
Value::is_truthy

fn Value::is_truthy(self : Value) -> Bool

JS truthiness: Null/false/0/NaN/"" are false; lists, maps and functions are always true (like JS objects). The truthy? predicate differs on purpose: it treats empty collections as falsy (see pred_truthy).

#
Value::item

fn Value::item(self : Value, key : PathKey, default? : Value) -> Value

An item by key, or the default.

#
Value::item_opt

fn Value::item_opt(self : Value, key : PathKey) -> Value?

An item by key: a list index, a map key, or whatever a custom collection resolves through obj_item. None when there is no such item.

#
Value::key

fn Value::key(self : Value, k : String, default? : Value) -> Value

v.key("id") — an item by name. Distinct from field: a Map answers both, but a component instance keys its SEQUENCE here and its FIELDS there.

#
Value::list

fn Value::list(self : Value) -> Array[Value]

The list payload; [] for non-lists (shared empty NOT returned — fresh).

#
Value::list_opt

fn Value::list_opt(self : Value) -> Array[Value]?

#
Value::map

fn Value::map(self : Value) -> Map[String, Value]

The map payload; empty for non-maps (fresh, like Value::list).

#
Value::map_opt

fn Value::map_opt(self : Value) -> Map[String, Value]?

#
Value::num

fn Value::num(self : Value, default? : Double) -> Double

#
Value::num_opt

fn Value::num_opt(self : Value) -> Double?

#
Value::schema

fn Value::schema(self : Value) -> SchemaInfo?

What this value DECLARES, when it is a component instance that says.

#
Value::size

fn Value::size(self : Value, default? : Int) -> Int

How many items, or 0. The total form of size_of.

#
Value::snapshot

fn Value::snapshot(self : Value) -> Map[String, Value]

name -> value for every declared field, in declaration order.

What every consumer that wanted to SHOW an instance had been faking: the inspector read the names off a Components registry, the example builder took them as a parameter, the dyncomp host parsed them out of a JSON projection.

#
Value::str

fn Value::str(self : Value, default? : String) -> String

#
Value::str_opt

fn Value::str_opt(self : Value) -> String?

#
Value::to_display_string

fn Value::to_display_string(self : Value) -> String

JS ${v} template-interpolation semantics. Approximations: List joins elements with ","; Map and Fn render as opaque markers (JS "[object Object]" / function source are not worth mirroring).

#
Value::to_json

fn Value::to_json(self : Value) -> Json

Json shape of a Value.

A DESCRIBED instance projects to its declared fields, recursively — which is what makes a state dump JSON rather than a debug string. It used to flatten to null along with Fn, because nothing could enumerate an Obj; the consequence was a state_json() that returned the Show rendering and said so in a comment, and a dyncomp migration path that asked the GUEST to project its own state (a to-json method, since removed from the contract: a wasm guest declares its fields now, so this walks them like any other).

An instance that declares nothing is still null: with no schema there is no field list to project, and inventing one is what the schema work removed. Fn stays null unconditionally (JS structured clone would throw on a function too), so a method or an unrendered handler inside a described instance projects as null rather than taking the whole object with it.

#
Value::with_at

fn Value::with_at(self : Value, p : Path, v : Value) -> Value

Copy-on-write write at a path, total: the SAME value back when the path does not resolve (Path::set_value's own contract).

#
Value::with_field

fn Value::with_field(self : Value, name : String, v : Value) -> Value

Copy-on-write field write, total: the value unchanged when the field cannot be written.

#
Value::with_field_opt

fn Value::with_field_opt(self : Value, name : String, v : Value) -> Value?

Copy-on-write field write; None when there is no such field to write (no such field on an instance, not a container at all). The partial form is what step_put needs: a step that cannot be addressed must leave the whole tree untouched rather than silently write nothing.

#
Value::with_item

fn Value::with_item(self : Value, key : PathKey, v : Value) -> Value

Copy-on-write item write, total. Same identity contract as with_field.

#
Value::with_item_opt

fn Value::with_item_opt(self : Value, key : PathKey, v : Value) -> Value?

Copy-on-write item write; None when the key addresses no item (an index out of range, a key of the wrong kind for the container).

#
builtin

fn builtin(name : String) -> Builtin?

The builtin a name denotes, or None when nothing does.

#
builtin_names

fn builtin_names() -> Array[String]

Every builtin name in BYTE order — the vocabulary a "did you mean" search ranges over, and the reason it is public rather than a private table.

Not Array::sort: MoonBit's Compare for String is length-first, which would answer null?, empty?, falsy?, equals?, truthy?. That is still deterministic, and it reads as unordered to the person the list is for.

#
invariant_failed

fn invariant_failed(handler~ : String, pred~ : String, sentence? : String, state? : Value) -> Unit

An invariant did not hold after the body ran. Same abandonment, different reason: the rule is the component's rather than this handler's.

#
mutator_name

fn mutator_name(verb : String, field : String) -> String

The name of a field's generated mutator: count + set -> setCount.

The convention lives HERE, in one function, because two places have to agree on it and neither can see the other. component() builds the mutators by concatenating it, and the state editor — which holds a bare Value and calls them by name through Obj::obj_field — reconstructed the same string from its own copy. Two spellings of one convention, either of which could have been changed alone.

#
on_refusal

fn on_refusal(f : (Refusal) -> Unit) -> (() -> Unit)

Switch the channel on. Returns the uninstall, which puts back whatever listener was there before — so a test that installs one inside another's lifetime cannot silently keep it.

#
postcondition_failed

fn postcondition_failed(handler~ : String, pred~ : String, sentence? : String, state? : Value) -> Unit

A postcondition did not hold after the body ran, so the transition was abandoned whole — state and effects together. state is the SUCCESSOR that was thrown away, which is the one worth looking at.

#
precondition_failed

fn precondition_failed(handler~ : String, pred~ : String, sentence? : String, state? : Value) -> Unit

A precondition did not hold, so the handler declined before touching anything. state is the state as it ARRIVED — nothing has moved yet.

#
refuse

fn refuse(r : Refusal) -> Unit

Report a refusal. A no-op when nothing is listening.

Inside a dispatch the record is HELD rather than delivered, so the one that decided is the one that escapes. A refusal raised outside any dispatch — a render-time name that nothing answers — is its own chain end and goes straight out.

#
refusing

fn refusing() -> Bool

Whether anything is listening.

Producers ask this before they build a record, and take the warn path when the answer is no. It is the reason a rejected state can be carried at all: nobody pays for it until somebody wants it.

#
size_of

fn size_of(v : Value) -> Int?

JS sizeOf: .size (immutable collections) or .length (string/array).

A custom collection answers too, through obj_size. It used to be None — so an instance @each iterated happily had no length, empty? was always false for it and the generated xLen mutator returned Null. An instance that is NOT a sequence still answers None, which is the honest reading: a component has fields, not a size.

#
step_get

fn step_get(node : Value, step : Step) -> Value?

#
step_keys_label

fn step_keys_label(keys : Array[StepKey]) -> String

Addressing steps as readable text: value.rows[1].title.

Over StepKey rather than Step, because that is the projection a transaction is RECORDED as (ObserveRecord.path_keys) — so a log line and a Path print the same way without the log having to keep the Path.

#
step_with_key

fn step_with_key(step : Step, key : PathKey) -> Step

Key a step with the si/sk an §Each§ meta carried (JS Step.withIndex / withKey, used by event reconstruction's applyKey): a plain field access under a keyed meta addresses the keyed item. Steps that carry their own key (or no field) are returned unchanged.

#
warn

fn warn(msg : String) -> Unit

Report a runtime warning through warn_hook.

#
warn_hook

let warn_hook :
Ref
[(String) -> Unit]

Where runtime warnings go. Defaults to println (the closest portable analogue of JS console.warn); a host can redirect it — e.g. into an error pane or the browser console — without threading a context through eval.