# ComponentRegistry _(no submodules)_ ## Purpose `ComponentRegistry` translates `ComponentSchema` references into stable integer IDs. These IDs are used as bit positions in archetype bitsets, and registering a schema is the first step before it can be added to any entity. The registry enforces three contracts: identity-stable registration (same schema → same ID), name-uniqueness (no two schemas may share a `name`), and a hard cap on how many distinct types can be registered (`maxComponents`, default 256, matching the bitset's typical 8-word allocation). The registry is owned by `World` but exposed publicly so editors, profilers, and serialisation layers can iterate every registered type. ## Features - Idempotent `register` — passing the same schema reference returns the same ID. - Throws structured `EcsError` with stable codes for every failure mode (`COMPONENT_NAME_CONFLICT`, `COMPONENT_LIMIT_REACHED`, `COMPONENT_NOT_REGISTERED`). - Bi-directional lookup: schema → ID, ID → schema, name → schema. - `maxComponents` cap configured at construction; validates positive integer. - `count`, `has`, and `schemas()` accessors for inspection. ## Usage: Basic Example ```ts import { ComponentRegistry, defineComponent } from "@suaveplan/ecs"; const Position = defineComponent("Position", { x: 0, y: 0 }); const registry = new ComponentRegistry(); const positionId = registry.register(Position); console.log(positionId); // 0 console.log(registry.register(Position) === positionId); // true ``` ## Usage: Advanced Example ```ts import { ComponentRegistry, defineComponent, ECS_CODES, EcsError, } from "@suaveplan/ecs"; const registry = new ComponentRegistry(4); registry.register(defineComponent("A", {})); registry.register(defineComponent("B", {})); registry.register(defineComponent("C", {})); registry.register(defineComponent("D", {})); try { registry.register(defineComponent("E", {})); } catch (err) { if (err instanceof EcsError && err.code === ECS_CODES.COMPONENT_LIMIT_REACHED) { console.warn("raise maxComponents in WorldConfig"); } } ``` ## API Reference | Member | Description | |---|---| | `new ComponentRegistry(maxComponents?)` | Default `maxComponents` is 256. Rejects non-positive integers. | | `register(schema)` | Return stable ID. Throws `ECS.COMPONENT_NAME_CONFLICT` / `ECS.COMPONENT_LIMIT_REACHED`. | | `getId(schema)` | Return ID, or throw `ECS.COMPONENT_NOT_REGISTERED`. | | `getSchema(id)` / `getSchemaByName(name)` | Lookups; return `undefined` when missing. | | `has(schema)` | Boolean membership. | | `count` | Number of registered schemas. | | `schemas()` | Read-only snapshot in registration order. | | `maxComponents` | Hard cap configured at construction. | ## Implementation Notes - Three maps back the registry — schema → id, id → schema, name → schema. The slight memory overhead pays off in O(1) lookups across all three directions. - IDs are dense and monotonically increasing starting at `0`. They are intentionally not stable across processes: the same schema registered second in process A and first in process B yields different IDs. Persistence layers use `schema.name`, never the ID. - Name uniqueness is enforced via the dedicated `nameToSchema` map; this catches the most common bug — two `defineComponent("Position", …)` calls in different modules — at registration time rather than at the first archetype-create. - The `maxComponents` cap is also the bitset size for archetype masks. Exceeding it would mean an archetype's mask runs off the end of its `Uint32Array`, so the limit is hard, not advisory.