import type { EntityHandle } from './codec.js'; import type { EntityRecord } from './record.js'; import type { ComponentDef, ReadOf, Schema, WriteOf } from '@ecsia/schema'; /** * Resolves the (archetype, component) accessor singleton for an entity, pokes its row/handle, and * returns it. The world installs one resolver per world; binds against a directly-allocated * column set, binds against the entity's real archetype (the archetype-binding seam). */ export interface AccessorResolver { /** `lenient` mirrors the ref's binding: a lenient resolve (observer-window read of a dying * entity) opts out of the dev-mode stale-view guard the same way it opts out of #assertFresh. */ resolveRead(handle: EntityHandle, archetypeId: number, row: number, def: unknown, lenient?: boolean): unknown; resolveWrite(handle: EntityHandle, archetypeId: number, row: number, def: unknown, lenient?: boolean): unknown; } /** * The accessor surface installed on `EntityRef`. The bare `entity.` getter shorthand * resolves to `read()` and is Readonly; `write()` returns the mutable singleton whose * setters call world.trackWrite. */ export interface EntityAccessors { read(def: unknown): unknown; write(def: unknown): unknown; } /** * EntityRef is a POOLED SINGLETON: there is exactly ONE instance per world, and `world.entity(h)` * re-points (rebinds) that one object at `h` and returns it — it does NOT allocate a fresh ref per * call. The pooling contract is therefore: * * - The handle you bind is valid only until the NEXT `world.entity(...)` call, or until the bound * entity is despawned / structurally moved. A reference captured in a local and used across * either event aliases the wrong row. * - Do NOT hold the ref across a `world.entity()` call. Re-resolve with `world.entity(h)` at the * point of use, or extract the plain field values you need into locals first. * * To turn the classic silent-corruption footgun into a loud failure, the RANDOM-ACCESS read()/write() * accessors verify on every call that the currently-bound handle is still alive and still occupies the * location this ref cached at bind time; a stale/recycled/moved binding THROWS instead of reading or * writing the wrong entity's row. (Query-iteration element accessors are a separate, hot path and are * intentionally NOT guarded here.) */ export declare class EntityRef { #private; __handle: EntityHandle; __archetypeId: number; __row: number; /** Set when bound via `{ lenient: true }` (e.g. an onRemove observer resolving a just-despawned entity). */ __lenient: boolean; constructor(records: EntityRecord); /** Public, frozen-surface accessor for the bound handle (the non-`__` form referenced by ). */ get handle(): EntityHandle; /** Inject the world's accessor resolver (world wiring). */ __setResolver(resolver: AccessorResolver): void; /** Inject the world's liveness probe so random-access read/write can fail loud on a stale binding. */ __setLiveness(isAlive: (handle: EntityHandle) => boolean): void; /** Re-point this pooled ref at a (validated-alive) handle and resolve its location. */ __bind(handle: EntityHandle, lenient?: boolean): this; /** * Deeply-`Readonly` view of `def`'s fields for this entity. * The `const C` parameter recovers the inferred `ReadOf` so a random-access read is typed without * caller casts; assignment through it is a TS2540 compile error. RANDOM-ACCESS path: guarded against a * stale/recycled/moved pooled binding (see class jsdoc). */ read>(def: C): ReadOf; /** * Mutable, write-tracked `WriteView` of `def`'s fields for this entity. The `const C` parameter * recovers the inferred `WriteOf` so a random-access write is typed without caller casts; every * setter additionally drives the write log (the only tracked-mutation path). RANDOM-ACCESS path: * guarded against a stale/recycled/moved pooled binding (see class jsdoc). */ write>(def: C): WriteOf; } //# sourceMappingURL=ref.d.ts.map