# World _(no submodules)_ ## Purpose `World` is the central container for an ECS instance. It owns the component registry, entity pool, archetype graph, query cache, system scheduler, and the typed `EventBus` that fires entity-lifecycle events. Every public mutator is instrumented with OpenTelemetry — `spawn` / `despawn` increment a counter, `execute` records into a histogram — so production behaviour is observable through the same in-memory `MeterProvider` used in tests. The `createWorld(config?)` factory is the canonical entry point. `World` itself is exported so consumers can subclass or interrogate the instance. ## Features - Archetype-graph mutations: `addComponent` / `removeComponent` move entities between archetypes lazily. - Idempotent `addComponent` — no-op when the component already exists. - Component validation via descriptor-attached Zod schemas; throws `ECS.COMPONENT_VALIDATION_FAILED` on rejection. - System scheduling with phase + priority ordering through `SystemScheduler`. - `snapshot` / `restore` round-trip with Zod-validated payloads. - Typed `EventBus` — single-object payloads matching the `@suaveplan/events` shape. - OpenTelemetry counter (`ecs.entity.lifecycle_total`) and histogram (`ecs.world.execute_duration_ms`). ## Usage: Basic Example ```ts import { createWorld, defineComponent, defineSystem } from "@suaveplan/ecs"; const Position = defineComponent("Position", { x: 0, y: 0 }); const Velocity = defineComponent("Velocity", { dx: 1, dy: 0 }); const world = createWorld(); world.addSystem(defineSystem({ name: "physics", query: { include: [Position, Velocity] }, execute: ({ entities, world, dt }) => { for (const entity of entities) { const p = world.getComponent(entity, Position)!; const v = world.getComponent(entity, Velocity)!; world.setComponent(entity, Position, { x: p.x + v.dx * dt, y: p.y + v.dy * dt, }); } }, })); const player = world.spawn(); world.addComponent(player, Position, { x: 0, y: 0 }); world.addComponent(player, Velocity, { dx: 5, dy: 0 }); world.execute(1 / 60); ``` ## Usage: Advanced Example ```ts import { createWorld, defineComponent } from "@suaveplan/ecs"; const Position = defineComponent("Position", { x: 0, y: 0 }); const world = createWorld({ maxComponents: 128 }); world.registerComponent(Position); world.events.on("entitySpawned", ({ entity }) => { console.log("spawned", entity); }); world.events.on("componentAdded", ({ entity, component }) => { console.log(component, "->", entity); }); const snapshot = world.snapshot(); // ... ship snapshot over the wire ... const replay = createWorld(); replay.registerComponent(Position); replay.restore(snapshot); ``` ## API Reference | Member | Description | |---|---| | `createWorld(config?)` / `new World(config)` | Build a world. `maxComponents` defaults to 256. | | `events` | `EventBus` for entity lifecycle. | | `registry` | Underlying `ComponentRegistry`. | | `registerComponent(schema)` | Pre-register a schema (idempotent). | | `spawn()` | Allocate an entity and emit `entitySpawned`. | | `despawn(entity)` | Despawn and recycle the entity. No-op when unknown. | | `isAlive(entity)` | Boolean liveness check. | | `addComponent(entity, schema, data?)` | Attach a component; validates Zod-attached schemas. | | `removeComponent(entity, schema)` | Detach a component. | | `getComponent(entity, schema)` | Read a component (or `undefined`). | | `setComponent(entity, schema, data)` | Merge partial data; revalidates. | | `hasComponent(entity, schema)` | Membership check. | | `query(descriptor)` | One-off entity query. | | `addSystem(system)` / `removeSystem(name)` | System lifecycle. | | `execute(dt)` | Run every enabled system; records duration histogram. | | `snapshot()` / `restore(snap)` | Persistence pair. `restore` Zod-validates the input. | | `clear()` | Wipe entities + archetypes, keep systems. | | `entityCount` / `archetypeCount` / `getSystems()` | Inspection accessors. | ## Implementation Notes - Archetype identity is the bitset key (`mask.toKey()`); `getOrCreateArchetype` deduplicates archetypes through a `Map`. - `addComponent` and `removeComponent` go through a swap-remove from the old archetype, mutate the component map, then insert into the new archetype. The destination archetype is materialised lazily. - `setComponent` does **not** change archetype membership — it merges into the existing column slot and re-validates against the descriptor's Zod schema. - `restore` is the only persistence boundary that validates input with Zod (`WorldSnapshotSchema`). Failures throw `ECS.SNAPSHOT_INVALID` with the Zod issues in `context`. Unknown component names in the snapshot are silently skipped, so adding new component types to consumer code does not break older snapshots. - OTel instruments resolve through the global `MeterProvider`. In tests, the package's `tests/setup/polyfills.ts` registers an in-memory `MeterProvider` before any module imports so the instruments bind to it. - The world's `EventBus` uses single-object payloads (`{ entity }`, `{ entity, component }`) consistent with the `@suaveplan/events` contract, rather than upstream's tuple shape.