Skip to content

Value

Defined in: packages/engine/src/vm/Value.ts:391

Universal runtime value for the solve-js VM.

Carries a ValueType discriminant, a polymorphic value payload, and an optional unit string (for UoM values). Treated as immutable after construction, the arena reuses objects internally via recycle(), but external code should never mutate Value fields.

A cached _cachedNumber avoids repeated toNumber() computation on hot paths (ADD/SUB/MUL in the VM dispatch loop).

new Value(
type,
value,
unit?
): Value;

Defined in: packages/engine/src/vm/Value.ts:528

ParameterType
typeValueType
value| string | number | bigint | boolean | SplitData | SymbolicNode | MatrixData | RangeData | ColourData | ChartData | IpCidrData
unit?string

Value

optional datetimeSpan?: boolean;

Defined in: packages/engine/src/vm/Value.ts:526

That this quantity is the gap between two datetimes, rather than a duration someone wrote down.

Both are milliseconds and both are a Uom in "ms", so the unit cannot tell them apart, and the formatter used to render every one of them as a clock. That is right for 10:30 - 08:00, which reads 2:30, and wrong for 40ms + 120ms, which read 0:00 instead of 190 ms: a latency budget is a quantity and not a time of day. Set where two datetimes subtract, and read only by the formatter.

It survives the arithmetic that keeps a span a span: adding two spans, scaling one by a plain number, and totalling a column of them, so a timesheet still reads as a clock and half a shift is 0:30. It does not survive a conversion, because (10:30 - 08:00) in ms asked for milliseconds and should be given them, nor combining with a quantity somebody typed. Cleared by recycle alongside the other sidecars.


optional decimalPlaces?: number;

Defined in: packages/engine/src/vm/Value.ts:468

The number of decimal places this value should DISPLAY at, when it has been given an explicit precision.

A display sidecar, not a value one: value is unchanged, so every .value/toNumber() reader and all arithmetic behave exactly as before, and a value with no decimalPlaces formats the way it always did (the global two-place default with trailing zeros trimmed). It is set only by an explicit precision request, <x> to N dp and round(x, N), so that 3.14159 to 4 dp shows 3.1416 and 1.5 to 2 dp shows 1.50 rather than the value being rounded but then displayed at the default two places. It is NOT propagated through arithmetic (a later + 1 re-decides precision), which is why nothing that did not ask for a precision is affected. Cleared by recycle alongside the other sidecars.


optional exact?: DecimalData;

Defined in: packages/engine/src/vm/Value.ts:417

The exact base-ten value this Value stands for, when it has one.

A sidecar rather than a replacement for value: money and decimal-point literals set it to a DecimalData so that same-currency arithmetic and display can be exact (“$0.10 + $0.20” is “$0.30”, not “$0.30000000000000004”), while value stays the nearest double so every existing consumer that reads .value or toNumber() is unchanged. The plain Number-times-Number fast paths deliberately ignore it, which is why a bare “0.1 + 0.2” still answers the double it always did: exactness is carried only where a unit-bearing operand asks for it. Cleared by recycle so a reused arena Value never inherits a stale exact.


optional grain?: DatetimeGrain;

Defined in: packages/engine/src/vm/Value.ts:492

What this instant anchors: a calendar day, a wall-clock reading on one, or a fixed point on the timeline.

The fifth sidecar, the same shape as exact, rational, uncertainty and decimalPlaces and for the same reason: a Datetime’s payload is epoch milliseconds and has to stay a plain number, because the arena union, the worker DTO, the snapshot’s Datetime variant and the Datetime SUB path all read it as one. The number cannot say which of three questions it is the answer to, and the difference is not recoverable from it: formatDatetime decides whether to print a time by testing whether the local hour, minute, second and millisecond are all zero, which is a guess that 2026-04-03T09:00:00+09:00 under TZ=UTC gets wrong, because the nine o’clock the reader typed IS UTC midnight.

Set only where the shape is known: a date literal is 'date', a wall-clock literal (2026-04-03T09:30, 6pm) is 'datetime', a literal carrying Z or an offset is 'instant', and so is now. Absent means not recorded, which every reader treats as an instant rather than inferring one. Nothing in 2.26.0 displays it: the formatter does not read it, so every rendered date is byte-identical to 2.25.0. Cleared by recycle alongside the other sidecars.


optional rational?: Rational;

Defined in: packages/engine/src/vm/Value.ts:435

The exact rational value this Value stands for, when it has one.

The second sidecar, the same shape as exact and for the same reason: a fraction has no exact base-ten form (1/3 is not any decimal), so exact fraction arithmetic needs a numerator/denominator pair rather than a coefficient and a scale. Integer division seeds it (“1/3” carries the Rational 1/3), and +, -, *, / between rational-bearing numbers keep it reduced, so “1/49 * 49” is exactly 1 and “5/6 - 1/6 - 1/6

  • 1/6 - 1/6 - 1/6” is exactly 0 rather than the 1.6e-16 the doubles drift to. value still holds the nearest double, recomputed from the exact rational so accumulation error never creeps in, which is why the default display and every .value/toNumber() reader are unchanged. Only a fraction written with ”/” carries it, so a decimal literal (“0.1”) and a transcendental result (“sqrt(2)”) stay the plain doubles they were. Cleared by recycle alongside exact.

optional timedOut?: boolean;

Defined in: packages/engine/src/vm/Value.ts:403

Set by async resolvers when a fetch timed out, the result is a fallback (typically 0).


type: ValueType;

Defined in: packages/engine/src/vm/Value.ts:399


optional uncertainty?: number;

Defined in: packages/engine/src/vm/Value.ts:452

The one-sigma uncertainty (standard error) this value carries, when it has one.

The third sidecar, the same shape as exact and rational and for the same reason: a measurement written 12.3 ± 0.5 is still the number 12.3 everywhere it is read as one, so the type stays ValueType.Number and value stays the center, while this non negative field carries the tolerance. The ± (or ASCII +/-) operator seeds it, and +, -, *, / propagate it in quadrature for independent errors (see vm/VMConversion.ts’s uncertainOp). Everything else (a comparison, a transcendental function, a unit conversion) reads the center through toNumber() and drops the tolerance, which is why a value with no uncertainty behaves exactly as a plain number always did. Cleared by recycle alongside the other two sidecars.


optional unit?: string;

Defined in: packages/engine/src/vm/Value.ts:401


value:
| string
| number
| bigint
| boolean
| SplitData
| SymbolicNode
| MatrixData
| RangeData
| ColourData
| ChartData
| IpCidrData;

Defined in: packages/engine/src/vm/Value.ts:400


optional zone?: string;

Defined in: packages/engine/src/vm/Value.ts:507

The zone this instant should be read and displayed in, when the line named one.

A zone reference in the encoding calendar/IntlZone.ts documents: an IANA name ("Asia/Tokyo") or a fixed offset ("UTCOFFSET:540"). Set by <datetime> in <zone> and by an ISO literal carrying Z or an explicit offset, which names an offset rather than a zone and so records one. Absent means the value is read in the calendar backend’s own zone, which is what every Datetime meant before this sidecar existed. Like grain it is recorded and readable in 2.26.0 and read by the formatter from 3.0. Cleared by recycle alongside the other sidecars.

get errorCode(): string | undefined;

Defined in: packages/engine/src/vm/Value.ts:690

The stable ErrorFactory code of an Error value, else undefined.

The code is the machine-branchable identifier (the message is the human-facing text, errorMessage). It reads from the same slot errorValue writes, so it is defined for exactly the values isError accepts and undefined for every other type.

string | undefined


get errorMessage(): string | undefined;

Defined in: packages/engine/src/vm/Value.ts:698

The human-facing message of an Error value, else undefined. Paired with errorCode, which carries the branchable identifier.

string | undefined

clone(): Value;

Defined in: packages/engine/src/vm/Value.ts:594

A fresh Value carrying everything this one does: the payload, the unit, the cached number and every sidecar, present or future.

The inverse of recycle, and written as a whole-object copy rather than a list of fields on purpose. persistentValue() used to name the sidecars it copied, and the two it did not name (decimalPlaces, timedOut) were silently dropped by every viewport evaluation, so 3.14159 to 4 dp displayed as 3.14 there and as 3.1416 on a single line. A sidecar added later is carried here without this method knowing its name, which closes that class of bug rather than the one instance.

Shallow, as the old copy was: a matrix, a chart or a symbolic tree is shared with the original, which is fine because a Value’s payload is treated as immutable everywhere outside the arena’s own recycle().

Value

A new Value equal to this one in every field.


isBigInt(): this is Value & { value: bigint };

Defined in: packages/engine/src/vm/Value.ts:608

this is Value & { value: bigint }


isChart(): this is Value & { value: ChartData };

Defined in: packages/engine/src/vm/Value.ts:635

this is Value & { value: ChartData }


isColour(): this is Value & { value: ColourData };

Defined in: packages/engine/src/vm/Value.ts:631

this is Value & { value: ColourData }


isError(): boolean;

Defined in: packages/engine/src/vm/Value.ts:658

An Error value: a fault carrying a code and message, propagated through the DAG when a plugin or opcode cannot produce a quantity.

An Error reads as the number 0 through toNumber, so a numeric consumer that does not check this first cannot tell the fault apart from a real zero: exactly the silently-wrong result the engine guards against internally with faultedOperand. A host reading a live Value off a result should branch on this (or isFault) before trusting the number, and read errorCode/errorMessage for the detail.

boolean


isFault(): boolean;

Defined in: packages/engine/src/vm/Value.ts:678

Either fault, an Error or a Pending: the single predicate to check before trusting toNumber. It mirrors the internal faultedOperand rule that every opcode applies to its operands, so a host reading a result makes the same distinction the engine does rather than computing with a zero it cannot tell from a real one.

boolean


isHex(): this is Value & { value: number };

Defined in: packages/engine/src/vm/Value.ts:604

this is Value & { value: number }


isIpCidr(): this is Value & { value: IpCidrData };

Defined in: packages/engine/src/vm/Value.ts:639

this is Value & { value: IpCidrData }


isMatrix(): this is Value & { value: MatrixData };

Defined in: packages/engine/src/vm/Value.ts:616

this is Value & { value: MatrixData }


isNaN(): boolean;

Defined in: packages/engine/src/vm/Value.ts:757

boolean


isNumber(): this is Value & { value: number };

Defined in: packages/engine/src/vm/Value.ts:600

this is Value & { value: number }


isPending(): boolean;

Defined in: packages/engine/src/vm/Value.ts:667

A Pending value: an async result that has not yet resolved, holding the query key it is waiting on. Reads as 0 through toNumber, the same caveat as isError: a settled value arrives on a later evaluation.

boolean


isRange(): this is Value & { value: RangeData };

Defined in: packages/engine/src/vm/Value.ts:627

this is Value & { value: RangeData }


isString(): this is Value & { value: string };

Defined in: packages/engine/src/vm/Value.ts:612

this is Value & { value: string }


isSymbolic(): this is Value & { value: SymbolicNode };

Defined in: packages/engine/src/vm/Value.ts:643

this is Value & { value: SymbolicNode }


isVectorShape(): boolean;

Defined in: packages/engine/src/vm/Value.ts:621

A Matrix shaped like a vector, 1×N (row) or N×1 (column).

boolean


recycle(
type,
value,
unit?
): void;

Defined in: packages/engine/src/vm/Value.ts:547

Phase 5.3: Reset all fields for arena reuse. Called by ValueArena.acquire(), zero allocation, just field assignment.

ParameterType
typeValueType
value| string | number | bigint | boolean | SplitData | SymbolicNode | MatrixData | RangeData | ColourData | ChartData | IpCidrData
unit?string

void


toNumber(): number;

Defined in: packages/engine/src/vm/Value.ts:702

number