Host API Types Reference

Host API Types Reference

RillFieldDef

RillFieldDef describes a single field within a compound type. All four compound collection types use RillFieldDef to represent their element or field definitions.

interface RillFieldDef {
  name?: string;
  type: TypeStructure;
  defaultValue?: RillValue;
  annotations?: Record<string, RillValue>;
}
FieldTypeRequiredDescription
namestringNoField name. Present for dict, ordered, and closure params. Absent for positional tuple elements
typeTypeStructureYesStructural type of this field or element
defaultValueRillValueNoDefault value when the field is omitted. undefined = field is required. Detect presence with field.defaultValue !== undefined
annotationsRecord<string, RillValue>NoEvaluated annotation values keyed by annotation name. Property is absent when no annotations are declared

See TypeStructure for field access patterns per collection type.


TypeStructure

TypeStructure is a discriminated union that describes the structural shape of any rill value. The runtime uses it for type checking, type inference, and formatting.

type TypeStructure =
  | { kind: 'number' }
  | { kind: 'string' }
  | { kind: 'bool' }
  | { kind: 'datetime' }
  | { kind: 'duration' }
  | { kind: 'list'; element?: TypeStructure }
  | { kind: 'dict'; fields?: Record<string, RillFieldDef>; valueType?: TypeStructure }
  | { kind: 'tuple'; elements?: RillFieldDef[]; valueType?: TypeStructure }
  | { kind: 'ordered'; fields?: RillFieldDef[] }
  | { kind: 'vector'; dimensions: number }
  | { kind: 'closure'; params?: RillFieldDef[]; ret?: TypeStructure }
  | { kind: 'type'; typeName: string }
  | { kind: 'union'; types: TypeStructure[] }
  | { kind: 'stream'; chunk?: TypeStructure; ret?: TypeStructure }
  | { kind: 'any' }
  | { kind: string; data?: unknown }; // catch-all for types without parameterized structure

The kind field is the discriminator. Leaf variants (number, string, bool, datetime, duration, any) carry no sub-fields. Compound variants carry their structural sub-fields.

Field Constraints

VariantFieldTypeSemantics
datetimeN/AN/ALeaf variant. No sub-fields
durationN/AN/ALeaf variant. No sub-fields
listelementTypeStructure | undefinedElement type for all list members. Absent when element type is unknown
dictfieldsRecord<string, RillFieldDef> | undefinedNamed fields with individual types and optional annotations. Present for structural dicts
dictvalueTypeTypeStructure | undefinedValue type for all dict values. Present for uniform dicts. No per-field annotations
tupleelementsRillFieldDef[] | undefinedOrdered positional fields. Index access: type.elements[i]
orderedfieldsRillFieldDef[] | undefinedNamed ordered fields. Index access: type.fields[i]. Present for structural ordered types
orderedvalueTypeTypeStructure | undefinedValue type for all ordered values. Present for uniform ordered types
vectordimensionsnumberEmbedding dimension count
typetypeNamestringName of the registered host type
uniontypesTypeStructure[]Non-empty array of member types
closureparamsRillFieldDef[] | undefinedParameter definitions. Absent when closure is untyped
closureretTypeStructure | undefinedReturn type. Absent when return type is unspecified
streamchunkTypeStructure | undefinedOptional structural type of each chunk value
streamretTypeStructure | undefinedOptional structural type of the resolved return value

Annotation Access Patterns

Host applications read field annotations through the annotations property on RillFieldDef. The property is optional: absent when no annotations are declared. When present, it is a plain object, never null.

ContainerTypeStructure pathType
dict (structural)type.fields[fieldName].annotationsRecord<string, RillValue> | undefined
orderedtype.fields[index].annotationsRecord<string, RillValue> | undefined
tupletype.elements[index].annotationsRecord<string, RillValue> | undefined
closuretype.params[index].annotationsRecord<string, RillValue> | undefined

Uniform dicts (dict(string)) carry no fields record and have no per-field annotations.

// Reading annotations from a structural dict field
function getFieldDescription(
  type: TypeStructure & { kind: 'dict'; fields: Record<string, RillFieldDef> },
  fieldName: string
): string | undefined {
  const field = type.fields[fieldName];
  if (!field?.annotations) return undefined;
  const desc = field.annotations['description'];
  return typeof desc === 'string' ? desc : undefined;
}

Host Interop Shapes (toNativeValue)

toNativeValue() converts opaque rill types to plain JavaScript objects. Datetime and duration produce these shapes:

TypeNative ShapeFields
datetime{ unix: number, iso: string }unix: UTC ms since epoch. iso: ISO 8601 string
duration{ months: number, ms: number }months: calendar month count. ms: fixed milliseconds

Exports

// Structural type system
export type { TypeStructure, RillFieldDef, RillFieldDescriptor };
export {
  inferStructure,
  inferElementType,
  commonType,
  structureEquals,
  structureMatches,
  formatStructure,
  buildFieldDescriptor,
  paramsToStructuralType,
  structureToTypeValue,
  serializeValue,
  copyValue,
  deserializeValue,
  isIterator,
};

// Error atom registry
export { resolveAtom, atomName };
export type { RillAtom, RillStatus, InvalidateMeta, InvalidMeta, TraceFrame };
export type { ExtensionFactoryCtx };

// Halt formatting
export { formatHalt };

Atom registration is not a top-level export. Extensions register atoms via ctx.registerErrorCode(name, kind) on ExtensionFactoryCtx at factory init time. See ExtensionFactoryCtx below and Error Handling for details.

inferStructure

inferStructure(value: RillValue): TypeStructure

Returns the structural type descriptor for any rill value. Primitive values return leaf variants. Compound values return their respective variants with sub-fields populated from the value’s actual structure. Callable values return { kind: 'closure' }.

Pure function. Result is a frozen object.

inferElementType

inferElementType(elements: RillValue[]): TypeStructure

Infers the element type for a list by folding element types left-to-right using commonType. Starts with the first element’s type and folds each subsequent element’s type via commonType. When commonType returns null, throws RILL-R002.

InputResult
Empty array{ kind: 'any' }
Uniform-type arrayStructural type of the common element
Same-compound elements with differing sub-structureBare compound type (e.g., { kind: 'list' })
Mixed top-level typesThrows RILL-R002

commonType

commonType(a: TypeStructure, b: TypeStructure): TypeStructure | null

Returns the most specific shared type for two TypeStructure values. Used inside inferElementType to fold element types.

Cascade order:

StepConditionResult
Any-narrowingEither input is { kind: 'any' }The concrete (non-any) type
Structural matchstructureEquals(a, b) is truea
Bare type fallbackSame compound kind but structurally unequalBare compound type with sub-fields omitted (e.g., { kind: 'list' })
IncompatibleTop-level kind values differnull (caller raises RILL-R002)

Pure function, no side effects.

structureEquals

structureEquals(a: TypeStructure, b: TypeStructure): boolean

Compares two structural types for deep structural equality. Switches on the kind discriminator. Leaf variants compare by kind alone. Compound variants compare sub-fields recursively.

Pure function. Variants with different kind values always return false.

structureMatches

structureMatches(value: RillValue, type: TypeStructure): boolean

Checks if a value matches a structural type descriptor. Used by the :? runtime type check operator.

Type descriptorMatching behavior
{ kind: 'any' }Matches all values
Compound variantDeep structural match against sub-fields

Closure parameter default compatibility:

Value param defaultValueType param defaultValueResult
PresentAbsenttrue (superset satisfies)
AbsentPresentfalse (missing contract)
PresentPresenttrue if values are deeply equal
AbsentAbsenttrue

Pure function.

formatStructure

formatStructure(type: TypeStructure): string

Formats a structural type descriptor as a human-readable string.

InputOutput
{ kind: 'number' }"number"
{ kind: 'string' }"string"
{ kind: 'bool' }"bool"
{ kind: 'any' }"any"
{ kind: 'list', element: { kind: 'number' } }"list(number)"
{ kind: 'dict', valueType: { kind: 'string' } }"dict(string)"
{ kind: 'closure', params: [{ name: 'x', type: { kind: 'number' } }] }"closure"
{ kind: 'tuple', elements: [{ name: 'x', type: { kind: 'number' } }] }"tuple(x: number)"

buildFieldDescriptor

buildFieldDescriptor(
  structuralType: TypeStructure & { kind: 'dict' },
  fieldName: string,
  location: SourceLocation
): RillFieldDescriptor

Builds a frozen RillFieldDescriptor for a named field within a structural dict type.

structuralType must narrow to { kind: 'dict' } at the call site. The field must exist in valueType or RILL-R003 is thrown.

RillFieldDescriptor

RillFieldDescriptor carries the full RillFieldDef for a specific named field. The runtime produces descriptors when evaluating field-access expressions on typed dicts.

interface RillFieldDescriptor {
  readonly __rill_field_descriptor: true;
  readonly fieldName: string;
  readonly fieldType: RillFieldDef;
}
FieldTypeDescription
__rill_field_descriptortrueBrand field. Distinguishes descriptors from plain dicts
fieldNamestringName of the accessed field
fieldTypeRillFieldDefFull field definition including type and optional default

paramsToStructuralType

paramsToStructuralType(params: RillParam[]): TypeStructure

Builds a TypeStructure closure variant from a parameter list. Return type is always { kind: 'any' }. Result is a frozen object.

Param shapeMaps to
param.type definedThe TypeStructure from param.type
param.type undefined{ kind: 'any' }

structureToTypeValue

structureToTypeValue(type: TypeStructure): RillTypeValue

Converts a TypeStructure descriptor into the RillTypeValue runtime representation used in RillFunction.returnType and RillParam.type fields.

import { structureToTypeValue } from '@rcrsr/rill';

const fn: RillFunction = {
  params: [{ name: 'text', type: { kind: 'string' } }],
  fn: (args) => args.text,
  returnType: structureToTypeValue({ kind: 'string' }),
};

serializeValue

serializeValue(value: RillValue): unknown

Converts a rill value to a JSON-serializable representation. Delegates to TypeProtocol.serialize for host types when registered. Primitive and built-in compound types produce plain JS values.

deserializeValue

deserializeValue(data: unknown, typeName: string): RillValue

Restores a runtime value from serialized data. Looks up the named type’s TypeProtocol.deserialize function and applies it to data. Throws if the type is not registered or the protocol has no deserialize implementation.

copyValue

copyValue(value: RillValue): RillValue

Produces a deep copy of any rill value. Used internally for default value hydration and value isolation. Delegates to TypeProtocol for host types when registered.

isIterator

isIterator(value: RillValue): boolean

Returns true when value is a lazy rill iterator (produced by range, repeat, or similar). Use this to guard code that must not consume a sequence prematurely.


ExtensionFactoryCtx

ExtensionFactoryCtx is the factory-phase context passed as the second argument to async extension factories.

interface ExtensionFactoryCtx {
  registerErrorCode(name: string, kind: string): void;
  readonly signal: AbortSignal;
}
MemberDescription
registerErrorCodeRegisters a domain atom by name and kind. Atoms become available as #NAME literals in scripts.
signalFires when the runtime disposes before the factory completes.

See Host API Reference for usage examples.


InvalidateMeta

InvalidateMeta is the metadata descriptor passed to ctx.invalidate and returned by the detector in ctx.catch.

interface InvalidateMeta {
  readonly code: string;
  readonly provider: string;
  raw?: Record<string, unknown>;
}
FieldTypeDescription
codestringRegistered atom name (e.g. "TIMEOUT"). Unregistered names resolve to #R001.
providerstringOrigin identifier for the failure (e.g. the extension name).
rawRecord<string, unknown>Optional provider-specific payload. A message key populates the display message.

InvalidMeta

InvalidMeta is the alias used by the detector callback in ctx.catch. It is identical to InvalidateMeta.

type InvalidMeta = InvalidateMeta;

RillAtom

RillAtom is the opaque interned handle for a registered atom. The runtime creates handles exclusively via the atom registry.

interface RillAtom {
  readonly __rill_atom: true;
  readonly name: string;
  readonly kind: string;
}
FieldTypeDescription
namestringBare uppercase atom name without the # sigil (e.g. "TIMEOUT")
kindstringClassification tag supplied at registration (e.g. "network", "domain")

Use atomName(atom) to retrieve the name string. Use resolveAtom(name) to look up a handle by name.

import { resolveAtom, atomName } from '@rcrsr/rill';

const atom = resolveAtom('TIMEOUT');
atomName(atom);  // "TIMEOUT"

RillStatus

RillStatus is the fixed-shape metadata sidecar attached to every RillValue. Valid values share one frozen empty-status singleton. Invalid values carry a populated clone.

interface RillStatus {
  readonly code: RillAtom;
  readonly message: string;
  readonly provider: string;
  readonly raw: Readonly<Record<string, RillValue>>;
  readonly trace: ReadonlyArray<TraceFrame>;
}
FieldTypeDescription
codeRillAtomAtom identity. #ok means valid; any other atom means invalid.
messagestringHuman-readable description. "" on valid values.
providerstringOrigin identifier. "" on valid values.
rawRecord<string, RillValue>Provider-specific payload bag. Frozen empty object on valid values.
traceReadonlyArray<TraceFrame>Append-only trace chain. Empty on valid values.

TraceFrame

TraceFrame is one entry in the append-only trace chain on an invalid value. Frames record origin first, latest last.

interface TraceFrame {
  readonly site: string;
  readonly kind: 'host' | 'type' | 'access' | 'guard-caught' | 'guard-rethrow' | 'wrap';
  readonly fn: string;
  readonly wrapped: Readonly<Record<string, RillValue>>;
}
FieldTypeDescription
sitestringSource location in file.rill:line[:col] form. "" when location is unavailable.
kindTraceKindOne of the 6 normative kinds below.
fnstringHost function name, operator, or type op. "" when not applicable.
wrappedRecord<string, RillValue>Prior status dict on wrap frames. {} on all other kinds.

Kind values:

KindAppended when
hostExtension calls ctx.invalidate. First frame.
typeType assertion or conversion fails.
accessInvalid value is accessed via pipe, method, or encode.
guard-caughtA guard block catches the halt.
guard-rethrowA caught invalid value is re-accessed and halts again.
wraperror "..." wraps an invalid value. wrapped carries prior status.

TypeDefinition

TypeDefinition is the registration contract for host-provided types. Pass instances to registerType when configuring the runtime.

interface TypeDefinition {
  name: string;
  identity: (v: RillValue) => boolean;
  isLeaf: boolean;
  immutable: boolean;
  methods: Record<string, RillFunction>;
  protocol: TypeProtocol;
}
FieldTypeRequiredDescription
namestringYesUnique type name. Appears in error messages and TypeStructure descriptors
identity(v: RillValue) => booleanYesReturns true when v is an instance of this type
isLeafbooleanYestrue = type has no sub-structure. false = type may carry fields
immutablebooleanYestrue = values of this type are immutable after construction
methodsRecord<string, RillFunction>YesMethod functions accessible via .methodName on type instances
protocolTypeProtocolYesBehavioral protocol for formatting, equality, comparison, and serialization
import type { TypeDefinition, TypeProtocol } from '@rcrsr/rill';

const dateType: TypeDefinition = {
  name: 'date',
  identity: (v) => v instanceof Date,
  isLeaf: true,
  immutable: true,
  methods: {
    format: {
      params: [{ name: 'pattern', type: { kind: 'string' } }],
      fn: (args) => (args.$self as Date).toLocaleDateString(),
      returnType: structureToTypeValue({ kind: 'string' }),
    },
  },
  protocol: {
    format: (v) => (v as Date).toISOString(),
    serialize: (v) => (v as Date).toISOString(),
    deserialize: (data) => new Date(data as string),
  },
};

TypeProtocol

TypeProtocol defines behavioral contracts for a registered host type. All fields are optional except format.

interface TypeProtocol {
  format: (v: RillValue) => string;
  structure?: (v: RillValue) => TypeStructure;
  eq?: (a: RillValue, b: RillValue) => boolean;
  compare?: (a: RillValue, b: RillValue) => number;
  convertTo?: Record<string, (v: RillValue) => RillValue>;
  serialize?: (v: RillValue) => unknown;
  deserialize?: (data: unknown) => RillValue;
}
FieldTypeRequiredDescription
format(v: RillValue) => stringYesReturns the display string for a value. Used in log, string interpolation, and error messages
structure(v: RillValue) => TypeStructureNoReturns the structural descriptor for a value. Absent = type has no parameterized structure
eq(a: RillValue, b: RillValue) => booleanNoEquality test. Absent = identity comparison (===)
compare(a: RillValue, b: RillValue) => numberNoOrdering: negative, zero, or positive. Absent = type is unordered; > and < raise RILL-R002
convertToRecord<string, (v: RillValue) => RillValue>NoExplicit conversion targets for the -> type operator. Keys are target type names
serialize(v: RillValue) => unknownNoConverts value to JSON-serializable form. Used by serializeValue
deserialize(data: unknown) => RillValueNoRestores value from serialized data. Used by deserializeValue
import type { TypeProtocol } from '@rcrsr/rill';

const dateProtocol: TypeProtocol = {
  format: (v) => (v as Date).toISOString(),
  eq: (a, b) => (a as Date).getTime() === (b as Date).getTime(),
  compare: (a, b) => (a as Date).getTime() - (b as Date).getTime(),
  serialize: (v) => (v as Date).toISOString(),
  deserialize: (data) => new Date(data as string),
};

AST Node Types

AST node types are exported from @rcrsr/rill for tools that walk parsed scripts (linters, analyzers, codegen). The full list of node names ships in index.ts; this section documents nodes whose shape includes metadata not obvious from their name.

DictEntryNode

interface DictEntryNode {
  readonly type: 'DictEntry';
  readonly key:
    | string
    | number
    | boolean
    | ListLiteralNode
    | DictKeyVariable
    | DictKeyComputed;
  readonly value: ExpressionNode;
  readonly keyForm?: 'identifier' | 'string';
  // ...standard BaseNode span/location fields
}

keyForm records how a string key appeared in source:

Source formkeyForm
dict[name: 1] (bare identifier)'identifier'
dict["name": 1] (quoted string)'string'
Number, boolean, computed, variable, or list keysundefined
Internal '...' spread sentinelundefined

keyForm is parser metadata only. Runtime semantics are unchanged: dict[a: 1] and dict["a": 1] are AST- and value-equal. Use keyForm in linting and analysis tools that want to treat quoted keys as an intentional escape for foreign API names (for example a snake_case rule that allows dict["maxResults": ...] while flagging dict[maxResults: ...]).


See Also


Migration: 0.18.0 Renamed Exports

v0.18.0 renames 7 exports in @rcrsr/rill to remove the rill-prefixed naming convention and align with the TypeStructure terminology introduced in v0.17.0. All old names are removed; update imports before upgrading.

Symbol Mapping Table

Old NameNew NameImport Change
RillTypeTypeStructureimport type { TypeStructure } from '@rcrsr/rill'
formatStructuralTypeformatStructureimport { formatStructure } from '@rcrsr/rill'
inferStructuralTypeinferStructureimport { inferStructure } from '@rcrsr/rill'
structuralTypeEqualsstructureEqualsimport { structureEquals } from '@rcrsr/rill'
structuralTypeMatchesstructureMatchesimport { structureMatches } from '@rcrsr/rill'
isRillIteratorisIteratorimport { isIterator } from '@rcrsr/rill'
rillTypeToTypeValuestructureToTypeValueimport { structureToTypeValue } from '@rcrsr/rill'

Codemod

No automated codemod is available for this rename. Use find-and-replace across your codebase for each old name. The old names do not exist in v0.18.0; TypeScript will report errors at every import site, making all affected locations visible before runtime.