Saxonberg Server API
    Preparing search index...

    Static API for containment and movement operations.

    Index

    Constructors

    Methods

    • Find the first reachable Stuff matching predicate, searched in "on your person, then the room" order: installed augmentations (slot occupants), then carried inventory, then the surrounding location's contents. Returns null when nothing matches.

      The reach surface mirrors the canReach validator's criteria (inventory + location contents), extended with slot occupants so an installed implant counts as on-person, plus two legs for the three-base capability model: the self leg (a capability composed directly on the actor / its species) and the descend-into-host leg (an incorporeal update Idea hosted on an attunement host — the actor itself, an installed implant, or a carried attuned Thing). No global index — scans only the actor and the given location.

      Order (on-your-person first): self → self's hosted updates → slot occupants (+ their hosted updates) → carried (+ carried hosts' hosted updates) → location contents.

      Generalizes the old check-inventory-and-augs scans: fast travel uses it to find a credential (card Thing, or hosted credential update) or the node you're standing at, but it is deliberately predicate-agnostic.

      Guardrail — findReachable vs. MQL. This answers exactly one question: is there a reachable bearer of capability-type X for the engine to route behavior through? — keyed on a mixin type, first-match, returning a type-narrowed Stuff & T. It is NOT a query engine. Anything keyed on identity / keywords / properties / user input — argument resolution, choosing among matches, filtering, live/subscribable results — belongs to MQL, never here. The host-descent leg is bounded to a single concept and a single level; do not teach this helper another leg.

      Type Parameters

      • T

      Parameters

      • actor: Stuff
      • location: ContainerStuff | null
      • predicate: (s: Stuff) => s is Stuff & T

      Returns Stuff & T | null

    • Force-bypass variant of move(). Pre-flight invariants still fire (those are programmatic-contract guards, not policy); canMove / canRemoveContainable / canAddContainable witnesses still fire (so observers / audit hooks see the call) but their veto results are ignored. Post-move on* hooks fire identically.

      Gated to the TeleportController / GotoController — the narrow-entry pattern. Only the teleport/goto controllers can reach this entry point; each does the AccessApi.can(giver, 'force-teleport' | 'force-goto', ...) check before invoking. Combined, the mutation has exactly one legitimate entry path AND that path enforces who is authorized.

      Each controller is cloned per execution (teleport/goto --force), and FromModule matches it by its class module id (code provenance), so the cloned instances are admitted directly — an AnyOf of the two controllers' FromModule gates, no FromTemplate arms. Direct calls from any other module throw SecurityError.

      Parameters

      • item: ContainableStuff
      • to: ContainerStuff | null

      Returns void

    • Get contents from a container object

      Usage:

      const inventory = ContainmentApi.getContents(avatar);
      const locationContents = ContainmentApi.getContents(location);

      Parameters

      • container: ContainerStuff

      Returns ContainableStuff[]

    • Filter a contents snapshot to the loose (top-level) items — those NOT resting on another item in the same set. Items resting on a listed surface (the bottles on the back-bar) are represented by that surface and discovered by examining it (look <surface>); listing them as room contents is the clutter we avoid. Shared by every contents-presentation surface — look, sense, and the inspection-pane projection — so they agree. Pure: the containment walk is unchanged; this only shapes what's presented at top level.

      Parameters

      Returns Stuff[]

    • Move an item to to, or detach it (when to === null).

      Pipeline:

      1. Pre-flight invariants (Exitable layering, zone crossing).
      2. can* Witness hooks — short-circuit on the first veto.
      3. item.setContainer(to) — atomic state mutation.
      4. on* Witness hooks (post-mutation, never veto).

      Zone is NOT restamped on move — it should reflect whichever zone created the item, not whichever container it currently sits in. Cross-zone movement rules are enforced by the pre-flight invariants (Exitables can't cross zones via containment) but the zone field itself is set at clone time and stays put.

      Parameters

      • item: ContainableStuff
      • to: ContainerStuff | null

      Returns void

      ContainmentError on invariant violations or hook vetoes.

    • Place item in env without firing movement witnesses, running capacity validators, or triggering the glob merge-on-arrival ripple. The matter is treated as if it were already in env; this call just records the topological fact.

      Precondition: item.getContainer() === null. This is NOT a relocation primitive — existing-env Stuffs go through ContainmentApi.move. Throws when violated.

      Use when the placement is semantically NOT an arrival:

      • Glob split (splitoff is freshly cloned, has no container).
      • First-placement bootstrap paths after StuffApi.clone that deliberately bypass arrival hooks.
      • Hot-reload re-attachment (post-clone, pre-relink).

      Use ContainmentApi.move when the placement IS movement (an existing Stuff genuinely entered env from elsewhere).

      What's preserved (always):

      • Containment graph integrity (atomic three-update via setContainer).
      • Mixin compatibility — item must be Containable, env must be Container. Putting a non-Containable somewhere or accepting contents into a non-Container would corrupt the graph regardless of who's observing.
      • Fresh-placement precondition.

      What's bypassed:

      • Capacity validators (matter-was-already-there assumption).
      • can* / on* witnesses (placement is not movement).
      • Merge-on-arrival ripple for globs.
      • Recency-stack bookkeeping (no command-contribution delta — the matter is treated as already-present).

      Security: gated by SecurityPolicies.ApiOnly because the skipped checks make this primitive more powerful than move. The fresh-placement precondition rules out the obvious abuse (smuggling, teleport-past-guard) — existing-env Stuffs must go through move, period.

      Parameters

      • item: ContainableStuff
      • env: ContainerStuff

      Returns void

    • Place item on surface — the on-surface analogue of move. Under Option D (see docs/plans/affordance-verb-plan.md § 2), containment stays hierarchical and exclusive; the supporting surface is an orthogonal auxiliary pointer.

      Pipeline:

      1. Resolve target environment as the surface's container. Surfaces themselves are Containable; their environment is where the supported items live (e.g., the desk lives in the room; apples on the desk are also in the room).
      2. Run the surface's canRest(item) veto. Throws on programmatic-contract failure (validators upstream produce friendly user-input messages).
      3. move(item, targetEnv) — this fires the usual container change hooks AND clears any prior restingOn as part of the change-of-container invariant.
      4. Set the auxiliary restingOn pointer to the surface. Order matters: move() in step 3 clears restingOn; the _setRestingOn call after it restamps to the new surface.

      Parameters

      Returns void

      Error when the surface has no environment to place the item into, OR when surface.canRest(item) returns false.

    • Resolve a spawn/landing reference into the live Container to place something in. The reference is EITHER a Warren — land in its lazily created (and migration-tracked) host via getHost() — or an ordinary location: a singleton room is reused, a non-singleton is cloned fresh (StuffApi.singletonOrClone).

      Returns the resolved container plus the Warren when the ref named one, so a caller that must follow host migration — a self-seating fixture — can register with it; warren is null for a plain location.

      This is the warren-aware landing resolution shared by avatar spawn (game entry, Avatar.applyStartLocation) and self-seating fixtures (FixtureMixin.seatSelf). StuffApi.singletonOrClone stays the generic, domain-free primitive; this is its Warren-aware sibling. The Warren value is loaded dynamically so the static import graph stays acyclic (Warren imports ContainmentApi).

      Parameters

      • ref: string

      Returns Promise<{ container: Stuff & Container; warren: Warren | null }>