@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.
- package/README.md +7 -1
- package/dist/component.d.ts +57 -51
- package/dist/component.js +25 -9
- package/dist/component.js.map +1 -1
- package/dist/component_meta.d.ts +71 -66
- package/dist/component_meta.js +331 -34
- package/dist/component_meta.js.map +1 -1
- package/dist/dsl.js +1 -1
- package/dist/dsl.js.map +1 -1
- package/dist/entity/entity.base.js +4 -0
- package/dist/entity/entity.base.js.map +1 -1
- package/dist/entity/entity.components.d.ts +39 -27
- package/dist/entity/entity.components.js +258 -74
- package/dist/entity/entity.components.js.map +1 -1
- package/dist/entity/entity.d.ts +1 -2
- package/dist/entity/entity.js +2 -3
- package/dist/entity/entity.js.map +1 -1
- package/dist/entity/entity.lifecycle.d.ts +7 -1
- package/dist/entity/entity.lifecycle.js +40 -53
- package/dist/entity/entity.lifecycle.js.map +1 -1
- package/dist/entity/entity.queries.js +30 -0
- package/dist/entity/entity.queries.js.map +1 -1
- package/dist/entity/entity.relationships.d.ts +4 -0
- package/dist/entity/entity.relationships.js +18 -10
- package/dist/entity/entity.relationships.js.map +1 -1
- package/dist/index.d.ts +7 -4
- package/dist/index.js +4 -2
- package/dist/index.js.map +1 -1
- package/dist/inject.d.ts +63 -14
- package/dist/inject.js +165 -89
- package/dist/inject.js.map +1 -1
- package/dist/modules/identity.js +2 -2
- package/dist/modules/identity.js.map +1 -1
- package/dist/modules/implements.js +2 -2
- package/dist/modules/implements.js.map +1 -1
- package/dist/modules/pipeline.js +5 -5
- package/dist/modules/pipeline.js.map +1 -1
- package/dist/modules/relationship_types.d.ts +70 -3
- package/dist/modules/relationship_types.js +70 -3
- package/dist/modules/relationship_types.js.map +1 -1
- package/dist/modules/relationships.d.ts +31 -8
- package/dist/modules/relationships.js +120 -30
- package/dist/modules/relationships.js.map +1 -1
- package/dist/modules/singleton.js +2 -2
- package/dist/modules/singleton.js.map +1 -1
- package/dist/modules/snapshot.d.ts +26 -0
- package/dist/modules/snapshot.js +45 -0
- package/dist/modules/snapshot.js.map +1 -0
- package/dist/modules/snapshot_types.d.ts +85 -0
- package/dist/modules/snapshot_types.js +130 -0
- package/dist/modules/snapshot_types.js.map +1 -0
- package/dist/package.json +2 -2
- package/dist/query/callbacks.d.ts +9 -7
- package/dist/query/callbacks.js +27 -41
- package/dist/query/callbacks.js.map +1 -1
- package/dist/query/grouped_query.js +7 -7
- package/dist/query/grouped_query.js.map +1 -1
- package/dist/query/grouping.d.ts +6 -2
- package/dist/query/grouping.js +84 -15
- package/dist/query/grouping.js.map +1 -1
- package/dist/query/query.d.ts +4 -4
- package/dist/query/query.js +37 -14
- package/dist/query/query.js.map +1 -1
- package/dist/snapshot/capture.d.ts +1 -0
- package/dist/snapshot/capture.js +212 -0
- package/dist/snapshot/capture.js.map +1 -0
- package/dist/snapshot/clone.d.ts +1 -0
- package/dist/snapshot/clone.js +107 -0
- package/dist/snapshot/clone.js.map +1 -0
- package/dist/snapshot/index.d.ts +5 -0
- package/dist/snapshot/index.js +21 -0
- package/dist/snapshot/index.js.map +1 -0
- package/dist/snapshot/preflight.d.ts +1 -0
- package/dist/snapshot/preflight.js +40 -0
- package/dist/snapshot/preflight.js.map +1 -0
- package/dist/snapshot/restore.d.ts +1 -0
- package/dist/snapshot/restore.js +111 -0
- package/dist/snapshot/restore.js.map +1 -0
- package/dist/snapshot/types.d.ts +135 -0
- package/dist/snapshot/types.js +26 -0
- package/dist/snapshot/types.js.map +1 -0
- package/dist/system.d.ts +3 -1
- package/dist/system.js +33 -23
- package/dist/system.js.map +1 -1
- package/dist/terms/all_term.js +19 -0
- package/dist/terms/all_term.js.map +1 -1
- package/dist/terms/any_term.js +19 -0
- package/dist/terms/any_term.js.map +1 -1
- package/dist/terms/not_term.js +4 -0
- package/dist/terms/not_term.js.map +1 -1
- package/dist/terms/target_term.d.ts +1 -2
- package/dist/terms/target_term.js +2 -4
- package/dist/terms/target_term.js.map +1 -1
- package/dist/terms/term.d.ts +2 -0
- package/dist/terms/term.js +59 -10
- package/dist/terms/term.js.map +1 -1
- package/dist/util/array_map.d.ts +43 -12
- package/dist/util/array_map.js +90 -41
- package/dist/util/array_map.js.map +1 -1
- package/dist/util/dense_set.js +26 -0
- package/dist/util/dense_set.js.map +1 -1
- package/dist/world/world.base.js +12 -0
- package/dist/world/world.base.js.map +1 -1
- package/dist/world/world.d.ts +7 -0
- package/dist/world/world.deferred.js +17 -0
- package/dist/world/world.deferred.js.map +1 -1
- package/dist/world/world.entities.d.ts +13 -2
- package/dist/world/world.entities.js +16 -2
- package/dist/world/world.entities.js.map +1 -1
- package/dist/world/world.js +15 -3
- package/dist/world/world.js.map +1 -1
- package/dist/world/world.pipeline.d.ts +2 -2
- package/dist/world/world.pipeline.js +4 -2
- package/dist/world/world.pipeline.js.map +1 -1
- package/dist/world/world.queries.d.ts +7 -0
- package/dist/world/world.queries.js +52 -0
- package/dist/world/world.queries.js.map +1 -1
- package/dist/world/world.transfers.d.ts +137 -0
- package/dist/world/world.transfers.js +188 -0
- package/dist/world/world.transfers.js.map +1 -0
- package/docs/README.md +18 -6
- package/docs/components.md +91 -48
- package/docs/concepts.md +21 -17
- package/docs/design-guide.md +125 -89
- package/docs/entities.md +62 -72
- package/docs/execution-model.md +26 -10
- package/docs/getting-started.md +6 -6
- package/docs/glossary.md +19 -10
- package/docs/modules.md +8 -8
- package/docs/queries-and-filters.md +15 -10
- package/docs/relationships.md +250 -115
- package/docs/snapshots.md +181 -0
- package/docs/systems.md +64 -21
- package/docs/utilities.md +10 -6
- package/package.json +2 -2
- package/skills/vecs/SKILL.md +5 -4
package/docs/execution-model.md
CHANGED
|
@@ -49,9 +49,9 @@ world.system("Render").phase(ON_STORE).with(Sprite, Position).each([Sprite, Posi
|
|
|
49
49
|
```
|
|
50
50
|
|
|
51
51
|
`.phase(anchor)` accepts a phase name or an entity. It sets `DependsOn` on the system's backing
|
|
52
|
-
entity (replacing
|
|
53
|
-
entity if the name is new. **A system lives in exactly one phase.** If a concern
|
|
54
|
-
phases, split it into two systems.
|
|
52
|
+
entity (atomically replacing its previous exclusive edge) and tags the anchor with `Phase`,
|
|
53
|
+
creating the anchor entity if the name is new. **A system lives in exactly one phase.** If a concern
|
|
54
|
+
needs work in two phases, split it into two systems.
|
|
55
55
|
|
|
56
56
|
### Ordering guarantees
|
|
57
57
|
|
|
@@ -81,7 +81,7 @@ to the spec so the result flattens to systems.
|
|
|
81
81
|
### Custom phases: `insertPhaseAfter`
|
|
82
82
|
|
|
83
83
|
`insertPhaseAfter(phase, anchor)` splices an existing `Phase` entity into the spine directly after
|
|
84
|
-
`anchor`,
|
|
84
|
+
`anchor`, replacing the anchor's downstream `DependsOn` edges with edges to the new one:
|
|
85
85
|
|
|
86
86
|
```ts
|
|
87
87
|
import { insertPhaseAfter, ON_UPDATE, Phase } from "@vworlds/vecs";
|
|
@@ -98,8 +98,14 @@ built-in taxonomy.
|
|
|
98
98
|
|
|
99
99
|
Inside a system body, a `Query.forEach` / `Filter.forEach` iteration, or any `world.defer(...)`
|
|
100
100
|
block, the world is in **deferred mode**: entity mutations (`add` / `attach` / `set` / `ensure` /
|
|
101
|
-
`
|
|
102
|
-
commands instead of applied inline. The queue drains when
|
|
101
|
+
`remove` / `destroy` / `modified`, explicit relationship edge additions/removals, and
|
|
102
|
+
`world.entity()` creation) are queued as commands instead of applied inline. The queue drains when
|
|
103
|
+
the scope that opened deferral ends.
|
|
104
|
+
|
|
105
|
+
All commands share one FIFO queue, including relationship edge commands and commands enqueued by
|
|
106
|
+
hooks while that queue is draining. Their final state is therefore determined by call order. For
|
|
107
|
+
example, deferred `add(R, a)`, `add(R, b)`, `remove(R, a)` leaves only `b`; on an `Exclusive`
|
|
108
|
+
relationship each queued addition atomically replaces the edge present when that command executes.
|
|
103
109
|
|
|
104
110
|
For the pipeline this means: **a structural change made by system A is applied in the flush
|
|
105
111
|
immediately after A finishes, so it is visible to every later system in the same tick** — later in
|
|
@@ -119,8 +125,10 @@ While a mutation is queued but not yet applied, reads return the pre-mutation st
|
|
|
119
125
|
| `entity.ensure(C)` | A mutable component, even when newly created. |
|
|
120
126
|
| `entity.remove(C)` | Still the component instance. |
|
|
121
127
|
|
|
122
|
-
|
|
123
|
-
|
|
128
|
+
Relationship reads follow the same committed-state rule. After queued `add(R, target)` or
|
|
129
|
+
`remove(R, target)`, `has(R)`, `target(R)`, `targets(R)`, `hasTarget(R, target)`, and reverse
|
|
130
|
+
`children(R)` continue to report the state from before the deferred scope until the FIFO queue
|
|
131
|
+
applies those edge commands.
|
|
124
132
|
|
|
125
133
|
Outside any deferred scope (top-level user code), the same calls execute inline and effects are
|
|
126
134
|
visible immediately.
|
|
@@ -146,6 +154,8 @@ component:
|
|
|
146
154
|
|
|
147
155
|
- **Hooks** (`onAdd` / `onSet` / `onRemove`) fire synchronously at the moment the command is
|
|
148
156
|
applied — inline outside deferred mode, or during the drain.
|
|
157
|
+
- **Relationship edge hooks** (`onTargetAdd` / `onTargetRemove`) fire synchronously after each
|
|
158
|
+
edge and reverse-index change. Edge changes do not fire `onSet`.
|
|
149
159
|
- **Standalone `Query` callbacks** (`enter` / `update` / `exit`) also fire synchronously during
|
|
150
160
|
routing.
|
|
151
161
|
- **Systems** queue events into an ordered **inbox** instead, and replay the inbox at the top of
|
|
@@ -157,8 +167,14 @@ Three routing rules worth memorizing:
|
|
|
157
167
|
- **`update(C)` is change-only unless you opt into entry.** When an entity enters a system,
|
|
158
168
|
`{ onEnter: true }` update watchers push an `update` event for already-present watched components.
|
|
159
169
|
Use that for initialization; leave it off when a separate `enter` owns setup.
|
|
160
|
-
- **`
|
|
161
|
-
|
|
170
|
+
- **`enter` and `exit` injection read snapshots.** Direct components, singular/plural target
|
|
171
|
+
projections, and `Iter.src` are captured when the event is routed, so inbox replay retains those
|
|
172
|
+
component and entity references even if storage or edges change before the system runs. Component
|
|
173
|
+
objects are not deep-cloned.
|
|
174
|
+
- **`enter` is drain-time truthful.** A system only receives `enter` when the entity is still a
|
|
175
|
+
member when that system runs. If it left before the inbox drains, vecs drops both that queued
|
|
176
|
+
`enter` and its paired `exit`; an entity that re-enters before draining receives the ordered
|
|
177
|
+
lifecycle events for its final membership.
|
|
162
178
|
- **In-place mutation needs `modified`.** Assigning to component fields directly notifies no one.
|
|
163
179
|
Call `entity.modified(C)` (or use `set` / `getMut` / `ensure`) to route the change.
|
|
164
180
|
|
package/docs/getting-started.md
CHANGED
|
@@ -109,12 +109,12 @@ entity:
|
|
|
109
109
|
```ts
|
|
110
110
|
world
|
|
111
111
|
.component(Health)
|
|
112
|
-
.
|
|
113
|
-
.
|
|
112
|
+
.on("add", (entity, h) => console.log(`entity ${entity.eid} spawned with hp=${h.hp}`))
|
|
113
|
+
.on("remove", (entity) => console.log(`entity ${entity.eid} died`));
|
|
114
114
|
```
|
|
115
115
|
|
|
116
|
-
`
|
|
117
|
-
destroyed, and `
|
|
116
|
+
The `add` event fires when the component is first attached, `remove` when it is detached or its
|
|
117
|
+
entity is destroyed, and `set` whenever its data is set or marked modified.
|
|
118
118
|
|
|
119
119
|
## 6. Spawn entities
|
|
120
120
|
|
|
@@ -167,8 +167,8 @@ world.component(Position);
|
|
|
167
167
|
world.component(Velocity);
|
|
168
168
|
world
|
|
169
169
|
.component(Health)
|
|
170
|
-
.
|
|
171
|
-
.
|
|
170
|
+
.on("add", (entity, h) => console.log(`entity ${entity.eid} spawned with hp=${h.hp}`))
|
|
171
|
+
.on("remove", (entity) => console.log(`entity ${entity.eid} died`));
|
|
172
172
|
|
|
173
173
|
world
|
|
174
174
|
.system("Move")
|
package/docs/glossary.md
CHANGED
|
@@ -25,7 +25,7 @@ sources strip/die). [Components](./components.md#cleanup-policies-destroying-a-c
|
|
|
25
25
|
|
|
26
26
|
**Companion (wrapper) component** — a local, non-networked component holding a non-ECS resource
|
|
27
27
|
(render object, WASM handle) so the entity's component set drives the resource lifecycle.
|
|
28
|
-
[Components](./components.md#
|
|
28
|
+
[Components](./components.md#resources-as-components-non-ecs-objects).
|
|
29
29
|
|
|
30
30
|
**Component** — a registered plain data class with a no-argument constructor; pure data, no
|
|
31
31
|
behavior. [Components](./components.md).
|
|
@@ -44,6 +44,10 @@ pipeline). [Execution model](./execution-model.md#deferred-mutation-and-flush-bo
|
|
|
44
44
|
**Exclusive components** — a declared group where at most one member may exist on an entity;
|
|
45
45
|
setting one removes the others. [Components](./components.md#exclusive-component-groups).
|
|
46
46
|
|
|
47
|
+
**Exclusive relationship** — a relationship carrying the `Exclusive` trait, restricting each
|
|
48
|
+
source to at most one target. Adding a different edge replaces the current edge. This is separate
|
|
49
|
+
from an exclusive component group. [Relationships](./relationships.md).
|
|
50
|
+
|
|
47
51
|
**Events** — a minimal typed event emitter helper exported for package-level events. It is not an
|
|
48
52
|
entity lifecycle API. [Utilities](./utilities.md#events).
|
|
49
53
|
|
|
@@ -51,8 +55,8 @@ entity lifecycle API. [Utilities](./utilities.md#events).
|
|
|
51
55
|
[Queries and filters](./queries-and-filters.md#filters-one-shot-scans).
|
|
52
56
|
|
|
53
57
|
**Hook** — an `onAdd` / `onSet` / `onRemove` callback registered per component class, or an
|
|
54
|
-
`
|
|
55
|
-
[Components](./components.md#hooks-
|
|
58
|
+
`onTargetAdd` / `onTargetRemove` callback for relationship edges, fired synchronously at the
|
|
59
|
+
mutation site. [Components](./components.md#hooks-component-and-relationship-edges).
|
|
56
60
|
|
|
57
61
|
**Implements / interface alias** — a declaration that a concrete component is queryable through
|
|
58
62
|
abstract/interface component types; aliases are read-only views of the same instance.
|
|
@@ -62,7 +66,8 @@ abstract/interface component types; aliases are read-only views of the same inst
|
|
|
62
66
|
top of the system's run. [Execution model](./execution-model.md#reactive-routing).
|
|
63
67
|
|
|
64
68
|
**Injection** — resolving component instances per entity into a typed callback tuple, including
|
|
65
|
-
`target` (relationship target
|
|
69
|
+
singular `target` (the first relationship target), plural `targets` (one projection row per target),
|
|
70
|
+
and `down` (fan-out over children) markers.
|
|
66
71
|
[Systems](./systems.md#component-injection).
|
|
67
72
|
|
|
68
73
|
**Iter** — an opt-in reusable cursor passed to callbacks instead of the bare entity, exposing the
|
|
@@ -72,9 +77,10 @@ visited entity and the source entity of each injected slot.
|
|
|
72
77
|
**Module** — a packaging unit (`class M extends Module<Config>`) whose `init()` registers
|
|
73
78
|
components, systems, and policy once per world. [Modules](./modules.md).
|
|
74
79
|
|
|
75
|
-
**Owned target entity** —
|
|
76
|
-
|
|
77
|
-
relationship is removed, or when the source dies.
|
|
80
|
+
**Owned target entity** — an explicitly created zero-or-one aggregate entity targeted through a
|
|
81
|
+
relationship carrying both `Exclusive` and `OwnedTarget`. It is parented to the source and destroyed
|
|
82
|
+
when displaced, when its edge or relationship is removed, or when the source dies.
|
|
83
|
+
[Relationships](./relationships.md#owned-targets).
|
|
78
84
|
|
|
79
85
|
**Phase** — an entity used as a pipeline ordering anchor (tagged with the `Phase` component);
|
|
80
86
|
systems attach to exactly one phase via `DependsOn`.
|
|
@@ -90,8 +96,10 @@ ordered query over system entities. [Execution model](./execution-model.md#the-p
|
|
|
90
96
|
`source` / `parent` / `children` / `test`).
|
|
91
97
|
[Queries and filters](./queries-and-filters.md#the-query-dsl).
|
|
92
98
|
|
|
93
|
-
**Relationship** — a component
|
|
94
|
-
with reverse lookup (`children`) and cleanup cascades.
|
|
99
|
+
**Relationship** — a component holding explicit edges from its source entity to one or more targets,
|
|
100
|
+
with `target` / `targets` / `hasTarget` lookup, reverse lookup (`children`), and cleanup cascades.
|
|
101
|
+
Target storage is private; mutate edges with `add(Rel, target)` and `remove(Rel, target)`.
|
|
102
|
+
[Relationships](./relationships.md).
|
|
95
103
|
|
|
96
104
|
**Silence domain / `ignoreSource`** — a query configuration that drops `update` events originating
|
|
97
105
|
from systems matching another query, breaking write feedback loops.
|
|
@@ -109,7 +117,8 @@ how often a system fires. [Systems](./systems.md#cadence-interval-rate-and-tick-
|
|
|
109
117
|
|
|
110
118
|
**Trait (component trait)** — policy attached as a component to a registered component entity,
|
|
111
119
|
configuring every use of that component type. Built-ins include `Singleton`, `Implements`,
|
|
112
|
-
`Traversable`, and `OwnedTarget`.
|
|
120
|
+
`Exclusive`, `Traversable`, and `OwnedTarget`.
|
|
121
|
+
[Components](./components.md#component-traits-policy-on-component-entities).
|
|
113
122
|
|
|
114
123
|
**Watchlist** — the set of component types a query or system reacts to via `update`, stored as a
|
|
115
124
|
bitmask. [Systems](./systems.md#reactive-callbacks-enter-update-exit).
|
package/docs/modules.md
CHANGED
|
@@ -62,7 +62,7 @@ is often one module per item plus a thin umbrella that composes them:
|
|
|
62
62
|
```ts
|
|
63
63
|
export class PhaserRenderModule extends Module<{ scene: Scene }> {
|
|
64
64
|
public init(cfg: { scene: Scene }): void {
|
|
65
|
-
this.world.component(GameObject).
|
|
65
|
+
this.world.component(GameObject).on("remove", (_e, obj) => obj.destroy()); // GameObject: Implements marker over every Phaser object
|
|
66
66
|
this.world.module(ArcModule, { scene: cfg.scene });
|
|
67
67
|
this.world.module(RectangleModule, { scene: cfg.scene });
|
|
68
68
|
// … shape modules first …
|
|
@@ -89,13 +89,13 @@ Guidelines:
|
|
|
89
89
|
Every `World` constructor installs these (`lib/vecs/src/world/world.ts`); you interact with their
|
|
90
90
|
_components_, not with the module objects:
|
|
91
91
|
|
|
92
|
-
| Module | Provides
|
|
93
|
-
| --------------------- |
|
|
94
|
-
| `IdentityModule` | The `Identity` name component and the name → entity index behind `world.entity("name")` / `entity.name`.
|
|
95
|
-
| `ImplementsModule` | Maintains [`Implements` interface aliases](./components.md#interface-aliases-with-implements) and their transitive closure.
|
|
96
|
-
| `SingletonModule` | Enforces the [`Singleton`](./components.md#singletons-and-world-global-data) one-carrier constraint.
|
|
97
|
-
| `RelationshipsModule` | Registers `
|
|
98
|
-
| `PipelineModule` | Creates the eight [built-in phases](./execution-model.md#phases-the-pipeline-spine) and installs the default pipeline query.
|
|
92
|
+
| Module | Provides |
|
|
93
|
+
| --------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
94
|
+
| `IdentityModule` | The `Identity` name component and the name → entity index behind `world.entity("name")` / `entity.name`. |
|
|
95
|
+
| `ImplementsModule` | Maintains [`Implements` interface aliases](./components.md#interface-aliases-with-implements) and their transitive closure. |
|
|
96
|
+
| `SingletonModule` | Enforces the [`Singleton`](./components.md#singletons-and-world-global-data) one-carrier constraint. |
|
|
97
|
+
| `RelationshipsModule` | Registers relationship traits, marks built-in single-target relationships `Exclusive`, installs explicit-edge [`OwnedTarget`](./relationships.md#owned-targets) ownership, and maintains exclusive [`Traversable` depth tracking](./relationships.md#traversal-order-traversable-and-cascade). |
|
|
98
|
+
| `PipelineModule` | Creates the eight [built-in phases](./execution-model.md#phases-the-pipeline-spine) and installs the default pipeline query. |
|
|
99
99
|
|
|
100
100
|
All are exported, so a custom world setup can reuse them.
|
|
101
101
|
|
|
@@ -17,7 +17,7 @@ It is accepted by `query().with`, `system().with`, and `world.filter`:
|
|
|
17
17
|
| `{ any: [q1, q2] }` | At least one sub-expression matches |
|
|
18
18
|
| `{ not: q }` | Sub-expression must not match |
|
|
19
19
|
| `{ only: [A, B] }` | Entity has exactly `A` and `B`, nothing else |
|
|
20
|
-
| `{ target: [R, q] }` |
|
|
20
|
+
| `{ target: [R, q] }` | At least one of relationship `R`'s targets matches `q` |
|
|
21
21
|
| `{ source: [R, q] }` | Filter-only: some entity targeting this one through `R` matches `q` |
|
|
22
22
|
| `{ parent: q }` | Built-in `ChildOf` target matches `q` |
|
|
23
23
|
| `{ children: q }` | Filter-only: some `ChildOf` child matches `q` |
|
|
@@ -36,12 +36,13 @@ Class-valued terms are components — register them first; an unregistered class
|
|
|
36
36
|
whose changes should re-evaluate it.
|
|
37
37
|
|
|
38
38
|
`target` and `source` take tuple form only: `{ target: [RelationshipClass, innerDSL] }`. The first
|
|
39
|
-
element must extend `Relationship` (a plain component throws). `target`
|
|
40
|
-
against
|
|
41
|
-
candidate and matches when at least one does.
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
39
|
+
element must extend `Relationship` (a plain component throws). `target` is existential: it evaluates
|
|
40
|
+
the inner DSL against every relationship target and matches when at least one does. `source`
|
|
41
|
+
evaluates it against each entity pointing at the candidate and also matches when at least one does.
|
|
42
|
+
`parent` / `children` are the same operators specialized to the exclusive `ChildOf` relationship.
|
|
43
|
+
**`source` and `children` are non-reactive**: exact for each filter scan, but rejected by tracked
|
|
44
|
+
queries and systems. `target` / `parent` are fully reactive to edge changes and target-side changes
|
|
45
|
+
— see [Relationships](./relationships.md#querying-through-relationships).
|
|
45
46
|
|
|
46
47
|
### `QuerySpec`: adding `cascade` and `hint`
|
|
47
48
|
|
|
@@ -144,15 +145,19 @@ Two forms (`lib/vecs/src/query/grouped_query.ts`):
|
|
|
144
145
|
|
|
145
146
|
- `groupBy([components], keyFn)` / `groupBy({ inject: [components] }, keyFn)` — bucket by a
|
|
146
147
|
computed key; injected components are watched, so changing them re-keys the entity.
|
|
147
|
-
- `groupBy(RelationshipClass)` / `groupBy({ relationship: RelationshipClass })` —
|
|
148
|
-
|
|
149
|
-
|
|
148
|
+
- `groupBy(RelationshipClass)` / `groupBy({ relationship: RelationshipClass })` — place each member
|
|
149
|
+
in one bucket per relationship target. Adding or removing edges reconciles only the affected
|
|
150
|
+
buckets; an exclusive replacement moves the member from the old target bucket to the new one.
|
|
150
151
|
|
|
151
152
|
The result is a `GroupedQuery<K, R>` adding `group(key)`, `groups()`, `groupKeys()`, `groupCount`,
|
|
152
153
|
and `onGroupEnter` / `onGroupExit` callbacks for bucket creation and disposal. Each `Group` exposes
|
|
153
154
|
`key`, `count`, `has`, iteration, and injected `forEach`. Grouping implies tracking. Systems do
|
|
154
155
|
not support `groupBy` — keep the grouped query standalone and read it from a system.
|
|
155
156
|
|
|
157
|
+
A many-target member can therefore appear in several groups at once. Re-adding an existing edge
|
|
158
|
+
does not duplicate it within a bucket, removing one edge leaves its other buckets intact, and
|
|
159
|
+
destroying a target removes that target's bucket according to normal relationship cleanup.
|
|
160
|
+
|
|
156
161
|
## Filters: one-shot scans
|
|
157
162
|
|
|
158
163
|
`world.filter(spec)` returns a `Filter` (`lib/vecs/src/filter.ts`): a non-reactive predicate that
|