The set of identifiers a sohl.entity.expr.SafeExpression may use at one call site — SoHL's answer to "what is actually in scope here?".

Before scopes existed, every call site built an ad-hoc object literal and handed it to evaluate(); nothing connected the identifiers an author could write to the identifiers that site actually bound, so an out-of-scope name (say actorLogic where only itemLogic is bound) parsed cleanly, threw at evaluation, was caught by the caller, and silently disabled the feature. A scope makes that contract a declared, checkable object:

  • Construction rejects an undeclared identifier, so the mistake surfaces once, where the expression is authored — not as a warning per render.
  • bind checks the other direction: that the call site supplies exactly what it promised.
  • names and describe drive the editor's autocomplete and the generated documentation, so neither can drift from the code.

Scopes are declared in expression-scopes.mjs and reached through the expressionScopes registry; they are never constructed ad hoc.

expressionScopes — the registry of every declared scope.

Constructors

Properties

Accessors

Methods

Constructors

Properties

id: string

Stable dotted id, e.g. "action.visible".

Accessors

  • get open(): boolean
  • Whether undeclared identifiers are permitted. true only for genuinely dynamic contexts, where the declared bindings serve as documentation and autocomplete rather than as a gate.

    Returns boolean

Methods

  • Whether an identifier may appear in an expression using this scope — has, or anything at all when the scope is open.

    Parameters

    • name: string

      The identifier to check.

    Returns boolean

    Whether the identifier is permitted.

  • Check a call site's evaluation context against this declaration and return it, ready to hand to evaluate().

    This guards the direction construction-time validation cannot: a call site that quietly stops binding something the scope promises (or invents a key the scope never declared) would otherwise leave every expression using that identifier silently reading undefined. A mismatch is logged once per distinct shape and the context is passed through unchanged — a binding bug must not take the sheet down with it.

    An open scope accepts any keys and is passed straight through.

    Parameters

    • context: Record<string, unknown>

      The bindings the call site is supplying.

    Returns Record<string, unknown>

    context, unchanged.

  • The authoring description of one declared identifier.

    Parameters

    • name: string

      The identifier to describe.

    Returns undefined | string

    Its description, or undefined if not declared.

  • Whether an identifier is declared by this scope. Note this asks about the declaration, not about a runtime context — a declared binding may still evaluate to undefined (an unresolved item's itemLogic, say), which is an absent value, not an out-of-scope identifier.

    Parameters

    • name: string

      The identifier to look up.

    Returns boolean

    Whether the identifier is declared.