Saxonberg Server API
    Preparing search index...

    Hierarchy

    • ExitableVesselBase
      • 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

    _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
    _restingOn: Stuff & Surfaced | null = null

    Runtime-only auxiliary support pointer — Pattern B live ref. Holds a direct reference to the supporting Surfaced host (null when no support). NOT in persistentFields; resets to null on hydrate. R2.3 self-heal in getRestingOn clears the slot if the supporter has been destructed.

    Pattern B chosen over Pattern A templatePath stamping because non-singleton surfaces (e.g., multiple identical tables in a dining hall) can't be addressed unambiguously by templatePath. The cross-restart loss is small — items reappear in their container, just without the on-surface precision.

    _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.

    door: default | null = null

    The defining door for this bearer's synthesized exits. null means no door — synthesized exits will pass freely.

    environment: Stuff & Container | null = null

    Live reference to the container. NOT a persistent field — cross-Stuff references would round-trip badly through the Hydrator's reflection. The container relationship is rebuilt at clone time via the applyContainer instruction-field path (see static instructionFields above) or by direct ContainmentApi.move calls after hydration.

    Auxiliary restingOn is different — it's a Pattern A path-string (_restingOnPath) that DOES persist; see static persistentFields below.

    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 = 'DoorBearingMixin'
    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. senseStripAugmenter reads the per-call filter from AugmentOpts and the viewer's sensorium (derived from BodyPlan.getModalities()), and drops <sense channel="X">…</sense> regions and <detail sense="X"> wrappings whose channel isn't in filter ∩ sensorium.

    Lives on VisibleMixin (not DetailedMixin) because <sense> regions can appear in any Visible-mixed long, with or without detail authoring. Ordering: VisibleMixin sits above Detailed in the typical composition chain, so the parent-first walker runs senseStripAugmenter BEFORE wrapDetailKeysAugmenter — strip-then-wrap is correct because wrapping inside a region destined for the strip is wasted work.

    persistentFields: string[] = ...

    Persistent fields declared by this mixin. Used by PersistApi for automatic synchronization.

    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

    • get transmissionFactor(): number

      Accessor pair owns the per-field invariant (the project rule); setTransmissionFactor delegates here so the Hydrator's Phase-1 dispatch and in-process callers share one validation point.

      Returns number

    • set transmissionFactor(value: number): void

      Parameters

      • value: number

      Returns void

    Methods

    • Privileged setter for the auxiliary restingOn pointer. Reachable only from ContainmentApi.move / ContainmentApi.placeOn. Pass null to clear.

      Stores the supporting Surfaced ref directly (Pattern B); runtime-only — see the field declaration's JSDoc for the persistence rationale.

      Parameters

      Returns void

    • 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>

    • Phase 2 applier — see the interface docstring for semantics. Compare-and-move idempotency: no-op when the current container's templatePath matches the declared path; otherwise resolve the target via StuffApi.singleton and ContainmentApi.move into it. The singleton-target invariant is enforced at template-save time by TemplateApi.validateSingletonContainerTarget.

      Parameters

      • path: string

      Returns Promise<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>

    • 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 the current container.

      R2.3 self-heal: if environment points at a destroyed Container (a path bypassed the eager evacuation in Container.cleanupOnDestruct), clear the slot and return null. Cheap one-liner backstop for S1 / S8.

      Returns Stuff & Container | null

    • Synthesize the entry exit from the vessel's current environment into this vessel. Returns undefined when the vessel has no environment (i.e. it isn't placed anywhere). Used by go <vessel-keyword> so the command controller doesn't need to hand-build an Exit.

      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

    • Resolve the auxiliary restingOn pointer. Pattern B live ref; R2.3 self-heal clears the slot if the supporting surface has been destructed since the last set.

      Returns null when no support OR the supporter has been destructed. The caller can't tell the two apart from the return value; that's deliberate — absence of support is the same observable as a stale ref.

      Returns Stuff & Surfaced | null

    • Walk the container chain to the topmost non-null environment. Returns null when already at the root.

      Containment is acyclic by construction (a Container can't contain its own ancestor — setContainer's atomic update is the chokepoint), so the loop is bounded by the depth of the world's nesting.

      Returns Stuff & Container | null

    • 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

    • 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

    • Invalidate the synthesized exit caches AND migrate the door's BoundaryAnchor pair from (vessel, oldEnv) to (vessel, newEnv). Fires once per ContainmentApi.move transition.

      The (vessel, env) anchor-pair is the runtime install on the vessel side of the door's Boundary representation: a closed vessel-door blocks light flowing between vessel interior and its current environment. As the vessel relocates, the boundary follows.

      Parameters

      Returns void

    • State-mutation chokepoint. Reachable only from ContainmentApi.move; cross-Container contents mutation must not be subclass-extensible (@Final) or shadow-bypassable (@Unshadowable).

      Atomic across three updates: detach from the old container, attach to the new, update the field. null argument is the detach case; the policy rejects calls from anywhere other than ContainmentApi, so setContainer(null) outside the Api throws — the legitimate detach is ContainmentApi.move(item, null).

      Parameters

      Returns void

    • Door change: drop any cached synthesized exits (so the next access recreates them with the new door) and migrate the BoundaryAnchor pair on (vessel, environment) — old door's anchors are torn down, new door's anchors are installed if the vessel is currently placed.

      Parameters

      Returns void

    • 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