Saxonberg Server API
    Preparing search index...

    Class StuffApi

    Static API for object management and registry.

    Index

    Constructors

    Methods

    • Resolve the backing class constructor for a template path. Loads the template doc, reads its class, and resolves it via loadClassByPath. Throws when no template exists at ref.

      Lets a caller dispatch on a target's class (its mixins, its inheritance) without instantiating it — e.g. singletonOrClone deciding clone-vs-singleton, or a spawn applier checking cls.prototype instanceof Warren.

      Parameters

      • ref: string

      Returns Promise<AnyConstructor>

    • Clone an object from a template in the domain collection.

      Pipeline:

      1. Load the template doc by path.
      2. Dynamic-import the backing class module.
      3. Construct an empty backing (no-arg ctor) and stamp its zone.
      4. Register the instance so recursive resolution during hydrate / initialize can observe the in-flight object.
      5. If the template names a hydratorClass, resolve it and await hydrator.hydrate(backing, doc.data). When absent, no hydration step runs — templates that want generic mixin-field copy must opt in by naming '/lib/persistence/PersistentHydrator'.
      6. If the backing composes PostRegistrationMixin, await postRegister(context), forwarding the caller-supplied context.

      If hydration or postRegister throws, the object is unregistered before the error propagates.

      The optional context is a caller-supplied bag threaded through to postRegister. It carries runtime setup that cannot come from the template's data — e.g., an authenticated User for an avatar. Objects that don't care ignore it; objects that do (Avatar) declare a narrower context type locally and read what they need.

      Type Parameters

      Parameters

      • templatePath: string

        Path to the template (e.g., "/obj/Avatar/")

      • Optionalcontext: unknown

        Optional runtime context passed to postRegister

      Returns Promise<T>

      The cloned and registered object

      const avatar = await StuffApi.clone<Avatar>('/obj/Avatar/abc', { user });
      const room = await StuffApi.clone('/home/bobalu/workroom');
    • Copy a single named field's value from src to dst through the inter-stuff method surface.

      Prefers src.getX() / dst.setX(value) (X = name with the first letter uppercased) — the canonical inter-stuff contract (per CLAUDE.md). Falls back to direct property access on either side when the accessor pair isn't defined, which covers the common case of bare persisted scalars that don't expose a custom getter / setter (a tarnished: boolean field on Coin).

      Used by GlobbableApi.split to clone the glob-identity field set onto the split-off; general enough to live on the Stuff registry rather than buried in glob.

      Doesn't validate the destination Stuff actually owns the field — the caller is responsible for picking field names that make sense for dst's class. Mismatched casing or typos write a dynamic property that nobody reads, silently. Treat as a framework primitive: the callers are short, well-typed lists of known fields (e.g., static globIdentityFields).

      Parameters

      Returns void

    • Create and register a Stuff object via a caller-supplied factory.

      Sister of clone(): same register / postRegister tail, no hydration step (the factory IS the construction). Use this for runtime-only objects whose construction needs explicit arguments and which don't round-trip through the CMS template pattern (Interactive being the canonical example — socketId, sessionId, user all flow through the closure).

      Registration happens BEFORE postRegister() so that recursive resolution during setup (e.g. a location whose exits resolve back to itself via the registry) can observe the in-flight instance. If postRegister() throws, the object is unregistered before the error propagates.

      Type Parameters

      Parameters

      • factory: () => T

        Function that constructs the object

      • Optionalcontext: unknown

        Optional runtime context passed to postRegister

      Returns Promise<T>

      The created and registered object

      const user = await StuffApi.create(() => new User());
      
    • Synchronous variant of create() for runtime objects whose construction is purely synchronous — no Hydrator.hydrate() step (the factory does the work) and no postRegister() (the class does not compose PostRegistrationMixin).

      Same sentinel-flip + Proxy-wrap + register guarantees as the async path, so the result is interception-mediated and tracked in the registry just like any other Stuff. Use this from inside sync helpers (e.g. Exitable.addBidirectionalExit's new Exit(...) calls) where awaiting create() would force the caller — and its callers — to become async too.

      Reach for create() whenever async hydration or post-registration matters; createSync() is the narrow-use sister.

      Guardrail: throws if the constructed Stuff composes PostRegistrationMixin. The point of createSync is "this Stuff has no async setup" — silently skipping postRegister() would yield a half-initialised object. The throw forces such classes to use the async create() path instead.

      Type Parameters

      Parameters

      • factory: () => T

      Returns T

    • Destroy an object.

      This is the canonical destruction entry point — Stuff.destroy() is @CallSecurity(ApiOnly) and rejects calls from outside the Api layer. Lifecycle ordering:

      1. canDestruct() Witness fires on the target. A { ok: false, reason } result throws DestructError and aborts the rest of the chain. (Force-bypass via forceDestruct() invokes the witness identically but skips the assertion — observers still see the call.)
      2. onDestruct() Witness fires on the target. Cleanup hook, runs while the target is still live (mirror of how the retired prepareDestroy() ran before destroy()).
      3. Privileged shadow detach removes every shadow from the host. Bypasses @ShadowSecurity({ detach }) because host destruction is unconditional.
      4. destroy() runs (FINAL, unshadowable) — marks _isDestroyed, unregisters from StuffApi.
      5. Events.StuffDestructed fires.

      Parameters

      • object: Stuff

        The object to destroy

      Returns void

    • Find an object by its stuffId. Returns undefined if not found or if the object has been destroyed.

      Parameters

      • stuffId: string

        The runtime ID to look up

      Returns Stuff | undefined

      The object, or undefined if not found

    • Find every runtime instance whose templatePath matches pattern under PathPatternApi glob syntax (*, **, ?).

      Backs the MQL path-glob seed (/obj/Avatar/*). Stuff without a template path are not in the index and never match. Result order is unspecified — callers that need stable ordering must sort.

      Type Parameters

      Parameters

      • pattern: string

      Returns T[]

    • Find the single runtime instance cloned from templatePath.

      Template paths identify classes of world objects — the same notion as MQL identity. For singleton system Ideas (one template per class), this is the canonical lookup.

      Returns the instance when exactly one exists, undefined when none, throws when multiple share the path. Throwing on multi is deliberate — if a caller treats the result as a singleton and silently picks an arbitrary one, bugs become non-deterministic. Use findAllByTemplatePath when multiple instances are legitimate.

      O(1) via the byTemplatePath index maintained in #updateIndexes.

      Type Parameters

      Parameters

      • path: string

      Returns T | undefined

    • Sync lookup for live templated Stuff instances (those that carry a path field) by exact path. Backs the MQL path-atom fallback: when findByPathGlob returns no clones, the resolver falls back to this so a non-glob path can address the template record itself (e.g. destruct /obj/Avatar/foo to remove the template doc when no live clone exists).

      Walks the registry and structurally matches via obj.path — avoids importing Template here, which would close the StuffApi → Template → Stuff → StuffApi cycle. Templates are in the registry by virtue of Template._materialize going through StuffApi.create. O(N) walk; called only on path-atom miss.

      Type Parameters

      Parameters

      • path: string

      Returns T[]

    • Force-bypass variant of destruct() — invokes the canDestruct witness identically (so audit hooks / observers fire as usual) but ignores the veto result. The onDestruct cleanup hook still runs.

      Gated to DestructController — the narrow-entry pattern. Only DestructController can reach this entry point; the controller does the AccessApi.can(giver, 'force-destruct', target) check before invoking. Combined, the mutation has exactly one legitimate entry path AND that path enforces who is authorized.

      The controller is cloned per execution (destruct -f), and FromModule matches it by its class module id (code provenance), so the cloned instance is admitted directly. Direct calls from any other module throw SecurityError.

      Parameters

      Returns void

    • Load and return the class constructor at classPath.

      Public companion to the inline class-loading logic in clone(): validates the path, consults HotReloadApi for an override blueprint, and falls back to a bare dynamic import. Returns the raw constructor (typed as unknown — caller decides what to do with it).

      Used by ZoneApi.isFolderClass and isSpatialZoneClass to resolve a template's class: field to its TS class so the check can be prototype instanceof Zone rather than membership in a central allow-list. Content devs add a folder class by extends Zone — no central registry to edit.

      Parameters

      • classPath: string

      Returns Promise<unknown>

    • Resolve a named export at classPath from the hot-reload registry, warming the path (lazy reload) on a cold miss. The synchronous, caller-named-export cousin of loadClassByPath (which resolves the file-basename export and falls back to a bare import). Used for path-resolved brain modules (exportName = 'brain'), whose concept-export is not the basename.

      Only class-like exports are retained by the registry, so the brain marker is a named class-expression (export const brain = class {…}). Returns null — never throws — for an invalid path, a frozen path, or a missing export, so callers (the behavior wiring, the CMS save-gate) treat "doesn't resolve" as a clean negative.

      Parameters

      • classPath: string
      • exportName: string

      Returns Promise<unknown>

    • Synchronous sibling of resolveExport: resolves a named export from the hot-reload registry with no lazy warm. Returns null if the path was never warmed, is frozen, or lacks the export. The per-invocation re-resolve seam for brains — BehavedMixin warms each brain path once at wire time (via the async resolveExport), then re-resolves the current class per fire through this sync path (a registry-map hit), so HMR propagates with no async on the hot path.

      Parameters

      • classPath: string
      • exportName: string

      Returns unknown

    • Cache-or-clone for templatePath-keyed singletons.

      Returns the unique live instance for path if one exists in the byTemplatePath index, otherwise routes through clone() to create one. Works on any class — composition with SingletonMixin is the separate enforcement layer that prevents bare clone() from producing duplicates.

      Throws when the index has multiple instances for path — that means the caller violated the singleton contract by also using clone() on a non-SingletonMixin class. Use clone() and track instances explicitly in that case.

      Type Parameters

      Parameters

      • path: string

        Template path (e.g., /narnia for the Narnia zone).

      • Optionalcontext: unknown

        Forwarded to clone() when this resolves to a first-time clone.

      Returns Promise<T>

    • Instantiate a template by the only question that matters at the generic layer: should it be a shared singleton or a fresh instance? If the class composes SingletonMixinsingleton(path) (reuse the one instance, clone-if-absent); otherwise → clone(path) (a fresh instance). Any domain semantics on top (a Warren landing in its host, a recall) belong in the caller, not here.

      Type Parameters

      Parameters

      • path: string
      • Optionalcontext: unknown

      Returns Promise<T>