@vworlds/vecs 1.0.45 → 1.0.46

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (136) hide show
  1. package/README.md +7 -1
  2. package/dist/component.d.ts +57 -60
  3. package/dist/component.js +24 -17
  4. package/dist/component.js.map +1 -1
  5. package/dist/component_meta.d.ts +67 -79
  6. package/dist/component_meta.js +331 -37
  7. package/dist/component_meta.js.map +1 -1
  8. package/dist/dsl.js +1 -1
  9. package/dist/dsl.js.map +1 -1
  10. package/dist/entity/entity.base.js +4 -0
  11. package/dist/entity/entity.base.js.map +1 -1
  12. package/dist/entity/entity.components.d.ts +39 -41
  13. package/dist/entity/entity.components.js +249 -82
  14. package/dist/entity/entity.components.js.map +1 -1
  15. package/dist/entity/entity.d.ts +1 -2
  16. package/dist/entity/entity.js +2 -3
  17. package/dist/entity/entity.js.map +1 -1
  18. package/dist/entity/entity.lifecycle.d.ts +7 -1
  19. package/dist/entity/entity.lifecycle.js +40 -50
  20. package/dist/entity/entity.lifecycle.js.map +1 -1
  21. package/dist/entity/entity.queries.js +30 -0
  22. package/dist/entity/entity.queries.js.map +1 -1
  23. package/dist/entity/entity.relationships.d.ts +4 -0
  24. package/dist/entity/entity.relationships.js +18 -10
  25. package/dist/entity/entity.relationships.js.map +1 -1
  26. package/dist/index.d.ts +7 -4
  27. package/dist/index.js +4 -2
  28. package/dist/index.js.map +1 -1
  29. package/dist/inject.d.ts +63 -14
  30. package/dist/inject.js +165 -89
  31. package/dist/inject.js.map +1 -1
  32. package/dist/modules/identity.js +2 -2
  33. package/dist/modules/identity.js.map +1 -1
  34. package/dist/modules/implements.js +2 -2
  35. package/dist/modules/implements.js.map +1 -1
  36. package/dist/modules/pipeline.js +5 -5
  37. package/dist/modules/pipeline.js.map +1 -1
  38. package/dist/modules/relationship_types.d.ts +70 -6
  39. package/dist/modules/relationship_types.js +70 -6
  40. package/dist/modules/relationship_types.js.map +1 -1
  41. package/dist/modules/relationships.d.ts +31 -9
  42. package/dist/modules/relationships.js +120 -44
  43. package/dist/modules/relationships.js.map +1 -1
  44. package/dist/modules/singleton.js +2 -2
  45. package/dist/modules/singleton.js.map +1 -1
  46. package/dist/modules/snapshot.d.ts +26 -0
  47. package/dist/modules/snapshot.js +45 -0
  48. package/dist/modules/snapshot.js.map +1 -0
  49. package/dist/modules/snapshot_types.d.ts +85 -0
  50. package/dist/modules/snapshot_types.js +130 -0
  51. package/dist/modules/snapshot_types.js.map +1 -0
  52. package/dist/package.json +2 -2
  53. package/dist/query/callbacks.d.ts +9 -7
  54. package/dist/query/callbacks.js +27 -41
  55. package/dist/query/callbacks.js.map +1 -1
  56. package/dist/query/grouped_query.js +7 -7
  57. package/dist/query/grouped_query.js.map +1 -1
  58. package/dist/query/grouping.d.ts +6 -2
  59. package/dist/query/grouping.js +84 -15
  60. package/dist/query/grouping.js.map +1 -1
  61. package/dist/query/query.d.ts +4 -4
  62. package/dist/query/query.js +37 -14
  63. package/dist/query/query.js.map +1 -1
  64. package/dist/snapshot/capture.d.ts +1 -0
  65. package/dist/snapshot/capture.js +212 -0
  66. package/dist/snapshot/capture.js.map +1 -0
  67. package/dist/snapshot/clone.d.ts +1 -0
  68. package/dist/snapshot/clone.js +107 -0
  69. package/dist/snapshot/clone.js.map +1 -0
  70. package/dist/snapshot/index.d.ts +5 -0
  71. package/dist/snapshot/index.js +21 -0
  72. package/dist/snapshot/index.js.map +1 -0
  73. package/dist/snapshot/preflight.d.ts +1 -0
  74. package/dist/snapshot/preflight.js +40 -0
  75. package/dist/snapshot/preflight.js.map +1 -0
  76. package/dist/snapshot/restore.d.ts +1 -0
  77. package/dist/snapshot/restore.js +111 -0
  78. package/dist/snapshot/restore.js.map +1 -0
  79. package/dist/snapshot/types.d.ts +135 -0
  80. package/dist/snapshot/types.js +26 -0
  81. package/dist/snapshot/types.js.map +1 -0
  82. package/dist/system.d.ts +3 -1
  83. package/dist/system.js +33 -23
  84. package/dist/system.js.map +1 -1
  85. package/dist/terms/all_term.js +19 -0
  86. package/dist/terms/all_term.js.map +1 -1
  87. package/dist/terms/any_term.js +19 -0
  88. package/dist/terms/any_term.js.map +1 -1
  89. package/dist/terms/not_term.js +4 -0
  90. package/dist/terms/not_term.js.map +1 -1
  91. package/dist/terms/target_term.d.ts +1 -2
  92. package/dist/terms/target_term.js +2 -4
  93. package/dist/terms/target_term.js.map +1 -1
  94. package/dist/terms/term.d.ts +2 -0
  95. package/dist/terms/term.js +59 -10
  96. package/dist/terms/term.js.map +1 -1
  97. package/dist/util/array_map.d.ts +43 -12
  98. package/dist/util/array_map.js +90 -41
  99. package/dist/util/array_map.js.map +1 -1
  100. package/dist/util/dense_set.js +26 -0
  101. package/dist/util/dense_set.js.map +1 -1
  102. package/dist/world/world.base.js +12 -0
  103. package/dist/world/world.base.js.map +1 -1
  104. package/dist/world/world.d.ts +7 -0
  105. package/dist/world/world.deferred.js +17 -0
  106. package/dist/world/world.deferred.js.map +1 -1
  107. package/dist/world/world.entities.d.ts +13 -2
  108. package/dist/world/world.entities.js +16 -2
  109. package/dist/world/world.entities.js.map +1 -1
  110. package/dist/world/world.js +15 -3
  111. package/dist/world/world.js.map +1 -1
  112. package/dist/world/world.pipeline.d.ts +2 -2
  113. package/dist/world/world.pipeline.js +4 -2
  114. package/dist/world/world.pipeline.js.map +1 -1
  115. package/dist/world/world.queries.d.ts +7 -0
  116. package/dist/world/world.queries.js +52 -0
  117. package/dist/world/world.queries.js.map +1 -1
  118. package/dist/world/world.transfers.d.ts +137 -0
  119. package/dist/world/world.transfers.js +188 -0
  120. package/dist/world/world.transfers.js.map +1 -0
  121. package/docs/README.md +18 -6
  122. package/docs/components.md +90 -53
  123. package/docs/concepts.md +21 -17
  124. package/docs/design-guide.md +125 -94
  125. package/docs/entities.md +62 -76
  126. package/docs/execution-model.md +26 -10
  127. package/docs/getting-started.md +6 -6
  128. package/docs/glossary.md +19 -10
  129. package/docs/modules.md +8 -8
  130. package/docs/queries-and-filters.md +15 -10
  131. package/docs/relationships.md +245 -124
  132. package/docs/snapshots.md +181 -0
  133. package/docs/systems.md +64 -21
  134. package/docs/utilities.md +10 -6
  135. package/package.json +2 -2
  136. package/skills/vecs/SKILL.md +5 -4
@@ -5,8 +5,9 @@ exclusivity, cleanup policies, interface aliases, and singletons.
5
5
 
6
6
  ## Components are plain data classes
7
7
 
8
- A component is an ordinary class with field initializers and a no-argument constructor. It does
9
- not inherit from a vecs base class — vecs instantiates it with `new ComponentClass()`:
8
+ An ordinary component is a class with field initializers and a no-argument constructor. Relationship
9
+ components are the exception: they extend `Relationship` so vecs can recognize their private edge
10
+ storage. Vecs instantiates either form with `new ComponentClass()`:
10
11
 
11
12
  ```ts
12
13
  class Position {
@@ -71,27 +72,30 @@ Pass `idPools` to the `World` constructor to change ranges (each entry is an `Id
71
72
  released this frame is not immediately recycled. `world.peekNextEid(poolName)` returns the next id
72
73
  a pool would allocate without reserving it. Id 0 is reserved.
73
74
 
74
- ## Hooks: `onAdd`, `onSet`, `onRemove`, `onRetarget`
75
+ ## Hooks: component and relationship edges
75
76
 
76
77
  The `Component` entity exposes lifecycle hooks fired for every entity carrying the component:
77
78
 
78
79
  ```ts
79
80
  world
80
81
  .component(Sprite)
81
- .onAdd((entity, sprite) => sprite.initialize(scene, entity))
82
- .onSet((entity, sprite) => sprite.syncToScene(entity))
83
- .onRemove((entity, sprite) => sprite.destroy(scene, entity));
82
+ .on("add", (entity, sprite) => sprite.initialize(scene, entity))
83
+ .on("set", (entity, sprite) => sprite.syncToScene(entity))
84
+ .on("remove", (entity, sprite) => sprite.destroy(scene, entity));
84
85
  ```
85
86
 
86
- | Hook | Fires when… |
87
- | ---------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
88
- | `onAdd` | The component is first attached (`add`, or `set` on an entity that lacked it). |
89
- | `onSet` | `entity.set(C, props)` applies data, `entity.attach(instance)` stores an instance, `entity.modified(C)` is called, or `getMut` / `ensure` marks it dirty. |
90
- | `onRemove` | The component is detached (`entity.remove(C)`), or the owning entity is destroyed. |
87
+ | Hook | Fires when… |
88
+ | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
89
+ | `add` | The component is first attached (`add`, or `set` on an entity that lacked an ordinary component). |
90
+ | `set` | `entity.set(C, props)` applies ordinary data, `entity.attach(instance)` stores an instance, `entity.modified(C)` is called, or `getMut` / `ensure` marks it dirty. |
91
+ | `remove` | The component is detached (`entity.remove(C)`), or the owning entity is destroyed. |
91
92
 
92
- Relationship component entities additionally expose `onRetarget`. It fires after an existing
93
- relationship changes to a different target, once `target` and `children` already reflect the new
94
- reverse-index state. Initial assignment, same-target `set`, and removal do not fire it.
93
+ Relationship component entities additionally expose the `targetAdd` and `targetRemove` events. Both
94
+ receive `(source, relationshipInstance, target)` after the edge and reverse index reach their new
95
+ state. An exclusive replacement fires `targetRemove` for the old edge before `targetAdd` for
96
+ the new edge. Adding or removing edges never fires `set`; it is reserved for ordinary fields on
97
+ an existing relationship. See [Relationships — edge hooks](./relationships.md#edge-hooks) for the
98
+ full ordering.
95
99
 
96
100
  Handlers receive `(entity, componentInstance)` — component instances do not carry entity
97
101
  references, so the entity is passed explicitly. Hooks fire synchronously when the corresponding
@@ -99,10 +103,10 @@ command is applied (see [Execution model](./execution-model.md#reactive-routing)
99
103
  resource management and cross-cutting reactions that aren't worth a system; use systems when the
100
104
  reaction belongs to a phase.
101
105
 
102
- Each lifecycle event has one handler slot per component. Calling `onAdd(handler)`,
103
- `onSet(handler)`, `onRemove(handler)`, or `onRetarget(handler)` replaces the current handler and
104
- returns the component entity for chaining. Call the method without an argument to read the current
105
- handler, or pass `undefined` to remove it.
106
+ Multiple handlers may be registered for the same event: they fire in registration
107
+ order. Call `on(event, handler)` to append, `on(event, handler, true)` to
108
+ prepend, and `off(event, handler)` to remove a handler by reference. All three
109
+ return the component entity for chaining.
106
110
 
107
111
  ## Component traits: policy on component entities
108
112
 
@@ -112,25 +116,30 @@ components **component traits**:
112
116
 
113
117
  ```ts
114
118
  world.component(Gravity).add(Singleton);
115
- world.component(Inventory).add(OwnedTarget);
116
- world.component(ChildOf).add(Traversable);
119
+ world.component(Inventory).add(Exclusive).add(OwnedTarget);
120
+ world.component(ChildOf).add(Traversable); // ChildOf is already Exclusive
117
121
  world.component(Orange).set(Implements, { interfaces: [Fruit] });
118
122
  ```
119
123
 
120
- | Trait/declaration | Applies to | Effect |
121
- | ----------------- | ---------------------------------- | -------------------------------------------------------------------- |
122
- | `Singleton` | Concrete components | Allows at most one carrier entity |
123
- | `Implements` | Non-relationship components | Exposes concrete data through abstract aliases |
124
- | `Traversable` | Relationships | Maintains hierarchy depth for cascade ordering |
125
- | `OwnedTarget` | Relationships other than `ChildOf` | Owns the active target through `ChildOf` and destructive replacement |
124
+ | Trait/declaration | Applies to | Effect |
125
+ | ----------------- | -------------------------------------------- | ---------------------------------------------------------------------------- |
126
+ | `Singleton` | Concrete components | Allows at most one carrier entity |
127
+ | `Implements` | Non-relationship components | Exposes concrete data through abstract aliases |
128
+ | `Exclusive` | Relationships | Restricts the relationship to one target with atomic replacement |
129
+ | `Traversable` | Exclusive relationships | Maintains hierarchy depth for cascade ordering |
130
+ | `OwnedTarget` | Exclusive relationships other than `ChildOf` | Owns each explicit target through `ChildOf` and destructive edge replacement |
131
+ | `OneOf` | Relationships | Constrains each target to be a direct `ChildOf` child of a declared parent |
126
132
 
127
133
  Traits declare policy; their built-in modules install the hooks and maintain the metadata that
128
134
  enforce it. Configure traits in feature-module setup rather than opportunistically inside systems.
129
135
  Do not assume all traits have the same lifecycle:
130
136
 
131
137
  - Add `Singleton` before component instances exist.
132
- - Configure owner hooks, then add `OwnedTarget`, before relationship instances exist; it wraps the
133
- current hooks and does not backfill.
138
+ - Add `Exclusive` before relationship instances exist. `OwnedTarget` and `Traversable` require it.
139
+ - Add `OwnedTarget` before relationship instances exist; it never creates a target implicitly and
140
+ does not replace public edge hooks.
141
+ - Add `OneOf` before relationship instances exist; it rejects every target that is not a direct
142
+ `ChildOf` child of the declared parent.
134
143
  - `Traversable` backfills existing relationship instances.
135
144
  - Setting or removing `Implements` recomputes interface aliases.
136
145
 
@@ -145,8 +154,10 @@ class Audited {}
145
154
 
146
155
  class AuditModule extends Module {
147
156
  public init(): void {
148
- this.world.component(Audited).onAdd((component) => {
149
- component.onSet((entity) => console.log("changed", component.meta.Class.name, entity.eid));
157
+ this.world.component(Audited).on("add", (component) => {
158
+ component.on("set", (entity) =>
159
+ console.log("changed", component.meta.Class.name, entity.eid)
160
+ );
150
161
  });
151
162
  }
152
163
  }
@@ -177,17 +188,22 @@ at any time. Exclusivity is how you model "exactly one of N" (one renderable per
177
188
  physics shape per body) — and it makes type swaps clean, because replacing the component fires the
178
189
  old one's `exit`/`onRemove` automatically.
179
190
 
191
+ This is distinct from the `Exclusive` relationship trait. `setExclusiveComponents(A, B)` chooses
192
+ which **component class** may exist; `world.component(R).add(Exclusive)` allows one instance of
193
+ relationship `R` as usual but limits it to one **target edge**, replacing that edge atomically. See
194
+ [Relationships — exclusive one-target relationships](./relationships.md#exclusive-one-target-relationships).
195
+
180
196
  ## Cleanup policies: destroying a component entity
181
197
 
182
198
  Every component key is backed by a component entity. What happens when you destroy that entity
183
199
  while other entities still carry the component is controlled by `meta.onDelete`
184
200
  (`CleanupPolicy` in `lib/vecs/src/component_meta.ts`):
185
201
 
186
- | Policy | Meaning |
187
- | ---------------------- | -------------------------------------------------------------------------------------------------- |
188
- | `CleanupPolicy.Throw` | Default. Destroying the component entity while other entities carry it throws and changes nothing. |
189
- | `CleanupPolicy.Remove` | Remove the component from every carrier, then destroy the component entity. |
190
- | `CleanupPolicy.Delete` | Destroy every carrier entity, then destroy the component entity. |
202
+ | Policy | Meaning |
203
+ | ---------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
204
+ | `CleanupPolicy.Throw` | Default. Destroying the component entity while other entities carry it throws. The synchronous `destroy()` call throws before mutating anything, so the world stays usable; a `Throw` policy reached during a deferred drain or a relationship cascade throws and leaves the world partial — discard it. |
205
+ | `CleanupPolicy.Remove` | Remove the component from every carrier, then destroy the component entity. |
206
+ | `CleanupPolicy.Delete` | Destroy every carrier entity, then destroy the component entity. |
191
207
 
192
208
  ```ts
193
209
  import { CleanupPolicy } from "@vworlds/vecs";
@@ -197,7 +213,29 @@ world.component(Temporary).destroy(); // strips Temporary from all carriers
197
213
  ```
198
214
 
199
215
  The `Throw` default also applies to plain entities used as component keys (`entity.add(keyEntity)`).
200
- A failed destroy leaves the component entity, its carriers, and the usage count untouched.
216
+
217
+ ## Snapshot policies: what a component may cross worlds with
218
+
219
+ A component type also declares how
220
+ [subtree snapshots](./snapshots.md) capture it, through `world.component(Type).snapshotPolicy(...)`.
221
+ The default — no declaration — deep-copies the instance's own enumerable fields, and **fails
222
+ loudly at capture** on a nested value that is not plain data, so a component carrying a class
223
+ instance must declare a policy saying how that instance is detached. `DontSnapshot` marks a
224
+ type as world-bound (an engine handle, a socket, a live `Query`), and `CustomSnapshot` with
225
+ `capture` / `materialize` callbacks is how a type that mixes declarative and runtime state hands over
226
+ only the declarative half:
227
+
228
+ ```ts
229
+ world.component(PhysicsEngine).snapshotPolicy(DontSnapshot);
230
+ ```
231
+
232
+ A policy is declared by the owning module and may change at any time: a capture binds the
233
+ materializer its policy resolved to, so an existing snapshot always restores the way it was
234
+ captured.
235
+ A synchronous `destroy()` throw (component still in use) leaves the world usable. A throw during
236
+ deferred drain or a cascade is irrecoverable — the component entity, its carriers, and the usage
237
+ count are in a partial state and the world must be discarded. Configure cleanup policies so cascades
238
+ are consistent; treat a mid-destroy throw as a programming error.
201
239
 
202
240
  Relationship components have a second, independent policy — `meta.onDeleteTarget` — that governs
203
241
  what happens to _sources_ when a relationship **target** is destroyed. See
@@ -285,7 +323,7 @@ the live object itself as a component** with `entity.attach(obj)` instead of kee
285
323
  const obj = scene.add.arc(0, 0); // a Phaser GameObject
286
324
  entity.attach(obj); // the GameObject is now a component, keyed by its class
287
325
 
288
- world.component(Phaser.GameObjects.Arc).onRemove((_entity, obj) => obj.destroy());
326
+ world.component(Phaser.GameObjects.Arc).on("remove", (_entity, obj) => obj.destroy());
289
327
  ```
290
328
 
291
329
  The entity's component set then _is_ the resource lifecycle: queries drive create/destroy, and
@@ -312,22 +350,21 @@ interface markers for polymorphic dispatch — is in the
312
350
  A `Component<T>` is an `Entity` subclass, so the full [entity API](./entities.md#reference)
313
351
  applies. On top of it:
314
352
 
315
- | Member | Description |
316
- | ---------------------- | -------------------------------------------------------------------------- |
317
- | `onAdd(handler?)` | Get, set, or remove the single add hook. Setter returns the component. |
318
- | `onRemove(handler?)` | Get, set, or remove the single remove hook. |
319
- | `onRetarget(handler?)` | Get, set, or remove the relationship retarget hook. Rejects non-relations. |
320
- | `onSet(handler?)` | Get, set, or remove the single set/modified hook. |
321
- | `meta` | The `ComponentMeta` bookkeeping record. |
353
+ | Member | Description |
354
+ | ----------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
355
+ | `on(event, handler, insert?)` | Register a handler for `add` / `remove` / `set` / `targetAdd` / `targetRemove` (plus the internal `policyAdd` / `policyRemove` / `targetPolicyAdd` / `targetPolicyRemove` events). `insert` defaults to `false` (append); `true` prepends. Rejects `target*` events on non-relationships. Returns the component. |
356
+ | `off(event, handler)` | Remove a previously-registered handler by reference. Idempotent. Returns the component. |
357
+ | `meta` | The `ComponentMeta` bookkeeping record. |
322
358
 
323
359
  ### `ComponentMeta` (`lib/vecs/src/component_meta.ts`)
324
360
 
325
- | Field | Description |
326
- | ---------------- | --------------------------------------------------------------------------------------------- |
327
- | `Class` | The component class constructor. |
328
- | `eid` | Component entity id assigned at registration. |
329
- | `bitPtr` | Pre-computed bit pointer into entity archetype bitsets. |
330
- | `isRelationship` | `true` when the class extends `Relationship`. |
331
- | `onDelete` | `CleanupPolicy` applied to carriers when the component entity is destroyed. Default `Throw`. |
332
- | `onDeleteTarget` | `CleanupPolicy` applied to sources when a relationship target is destroyed. Default `Remove`. |
333
- | `usageCount` | Number of entities currently carrying this component. |
361
+ | Field | Description |
362
+ | ----------------------- | --------------------------------------------------------------------------------------------- |
363
+ | `Class` | The component class constructor. |
364
+ | `eid` | Component entity id assigned at registration. |
365
+ | `bitPtr` | Pre-computed bit pointer into entity archetype bitsets. |
366
+ | `isRelationship` | `true` when the class extends `Relationship`. |
367
+ | `relationshipExclusive` | `true` when the relationship carries the `Exclusive` trait. |
368
+ | `onDelete` | `CleanupPolicy` applied to carriers when the component entity is destroyed. Default `Throw`. |
369
+ | `onDeleteTarget` | `CleanupPolicy` applied to sources when a relationship target is destroyed. Default `Remove`. |
370
+ | `usageCount` | Number of entities currently carrying this component. |
package/docs/concepts.md CHANGED
@@ -12,21 +12,22 @@ every idiom in the [Design guide](./design-guide.md) is a consequence of taking
12
12
 
13
13
  ## The pieces
14
14
 
15
- | Concept | What it is |
16
- | ------------------------ | ----------------------------------------------------------------------------------------------------------- |
17
- | **World** | Central container. Owns every entity, registered component, query, system, and the flat system pipeline. |
18
- | **Entity** | A numeric id (`eid`) with a set of components attached. Created via the world. |
19
- | **Component** | A registered plain data class with a no-argument constructor. No behavior. |
20
- | **System** | A `Query` plus per-tick logic (`update`, `each`, `run`), assigned to one pipeline phase. |
21
- | **Query** | A reactive, always-up-to-date set of entities matching a predicate, with `enter`/`exit`/`update` callbacks. |
22
- | **Filter** | A non-reactive, one-shot scan: walks all world entities on each `forEach` call. |
23
- | **Hook** | Lightweight component callbacks (`onAdd` / `onSet` / `onRemove`, plus relationship `onRetarget`). |
24
- | **Phase** | Marker component for entities used as pipeline ordering anchors. |
25
- | **Relationship** | A component whose `target` field points at another entity, with reverse lookup and cleanup cascades. |
26
- | **Component trait** | Policy attached to a component entity, configuring every use of that component type. |
27
- | **Module** | A unit of packaging: a class whose `init()` registers components, systems, phases, and policy. |
28
- | **Exclusive components** | A group of components where at most one may exist on any entity at a time. |
29
- | **Implements** | Declares abstract/interface component aliases for a concrete component, for polymorphic queries. |
15
+ | Concept | What it is |
16
+ | -------------------------- | ------------------------------------------------------------------------------------------------------------ |
17
+ | **World** | Central container. Owns every entity, registered component, query, system, and the flat system pipeline. |
18
+ | **Entity** | A numeric id (`eid`) with a set of components attached. Created via the world. |
19
+ | **Component** | A registered plain data class with a no-argument constructor. No behavior. |
20
+ | **System** | A `Query` plus per-tick logic (`update`, `each`, `run`), assigned to one pipeline phase. |
21
+ | **Query** | A reactive, always-up-to-date set of entities matching a predicate, with `enter`/`exit`/`update` callbacks. |
22
+ | **Filter** | A non-reactive, one-shot scan: walks all world entities on each `forEach` call. |
23
+ | **Hook** | Lightweight component callbacks (`onAdd` / `onSet` / `onRemove`, plus relationship edge hooks). |
24
+ | **Phase** | Marker component for entities used as pipeline ordering anchors. |
25
+ | **Relationship** | A component holding explicit edges to one or more target entities, with reverse lookup and cleanup cascades. |
26
+ | **Component trait** | Policy attached to a component entity, configuring every use of that component type. |
27
+ | **Module** | A unit of packaging: a class whose `init()` registers components, systems, phases, and policy. |
28
+ | **Exclusive components** | A group of components where at most one may exist on any entity at a time. |
29
+ | **Exclusive relationship** | A relationship carrying `Exclusive`, restricting each source to at most one target. |
30
+ | **Implements** | Declares abstract/interface component aliases for a concrete component, for polymorphic queries. |
30
31
 
31
32
  ## Data vs. behavior
32
33
 
@@ -46,8 +47,11 @@ right components, and adding a capability to an entity is just adding a componen
46
47
 
47
48
  Because a registered component is also an entity, it can itself carry components. Built-in
48
49
  **component traits** use that meta-ECS layer to declare reusable policy: `Singleton` constrains a
49
- component, `Implements` declares aliases, and `Traversable` / `OwnedTarget` configure relationships.
50
- The trait remains data; a module owns the hooks and metadata behavior it activates.
50
+ component, `Implements` declares aliases, `Exclusive` constrains relationship cardinality, and
51
+ `Traversable` / `OwnedTarget` configure exclusive relationships. The trait remains data; a module
52
+ owns the hooks and metadata behavior it activates. Relationship `Exclusive` is unrelated to an
53
+ exclusive component group: one limits edge count, while the other limits coexisting component
54
+ types.
51
55
 
52
56
  Prefer many small, single-purpose components over one large one. An entity that never rotates
53
57
  simply omits `Rotation`; absence encodes "not applicable". See