Skip to content

Public API tier table

declarative-hex-worlds publishes one umbrella entry plus a wide set of subpath exports. Every subpath stays supported through 1.0 — this is an asset-bundled library where mod authors, custom-renderer builders, and data-inspection tooling all have legitimate reasons to reach internals.

What changes between tiers is the stability contract, not the availability. Pick the tier that matches your tolerance for breakage; the tooling and TSDoc tags tell you which tier a symbol belongs to.

Semver-strict. Breaking changes only on major versions, always with a migration guide.

Subpath Purpose
. (umbrella) Default entry. Re-exports the consumer-facing surface from every tier-1 sub-package.
./react React bindings. First-class (NOT peer-gated). Hooks, providers, components.
./three Three.js bindings. First-class (NOT peer-gated). Loaders, disposers, scene helpers.
./cli CLI entry (also installed as the declarative-hex-worlds bin).
./manifest/schema MedievalHexagonManifest shape + validators.
./manifest/free Pre-baked FREE-edition manifest metadata.
./scenario GameboardScenario, blueprint/recipe/catalog/registry.
./blueprint GameboardBlueprintOptions and procedural board generation.
./gameboard Board lifecycle, occupancy, navigation.
./recipe Recipe DSL.
./coverage Release-readiness coverage ledger surface for build/review tooling, not runtime ECS adapter glue.
./compatibility Manifest/version compatibility helpers.
./errors GameboardError + typed subclasses (Epic D2).
./traits Single trait umbrella (all trait() declarations).
./types Shared primitive types + branded IDs (HexKey, ActorId, etc.).
./examples/*.json Bundled example scenario JSON.

TSDoc tag: @public.

The ./types subpath exports branded ID aliases and brand* helpers, but branded IDs are NOT yet enforced across the runtime. Public JSON, manifest, CLI, and ECS-facing shapes still use plain strings unless a domain row below states that enforcement has landed. Treat the aliases as opt-in compile-time helpers until the relevant domain moves to enforced.

Domain(s) Brands tracked Current status
./types HexKey, ActorId, TileId, PieceId, PlacementId, ScenarioId, QuestId, ObjectiveId, PatrolRouteId, AssetId Registry exported; brand* helpers are stable and available to consumers.
./coordinates, ./grid, ./layout, ./gameboard HexKey, TileId Not yet enforced; public APIs still accept coordinate objects and plain string IDs.
./scenario, ./recipe, ./blueprint, ./simulation, ./pieces ScenarioId, PieceId, PlacementId, TileId, AssetId Not yet enforced; scenario JSON and generated plans keep wire-compatible string fields.
./actors, ./movement, ./patrol, ./quests ActorId, PatrolRouteId, QuestId, ObjectiveId Not yet enforced; ECS traits, commands, and query helpers still expose string-compatible IDs.
./manifest/schema, ./manifest/free, ./ingest, ./runtime, ./react, ./three AssetId Not yet enforced; asset IDs remain manifest and URL-facing strings.
./cli, ./interop, ./compatibility, ./coverage Pass-through IDs only Not yet enforced; these surfaces preserve external wire formats and report existing domain values.

Semver-strict for what’s documented, but the surface contract is smaller than tier 1. Mod authors and custom-renderer consumers can depend on these; breaking changes still require a major + migration guide but the “what’s breaking” frame may be narrower (a specific function signature changing rather than the whole module shape).

Subpath Purpose
./koota World bootstrap, trait sets, query patterns.
./runtime High-level runtime composition.
./actors Actor surface + queries.
./movement Movement-agent surface.
./patrol Patrol-route surface.
./quests Quest surface.
./pieces Piece declarations + placement helpers.
./projection World-space placement projection.
./layout Seeded board layout generation.
./grid Honeycomb-grid wrappers + hex algebra.
./coordinates Hex coordinate algebra (umbrella over grid/projection/layout).
./validation Plan-level validators.
./rules Rule definitions + evaluation.

TSDoc tag: @public (no distinct tag — tier 2 is documented here in the table, not on every symbol).

Semver-permissive: minor versions may change these surfaces in non-trivial ways. Consumers who pin tier-3 imports accept this tradeoff. Useful for very-deep modding, debugging, and tools that need to inspect or hook the implementation.

Subpath Purpose
./commands Internal command-factory plumbing.
./selectors Per-render shape pickers used by React/Three bindings.
./systems Tickable system functions.
./world-rules Runtime rule-evaluation system.
./rule-types Rule typed shapes (re-exported from ./rules — prefer that).
./interop Runtime ECS adapter glue and schema migrations; release-readiness coverage is the separate stable ./coverage surface.
./ingest Source-tree walker + manifest emission (precursor to bootstrap).
./registry Tile/piece registries (re-exported from ./scenario).
./catalog KayKit asset catalog (re-exported from ./scenario).

TSDoc tag: @internal (still exported, still typed, still tested — just flagged so consumers see the support tier inline).

Phase 1’s architecture review (F1) noted that the 37+ subpath exports permanently pin every internal module as a semver surface point. The original recommendation was to demote ~10 subpaths and force everything through the umbrella.

Per PRD §4 Epic D1 (re-scoped), that was rejected: this library bundles the FREE KayKit pack so consumers get a working game out-of-box, and the “out-of-box” promise extends to consumers who want to reach internals for legitimate reasons (custom renderers, modding, data inspection). The right tradeoff is documented tiering, not gated demotion.

If a tier-3 surface stabilizes (a custom-renderer mod has been depending on ./selectors for two minor versions without breakage), it can be promoted to tier 2 via a CHANGELOG note. The reverse (demoting a tier-1 surface) is a major-version event.

Regardless of tier, every trait() declaration is exported from exactly one module. tsup’s splitting: true keeps shared chunks stable so that two consumers importing GameboardActor from different subpaths (e.g. one via ./actors, another via the umbrella) get the SAME trait identity — which is what koota’s useQuery reference-equality lookups depend on.

Don’t try to “optimize” by re-declaring a trait somewhere else. Don’t fork the trait file into a “local” copy. The trait-identity test (Epic E4, pending) pins this.