4.0 KiB
4.0 KiB
EcsError & ECS_CODES
(no submodules)
Purpose
EcsError is the typed error class for every failure mode raised by @suaveplan/ecs. It extends SuaveplanError from @suaveplan/error, which means every thrown error carries a stable, fully-qualified code string and a structured context object documenting the inputs at the failure site. Callers can discriminate failures from this package either by instanceof EcsError or by inspecting error.code.
ECS_CODES is the canonical registry of every code this package raises. The registry is generated through defineCodes("ECS", [...]) from @suaveplan/error, which fully-qualifies the string literals (e.g., "ECS.ENTITY_NOT_ALIVE") and exposes them as a frozen object.
Features
- Stable, fully-qualified codes —
ECS.ENTITY_NOT_ALIVE,ECS.COMPONENT_NOT_REGISTERED,ECS.COMPONENT_NAME_CONFLICT,ECS.COMPONENT_LIMIT_REACHED,ECS.COMPONENT_VALIDATION_FAILED,ECS.SYSTEM_NAME_CONFLICT,ECS.SNAPSHOT_INVALID. - Every code is exercised by
src/errors/ecs-error.test.tsfrom a real production code path — registry-to-throw-site coverage is a test invariant. - Structured
contextfield on every error ({ entity },{ name },{ issues }, etc.) so the wire-redacted error envelope remains useful for debugging. - Cause chain preserved through
SuaveplanError's base support. - Code type
EcsCodeis a literal union narrowable inswitchstatements.
Usage: Basic Example
import { createWorld, defineComponent, ECS_CODES, EcsError } from "@suaveplan/ecs";
const Position = defineComponent("Position", { x: 0, y: 0 });
const world = createWorld();
const entity = world.spawn();
world.despawn(entity);
try {
world.addComponent(entity, Position);
} catch (err) {
if (err instanceof EcsError && err.code === ECS_CODES.ENTITY_NOT_ALIVE) {
console.error("entity was already despawned");
}
}
Usage: Advanced Example
import { ECS_CODES, EcsError } from "@suaveplan/ecs";
function isRetryable(err: unknown): boolean {
if (!(err instanceof EcsError)) return false;
switch (err.code) {
case ECS_CODES.ENTITY_NOT_ALIVE:
case ECS_CODES.SNAPSHOT_INVALID:
return false;
case ECS_CODES.COMPONENT_VALIDATION_FAILED:
return false;
case ECS_CODES.COMPONENT_LIMIT_REACHED:
case ECS_CODES.COMPONENT_NAME_CONFLICT:
case ECS_CODES.COMPONENT_NOT_REGISTERED:
case ECS_CODES.SYSTEM_NAME_CONFLICT:
return false;
default:
return false;
}
}
API Reference
| Symbol | Description |
|---|---|
EcsError |
SuaveplanError-extending class. Constructor: new EcsError(code, messageKey, options?). |
EcsCode |
Type union over every registered code string. |
ECS_CODES |
Frozen registry returned by defineCodes. |
ECS_CODES.ENTITY_NOT_ALIVE |
Thrown from addComponent / removeComponent / setComponent for despawned entities. |
ECS_CODES.COMPONENT_NOT_REGISTERED |
registry.getId(schema) on an unknown schema. |
ECS_CODES.COMPONENT_NAME_CONFLICT |
Two distinct schemas sharing a name. |
ECS_CODES.COMPONENT_LIMIT_REACHED |
register past maxComponents. |
ECS_CODES.COMPONENT_VALIDATION_FAILED |
Zod validator rejected merged component data. |
ECS_CODES.SYSTEM_NAME_CONFLICT |
addSystem with a name already registered. |
ECS_CODES.SNAPSHOT_INVALID |
restore payload failed WorldSnapshotSchema. |
Implementation Notes
- Codes are namespaced under
ECS.so cross-package error log aggregation can filter by theECS.*prefix. EcsErrordoes not overrideError.prototype.toJSON—SuaveplanErroralready exposestoWire/toSpanEventfor serialisation, and we don't extend the contract.- The test suite enumerates
Object.values(ECS_CODES)and triggers every code through a production call. If a new code is added without a real throw site, the test will fail — this is the registered-but-unthrown drift guard required by the spec-conformance checklist (AGENTS.md§13).