import type { Entity } from "./types"; import { type MatchHost } from "./query-match"; /** * Host interface that QueryCache uses to read EntityManager state. * Decouples QueryCache from EntityManager's private fields. */ export interface QueryCacheHost extends MatchHost { getChildren(parentId: number): readonly number[]; allEntities(): IterableIterator>; componentIndex(component: keyof ComponentTypes): Set | undefined; } /** * Maintains incrementally-updated Maps of entity id → Entity matching the * static portion of registered query shapes (with / without / parentHas). * EntityManager calls the on* hooks on component add/remove, entity * removal, and parent change. The `changed` filter is applied as a * post-pass by the caller, since its threshold advances each tick. */ export default class QueryCache { private readonly host; private readonly caches; private readonly byComp; private readonly byParentComp; constructor(host: QueryCacheHost); /** * Returns the Map of entity id → Entity matching the (with, without, * parentHas) shape. Caches are interned by canonical shape — identical * shapes share a single Map across systems. Cold-start populates by * iterating the smallest matching component index. */ getOrCreate(withC: ReadonlyArray, withoutC: ReadonlyArray, parentHas: ReadonlyArray): Map>; /** Test-only: returns the number of distinct interned shapes. */ get cacheCount(): number; private populate; private matches; private reeval; onComponentChanged(entityId: number, componentName: keyof ComponentTypes): void; onParentChanged(childId: number): void; /** * Drops the entity from every cache. For parentHas caches, also drops * direct children — once this entity is gone, the parent-link severs * and children stop matching. Cascade-removed children fire their own * beforeRemoved first, so the extra delete is a harmless no-op there. */ onEntityRemoved(entityId: number): void; /** @internal Release all cached query shapes and entity memberships. */ clear(): void; }