Saxonberg Server API
    Preparing search index...

    Hierarchy

    • LoungeBase
      • default
    Index

    Constructors

    Properties

    Accessors

    Methods

    Constructors

    • Constructor - generates unique runtime ID.

      Subclass constructors should call super() and then initialize their fields. Use field initializers for default values where possible.

      IMPORTANT: Direct new SomeStuff() is rejected — every Stuff must be created via StuffApi.create(() => new YourClass()) or StuffApi.clone(...). The construction sentinel above guarantees this; raw new outside the Api layer throws here.

      Constructor-body method calls bypass the Proxy (the Proxy is installed AFTER the constructor returns). Initialize fields here; do NOT invoke methods that carry

      Returns default

      from inside a constructor body.

    Properties

    _address: string | null = null

    Declared address path in the namespace, or null. Sparse — null is the common case and the resolve-walk falls through it to a containment ancestor or the spatial zone.

    _atmosphere: string | null = null
    _biomePath: string | null = null
    _detailAtmospheres: Record<string, string> = {}
    _detailGravities: Record<string, Quantity<"m/s²">> = {}
    _detailHumidities: Record<string, Quantity<"%">> = {}
    _detailMaterialPaths: Record<string, string> = {}

    Per-Detail Material overrides — flat map from detailKey to the Material's templatePath. Stored as a plain Record (not a Map) so default JSON serialization handles it without a marshaller.

    _detailPressures: Record<string, Quantity<"Pa">> = {}
    _detailTemperatures: Record<string, Quantity<"K">> = {}
    _detailWinds: Record<string, Quantity<"m/s">> = {}
    _gravity: Quantity<"m/s²"> | null = null
    _humidity: Quantity<"%"> | null = null
    _materialPath: string | null = null

    Path to the bulk default Material singleton. Resolved lazily on each getMaterial() call so HMR replacement is observed immediately.

    _pressure: Quantity<"Pa"> | null = null
    _temperature: Quantity<"K"> | null = null
    _wind: Quantity<"m/s"> | null = null
    contents: Set<Stuff & Containable> = ...

    The contained items. Read access goes through getContents(); mutation goes through addContainable / removeContainable, which only Containable.setContainer may legitimately invoke.

    details: DetailMap = ...

    Hierarchical detail map. Host-internal storage; external callers go through getDetail / setDetail / removeDetail.

    exits: Map<string, default> = ...

    Explicit exit map. Derived exits (cartesian adjacency, vessel 'out') are NOT stored here — they are synthesized lazily by the zone and by ExitableVessel.getExit() respectively.

    Host-internal storage; external callers use getExits() / getExit(direction).

    fixtureSlots: Map<string, Stuff & Adornment> = ...

    Fixtures keyed by slot name. Single source of truth — the Slotted base's slots Map stays empty for Adornable hosts because occupy / vacate on Adornable are routed through addFixture / removeFixture.

    Runtime-only — fixtures are reconstructed by seed clone hooks (BoundaryApi.attachExistingBoundary).

    illustration: string | null = null
    longDescription: string = ''
    nextFixtureIndex: number = 1

    Counter for synthetic slot names. Instance-local, runtime-only.

    shortDescription: string = ''
    stuffId: string

    Runtime ID for this object (generated using nanoid). This is NOT the MongoDB _id - it's a runtime identifier.

    _mixinName: string = 'PostRegistrationMixin'
    commandContributions: CommandContributions = ...

    Visible is target-shape only — no verb contributions. See the mixin docstring for why look.yaml belongs on Perceiver's self bucket, not on Visible's target-side buckets.

    fieldMarshallers: {
        _gravity: string;
        _humidity: string;
        _pressure: string;
        _temperature: string;
        _wind: string;
    } & { mass: string } = ...

    Field-marshaller bindings for the five Quantity-typed bulk fields. The per-detail maps round-trip via standard JSON (Quantity.toJSON / fromJSON) — Record<string, Quantity<U>> serializes natively without a map marshaller.

    instructionFields: string[] = ...

    exits is the canonical instruction field shape for declarative content. applyExits consumes a Record<string, ExitInstruction> and installs the runtime entries — no paired getter for the spec (the runtime exits: Map<string, Exit> has its own API). See applyExits and feedback_property_vs_instruction_fields.

    markupAugmenters: MarkupAugmenter[] = ...

    Markup-augmenter contribution. The wrapper-style augmenter narrows the host to Detailed at runtime, then runs the existing wrapDetailKeywords regex pass. Picked up by VisibleMixin.getMarkupLong(viewer) via the prototype-chain walker — non-Detailed hosts never see this augmenter; hosts with Detailed-but-empty detail maps no-op cheaply inside the helper.

    persistentFields: string[] = []
    subscribableFields: SubscribableFieldDescriptor[] = ...

    Projection field for live subscriptions. Reads getObviousExits() (explicit ∪ zone-derived, !hidden) and shapes each entry as { direction } for the wire. Destination paths are deliberately NOT shipped — the pane renders a "go

    " click target, not a hyperlink to the destination.

    No dependsOnFields plumbing today: rooms with explicit exits settle at hydration and cartesian-derived exits are positional. If runtime exit add/remove ever becomes a hot path (door sealing, dynamic walls), wire addExit / removeExit to fire FieldChangedEvent { field: 'exits' } the same way Container's addContainable / removeContainable do for contents.

    Accessors

    Methods

    • Install a forward/back exit pair in one call. Both sides share the same Door reference when one is supplied, so opening from either side flips a single state.

      Reciprocity: the opposite direction is inferred from NavigationApi.invertDirection(direction) for cardinal directions. For non-cardinal labels ('office', 'portal', vessel-specific names) the caller MUST supply opts.opposite — there is no structural inverse to infer. Passing opts.opposite for a cardinal direction overrides the inferred inverse.

      Parameters

      Returns Promise<void>

    • State-mutation primitive. Locked down — only callable from Containable.setContainer. Use ContainmentApi.move(item, container) from application code.

      Fires FieldChangedEvent { field: 'contents' } after a real addition so the MQL subscription substrate's dependency index picks up containment-shape changes for the contents descriptor. The substrate matches on (KIND, 'field', 'contents') only — oldValue / newValue are inspected by the diff pass via re-projection of the host, not by the index, so the count delta carried here is informational (debugging / future coarse-grain optimizations) rather than load-bearing.

      Parameters

      Returns void

    • Phase 2 applier — clone each adornment template and attach it as a fixture. Mirrors applyPopulates, minus the singleton dispatch: fixtures are per-instance, so every entry is cloned fresh. A template that doesn't compose AdornmentMixin is a configuration error (it can't be a fixture) and throws, naming the path.

      Parameters

      Returns Promise<void>

    • Declarative applier — wires the template YAML's details: map into the runtime Map<DetailId, Detail>. Phase 1 of the hydrator bracket-assigned the plain YAML object onto this.details, breaking the Map shape; the first thing we do is reset it. Then walk the entries — each shaped either legacy ({ keywords?, description: string }) or new ({ keywords?, vision?, hearing?, smell?, touch?, taste? }) — and call setDetail for each. Aliases are [key, ...keywords] with duplicates squashed.

      Mixed-shape entries (legacy description AND any per-sense slot key in the same entry) throw — authors pick one shape per entry. Malformed entries (no recognized shape, non-object payload) are skipped with a warn. Nested children via the details: sub-key are deferred to a future revision; v1 is flat.

      Parameters

      • data: Record<string, unknown>

      Returns void

    • Declarative-content applier. The instruction field is consumed here: each ExitInstruction is translated into an explicit Exit (or addBidirectionalExit for cardinal / explicit-bidirectional entries). Destinations and doors lazy-clone via StuffApi.singleton, so a depended-on Location is materialized on first need; further singleton(x) calls during a cascade hit the registered proxy and short-circuit cycle scenarios.

      Per-direction idempotency: a matching existing exit (same destination, same door) is a no-op; a mismatch throws with a diagnostic naming both seed paths.

      Parameters

      Returns Promise<void>

    • Self-register with the declared Warren. Resolves the singleton (creating it if absent — the stray-clone heal), then joins via the Warren-owned addMember. The Warren check is a real instanceof against the canonical base class (unspoofable) — a non-Warren target or missing template warns and leaves the room an orphan (QA-catches tradeoff).

      Parameters

      • warrenPath: string

      Returns Promise<void>

    • Destroy this object.

      Locked down by @CallSecurity(ApiOnly) — only callers under mud/api/ (in practice, StuffApi.destruct) may invoke it. @Unshadowable because the unregistration path must always run; a shadow that intercepts and skips it would leak the object into the registry forever. @Final because subclass overrides would defeat the same invariant — the loader hook throws FinalViolationError at import time on any subclass redefinition.

      Subclass cleanup belongs on the optional onDestruct() witness (consulted by StuffApi.destruct while the target is still live); refusal logic belongs on canDestruct(). This terminal destroy() is the unshadowable mark-and-unregister step only.

      Returns void

    • Get a detail's per-sense slot value.

      Overload-friendly runtime dispatch:

      • getDetail(id) — returns vision slot.
      • getDetail(id, sense) where sense is a SenseChannel literal ('vision' | 'hearing' | 'smell' | 'touch' | 'taste') — returns that slot.
      • getDetail(id, sense, parent) — sense + nested parent.
      • getDetail(id, parent) where the second arg is NOT a known sense channel — treats it as legacy parent and returns the vision slot at the nested path.

      Parameters

      • id: string
      • OptionalsenseOrParent: string
      • Optionalparent: string

      Returns string | null

    • Alias-grouped enumeration of top-level (or parent-scoped) details. Walks the DetailMap at the requested level grouping keys by Detail-object identity, so aliases (multiple keys pointing at the same Detail object) bundle into one entry. hasChildren reflects whether the entry's Detail has its own nested DetailMap.

      Parameters

      • Optionalparent: string

      Returns DetailEntry[]

    • Merged lookup:

      1. explicit → wins
      2. (subclass hook — ExitableVessel overrides for 'out')
      3. zone-derived (only CartesianZone returns anything)
      4. undefined

      Parameters

      • direction: string

      Returns default | undefined

    • Read the recency timestamp. Read by the residency sweep — which calls it on the raw target (via RAW_TARGET) so the sweep's own introspection never counts as a touch.

      Returns number

    • Affordance-annotated long description — see the interface docstring for the augmenter pipeline contract. Calls Mml.augment with the host (this), the supplied viewer, and the per-call opts (the senses build threads opts.filter through here for verb-specific sense filtering). Every contributing mixin's augmenters run in parent-first → child-last order.

      Parameters

      Returns string

    • Exits displayed by look — explicit ∪ derived, filtered by !hidden.

      Zones that synthesize cardinal-derived exits (CartesianZone) opt in via Zone.hasDerivedAdjacency(); zones without adjacency derivation (SphericalZone) skip the iteration entirely so we don't waste 10 always-undefined deriveExit calls per query.

      Returns default[]

    • Self-presentation — the casual-register render string for this object, the answer to "what does this Stuff call itself?" Three- step resolution:

      1. Named.name if present and non-empty — the object's proper name ("Alice", "Excalibur", "Town Square").
      2. Visible.shortDescription if present and non-empty — the object's visual identity ("a heavy oak door").
      3. The baked-in fallback (DEFAULT_PRESENTATION).

      For a Globbable stack (quantity !== 1) the count folds in as an affix — "30 coins" — pluralized via GrammarApi.pluralize (which honors host-side getPluralForm() overrides for irregulars). Named takes precedence over Visible so a Named-with-description renders by its proper name; code that needs the formal register calls getFullName() when typed as Named.

      Viewer-blind by design. This is the shared baseline every Stuff exposes; the viewer-aware naming step (recognition / identification — see docs/subsystems/belief.md) composes on top of it. Left shadowable (NOT @Final) so masking / disguise effects can override the rendered identity via a method shadow.

      Returns string

    • Build the composable Mml fragment for this object's display name — the Mml sibling of getPresentation. Mml.ref (and so every <item> / <name> / … identity tag) renders this, not a raw string, so a name joins the compose chain as a fragment like everything else. The label is the already-resolved, viewer-aware name (recognition runs in the render layer and hands it in).

      Return null for the plain default — Mml.ref then wraps the label in Mml.text, which escapes it exactly once, so player-authored names / status decoration are safe by construction and the fragment is never re-escaped downstream. Override to build a richer fragment (a TPA terminal wraps its name in <color> to tint by state). The plain-string getPresentation stays the surface for non-prose consumers (logs, context.note, MQL scalars).

      Parameters

      • _label: string

      Returns Mml | null

    • Effective light-receiving floor area in m², used by VisionModality.lightAt to convert accumulated lumens to lux. The base is topology-agnostic and returns 1.0 (m²); concrete Location subclasses (CartesianLocation, SphericalLocation) override per their cell geometry. Larger rooms read dimmer for the same total flux.

      Returns number

    • Resolve the temperature at this scope (optionally narrowed by a detailKey). Routes through BiomeApi.resolveTemperatureFor, which walks innermost-container-outward then biome / zone / universe.

      Parameters

      • OptionaldetailKey: string

      Returns Promise<Quantity<"K">>

    • Read seam. Instance method, but unwraps via ProxyApi.unwrap before reaching the # slot — this inside an instance method called through the proxy is the proxy, and the # slot lives on the raw target.

      Returns string | null

    • Atmospheric-bearing volume of this scope, in m³. Concrete subclasses derive from their topology (CartesianLocation from cube cellSize³, SphericalLocation from (4/3)πr³). The default is null — a scope with no derivable volume.

      Returns Quantity<"m³"> | null

    • Membership test for a single detail id at the given level. Returns true iff the detail exists and ANY sense slot is populated (cheaper than walking the slot map externally).

      Parameters

      • id: string
      • Optionalparent: string

      Returns boolean

    • Check if this object has been destroyed.

      @Unshadowable: the destroyed-state read is a framework invariant — any shadow that lied about it would let consumers touch a torn-down Stuff. @Final: subclasses overriding this would defeat the same invariant; the loader hook throws FinalViolationError at import time on any subclass that redefines it.

      Returns boolean

    • Population witness — an arrival entered this room. Notify the Warren of the population change; when this room is the host and the arrival pushes it past the bud threshold, hand the newcomer to the Warren's admitArrival for re-seating. The handoff's synchronous prefix re-seats to an existing satellite within this very move (single perception); only a bud awaits.

      Parameters

      Returns void

    • Wire any inverse exit pointers (host fixtures are imperatively installed by the Warren, so there is rarely pending work). The instruction-field warren self-registration has already run via the Hydrator's Phase 2 (applyWarren) by the time this hook fires.

      Parameters

      • Optional_context: unknown

      Returns Promise<void>

    • Set detail(s) — supports multiple IDs (aliases). Accepts both the legacy string-description shape and the new per-sense slot map shape: setDetail(ids, "A brass handle.") → populates vision slot. setDetail(ids, { vision: "...", touch: "..." }) → per-slot.

      All IDs in a single call share one Detail object (alias semantics). Separate calls always produce separate Detail objects, even when slot values match.

      Per-field invariants: the slot-map shape must populate at least one channel (empty maps throw); empty-string slot values are rejected. A legacy string-description shape accepts empty string (preserves prior behavior for tests / edge cases).

      Parameters

      • ids: string[]
      • descriptionOrSlots: string | Partial<Record<SenseChannel, string>>
      • Optionalparent: string

      Returns number

    • Strict on Quantity<'kg'>. Callers holding a raw number wrap via Quantity.of(n, 'kg') at the call site; tag / alt-unit authoring is the marshaller's job, not a runtime API concern.

      Parameters

      Returns void

    • Override the temperature at the room/vessel scope or at a specific detailKey. null clears the override (the next read falls through to the chain).

      Parameters

      • value: Quantity<"K"> | null
      • OptionaldetailKey: string

      Returns void

    • Stamp this Stuff's templatePath and re-key the byTemplatePath index so future findByTemplatePath lookups see the new path. No-op when path matches the current value.

      Locked down by @CallSecurity(ApiOnly) because flipping a Stuff's identity post-clone would break FromTemplate policies and any caller-side caching of template-path identity. @Final @Unshadowable because the index update has to run for every successful set — a subclass override that forgot the index call (or a shadow that intercepted) would silently desync byTemplatePath.

      Unwraps via ProxyApi.unwrap so the #-slot access lands on the raw target (see comment on #templatePath above).

      Parameters

      • path: string

      Returns void

    • Set the spatial zone. Gated by FromSpatialZone — only the SpatialZone class and its subclasses (CartesianZone, SphericalZone) may call this through the proxy. The addLocation / removeLocation chokepoints on the zone side are the legitimate callers; everyone else is rejected.

      Clone-time seeding from StuffApi.#cloneInner doesn't go through this method — it uses the caller-allowlisted _stampZone seam below.

      @Final @Unshadowable because the index of substrate invariants that consult getZone() (containment's cross-zone gate, Mobile.traverse, MQL scope walks) trusts the slot's value; a subclass override or shadow that lied about it could break those invariants. No legitimate subclass needs to extend this anyway — the only legitimate write paths are the SpatialZone chokepoints and clone-time.

      Parameters

      Returns void

    • Refresh the recency timestamp to now. Timestamp-fixed (no caller-supplied value). Called on the raw target by the security gate on every successful dispatch (Phase 2) and by the residency presence walk.

      Returns void

    • Mutual-exit invariant check, run at Location load (via postRegister) and on traversal as a fallback. Walks ONLY the _pendingVerify set — exits the addExit-time triage flagged as "could be wired but isn't yet."

      For each pending exit:

      • If it has settled by some other path (its inverse was wired by the destination's own verifier, it became oneWay / blocked, etc.), evict from the pending set and move on.
      • Else attempt to resolve: if the destination is now loaded and a matching back-exit exists, wire the inverse pointer pair. If the destination is loaded but the topology is wrong, mark the exit blocked = true with a logged warning. Either way, evict.
      • Else (destination still unloaded, or back-exit's destination unloaded), leave in the pending set for a future pass.

      Idempotent. Zone-derived cartesian exits are synthesized per-call and inherently mutual by grid adjacency — they aren't tracked.

      Returns void

    • Composition constraint: LoungeMixin requires WarrenMemberMixin on the chain (it reads/writes the back-ref via getWarren). The TS bound documents it; this runtime check enforces it the first time an instance registers (mirrors GlobbableMixin's opt-in).

      Parameters

      Returns void