@vworlds/vecs 1.0.45 → 1.0.47

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
@@ -17,20 +17,84 @@ export class DependsOn extends Relationship {
17
17
  export class SilenceSource extends Relationship {
18
18
  }
19
19
  /**
20
- * Marker component that makes every target of a relationship a `ChildOf` child
20
+ * Pair-form trait: attaching `(With, Y)` to a component `X` makes adding `X`
21
+ * to an entity automatically add `Y` as well.
22
+ *
23
+ * Plain form — `X` and `Y` are both non-relationship components:
24
+ *
25
+ * ```ts
26
+ * world.component(Armor);
27
+ * world.component(Helmet).add(With, world.component(Armor));
28
+ * const e = world.entity().add(Helmet); // e also has Armor
29
+ * ```
30
+ *
31
+ * Relationship form — `X` and `Y` are both relationships; the same target is
32
+ * propagated:
33
+ *
34
+ * ```ts
35
+ * world.component(OwnedBy);
36
+ * world.component(WornBy).add(With, world.component(OwnedBy));
37
+ * const wearer = world.entity();
38
+ * const e = world.entity().add(WornBy, wearer); // e also has (OwnedBy, wearer)
39
+ * ```
40
+ *
41
+ * `With` is irrevocable and rejects attach while `X` is already in use, so
42
+ * configure it during world setup before any entity carries `X`.
43
+ */
44
+ export class With extends Relationship {
45
+ }
46
+ /**
47
+ * Pair-form trait constraining the targets of a relationship to direct
48
+ * children (via {@link ChildOf}) of a specified parent entity.
49
+ *
50
+ * Self/enum form — the relationship's own entity is the parent. A faction
51
+ * diplomacy alignment enum is a natural fit: every `Alignment` target must be
52
+ * a declared alignment value.
53
+ *
54
+ * ```ts
55
+ * class Alignment extends Relationship {}
56
+ * const alignment = world.component(Alignment).add(OneOf, world.component(Alignment));
57
+ * const good = world.entity().childOf(alignment);
58
+ * const evil = world.entity().childOf(alignment);
59
+ * const weather = world.entity();
60
+ * world.entity().add(Alignment, good); // ok: good is a child of Alignment
61
+ * world.entity().add(Alignment, weather); // throws: weather is not a child of Alignment
62
+ * ```
63
+ *
64
+ * Other-parent form — a different entity is the parent:
65
+ *
66
+ * ```ts
67
+ * class ResidesIn extends Relationship {}
68
+ * class Region extends Relationship {}
69
+ * world.component(ResidesIn).add(OneOf, world.component(Region));
70
+ * const forest = world.entity().childOf(world.component(Region));
71
+ * const weather = world.entity();
72
+ * world.entity().add(ResidesIn, forest); // ok: forest is a child of Region
73
+ * world.entity().add(ResidesIn, weather); // throws: weather is not a child of Region
74
+ * ```
75
+ *
76
+ * `OneOf` is irrevocable and rejects attach while the relationship is already
77
+ * in use, so configure it during world setup before any entity carries an
78
+ * edge through the relationship.
79
+ */
80
+ export class OneOf extends Relationship {
81
+ }
82
+ /** Marker trait restricting a relationship to one target. */
83
+ export class Exclusive {
84
+ }
85
+ /**
86
+ * Marker component that makes every explicit target of a relationship a `ChildOf` child
21
87
  * of the relationship source.
22
88
  *
23
89
  * Add during world setup, before creating relationship instances:
24
90
  *
25
91
  * ```ts
26
92
  * world.component(Inventory).add(OwnedTarget);
27
- * player.add(Inventory); // creates an owned target
93
+ * player.add(Inventory, inventory);
28
94
  * ```
29
95
  *
30
- * Adding the relationship creates a target when none was supplied. Retargeting
31
- * destroys the displaced target, and removing the relationship destroys its
32
- * current target. Configure owner lifecycle hooks before attaching this trait;
33
- * the trait wraps them and runs ownership behavior first.
96
+ * The relationship must also carry {@link Exclusive}. Removing or replacing an
97
+ * edge destroys its target.
34
98
  */
35
99
  export class OwnedTarget {
36
100
  }
@@ -1 +1 @@
1
- {"version":3,"file":"relationship_types.js","sourceRoot":"","sources":["../../src/modules/relationship_types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAGpD,0CAA0C;AAC1C,MAAM,OAAO,OAAQ,SAAQ,YAAY;CAAG;AAE5C,+CAA+C;AAC/C,MAAM,OAAO,SAAU,SAAQ,YAAY;CAAG;AAE9C;;;;;;;;GAQG;AACH,MAAM,OAAO,aAAc,SAAQ,YAAY;CAAG;AAElD;;;;;;;;;;;;;;;GAeG;AACH,MAAM,OAAO,WAAW;CAAG;AAE3B;;;;;;;;;;;;;GAaG;AACH,MAAM,OAAO,WAAW;IAAxB;QACE,+CAA+C;QAC/C,WAAM,GAAG,IAAI,GAAG,EAAkB,CAAC;IACrC,CAAC;CAAA"}
1
+ {"version":3,"file":"relationship_types.js","sourceRoot":"","sources":["../../src/modules/relationship_types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAGpD,0CAA0C;AAC1C,MAAM,OAAO,OAAQ,SAAQ,YAAY;CAAG;AAE5C,+CAA+C;AAC/C,MAAM,OAAO,SAAU,SAAQ,YAAY;CAAG;AAE9C;;;;;;;;GAQG;AACH,MAAM,OAAO,aAAc,SAAQ,YAAY;CAAG;AAElD;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,MAAM,OAAO,IAAK,SAAQ,YAAY;CAAG;AAEzC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,MAAM,OAAO,KAAM,SAAQ,YAAY;CAAG;AAE1C,6DAA6D;AAC7D,MAAM,OAAO,SAAS;CAAG;AAEzB;;;;;;;;;;;;;GAaG;AACH,MAAM,OAAO,WAAW;CAAG;AAE3B;;;;;;;;;;;;;GAaG;AACH,MAAM,OAAO,WAAW;IAAxB;QACE,+CAA+C;QAC/C,WAAM,GAAG,IAAI,GAAG,EAAkB,CAAC;IACrC,CAAC;CAAA"}
@@ -1,5 +1,5 @@
1
1
  import { Module } from "../module.js";
2
- export { ChildOf, DependsOn, OwnedTarget, SilenceSource, Traversable, } from "./relationship_types.js";
2
+ export { ChildOf, DependsOn, Exclusive, OneOf, OwnedTarget, SilenceSource, Traversable, With, } from "./relationship_types.js";
3
3
  /**
4
4
  * Built-in world module that registers the `ChildOf` relationship and
5
5
  * maintains ownership and depth tracking policies for relationship traits.
@@ -9,16 +9,26 @@ export { ChildOf, DependsOn, OwnedTarget, SilenceSource, Traversable, } from "./
9
9
  * destroying a parent recursively destroys the children that target it through
10
10
  * `ChildOf` (custom relationships default to `Remove`).
11
11
  *
12
- * When a relationship component entity has `Traversable` attached, the module
13
- * installs `onAdd`, `onSet`, and `onRemove` hooks on that relationship so that
14
- * every entity's depth is updated when the relationship is added, retargeted,
15
- * or removed.
12
+ * `Traversable` requires `Exclusive`. The module wraps target-edge hooks on a
13
+ * traversable relationship so every entity's depth is updated when its edge is
14
+ * added, replaced, or removed.
16
15
  *
17
16
  * When a relationship component entity has `OwnedTarget` attached, the module
18
- * wraps its hooks so adding the relationship creates a target when needed,
19
- * every target becomes a `ChildOf` child of its source, and targets are
20
- * destroyed when displaced or removed. Configure this trait after the
21
- * relationship's owner hooks and before creating relationship instances.
17
+ * wraps its target-edge hooks so every explicitly added target becomes a
18
+ * `ChildOf` child of its source and is destroyed when displaced or removed.
19
+ * `OwnedTarget` requires `Exclusive` and never creates a target implicitly.
20
+ *
21
+ * `OneOf` constrains the targets of a relationship to direct `ChildOf`
22
+ * children of a specified parent entity. Attach during setup as
23
+ * `relationship.add(OneOf, parent)`; the module installs a per-edge policy that
24
+ * rejects targets whose `ChildOf` parent is not the declared parent. `OneOf`
25
+ * is irrevocable once attached.
26
+ *
27
+ * `OwnedTarget` and `Traversable` are irrevocable once attached: both
28
+ * `entity.remove(Trait)` and `entity.attach(newInstance)` throw, and the world
29
+ * must be discarded after the throw. Teardown bypasses these guards —
30
+ * `Entity._destroy` does not fire `_onPolicyRemove` — so `clearAllEntities`
31
+ * can still tear down worlds with these traits configured.
22
32
  *
23
33
  * Depth is computed by walking `entity.target(relEid)` to the root. When an
24
34
  * entity's depth changes, every descendant is recursively recomputed.
@@ -27,4 +37,16 @@ export declare class RelationshipsModule extends Module {
27
37
  init(): void;
28
38
  private _cascadeUpdate;
29
39
  private _depthOf;
40
+ /**
41
+ * Make a pair-form trait irrevocable: removing an edge of `trait` throws
42
+ * once attached.
43
+ *
44
+ * Teardown fires `targetPolicyRemove` from two paths: `_detachRelationship`
45
+ * on the trait carrier (after `carrier._destroyed = true`) and the cleanup
46
+ * cascade when the pair target is destroyed (buffered via `_cleanupIncoming`
47
+ * → `_removeTarget`, where the carrier is still alive but the target is
48
+ * already gone). Skip the irrevocability guard in either case; see the
49
+ * teardown contract in the class JSDoc.
50
+ */
51
+ private _forbidPairTraitRemoval;
30
52
  }
@@ -1,7 +1,7 @@
1
1
  import { CleanupPolicy } from "../component_meta.js";
2
2
  import { Module } from "../module.js";
3
- import { ChildOf, OwnedTarget, Traversable } from "./relationship_types.js";
4
- export { ChildOf, DependsOn, OwnedTarget, SilenceSource, Traversable, } from "./relationship_types.js";
3
+ import { ChildOf, DependsOn, Exclusive, OneOf, OwnedTarget, SilenceSource, Traversable, With, } from "./relationship_types.js";
4
+ export { ChildOf, DependsOn, Exclusive, OneOf, OwnedTarget, SilenceSource, Traversable, With, } from "./relationship_types.js";
5
5
  /**
6
6
  * Built-in world module that registers the `ChildOf` relationship and
7
7
  * maintains ownership and depth tracking policies for relationship traits.
@@ -11,16 +11,26 @@ export { ChildOf, DependsOn, OwnedTarget, SilenceSource, Traversable, } from "./
11
11
  * destroying a parent recursively destroys the children that target it through
12
12
  * `ChildOf` (custom relationships default to `Remove`).
13
13
  *
14
- * When a relationship component entity has `Traversable` attached, the module
15
- * installs `onAdd`, `onSet`, and `onRemove` hooks on that relationship so that
16
- * every entity's depth is updated when the relationship is added, retargeted,
17
- * or removed.
14
+ * `Traversable` requires `Exclusive`. The module wraps target-edge hooks on a
15
+ * traversable relationship so every entity's depth is updated when its edge is
16
+ * added, replaced, or removed.
18
17
  *
19
18
  * When a relationship component entity has `OwnedTarget` attached, the module
20
- * wraps its hooks so adding the relationship creates a target when needed,
21
- * every target becomes a `ChildOf` child of its source, and targets are
22
- * destroyed when displaced or removed. Configure this trait after the
23
- * relationship's owner hooks and before creating relationship instances.
19
+ * wraps its target-edge hooks so every explicitly added target becomes a
20
+ * `ChildOf` child of its source and is destroyed when displaced or removed.
21
+ * `OwnedTarget` requires `Exclusive` and never creates a target implicitly.
22
+ *
23
+ * `OneOf` constrains the targets of a relationship to direct `ChildOf`
24
+ * children of a specified parent entity. Attach during setup as
25
+ * `relationship.add(OneOf, parent)`; the module installs a per-edge policy that
26
+ * rejects targets whose `ChildOf` parent is not the declared parent. `OneOf`
27
+ * is irrevocable once attached.
28
+ *
29
+ * `OwnedTarget` and `Traversable` are irrevocable once attached: both
30
+ * `entity.remove(Trait)` and `entity.attach(newInstance)` throw, and the world
31
+ * must be discarded after the throw. Teardown bypasses these guards —
32
+ * `Entity._destroy` does not fire `_onPolicyRemove` — so `clearAllEntities`
33
+ * can still tear down worlds with these traits configured.
24
34
  *
25
35
  * Depth is computed by walking `entity.target(relEid)` to the root. When an
26
36
  * entity's depth changes, every descendant is recursively recomputed.
@@ -29,50 +39,61 @@ export class RelationshipsModule extends Module {
29
39
  init() {
30
40
  const world = this.world;
31
41
  const childOf = world.component(ChildOf);
42
+ const dependsOn = world.component(DependsOn);
43
+ const exclusive = world.component(Exclusive);
32
44
  const ownedTarget = world.component(OwnedTarget);
33
- const traversable = world.component(Traversable);
45
+ const silenceSource = world.component(SilenceSource);
46
+ const traversableTrait = world.component(Traversable);
47
+ const withTrait = world.component(With);
48
+ const oneOfTrait = world.component(OneOf);
34
49
  childOf.meta.onDeleteTarget = CleanupPolicy.Delete;
35
- ownedTarget.onAdd((rel) => {
50
+ exclusive.meta.on("policyAdd", (relationship) => {
51
+ if (!relationship.meta.isRelationship) {
52
+ throw new Error("Exclusive can only be added to a Relationship component");
53
+ }
54
+ if (relationship.meta.usageCount > 0) {
55
+ throw new Error("Exclusive cannot be added or removed while the relationship is in use");
56
+ }
57
+ relationship.meta.relationshipExclusive = true;
58
+ });
59
+ exclusive.meta.on("policyRemove", (relationship) => {
60
+ if (relationship.has(OwnedTarget) || relationship.has(Traversable)) {
61
+ throw new Error("Exclusive cannot be removed while a dependent relationship trait exists");
62
+ }
63
+ if (relationship.meta.usageCount > 0) {
64
+ throw new Error("Exclusive cannot be added or removed while the relationship is in use");
65
+ }
66
+ relationship.meta.relationshipExclusive = false;
67
+ });
68
+ ownedTarget.meta.on("policyAdd", (rel) => {
36
69
  if (!rel.meta.isRelationship) {
37
70
  throw new Error("OwnedTarget can only be added to a Relationship component");
38
71
  }
39
72
  if (rel === childOf) {
40
73
  throw new Error("OwnedTarget cannot be added to ChildOf");
41
74
  }
42
- const onAdd = rel.onAdd();
43
- const onSet = rel.onSet();
44
- const onRetarget = rel.onRetarget();
45
- const onRemove = rel.onRemove();
46
- rel
47
- .onAdd((source, component) => {
48
- const relationship = component;
49
- relationship.target ?? (relationship.target = world.entity());
50
- relationship.target.childOf(source);
51
- onAdd?.(source, component);
52
- })
53
- .onSet((source, relationship) => {
54
- relationship.target.childOf(source);
55
- onSet?.(source, relationship);
56
- })
57
- .onRetarget((source, oldTarget, newTarget) => {
58
- oldTarget.destroy();
59
- onRetarget?.(source, oldTarget, newTarget);
60
- })
61
- .onRemove((source, relationship) => {
62
- relationship.target.destroy();
63
- onRemove?.(source, relationship);
64
- });
75
+ if (!rel.has(Exclusive)) {
76
+ throw new Error("OwnedTarget requires Exclusive");
77
+ }
78
+ rel.meta.on("targetPolicyAdd", (source, _relationship, target) => target.add(ChildOf, source));
79
+ rel.meta.on("targetPolicyRemove", (_source, _relationship, target) => target.destroy());
65
80
  });
66
- traversable.onAdd((rel, traversable) => {
81
+ ownedTarget.meta.on("policyRemove", () => {
82
+ throw new Error("OwnedTarget cannot be removed once added");
83
+ });
84
+ traversableTrait.meta.on("policyAdd", (rel, component) => {
85
+ if (!rel.meta.isRelationship) {
86
+ throw new Error("Traversable can only be added to a Relationship component");
87
+ }
88
+ if (!rel.has(Exclusive)) {
89
+ throw new Error("Traversable requires Exclusive");
90
+ }
91
+ const traversable = component;
67
92
  const relEid = rel.eid;
68
- rel
69
- .onAdd((entity) => {
93
+ rel.meta.on("targetPolicyAdd", (entity) => {
70
94
  this._cascadeUpdate(entity, relEid, traversable);
71
- })
72
- .onSet((entity) => {
73
- this._cascadeUpdate(entity, relEid, traversable);
74
- })
75
- .onRemove((entity) => {
95
+ });
96
+ rel.meta.on("targetPolicyRemove", (entity) => {
76
97
  traversable.depths.delete(entity);
77
98
  for (const child of entity._childrenByEid(relEid) ?? []) {
78
99
  this._cascadeUpdate(child, relEid, traversable);
@@ -84,7 +105,43 @@ export class RelationshipsModule extends Module {
84
105
  }
85
106
  });
86
107
  });
87
- childOf.add(Traversable);
108
+ traversableTrait.meta.on("policyRemove", () => {
109
+ throw new Error("Traversable cannot be removed once added");
110
+ });
111
+ withTrait.meta.on("targetPolicyAdd", (xEntity, _withRelationship, yEntity) => {
112
+ const xMeta = xEntity.meta;
113
+ const yMeta = yEntity.meta;
114
+ if (xMeta.isRelationship !== yMeta.isRelationship) {
115
+ throw new Error("With: the component and its target must both be relationships or both be plain components");
116
+ }
117
+ if (xMeta.usageCount > 0) {
118
+ throw new Error("With cannot be added while the target component is in use");
119
+ }
120
+ if (xMeta.isRelationship) {
121
+ xMeta.on("targetPolicyAdd", (source, _rel, target) => source.add(yEntity, target));
122
+ }
123
+ else {
124
+ xMeta.on("policyAdd", (entity) => entity.add(yEntity));
125
+ }
126
+ });
127
+ this._forbidPairTraitRemoval(withTrait, "With");
128
+ oneOfTrait.meta.on("targetPolicyAdd", (rel, _oneOf, parent) => {
129
+ if (!rel.meta.isRelationship) {
130
+ throw new Error("OneOf can only be added to a Relationship component");
131
+ }
132
+ if (rel.meta.usageCount > 0) {
133
+ throw new Error("OneOf cannot be added while the relationship is in use");
134
+ }
135
+ rel.meta.on("targetPolicyAdd", (_source, _relationship, target) => {
136
+ if (target.parent !== parent) {
137
+ throw new Error(`OneOf: target ${target.eid} is not a child of entity ${parent.eid}`);
138
+ }
139
+ });
140
+ });
141
+ this._forbidPairTraitRemoval(oneOfTrait, "OneOf");
142
+ childOf.add(Exclusive).add(Traversable);
143
+ dependsOn.add(Exclusive).add(Traversable);
144
+ silenceSource.add(Exclusive);
88
145
  }
89
146
  _cascadeUpdate(entity, relEid, traversable, visited = new Set()) {
90
147
  if (visited.has(entity)) {
@@ -116,5 +173,24 @@ export class RelationshipsModule extends Module {
116
173
  }
117
174
  return this._depthOf(parent, relEid, traversable, visited) + 1;
118
175
  }
176
+ /**
177
+ * Make a pair-form trait irrevocable: removing an edge of `trait` throws
178
+ * once attached.
179
+ *
180
+ * Teardown fires `targetPolicyRemove` from two paths: `_detachRelationship`
181
+ * on the trait carrier (after `carrier._destroyed = true`) and the cleanup
182
+ * cascade when the pair target is destroyed (buffered via `_cleanupIncoming`
183
+ * → `_removeTarget`, where the carrier is still alive but the target is
184
+ * already gone). Skip the irrevocability guard in either case; see the
185
+ * teardown contract in the class JSDoc.
186
+ */
187
+ _forbidPairTraitRemoval(trait, name) {
188
+ trait.meta.on("targetPolicyRemove", (carrier, _trait, target) => {
189
+ if (carrier.destroyed || target.destroyed) {
190
+ return;
191
+ }
192
+ throw new Error(`${name} cannot be removed once added`);
193
+ });
194
+ }
119
195
  }
120
196
  //# sourceMappingURL=relationships.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"relationships.js","sourceRoot":"","sources":["../../src/modules/relationships.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"relationships.js","sourceRoot":"","sources":["../../src/modules/relationships.ts"],"names":[],"mappings":"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"}
@@ -23,12 +23,12 @@ export class Singleton {
23
23
  */
24
24
  export class SingletonModule extends Module {
25
25
  init() {
26
- this.world.component(Singleton).onAdd((target) => {
26
+ this.world.component(Singleton).on("add", (target) => {
27
27
  const targetMeta = target.meta;
28
28
  if (targetMeta.usageCount !== 0) {
29
29
  throw new Error(`Cannot make component ${targetMeta.Class.name} singleton; ${targetMeta.usageCount} instance(s) already exist`);
30
30
  }
31
- targetMeta.onAdd((entity) => {
31
+ targetMeta.on("add", (entity) => {
32
32
  if (entity !== target || targetMeta.usageCount !== 1) {
33
33
  throw new Error(`Cannot add singleton component ${targetMeta.Class.name} to entity ${entity.eid}; singleton instance is entity ${target.eid}`);
34
34
  }
@@ -1 +1 @@
1
- {"version":3,"file":"singleton.js","sourceRoot":"","sources":["../../src/modules/singleton.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAEtC;;;;;;;;;;;;GAYG;AACH,MAAM,OAAO,SAAS;CAAG;AAEzB;;;;;;GAMG;AACH,MAAM,OAAO,eAAgB,SAAQ,MAAM;IAClC,IAAI;QACT,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC,KAAK,CAAC,CAAC,MAAc,EAAQ,EAAE;YAC7D,MAAM,UAAU,GAAG,MAAM,CAAC,IAAI,CAAC;YAC/B,IAAI,UAAU,CAAC,UAAU,KAAK,CAAC,EAAE,CAAC;gBAChC,MAAM,IAAI,KAAK,CACb,yBAAyB,UAAU,CAAC,KAAK,CAAC,IAAI,eAAe,UAAU,CAAC,UAAU,4BAA4B,CAC/G,CAAC;YACJ,CAAC;YAED,UAAU,CAAC,KAAK,CAAC,CAAC,MAAM,EAAE,EAAE;gBAC1B,IAAI,MAAM,KAAK,MAAM,IAAI,UAAU,CAAC,UAAU,KAAK,CAAC,EAAE,CAAC;oBACrD,MAAM,IAAI,KAAK,CACb,kCAAkC,UAAU,CAAC,KAAK,CAAC,IAAI,cAAc,MAAM,CAAC,GAAG,kCAAkC,MAAM,CAAC,GAAG,EAAE,CAC9H,CAAC;gBACJ,CAAC;YACH,CAAC,CAAC,CAAC;YACH,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QACrB,CAAC,CAAC,CAAC;IACL,CAAC;CACF"}
1
+ {"version":3,"file":"singleton.js","sourceRoot":"","sources":["../../src/modules/singleton.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAEtC;;;;;;;;;;;;GAYG;AACH,MAAM,OAAO,SAAS;CAAG;AAEzB;;;;;;GAMG;AACH,MAAM,OAAO,eAAgB,SAAQ,MAAM;IAClC,IAAI;QACT,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,MAAc,EAAQ,EAAE;YACjE,MAAM,UAAU,GAAG,MAAM,CAAC,IAAI,CAAC;YAC/B,IAAI,UAAU,CAAC,UAAU,KAAK,CAAC,EAAE,CAAC;gBAChC,MAAM,IAAI,KAAK,CACb,yBAAyB,UAAU,CAAC,KAAK,CAAC,IAAI,eAAe,UAAU,CAAC,UAAU,4BAA4B,CAC/G,CAAC;YACJ,CAAC;YAED,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,MAAM,EAAE,EAAE;gBAC9B,IAAI,MAAM,KAAK,MAAM,IAAI,UAAU,CAAC,UAAU,KAAK,CAAC,EAAE,CAAC;oBACrD,MAAM,IAAI,KAAK,CACb,kCAAkC,UAAU,CAAC,KAAK,CAAC,IAAI,cAAc,MAAM,CAAC,GAAG,kCAAkC,MAAM,CAAC,GAAG,EAAE,CAC9H,CAAC;gBACJ,CAAC;YACH,CAAC,CAAC,CAAC;YACH,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QACrB,CAAC,CAAC,CAAC;IACL,CAAC;CACF"}
@@ -0,0 +1,26 @@
1
+ import { Module } from "../module.js";
2
+ export { type ResolvedSnapshotPolicy, type SnapshotCapture, type SnapshotMaterialize, type SnapshotPolicyType, ClonePublic, CustomSnapshot, DontSnapshot, OnSnapshot, _declareSnapshotPolicy, _resolveSnapshotPolicy, } from "./snapshot_types.js";
3
+ /**
4
+ * Built-in world module that registers the `OnSnapshot` trait and its policy
5
+ * values.
6
+ *
7
+ * Registered automatically by every {@link World}. `OnSnapshot` is an
8
+ * `Exclusive` pair-form trait restricted with {@link OneOf} to its own policy
9
+ * children, so a component entity always names exactly one policy:
10
+ * {@link ClonePublic} (the default), {@link DontSnapshot}, or
11
+ * {@link CustomSnapshot}.
12
+ *
13
+ * A policy may be declared, changed, or revoked at any time. It does not have to
14
+ * precede the component's first carrier, because a capture *binds* the
15
+ * materializer it resolved: {@link World.snapshotSubtree} stores that function
16
+ * in each record, so a snapshot always restores the way it was captured and a
17
+ * later policy declaration cannot retroactively change one.
18
+ *
19
+ * The module also marks vecs' own world-bound component types
20
+ * {@link DontSnapshot}: a
21
+ * live {@link Query} or {@link System} owns live membership, pending events, and
22
+ * pipeline slots, so it can never be reconstructed in another world.
23
+ */
24
+ export declare class SnapshotModule extends Module {
25
+ init(): void;
26
+ }
@@ -0,0 +1,45 @@
1
+ // `SnapshotModule` installs the `OnSnapshot` trait's runtime invariants. The
2
+ // trait, its policy values, and the declaration helpers live in
3
+ // `snapshot_types.ts`, which is dependency-light on purpose: `component.ts`
4
+ // imports it, so it must not reach back through `module.ts`.
5
+ import { Module } from "../module.js";
6
+ import { Query } from "../query/index.js";
7
+ import { System } from "../system.js";
8
+ import { Exclusive, OneOf } from "./relationship_types.js";
9
+ import { ClonePublic, CustomSnapshot, DontSnapshot, OnSnapshot } from "./snapshot_types.js";
10
+ export { ClonePublic, CustomSnapshot, DontSnapshot, OnSnapshot, _declareSnapshotPolicy, _resolveSnapshotPolicy, } from "./snapshot_types.js";
11
+ /**
12
+ * Built-in world module that registers the `OnSnapshot` trait and its policy
13
+ * values.
14
+ *
15
+ * Registered automatically by every {@link World}. `OnSnapshot` is an
16
+ * `Exclusive` pair-form trait restricted with {@link OneOf} to its own policy
17
+ * children, so a component entity always names exactly one policy:
18
+ * {@link ClonePublic} (the default), {@link DontSnapshot}, or
19
+ * {@link CustomSnapshot}.
20
+ *
21
+ * A policy may be declared, changed, or revoked at any time. It does not have to
22
+ * precede the component's first carrier, because a capture *binds* the
23
+ * materializer it resolved: {@link World.snapshotSubtree} stores that function
24
+ * in each record, so a snapshot always restores the way it was captured and a
25
+ * later policy declaration cannot retroactively change one.
26
+ *
27
+ * The module also marks vecs' own world-bound component types
28
+ * {@link DontSnapshot}: a
29
+ * live {@link Query} or {@link System} owns live membership, pending events, and
30
+ * pipeline slots, so it can never be reconstructed in another world.
31
+ */
32
+ export class SnapshotModule extends Module {
33
+ init() {
34
+ const world = this.world;
35
+ const onSnapshot = world.component(OnSnapshot);
36
+ world.component(ClonePublic).childOf(onSnapshot);
37
+ world.component(DontSnapshot).childOf(onSnapshot);
38
+ world.component(CustomSnapshot).childOf(onSnapshot);
39
+ onSnapshot.add(OneOf, onSnapshot);
40
+ onSnapshot.add(Exclusive);
41
+ world.component(Query).snapshotPolicy(DontSnapshot);
42
+ world.component(System).snapshotPolicy(DontSnapshot);
43
+ }
44
+ }
45
+ //# sourceMappingURL=snapshot.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"snapshot.js","sourceRoot":"","sources":["../../src/modules/snapshot.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAC7E,gEAAgE;AAChE,4EAA4E;AAC5E,6DAA6D;AAE7D,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AACtC,OAAO,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAC1C,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAEtC,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAE5F,OAAO,EAKL,WAAW,EACX,cAAc,EACd,YAAY,EACZ,UAAU,EACV,sBAAsB,EACtB,sBAAsB,GACvB,MAAM,qBAAqB,CAAC;AAE7B;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,OAAO,cAAe,SAAQ,MAAM;IACjC,IAAI;QACT,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC;QACzB,MAAM,UAAU,GAAG,KAAK,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC;QAE/C,KAAK,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;QACjD,KAAK,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;QAClD,KAAK,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;QAEpD,UAAU,CAAC,GAAG,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC;QAClC,UAAU,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QAE1B,KAAK,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,cAAc,CAAC,YAAY,CAAC,CAAC;QACpD,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,cAAc,CAAC,YAAY,CAAC,CAAC;IACvD,CAAC;CACF"}
@@ -0,0 +1,85 @@
1
+ import type { ComponentInstance } from "../component_meta.js";
2
+ import { Relationship } from "../component_meta.js";
3
+ /**
4
+ * Captures one live component instance into detached, world-agnostic data.
5
+ *
6
+ * The returned value is stored in the snapshot and handed back to
7
+ * {@link SnapshotMaterialize} verbatim, so it must not reference the source
8
+ * world, its entities, or any mutable instance the source still holds.
9
+ */
10
+ export type SnapshotCapture = (instance: ComponentInstance) => unknown;
11
+ /**
12
+ * Constructs one destination-owned component instance from previously captured
13
+ * data. The returned instance must be freshly constructed and belong to no
14
+ * other entity.
15
+ */
16
+ export type SnapshotMaterialize = (data: unknown) => ComponentInstance;
17
+ /**
18
+ * Built-in pair-form trait declaring how {@link World.snapshotSubtree} captures
19
+ * one component type.
20
+ *
21
+ * The trait is attached to the **component entity**, not to ordinary entities,
22
+ * and its target selects the policy. It is exclusive, one-of
23
+ * {@link OneOf | restricted to its own children}. Configure it through the
24
+ * owning module; declarations affect future captures, while each existing
25
+ * snapshot retains the materializer bound at its capture time.
26
+ *
27
+ * ```ts
28
+ * // Declare "this type never crosses worlds" (resources, live queries, engines).
29
+ * world.component(PhysicsEngine).snapshotPolicy(DontSnapshot);
30
+ *
31
+ * // Declare a bespoke capture that drops runtime handles.
32
+ * world.component(Body).snapshotPolicy(CustomSnapshot, {
33
+ * capture: (body) => ({ ...body, _bodyId: undefined }),
34
+ * materialize: (data) => Object.assign(new Body(), data),
35
+ * });
36
+ * ```
37
+ *
38
+ * A type with no {@link OnSnapshot} edge uses {@link ClonePublic}, so ordinary
39
+ * plain-data components need no declaration at all.
40
+ */
41
+ export declare class OnSnapshot extends Relationship {
42
+ /**
43
+ * Capture hook for the {@link CustomSnapshot} policy. `undefined` for every
44
+ * other policy.
45
+ */
46
+ capture: SnapshotCapture | undefined;
47
+ /**
48
+ * Materialize hook for the {@link CustomSnapshot} policy. `undefined` for
49
+ * every other policy.
50
+ */
51
+ materialize: SnapshotMaterialize | undefined;
52
+ }
53
+ /**
54
+ * Default snapshot policy: construct a fresh instance of the component class
55
+ * and clone the source instance's own enumerable fields onto it.
56
+ *
57
+ * Shallow, and therefore exactly as aliasing-sensitive as `set`: a component
58
+ * holding a nested object shares it with the source until something copies it.
59
+ * A type that must not carry runtime handles across worlds needs its own
60
+ * policy — see {@link CustomSnapshot}.
61
+ */
62
+ export declare class ClonePublic {
63
+ }
64
+ /**
65
+ * Snapshot policy declaring that a component type never crosses worlds.
66
+ *
67
+ * Use it for attached resources and other instances whose identity is bound to
68
+ * a single world: live queries and systems, engine handles, socket sessions.
69
+ * A snapshot silently omits an unselected `DontSnapshot` type, but *selecting*
70
+ * one explicitly is a preflight error, so a caller cannot silently lose state
71
+ * it asked for.
72
+ */
73
+ export declare class DontSnapshot {
74
+ }
75
+ /**
76
+ * Snapshot policy delegating capture and materialization to the owner.
77
+ *
78
+ * Register it with {@link Component.snapshotPolicy}, passing the `capture` and
79
+ * `materialize` callbacks; the callbacks are stored on that component entity's
80
+ * own {@link OnSnapshot} edge, so each component type keeps a separate policy.
81
+ */
82
+ export declare class CustomSnapshot {
83
+ }
84
+ /** The policy values accepted by {@link Component.snapshotPolicy}. */
85
+ export type SnapshotPolicyType = typeof ClonePublic | typeof CustomSnapshot | typeof DontSnapshot;