import { Plugin, EngineContext, SystemScheduler, Entity, Scene } from '@yagejs/core'; import { DebugVectorProvider, DebugVectorOptions, DebugRegistry, DebugContributor, StatsApi } from './api.js'; export { DebugGraphics, DebugRegistryKey, HudDebugApi, SceneWorldDebugApi, WorldDebugApi } from './api.js'; /** Configuration for the DebugPlugin. */ interface DebugConfig { /** Key code to toggle debug overlay. Default: "Backquote" */ toggleKey?: string; /** Key code to advance one fixed-timestep frame while the debug clock is frozen. Default: "Period" */ stepKey?: string; /** Max pooled Graphics objects. Default: 256 */ maxGraphics?: number; /** Max HUD text lines. Default: 32 */ maxHudLines?: number; /** Whether the overlay starts enabled. Default: false */ startEnabled?: boolean; /** * Whether the Inspector records bus, entity and scene events at startup. * Default: true. Set false to skip the per-event allocation entirely (the EventBus * tap never attaches) when nothing reads `inspector.events.getLog()` or * `waitFor()` — toggle back on at runtime with `inspector.events.setEnabled(true)`. */ eventLog?: boolean; /** Initial flag overrides, keyed by "contributorName.flagName". */ flags?: Record; /** * If set, every scene's RNG is initialized with this seed instead of an * unspecified default. Use for deterministic E2E runs. Leave undefined for * normal debug builds so randomness behaves the same as in production. */ deterministicSeed?: number; /** * If true, the engine boots with a paused Pixi ticker so no auto-frames * tick before user code can intervene — equivalent to calling * `inspector.time.freeze()` at frame zero, but applied during plugin * `install()` (before `loop.start()`). Use for deterministic E2E runs: * any later `engine.scenes.push(...)` resolves under frozen time, so * snapshots taken right after only depend on the explicit `time.step()` * calls — not on how many auto-frames slipped in between * `await engine.start()` and the test's first `freeze()`. */ startFrozen?: boolean; } interface LayerTransformSnapshot { x: number; y: number; scaleX: number; scaleY: number; rotation: number; } interface CameraStackSnapshot { scene: string; name: string | undefined; priority: number; /** Whether the camera applies: its own `enabled` flag and an active entity. */ enabled: boolean; } /** * Renderer-aware diagnostics exposed through the inspector extension * namespace `debug`. Kept out of the core Inspector surface so the core * package stays renderer-agnostic while plugins can still publish optional * runtime helpers in a uniform way. */ interface DebugDiagnostics { getLayerTransform(sceneName: string, layerName: string): LayerTransformSnapshot | undefined; getCameraStack(): CameraStackSnapshot[]; /** Whether the HUD layer (text readouts) is currently visible. */ isHudVisible(): boolean; /** * Show/hide the HUD layer — the screen-space text readouts (FPS, system * timings, entity counts). World-space debug graphics (collider outlines * etc.) are unaffected. The stage re-renders immediately, so the change * lands on the canvas even while the debug clock is frozen — capture * tooling can hide the wall-clock-dependent text before screenshotting * without stepping the simulation. */ setHudVisible(visible: boolean): void; } /** * Debug overlay plugin. Mounts a private `DebugScene` through * `SceneManager._mountDetached` so it goes through the same scoped-DI * lifecycle as stacked scenes (the renderer's `beforeEnter` hook creates * its render tree) while staying off the user-visible scene stack. */ declare class DebugPlugin implements Plugin { readonly name = "debug"; readonly version = "3.0.0"; readonly dependencies: readonly ["renderer"]; private readonly config; private registry; private stats; private graphicsPool; private textPool; private worldApi; private hudApi; private renderSystem; private systemTimings; private originalUpdates; private keyListener; private context; private renderer; private scheduler; private sceneManager; private debugScene; private hudContainer; private provider; private eventUnsubs; private clock; private uninstallInspector; constructor(config?: DebugConfig); install(context: EngineContext): void; registerSystems(scheduler: SystemScheduler): void; onStart(): Promise; onDestroy(): void; private materializeDebugScene; private teardownDebugScene; private setUpDebugInfra; private tearDownDebugInfra; private registerInspectorDiagnostics; } /** A registered vector arrow, with every option resolved to a value. */ interface VectorEntry { readonly entity: Entity; readonly scene: Scene; /** * The entity life this registration belongs to. Both destruction and an * `EntityPool` release end a life and move `Entity.generation` on, so a * mismatch means the registration is stale. */ readonly generation: number; readonly vector: DebugVectorProvider; readonly scale: number; readonly color: number; readonly alpha: number; readonly originX: number; readonly originY: number; readonly minLength: number; readonly width: number; readonly headSize: number; } /** * The vector arrows registered through `DebugRegistry.drawVector`, indexed by * entity id so a destroyed entity's registrations can be dropped in one step * — that happens on the `entity:destroyed` bus event, which fires whether or * not the overlay is on, so nothing accumulates in a build that never draws. * * A pooled entity is never destroyed, so that event never covers it: the pool * ends a lease by moving the member's `generation` on and parking it dormant. * Every registration records the life it was made in, and a stale one is * dropped both while drawing and on the next `add` for the same entity — * `add` because it is the one cleanup point that still runs with the overlay * off, which is exactly when a per-lease registration would otherwise pile up * unseen. */ declare class VectorDrawStore { private readonly byEntity; private count; /** Register an arrow, returning an idempotent disposer. */ add(entity: Entity, vector: DebugVectorProvider, options?: DebugVectorOptions): () => void; /** Drop one arrow. Called by its disposer and when its life has ended. */ remove(entityId: number, entry: VectorEntry): void; /** Drop every arrow registered for an entity. */ dropEntity(entityId: number): void; clear(): void; /** How many arrows are registered. */ get size(): number; /** Safe to `dropEntity` mid-iteration — Map and Set iterators tolerate it. */ [Symbol.iterator](): IterableIterator; } /** Concrete implementation of the DebugRegistry interface. */ declare class DebugRegistryImpl implements DebugRegistry { readonly contributors: Map; /** Backs `drawVector`; read by the built-in `vectors` contributor. */ readonly vectors: VectorDrawStore; enabled: boolean; private flags; register(contributor: DebugContributor): void; drawVector(entity: Entity, vector: DebugVectorProvider, options?: DebugVectorOptions): () => void; isEnabled(): boolean; isFlagEnabled(contributorName: string, flag: string): boolean; toggle(): void; toggleFlag(contributorName: string, flag: string): void; setFlag(contributorName: string, flag: string, value: boolean): void; } /** Rolling-window statistics store backed by Float64Array ring buffers. */ declare class StatsStore implements StatsApi { private rings; push(key: string, value: number): void; average(key: string): number; latest(key: string): number; min(key: string): number; max(key: string): number; } export { type CameraStackSnapshot, type DebugConfig, DebugContributor, type DebugDiagnostics, DebugPlugin, DebugRegistry, DebugRegistryImpl, DebugVectorOptions, DebugVectorProvider, type LayerTransformSnapshot, StatsApi, StatsStore };