@vworlds/vecs 1.0.44 → 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 -51
  3. package/dist/component.js +25 -9
  4. package/dist/component.js.map +1 -1
  5. package/dist/component_meta.d.ts +71 -66
  6. package/dist/component_meta.js +331 -34
  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 -27
  13. package/dist/entity/entity.components.js +258 -74
  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 -53
  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 -3
  39. package/dist/modules/relationship_types.js +70 -3
  40. package/dist/modules/relationship_types.js.map +1 -1
  41. package/dist/modules/relationships.d.ts +31 -8
  42. package/dist/modules/relationships.js +120 -30
  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 +91 -48
  123. package/docs/concepts.md +21 -17
  124. package/docs/design-guide.md +125 -89
  125. package/docs/entities.md +62 -72
  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 +250 -115
  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
package/README.md CHANGED
@@ -66,13 +66,19 @@ The full documentation lives in [`docs/`](./docs/README.md). Recommended reading
66
66
  3. [Execution model](./docs/execution-model.md) — phases, deferred mutation, event routing.
67
67
  4. Subsystem guides: [Components and traits](./docs/components.md) · [Entities](./docs/entities.md) ·
68
68
  [Systems](./docs/systems.md) · [Queries and filters](./docs/queries-and-filters.md) ·
69
- [Relationships, targets, and ownership](./docs/relationships.md) · [Modules](./docs/modules.md)
69
+ [Relationships, edges, targets, and ownership](./docs/relationships.md) · [Modules](./docs/modules.md) ·
70
+ [Subtree snapshots](./docs/snapshots.md)
70
71
  5. [Design guide](./docs/design-guide.md) — patterns and anti-patterns from real designs.
71
72
 
72
73
  Plus the [Utilities](./docs/utilities.md) guide for helper classes and the
73
74
  [Glossary](./docs/glossary.md) for terminology. The exhaustive per-symbol reference is the JSDoc on
74
75
  the source under [`src/`](./src/) — read it in your IDE.
75
76
 
77
+ Networking is documented alongside the packages that own it:
78
+ [Remote procedure calls](../vecs-protocol/docs/rpc.md) ·
79
+ [Sessions and caller context](../vecs-server/docs/sessions.md) ·
80
+ [Client world](../vecs-client/docs/client-world.md).
81
+
76
82
  ## Build & Test
77
83
 
78
84
  ```sh
@@ -1,66 +1,72 @@
1
1
  import { Entity } from "./entity/index.js";
2
- import type { ComponentInstance, ComponentType } from "./component_meta.js";
3
- export { CleanupPolicy, ComponentMeta, Implements, Relationship, type ComponentClass, type ComponentInstance, type ComponentRef, type ComponentRefArray, type ComponentType, type Hook, type RetargetHandler, } from "./component_meta.js";
4
- /** A component entity created by {@link World.component}. */
2
+ import { Relationship, type ComponentInstance, type ComponentType, type HookEvent, type HookHandler, type TargetHandler } from "./component_meta.js";
3
+ import { type SnapshotCapture, type SnapshotMaterialize, ClonePublic, CustomSnapshot, DontSnapshot } from "./modules/snapshot_types.js";
4
+ export { CleanupPolicy, ComponentMeta, Implements, Relationship, type ComponentClass, type ComponentInstance, type ComponentRef, type ComponentRefArray, type ComponentType, type HookEvent, type HookEventMap, type HookHandler, type TargetHandler, } from "./component_meta.js";
5
+ /**
6
+ * A per-component event map that narrows `add` / `remove` / `set` handlers to
7
+ * the instance type `T` produces, and narrows `targetAdd` / `targetRemove` /
8
+ * `targetPolicyAdd` / `targetPolicyRemove` handlers to `T`'s relationship
9
+ * instance type when `T` produces a relationship.
10
+ */
11
+ type ComponentEventMap<T extends ComponentType> = {
12
+ add: HookHandler<InstanceType<T>>;
13
+ remove: HookHandler<InstanceType<T>>;
14
+ set: HookHandler<InstanceType<T>>;
15
+ targetAdd: TargetHandler<InstanceType<T> & Relationship>;
16
+ targetRemove: TargetHandler<InstanceType<T> & Relationship>;
17
+ targetPolicyAdd: TargetHandler<InstanceType<T> & Relationship>;
18
+ targetPolicyRemove: TargetHandler<InstanceType<T> & Relationship>;
19
+ policyAdd: HookHandler;
20
+ policyRemove: HookHandler;
21
+ };
22
+ /** Callbacks declared alongside the {@link CustomSnapshot} policy value. */
23
+ export interface CustomSnapshotOptions<T extends ComponentInstance = ComponentInstance> {
24
+ /** Convert a live component instance into detached, world-agnostic data. */
25
+ capture: SnapshotCapture;
26
+ /** Construct a fresh, destination-owned instance from previously captured data. */
27
+ materialize: SnapshotMaterialize;
28
+ }
29
+ /**
30
+ * A component entity created by {@link World.component}.
31
+ */
5
32
  export declare class Component<T extends ComponentType = ComponentType> extends Entity {
6
33
  /**
7
- * Register a callback fired each time this component is **added** to an entity.
8
- *
9
- * The handler is called with the owning entity and the freshly created
10
- * component instance. Use it to initialise external state (e.g. scene objects)
11
- * that should exist for the lifetime of the component.
12
- *
13
- * @param handler - Callback invoked with `(entity, componentInstance)`.
14
- * @returns This `Component` entity, for chaining.
15
- *
16
- * @example
17
- * ```ts
18
- * world.component(Sprite).onAdd((entity, sprite) => sprite.initialize(scene));
19
- * ```
34
+ * Register a handler for a hook event, with the handler signature narrowed
35
+ * to this component's instance type (for `add` / `remove` / `set`) or its
36
+ * relationship instance type (for the `target*` events). See
37
+ * {@link ComponentMeta.on} for full semantics.
20
38
  */
21
- onAdd(handler: (entity: Entity, c: InstanceType<T>) => void): this;
22
- onAdd(handler: (entity: Entity, c: ComponentInstance) => void): this;
39
+ on<E extends HookEvent>(event: E, handler: ComponentEventMap<T>[E], insert?: boolean): this;
40
+ /** Remove a previously-registered handler by reference. See {@link ComponentMeta.off}. */
41
+ off<E extends HookEvent>(event: E, handler: ComponentEventMap<T>[E]): this;
23
42
  /**
24
- * Register a callback fired each time this component is **removed** from an
25
- * entity, or when the owning entity is destroyed.
43
+ * Declare how cross-world subtree snapshots capture this component type.
26
44
  *
27
- * The handler is called with the owning entity and the component instance that
28
- * is about to be detached. Use it to clean up any external state associated
29
- * with the component.
45
+ * Callable at any time. The declaration affects future captures only:
46
+ * snapshots already captured retain the materializer their policy resolved to
47
+ * at capture time, so changing or revoking this policy cannot alter how they
48
+ * restore.
30
49
  *
31
- * @param handler - Callback invoked with `(entity, componentInstance)`.
32
- * @returns This `Component` entity, for chaining.
50
+ * `ClonePublic` — the default — captures a fresh instance with the source's
51
+ * own enumerable fields cloned. `DontSnapshot` never captures the type.
33
52
  *
34
- * @example
35
- * ```ts
36
- * world.component(Sprite).onRemove((entity, sprite) => sprite.destroy(scene));
37
- * ```
53
+ * @param policy - `ClonePublic` or `DontSnapshot`.
54
+ * @returns This component entity, for chaining.
38
55
  */
39
- onRemove(handler: (entity: Entity, c: InstanceType<T>) => void): this;
40
- onRemove(handler: (entity: Entity, c: ComponentInstance) => void): this;
56
+ snapshotPolicy(policy: typeof ClonePublic | typeof DontSnapshot): this;
41
57
  /**
42
- * Register a callback fired each time this component's data is **set or
43
- * modified**.
44
- *
45
- * Fires when:
46
- * - `entity.set(C, props)` applies data to the component.
47
- * - `entity.attach(instance)` stores an existing instance.
48
- * - `entity.modified(C)` is called after in-place mutation.
49
- * - A system's `getMut(C)` marks the component dirty.
50
- * - `entity.ensure(C)` creates or marks the component dirty.
58
+ * Declare a bespoke capture/materialize policy for this component type.
51
59
  *
52
- * The handler receives the owning entity and the current component instance.
53
- * Component instances do not carry entity references, which is why the entity
54
- * is passed explicitly.
60
+ * Use it when a component mixes declarative state with runtime state that must
61
+ * not cross worlds — a physics handle, a socket, a compiled shader.
55
62
  *
56
- * @param handler - Callback invoked with `(entity, componentInstance)`.
57
- * @returns This `Component` entity, for chaining.
63
+ * Callable at any time. It affects future captures only; existing snapshots
64
+ * restore through the materializer bound when they were captured.
58
65
  *
59
- * @example
60
- * ```ts
61
- * world.component(Transform).onSet((entity, t) => renderer.syncTransform(entity.eid, t));
62
- * ```
66
+ * @param CustomSnapshot - Selects the owner-supplied policy.
67
+ * @param options - The `capture` and `materialize` callbacks, stored on this
68
+ * entity's own `OnSnapshot` edge so each component type keeps its policy.
69
+ * @returns This component entity, for chaining.
63
70
  */
64
- onSet(handler: (entity: Entity, c: InstanceType<T>) => void): this;
65
- onSet(handler: (entity: Entity, c: ComponentInstance) => void): this;
71
+ snapshotPolicy(policy: typeof CustomSnapshot, options: CustomSnapshotOptions<InstanceType<T>>): this;
66
72
  }
package/dist/component.js CHANGED
@@ -1,17 +1,33 @@
1
1
  import { Entity } from "./entity/index.js";
2
+ import { CustomSnapshot, _declareSnapshotPolicy, } from "./modules/snapshot_types.js";
2
3
  export { CleanupPolicy, ComponentMeta, Implements, Relationship, } from "./component_meta.js";
3
- /** A component entity created by {@link World.component}. */
4
+ /**
5
+ * A component entity created by {@link World.component}.
6
+ */
4
7
  export class Component extends Entity {
5
- onAdd(handler) {
6
- super.onAdd(handler);
7
- return this;
8
+ /**
9
+ * Register a handler for a hook event, with the handler signature narrowed
10
+ * to this component's instance type (for `add` / `remove` / `set`) or its
11
+ * relationship instance type (for the `target*` events). See
12
+ * {@link ComponentMeta.on} for full semantics.
13
+ */
14
+ on(event, handler, insert = false) {
15
+ return super.on(event, handler, insert);
8
16
  }
9
- onRemove(handler) {
10
- super.onRemove(handler);
11
- return this;
17
+ /** Remove a previously-registered handler by reference. See {@link ComponentMeta.off}. */
18
+ off(event, handler) {
19
+ return super.off(event, handler);
12
20
  }
13
- onSet(handler) {
14
- super.onSet(handler);
21
+ snapshotPolicy(policy, options) {
22
+ if (policy === CustomSnapshot) {
23
+ if (options?.capture === undefined || options.materialize === undefined) {
24
+ throw new Error(`snapshotPolicy(CustomSnapshot) requires both capture and materialize callbacks for ${this.meta.Class.name}`);
25
+ }
26
+ _declareSnapshotPolicy(this, policy, options.capture, options.materialize);
27
+ }
28
+ else {
29
+ _declareSnapshotPolicy(this, policy);
30
+ }
15
31
  return this;
16
32
  }
17
33
  }
@@ -1 +1 @@
1
- {"version":3,"file":"component.js","sourceRoot":"","sources":["../src/component.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAG3C,OAAO,EACL,aAAa,EACb,aAAa,EACb,UAAU,EACV,YAAY,GAQb,MAAM,qBAAqB,CAAC;AAE7B,6DAA6D;AAC7D,MAAM,OAAO,SAAmD,SAAQ,MAAM;IAkB5D,KAAK,CAAC,OAAyC;QAC7D,KAAK,CAAC,KAAK,CAAC,OAAyD,CAAC,CAAC;QACvE,OAAO,IAAI,CAAC;IACd,CAAC;IAoBe,QAAQ,CAAC,OAAyC;QAChE,KAAK,CAAC,QAAQ,CAAC,OAAyD,CAAC,CAAC;QAC1E,OAAO,IAAI,CAAC;IACd,CAAC;IA2Be,KAAK,CAAC,OAAyC;QAC7D,KAAK,CAAC,KAAK,CAAC,OAAyD,CAAC,CAAC;QACvE,OAAO,IAAI,CAAC;IACd,CAAC;CACF"}
1
+ {"version":3,"file":"component.js","sourceRoot":"","sources":["../src/component.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAU3C,OAAO,EAKL,cAAc,EAEd,sBAAsB,GACvB,MAAM,6BAA6B,CAAC;AAErC,OAAO,EACL,aAAa,EACb,aAAa,EACb,UAAU,EACV,YAAY,GAUb,MAAM,qBAAqB,CAAC;AA4B7B;;GAEG;AACH,MAAM,OAAO,SAAmD,SAAQ,MAAM;IAC5E;;;;;OAKG;IACa,EAAE,CAChB,KAAQ,EACR,OAAgC,EAChC,MAAM,GAAG,KAAK;QAEd,OAAO,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,OAA0B,EAAE,MAAM,CAAC,CAAC;IAC7D,CAAC;IAED,0FAA0F;IAC1E,GAAG,CAAsB,KAAQ,EAAE,OAAgC;QACjF,OAAO,KAAK,CAAC,GAAG,CAAC,KAAK,EAAE,OAA0B,CAAC,CAAC;IACtD,CAAC;IAmCM,cAAc,CACnB,MAA0B,EAC1B,OAAgD;QAEhD,IAAI,MAAM,KAAK,cAAc,EAAE,CAAC;YAC9B,IAAI,OAAO,EAAE,OAAO,KAAK,SAAS,IAAI,OAAO,CAAC,WAAW,KAAK,SAAS,EAAE,CAAC;gBACxE,MAAM,IAAI,KAAK,CACb,sFAAsF,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAC7G,CAAC;YACJ,CAAC;YACD,sBAAsB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,OAAO,EAAE,OAAO,CAAC,WAAW,CAAC,CAAC;QAC7E,CAAC;aAAM,CAAC;YACN,sBAAsB,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QACvC,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;CACF"}
@@ -7,12 +7,36 @@ export declare enum CleanupPolicy {
7
7
  }
8
8
  /** Base class for relationship components that point at another entity. */
9
9
  export declare abstract class Relationship {
10
- target: Entity;
10
+ private _targets;
11
11
  }
12
- /** Callback fired after a relationship moves from one existing target to another. */
13
- export type RetargetHandler = (source: Entity, oldTarget: Entity, newTarget: Entity) => void;
12
+ /** Callback fired after one relationship edge is added or removed. */
13
+ export type TargetHandler<C extends Relationship = Relationship> = (source: Entity, relationship: C, target: Entity) => void;
14
+ /** Callback fired for a component lifecycle event. */
15
+ export type HookHandler<C extends ComponentInstance = ComponentInstance> = (entity: Entity, component: C) => void;
14
16
  /** A component instance. Components are plain objects created with a no-arg constructor. */
15
17
  export type ComponentInstance = object;
18
+ /**
19
+ * Event → handler signature map for the unified `on` / `off` hook API.
20
+ *
21
+ * Each key is a hook event name; its value is the handler signature accepted
22
+ * by {@link ComponentMeta.on} and {@link ComponentMeta.off} for that event.
23
+ * The `target*` events require a relationship meta; registering one on a
24
+ * non-relationship meta throws at registration time.
25
+ */
26
+ export interface HookEventMap {
27
+ /** Fired when a component of this type is first attached to an entity. */
28
+ add: HookHandler;
29
+ /** Fired when a component of this type is removed from an entity. */
30
+ remove: HookHandler;
31
+ /** Fired when a component's data is set or modified. */
32
+ set: HookHandler;
33
+ /** Fired after a relationship edge is added. Relationship metas only. */
34
+ targetAdd: TargetHandler;
35
+ /** Fired after a relationship edge is removed. Relationship metas only. */
36
+ targetRemove: TargetHandler;
37
+ }
38
+ /** Event names accepted by {@link ComponentMeta.on} / {@link ComponentMeta.off}. */
39
+ export type HookEvent = keyof HookEventMap;
16
40
  /** A component constructor used as a component key. */
17
41
  export type ComponentType<T extends ComponentInstance = ComponentInstance> = abstract new (...args: any[]) => T;
18
42
  /** A component class constructor that can be instantiated by entity.add/set. */
@@ -24,69 +48,33 @@ export declare class Implements {
24
48
  interfaces: ComponentType[];
25
49
  }
26
50
  /**
27
- * Lifecycle hook for a registered component class. Exposed by
28
- * {@link World.component}.
51
+ * Lifecycle hook registration for a registered component class. Exposed by
52
+ * {@link World.component} and on every entity.
29
53
  *
30
54
  * Hooks are a lightweight alternative to building a {@link System} when all
31
55
  * you need is a callback on add / remove / set for a single component type.
32
- * Component entities expose the same registration methods, so calls chain:
56
+ * Registration chains because `on` / `off` return the receiver:
33
57
  *
34
58
  * ```ts
35
59
  * world.component(Sprite)
36
- * .onAdd((e, c) => initSprite(e, c))
37
- * .onRemove((e, c) => destroySprite(e, c))
38
- * .onSet((e, c) => syncSprite(e, c));
60
+ * .on("add", (e, c) => initSprite(e, c))
61
+ * .on("remove", (e, c) => destroySprite(e, c))
62
+ * .on("set", (e, c) => syncSprite(e, c));
39
63
  * ```
40
64
  *
65
+ * Multiple handlers may be registered for the same event: they fire in
66
+ * registration order (or in reverse registration order when `insert` is `true`).
67
+ * Remove a handler with {@link ComponentMeta.off} by the same reference used
68
+ * to register it. Callers rarely need to remove handlers.
69
+ *
41
70
  * Callbacks fire synchronously when the corresponding entity command is
42
71
  * applied: inline outside deferred mode, or while the world drains its command
43
- * queue inside a system / `forEach` / `defer` block.
44
- *
45
- * @typeParam C - Component class this hook is bound to.
72
+ * queue inside a system / `forEach` / `defer` block. An invariant violation
73
+ * thrown from a handler propagates out of the applying call (or out of
74
+ * `progress()` / `flush()` in deferred mode) and leaves the world partially
75
+ * mutated — treat it as a programming error and discard the world.
46
76
  */
47
- export interface Hook<C extends ComponentInstance = ComponentInstance> {
48
- /**
49
- * Register a handler invoked when a component of this type is first attached
50
- * to an entity (`entity.add(C)` or `entity.set(C, ...)` on an entity that
51
- * does not yet have the component).
52
- *
53
- * @param handler - Receives the entity and freshly created component instance.
54
- * @returns This hook, for chaining.
55
- */
56
- onAdd(handler: (entity: Entity, c: C) => void): Hook<C>;
57
- /**
58
- * Register a handler invoked when a component of this type is removed from
59
- * an entity (explicit `entity.remove(C)` or implicit removal during
60
- * `entity.destroy()`).
61
- *
62
- * @param handler - Receives the entity and component instance that was removed.
63
- * @returns This hook, for chaining.
64
- */
65
- onRemove(handler: (entity: Entity, c: C) => void): Hook<C>;
66
- /**
67
- * Register a handler invoked when a component's data is set or modified.
68
- *
69
- * Fires when:
70
- * - `entity.set(C, props)` applies data to the component.
71
- * - `entity.attach(instance)` stores an existing instance.
72
- * - `entity.modified(C)` is called after in-place mutation.
73
- * - A system's `getMut(C)` marks the component dirty.
74
- * - `entity.ensure(C)` creates or marks the component dirty.
75
- *
76
- * @param handler - Receives the entity and component instance whose data changed.
77
- * @returns This hook, for chaining.
78
- */
79
- onSet(handler: (entity: Entity, c: C) => void): Hook<C>;
80
- }
81
- /**
82
- * Bookkeeping record produced for each component eid.
83
- *
84
- * Holds the constructor, component eid, and pre-computed
85
- * {@link BitPtr} used by archetype checks. Implements {@link Hook}, so the
86
- * lifecycle handlers attached via `world.component(C)` are stored directly on
87
- * the meta object.
88
- */
89
- export declare class ComponentMeta implements Hook<ComponentInstance> {
77
+ export declare class ComponentMeta {
90
78
  /** The component class constructor this meta represents. */
91
79
  readonly Class: ComponentType;
92
80
  /** Component entity id assigned at registration time. */
@@ -95,25 +83,42 @@ export declare class ComponentMeta implements Hook<ComponentInstance> {
95
83
  readonly bitPtr: BitPtr;
96
84
  /** Whether this component is a relationship pointing at another entity. */
97
85
  readonly isRelationship: boolean;
86
+ /** Whether this relationship permits only one target. */
87
+ relationshipExclusive: boolean;
98
88
  /** Applied to entities carrying this component when this component entity is deleted. */
99
89
  onDelete: CleanupPolicy;
100
90
  /** Applied to entities targeting a deleted entity through this relationship. */
101
91
  onDeleteTarget: CleanupPolicy;
102
- private _usageCount;
103
92
  constructor(Class: ComponentType, ceid: EID);
104
93
  /** Number of entities currently carrying this component eid. */
105
94
  get usageCount(): number;
106
- /** @inheritdoc */
107
- onAdd(handler: (entity: Entity, c: ComponentInstance) => void): ComponentMeta;
108
- /** @inheritdoc */
109
- onRemove(handler: (entity: Entity, c: ComponentInstance) => void): ComponentMeta;
110
95
  /**
111
- * Register a handler fired after this relationship changes between two existing targets.
96
+ * Register a handler for a hook event.
97
+ *
98
+ * Multiple handlers may be registered for the same event; they fire in
99
+ * registration order. Pass `insert: true` to prepend the handler ahead of
100
+ * any already-registered handlers. Removing a handler is via {@link off} with
101
+ * the same reference.
102
+ *
103
+ * The `targetAdd`, `targetRemove`, `targetPolicyAdd`, and `targetPolicyRemove`
104
+ * events require this meta to be a relationship; registering one on a
105
+ * non-relationship meta throws at registration time.
106
+ *
107
+ * @param event - One of `add`, `remove`, `set`, `targetAdd`, `targetRemove`,
108
+ * `policyAdd`, `policyRemove`, `targetPolicyAdd`, `targetPolicyRemove`.
109
+ * @param handler - Handler whose signature is constrained by `event`.
110
+ * @param insert - When `true`, prepend the handler; when `false` (default), append.
111
+ * @returns This meta, for chaining.
112
+ */
113
+ on<E extends HookEvent>(event: E, handler: HookEventMap[E], insert?: boolean): this;
114
+ /**
115
+ * Remove a previously-registered handler by reference. Idempotent: removing
116
+ * an unregistered handler is a no-op.
112
117
  *
113
- * @param handler - Receives the source entity, previous target, and current target.
114
- * @returns This metadata object, for chaining.
118
+ * @param event - The event the handler was registered for.
119
+ * @param handler - The exact handler reference passed to {@link on}.
120
+ * @returns This meta, for chaining.
115
121
  */
116
- onRetarget(handler: RetargetHandler): ComponentMeta;
117
- /** @inheritdoc */
118
- onSet(handler: (entity: Entity, c: ComponentInstance) => void): ComponentMeta;
122
+ off<E extends HookEvent>(event: E, handler: HookEventMap[E]): this;
123
+ private _requireRelationship;
119
124
  }