@vworlds/vecs 1.0.15 → 1.0.16
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 +249 -119
- package/dist/component.d.ts +52 -76
- package/dist/component.js +60 -45
- package/dist/component.js.map +1 -1
- package/dist/component_meta.d.ts +98 -0
- package/dist/component_meta.js +65 -0
- package/dist/component_meta.js.map +1 -0
- package/dist/dsl.d.ts +46 -34
- package/dist/dsl.js +459 -61
- package/dist/dsl.js.map +1 -1
- package/dist/entity/entity.base.d.ts +57 -0
- package/dist/entity/entity.base.js +81 -0
- package/dist/entity/entity.base.js.map +1 -0
- package/dist/entity/entity.components.d.ts +117 -0
- package/dist/entity/entity.components.js +244 -0
- package/dist/entity/entity.components.js.map +1 -0
- package/dist/entity/entity.d.ts +35 -0
- package/dist/entity/entity.identity.d.ts +8 -0
- package/dist/entity/entity.identity.js +15 -0
- package/dist/entity/entity.identity.js.map +1 -0
- package/dist/entity/entity.js +33 -0
- package/dist/entity/entity.js.map +1 -0
- package/dist/entity/entity.lifecycle.d.ts +12 -0
- package/dist/entity/entity.lifecycle.js +111 -0
- package/dist/entity/entity.lifecycle.js.map +1 -0
- package/dist/entity/entity.queries.d.ts +3 -0
- package/dist/entity/entity.queries.js +33 -0
- package/dist/entity/entity.queries.js.map +1 -0
- package/dist/entity/entity.relationships.d.ts +9 -0
- package/dist/entity/entity.relationships.js +74 -0
- package/dist/entity/entity.relationships.js.map +1 -0
- package/dist/entity/index.d.ts +2 -0
- package/dist/entity/index.js +3 -0
- package/dist/entity/index.js.map +1 -0
- package/dist/filter.d.ts +27 -8
- package/dist/filter.js +33 -18
- package/dist/filter.js.map +1 -1
- package/dist/index.d.ts +13 -5
- package/dist/index.js +12 -2
- package/dist/index.js.map +1 -1
- package/dist/inject.d.ts +80 -0
- package/dist/inject.js +270 -0
- package/dist/inject.js.map +1 -0
- package/dist/module.d.ts +23 -0
- package/dist/module.js +17 -0
- package/dist/module.js.map +1 -0
- package/dist/modules/identity.d.ts +15 -0
- package/dist/modules/identity.js +41 -0
- package/dist/modules/identity.js.map +1 -0
- package/dist/modules/singleton.d.ts +26 -0
- package/dist/modules/singleton.js +41 -0
- package/dist/modules/singleton.js.map +1 -0
- package/dist/package.json +12 -1
- package/dist/phase.d.ts +2 -2
- package/dist/query/index.d.ts +6 -0
- package/dist/query/index.js +5 -0
- package/dist/query/index.js.map +1 -0
- package/dist/query/query.00.base.d.ts +23 -0
- package/dist/query/query.00.base.js +77 -0
- package/dist/query/query.00.base.js.map +1 -0
- package/dist/query/query.01.reactive.d.ts +7 -0
- package/dist/query/query.01.reactive.js +58 -0
- package/dist/query/query.01.reactive.js.map +1 -0
- package/dist/query/query.02.lifecycle.d.ts +6 -0
- package/dist/query/query.02.lifecycle.js +63 -0
- package/dist/query/query.02.lifecycle.js.map +1 -0
- package/dist/query/query.03.tracking.d.ts +15 -0
- package/dist/query/query.03.tracking.js +31 -0
- package/dist/query/query.03.tracking.js.map +1 -0
- package/dist/query/query.04.callbacks.d.ts +14 -0
- package/dist/query/query.04.callbacks.js +65 -0
- package/dist/query/query.04.callbacks.js.map +1 -0
- package/dist/query/query.05.updates.d.ts +14 -0
- package/dist/query/query.05.updates.js +81 -0
- package/dist/query/query.05.updates.js.map +1 -0
- package/dist/query/query.06.predicate.d.ts +13 -0
- package/dist/query/query.06.predicate.js +40 -0
- package/dist/query/query.06.predicate.js.map +1 -0
- package/dist/query/query.07.groups.d.ts +41 -0
- package/dist/query/query.07.groups.js +110 -0
- package/dist/query/query.07.groups.js.map +1 -0
- package/dist/query/query.d.ts +53 -0
- package/dist/query/query.js +138 -0
- package/dist/query/query.js.map +1 -0
- package/dist/relationship.d.ts +19 -0
- package/dist/relationship.js +18 -0
- package/dist/relationship.js.map +1 -0
- package/dist/system.d.ts +37 -23
- package/dist/system.js +80 -64
- package/dist/system.js.map +1 -1
- package/dist/terms/all_term.d.ts +32 -0
- package/dist/terms/all_term.js +41 -0
- package/dist/terms/all_term.js.map +1 -0
- package/dist/terms/any_term.d.ts +33 -0
- package/dist/terms/any_term.js +42 -0
- package/dist/terms/any_term.js.map +1 -0
- package/dist/terms/build.d.ts +62 -0
- package/dist/terms/build.js +382 -0
- package/dist/terms/build.js.map +1 -0
- package/dist/terms/component_term.d.ts +37 -0
- package/dist/terms/component_term.js +49 -0
- package/dist/terms/component_term.js.map +1 -0
- package/dist/terms/empty_term.d.ts +6 -0
- package/dist/terms/empty_term.js +12 -0
- package/dist/terms/empty_term.js.map +1 -0
- package/dist/terms/index.d.ts +11 -0
- package/dist/terms/index.js +12 -0
- package/dist/terms/index.js.map +1 -0
- package/dist/terms/not_term.d.ts +35 -0
- package/dist/terms/not_term.js +47 -0
- package/dist/terms/not_term.js.map +1 -0
- package/dist/terms/only_term.d.ts +47 -0
- package/dist/terms/only_term.js +79 -0
- package/dist/terms/only_term.js.map +1 -0
- package/dist/terms/predicate_term.d.ts +80 -0
- package/dist/terms/predicate_term.js +109 -0
- package/dist/terms/predicate_term.js.map +1 -0
- package/dist/terms/target_term.d.ts +43 -0
- package/dist/terms/target_term.js +87 -0
- package/dist/terms/target_term.js.map +1 -0
- package/dist/terms/term.d.ts +94 -0
- package/dist/terms/term.js +202 -0
- package/dist/terms/term.js.map +1 -0
- package/dist/terms/world_term.d.ts +68 -0
- package/dist/terms/world_term.js +99 -0
- package/dist/terms/world_term.js.map +1 -0
- package/dist/timer.js +2 -2
- package/dist/timer.js.map +1 -1
- package/dist/util/array_map.js +12 -0
- package/dist/util/array_map.js.map +1 -1
- package/dist/util/bitset.js +107 -22
- package/dist/util/bitset.js.map +1 -1
- package/dist/util/dense_set.d.ts +1 -0
- package/dist/util/dense_set.js +90 -0
- package/dist/util/dense_set.js.map +1 -0
- package/dist/util/id_pool.d.ts +23 -0
- package/dist/util/id_pool.js +194 -0
- package/dist/util/id_pool.js.map +1 -0
- package/dist/world/index.d.ts +3 -0
- package/dist/world/index.js +3 -0
- package/dist/world/index.js.map +1 -0
- package/dist/world/world.base.d.ts +6 -0
- package/dist/world/world.base.js +21 -0
- package/dist/world/world.base.js.map +1 -0
- package/dist/world/world.components.d.ts +67 -0
- package/dist/world/world.components.js +93 -0
- package/dist/world/world.components.js.map +1 -0
- package/dist/world/world.d.ts +29 -0
- package/dist/world/world.deferred.d.ts +13 -0
- package/dist/world/world.deferred.js +93 -0
- package/dist/world/world.deferred.js.map +1 -0
- package/dist/world/world.entities.d.ts +18 -0
- package/dist/world/world.entities.js +97 -0
- package/dist/world/world.entities.js.map +1 -0
- package/dist/world/world.js +39 -0
- package/dist/world/world.js.map +1 -0
- package/dist/world/world.modules.d.ts +12 -0
- package/dist/world/world.modules.js +21 -0
- package/dist/world/world.modules.js.map +1 -0
- package/dist/world/world.pipeline.d.ts +21 -0
- package/dist/world/world.pipeline.js +105 -0
- package/dist/world/world.pipeline.js.map +1 -0
- package/dist/world/world.pools.d.ts +9 -0
- package/dist/world/world.pools.js +59 -0
- package/dist/world/world.pools.js.map +1 -0
- package/dist/world/world.queries.d.ts +18 -0
- package/dist/world/world.queries.js +101 -0
- package/dist/world/world.queries.js.map +1 -0
- package/dist/world/world.storage.d.ts +7 -0
- package/dist/world/world.storage.js +26 -0
- package/dist/world/world.storage.js.map +1 -0
- package/package.json +12 -1
- package/dist/entity.d.ts +0 -215
- package/dist/entity.js +0 -457
- package/dist/entity.js.map +0 -1
- package/dist/query.d.ts +0 -251
- package/dist/query.js +0 -353
- package/dist/query.js.map +0 -1
- package/dist/world.d.ts +0 -389
- package/dist/world.js +0 -631
- package/dist/world.js.map +0 -1
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { Entity } from "../entity/index.js";
|
|
2
|
+
import type { World } from "../world/index.js";
|
|
3
|
+
import { Term } from "./term.js";
|
|
4
|
+
/**
|
|
5
|
+
* World-pinned leaf term that matches every entity carrying a specific
|
|
6
|
+
* component type.
|
|
7
|
+
*
|
|
8
|
+
* `ComponentTerm` is the component-presence index. It maintains the live set
|
|
9
|
+
* of entities that have the component identified by `componentEid`. The world
|
|
10
|
+
* routes `add`, `remove`, and `set`/`modified` commands here via the
|
|
11
|
+
* `notifyComponent*` helpers in `build.ts`.
|
|
12
|
+
*
|
|
13
|
+
* - **Membership** events (`componentAdded` / `componentRemoved`) propagate
|
|
14
|
+
* via the standard `_add`/`_delete` path and fan out to all consumers as
|
|
15
|
+
* enter/exit events.
|
|
16
|
+
* - **Data-only** changes (`componentChanged`) emit only a refresh hint via
|
|
17
|
+
* `_emitRefresh`, which wakes predicate terms watching this component
|
|
18
|
+
* without triggering a membership re-evaluation in terms that do not care
|
|
19
|
+
* about component values.
|
|
20
|
+
*/
|
|
21
|
+
export declare class ComponentTerm extends Term {
|
|
22
|
+
readonly componentEid: number;
|
|
23
|
+
constructor(world: World, key: string, componentEid: number, onDestroy: (term: Term) => void);
|
|
24
|
+
/** Called by `build.ts` when the component is added to `entity`. */
|
|
25
|
+
componentAdded(entity: Entity): void;
|
|
26
|
+
/**
|
|
27
|
+
* Called by `build.ts` when the component's value changes on `entity`
|
|
28
|
+
* without a membership change.
|
|
29
|
+
*
|
|
30
|
+
* Emits a refresh hint rather than an enter/exit event so that consumers
|
|
31
|
+
* watching for data updates (predicate terms) are woken while
|
|
32
|
+
* membership-only consumers are not disturbed.
|
|
33
|
+
*/
|
|
34
|
+
componentChanged(entity: Entity): void;
|
|
35
|
+
/** Called by `build.ts` when the component is removed from `entity`. */
|
|
36
|
+
componentRemoved(entity: Entity): void;
|
|
37
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { Term } from "./term.js";
|
|
2
|
+
/**
|
|
3
|
+
* World-pinned leaf term that matches every entity carrying a specific
|
|
4
|
+
* component type.
|
|
5
|
+
*
|
|
6
|
+
* `ComponentTerm` is the component-presence index. It maintains the live set
|
|
7
|
+
* of entities that have the component identified by `componentEid`. The world
|
|
8
|
+
* routes `add`, `remove`, and `set`/`modified` commands here via the
|
|
9
|
+
* `notifyComponent*` helpers in `build.ts`.
|
|
10
|
+
*
|
|
11
|
+
* - **Membership** events (`componentAdded` / `componentRemoved`) propagate
|
|
12
|
+
* via the standard `_add`/`_delete` path and fan out to all consumers as
|
|
13
|
+
* enter/exit events.
|
|
14
|
+
* - **Data-only** changes (`componentChanged`) emit only a refresh hint via
|
|
15
|
+
* `_emitRefresh`, which wakes predicate terms watching this component
|
|
16
|
+
* without triggering a membership re-evaluation in terms that do not care
|
|
17
|
+
* about component values.
|
|
18
|
+
*/
|
|
19
|
+
export class ComponentTerm extends Term {
|
|
20
|
+
constructor(world, key, componentEid, onDestroy) {
|
|
21
|
+
super(world, key, "component", true, onDestroy);
|
|
22
|
+
this.componentEid = componentEid;
|
|
23
|
+
this._initializeFromWorld();
|
|
24
|
+
}
|
|
25
|
+
/** Called by `build.ts` when the component is added to `entity`. */
|
|
26
|
+
componentAdded(entity) {
|
|
27
|
+
this._add(entity);
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Called by `build.ts` when the component's value changes on `entity`
|
|
31
|
+
* without a membership change.
|
|
32
|
+
*
|
|
33
|
+
* Emits a refresh hint rather than an enter/exit event so that consumers
|
|
34
|
+
* watching for data updates (predicate terms) are woken while
|
|
35
|
+
* membership-only consumers are not disturbed.
|
|
36
|
+
*/
|
|
37
|
+
componentChanged(entity) {
|
|
38
|
+
this._emitRefresh(entity);
|
|
39
|
+
}
|
|
40
|
+
/** Called by `build.ts` when the component is removed from `entity`. */
|
|
41
|
+
componentRemoved(entity) {
|
|
42
|
+
this._delete(entity);
|
|
43
|
+
}
|
|
44
|
+
/** @internal */
|
|
45
|
+
_matches(entity) {
|
|
46
|
+
return !entity._destroyed && entity._get(this.componentEid) !== undefined;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=component_term.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"component_term.js","sourceRoot":"","sources":["../../src/terms/component_term.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,OAAO,aAAc,SAAQ,IAAI;IACrC,YACE,KAAY,EACZ,GAAW,EACK,YAAoB,EACpC,SAA+B;QAE/B,KAAK,CAAC,KAAK,EAAE,GAAG,EAAE,WAAW,EAAE,IAAI,EAAE,SAAS,CAAC,CAAC;QAHhC,iBAAY,GAAZ,YAAY,CAAQ;QAIpC,IAAI,CAAC,oBAAoB,EAAE,CAAC;IAC9B,CAAC;IAED,oEAAoE;IAC7D,cAAc,CAAC,MAAc;QAClC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACpB,CAAC;IAED;;;;;;;OAOG;IACI,gBAAgB,CAAC,MAAc;QACpC,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC;IAC5B,CAAC;IAED,wEAAwE;IACjE,gBAAgB,CAAC,MAAc;QACpC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IACvB,CAAC;IAED,gBAAgB;IACN,QAAQ,CAAC,MAAc;QAC/B,OAAO,CAAC,MAAM,CAAC,UAAU,IAAI,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,KAAK,SAAS,CAAC;IAC5E,CAAC;CACF"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { World } from "../world/index.js";
|
|
2
|
+
import { Term } from "./term.js";
|
|
3
|
+
/** World-pinned leaf term that matches no entities; the materialization of `false`. */
|
|
4
|
+
export declare class EmptyTerm extends Term {
|
|
5
|
+
constructor(world: World, key: string, onDestroy: (term: Term) => void);
|
|
6
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Term } from "./term.js";
|
|
2
|
+
/** World-pinned leaf term that matches no entities; the materialization of `false`. */
|
|
3
|
+
export class EmptyTerm extends Term {
|
|
4
|
+
constructor(world, key, onDestroy) {
|
|
5
|
+
super(world, key, "empty", true, onDestroy);
|
|
6
|
+
}
|
|
7
|
+
/** @internal */
|
|
8
|
+
_matches(_entity) {
|
|
9
|
+
return false;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=empty_term.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"empty_term.js","sourceRoot":"","sources":["../../src/terms/empty_term.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,uFAAuF;AACvF,MAAM,OAAO,SAAU,SAAQ,IAAI;IACjC,YAAmB,KAAY,EAAE,GAAW,EAAE,SAA+B;QAC3E,KAAK,CAAC,KAAK,EAAE,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,CAAC,CAAC;IAC9C,CAAC;IAED,gBAAgB;IACN,QAAQ,CAAC,OAAe;QAChC,OAAO,KAAK,CAAC;IACf,CAAC;CACF"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export { Term, TermEventKind, type TermConsumer, type TermKind } from "./term.js";
|
|
2
|
+
export { WorldTerm } from "./world_term.js";
|
|
3
|
+
export { ComponentTerm } from "./component_term.js";
|
|
4
|
+
export { EmptyTerm } from "./empty_term.js";
|
|
5
|
+
export { AllTerm } from "./all_term.js";
|
|
6
|
+
export { AnyTerm } from "./any_term.js";
|
|
7
|
+
export { NotTerm } from "./not_term.js";
|
|
8
|
+
export { OnlyTerm } from "./only_term.js";
|
|
9
|
+
export { PredicateTerm, type MergedInfo } from "./predicate_term.js";
|
|
10
|
+
export { TargetTerm } from "./target_term.js";
|
|
11
|
+
export { getTerm, getCachedTerm, notifyComponentAdded, notifyComponentChanged, notifyComponentRemoved, notifyEntityCreated, notifyEntityDestroyed, } from "./build.js";
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export { Term } from "./term.js";
|
|
2
|
+
export { WorldTerm } from "./world_term.js";
|
|
3
|
+
export { ComponentTerm } from "./component_term.js";
|
|
4
|
+
export { EmptyTerm } from "./empty_term.js";
|
|
5
|
+
export { AllTerm } from "./all_term.js";
|
|
6
|
+
export { AnyTerm } from "./any_term.js";
|
|
7
|
+
export { NotTerm } from "./not_term.js";
|
|
8
|
+
export { OnlyTerm } from "./only_term.js";
|
|
9
|
+
export { PredicateTerm } from "./predicate_term.js";
|
|
10
|
+
export { TargetTerm } from "./target_term.js";
|
|
11
|
+
export { getTerm, getCachedTerm, notifyComponentAdded, notifyComponentChanged, notifyComponentRemoved, notifyEntityCreated, notifyEntityDestroyed, } from "./build.js";
|
|
12
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/terms/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAmD,MAAM,WAAW,CAAC;AAClF,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AACxC,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AACxC,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AACxC,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAmB,MAAM,qBAAqB,CAAC;AACrE,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EACL,OAAO,EACP,aAAa,EACb,oBAAoB,EACpB,sBAAsB,EACtB,sBAAsB,EACtB,mBAAmB,EACnB,qBAAqB,GACtB,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { Entity } from "../entity/index.js";
|
|
2
|
+
import type { World } from "../world/index.js";
|
|
3
|
+
import { Term } from "./term.js";
|
|
4
|
+
import type { QueryDSL } from "../dsl.js";
|
|
5
|
+
import type { getTerm as GetTermFn } from "./build.js";
|
|
6
|
+
/**
|
|
7
|
+
* Composite term that matches entities in the world but **not** in a child
|
|
8
|
+
* term.
|
|
9
|
+
*
|
|
10
|
+
* Children: `[worldTerm, child]`. The world leaf provides lifecycle
|
|
11
|
+
* (creation/destruction); the child provides the set to negate.
|
|
12
|
+
*/
|
|
13
|
+
export declare class NotTerm extends Term {
|
|
14
|
+
constructor(world: World, key: string, worldTerm: Term, child: Term, onDestroy: (term: Term) => void);
|
|
15
|
+
/**
|
|
16
|
+
* Build a `NotTerm` for the given child DSL.
|
|
17
|
+
*
|
|
18
|
+
* @param world - Owning world.
|
|
19
|
+
* @param childDSL - The DSL expression to negate.
|
|
20
|
+
* @param key - Canonical cache key.
|
|
21
|
+
* @param getTerm - Term canonicalizer / cache accessor.
|
|
22
|
+
* @param onDestroy - Called when the last consumer unsubscribes.
|
|
23
|
+
*/
|
|
24
|
+
static build(world: World, childDSL: QueryDSL, key: string, getTerm: typeof GetTermFn, onDestroy: (term: Term) => void): NotTerm;
|
|
25
|
+
/**
|
|
26
|
+
* No-op override: a pure negation term does not need to re-evaluate on a
|
|
27
|
+
* data or shape refresh.
|
|
28
|
+
*
|
|
29
|
+
* `NotTerm` membership flips only when the child's membership changes (a
|
|
30
|
+
* real enter/exit) or when the world emits a lifecycle event — both of
|
|
31
|
+
* which arrive via `onTermEvent`, not `onTermRefresh`. Reacting to refresh
|
|
32
|
+
* hints would cause spurious `_sync` calls with no change in result.
|
|
33
|
+
*/
|
|
34
|
+
onTermRefresh(_entity: Entity): void;
|
|
35
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { Term } from "./term.js";
|
|
2
|
+
/**
|
|
3
|
+
* Composite term that matches entities in the world but **not** in a child
|
|
4
|
+
* term.
|
|
5
|
+
*
|
|
6
|
+
* Children: `[worldTerm, child]`. The world leaf provides lifecycle
|
|
7
|
+
* (creation/destruction); the child provides the set to negate.
|
|
8
|
+
*/
|
|
9
|
+
export class NotTerm extends Term {
|
|
10
|
+
constructor(world, key, worldTerm, child, onDestroy) {
|
|
11
|
+
super(world, key, "not", false, onDestroy);
|
|
12
|
+
this._setChildren([worldTerm, child]);
|
|
13
|
+
this._initializeFromWorld();
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Build a `NotTerm` for the given child DSL.
|
|
17
|
+
*
|
|
18
|
+
* @param world - Owning world.
|
|
19
|
+
* @param childDSL - The DSL expression to negate.
|
|
20
|
+
* @param key - Canonical cache key.
|
|
21
|
+
* @param getTerm - Term canonicalizer / cache accessor.
|
|
22
|
+
* @param onDestroy - Called when the last consumer unsubscribes.
|
|
23
|
+
*/
|
|
24
|
+
static build(world, childDSL, key, getTerm, onDestroy) {
|
|
25
|
+
const worldTerm = getTerm(world, true);
|
|
26
|
+
const child = getTerm(world, childDSL);
|
|
27
|
+
return new NotTerm(world, key, worldTerm, child, onDestroy);
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* No-op override: a pure negation term does not need to re-evaluate on a
|
|
31
|
+
* data or shape refresh.
|
|
32
|
+
*
|
|
33
|
+
* `NotTerm` membership flips only when the child's membership changes (a
|
|
34
|
+
* real enter/exit) or when the world emits a lifecycle event — both of
|
|
35
|
+
* which arrive via `onTermEvent`, not `onTermRefresh`. Reacting to refresh
|
|
36
|
+
* hints would cause spurious `_sync` calls with no change in result.
|
|
37
|
+
*/
|
|
38
|
+
onTermRefresh(_entity) {
|
|
39
|
+
// Shape/data refreshes do not affect a pure negation unless the child emits
|
|
40
|
+
// a real membership event, or the world emits a lifecycle event.
|
|
41
|
+
}
|
|
42
|
+
/** @internal */
|
|
43
|
+
_matches(entity) {
|
|
44
|
+
return this._children[0].has(entity) && !this._children[1].has(entity);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=not_term.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"not_term.js","sourceRoot":"","sources":["../../src/terms/not_term.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAIjC;;;;;;GAMG;AACH,MAAM,OAAO,OAAQ,SAAQ,IAAI;IAC/B,YACE,KAAY,EACZ,GAAW,EACX,SAAe,EACf,KAAW,EACX,SAA+B;QAE/B,KAAK,CAAC,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,KAAK,EAAE,SAAS,CAAC,CAAC;QAC3C,IAAI,CAAC,YAAY,CAAC,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC,CAAC;QACtC,IAAI,CAAC,oBAAoB,EAAE,CAAC;IAC9B,CAAC;IAED;;;;;;;;OAQG;IACI,MAAM,CAAC,KAAK,CACjB,KAAY,EACZ,QAAkB,EAClB,GAAW,EACX,OAAyB,EACzB,SAA+B;QAE/B,MAAM,SAAS,GAAG,OAAO,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;QACvC,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;QACvC,OAAO,IAAI,OAAO,CAAC,KAAK,EAAE,GAAG,EAAE,SAAS,EAAE,KAAK,EAAE,SAAS,CAAC,CAAC;IAC9D,CAAC;IAED;;;;;;;;OAQG;IACa,aAAa,CAAC,OAAe;QAC3C,4EAA4E;QAC5E,iEAAiE;IACnE,CAAC;IAED,gBAAgB;IACN,QAAQ,CAAC,MAAc;QAC/B,OAAO,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IACzE,CAAC;CACF"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { Entity } from "../entity/index.js";
|
|
2
|
+
import type { World } from "../world/index.js";
|
|
3
|
+
import { Term } from "./term.js";
|
|
4
|
+
import type { getTerm as GetTermFn } from "./build.js";
|
|
5
|
+
/**
|
|
6
|
+
* Composite term that matches entities carrying **exactly** the specified set
|
|
7
|
+
* of component types — no more, no fewer.
|
|
8
|
+
*
|
|
9
|
+
* Internally composed from two children:
|
|
10
|
+
* - `children[0]`: an `AllTerm` (or array shorthand) for the required
|
|
11
|
+
* components, ensuring every listed component is present.
|
|
12
|
+
* - `children[1]`: the world leaf, ensuring the entity is live and
|
|
13
|
+
* non-component.
|
|
14
|
+
*
|
|
15
|
+
* `OnlyTerm` additionally subscribes to the **shape channel** of the world
|
|
16
|
+
* term so it is re-evaluated whenever *any* component is added to or removed
|
|
17
|
+
* from the entity — not just when one of the listed components changes
|
|
18
|
+
* membership. This is necessary because `only` requires `entity.components.size
|
|
19
|
+
* === componentEids.length`; adding an unlisted component must evict the entity
|
|
20
|
+
* even though the `AllTerm` child has not changed.
|
|
21
|
+
*/
|
|
22
|
+
export declare class OnlyTerm extends Term {
|
|
23
|
+
private readonly _componentEids;
|
|
24
|
+
constructor(world: World, key: string, allTerm: Term, worldTerm: Term, _componentEids: readonly number[], onDestroy: (term: Term) => void);
|
|
25
|
+
/**
|
|
26
|
+
* Build an `OnlyTerm` for the given component eid list.
|
|
27
|
+
*
|
|
28
|
+
* @param world - Owning world.
|
|
29
|
+
* @param componentEids - Numeric component type ids that must be present and
|
|
30
|
+
* comprise the entire component set.
|
|
31
|
+
* @param key - Canonical cache key.
|
|
32
|
+
* @param getTerm - Term canonicalizer / cache accessor.
|
|
33
|
+
* @param onDestroy - Called when the last consumer unsubscribes.
|
|
34
|
+
*/
|
|
35
|
+
static build(world: World, componentEids: readonly number[], key: string, getTerm: typeof GetTermFn, onDestroy: (term: Term) => void): OnlyTerm;
|
|
36
|
+
/**
|
|
37
|
+
* Shape-channel entry point: called directly by `WorldTerm` when the entity's
|
|
38
|
+
* component set changes.
|
|
39
|
+
*
|
|
40
|
+
* Overrides the base `onTermRefresh` to ensure a shape change always
|
|
41
|
+
* triggers a re-evaluation, regardless of which child emitted the hint.
|
|
42
|
+
* Without this override the base implementation would also call `_sync`,
|
|
43
|
+
* but the explicit override makes the intent clear and guards against future
|
|
44
|
+
* base-class changes.
|
|
45
|
+
*/
|
|
46
|
+
onTermRefresh(entity: Entity): void;
|
|
47
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { Term } from "./term.js";
|
|
2
|
+
import { WorldTerm } from "./world_term.js";
|
|
3
|
+
/**
|
|
4
|
+
* Composite term that matches entities carrying **exactly** the specified set
|
|
5
|
+
* of component types — no more, no fewer.
|
|
6
|
+
*
|
|
7
|
+
* Internally composed from two children:
|
|
8
|
+
* - `children[0]`: an `AllTerm` (or array shorthand) for the required
|
|
9
|
+
* components, ensuring every listed component is present.
|
|
10
|
+
* - `children[1]`: the world leaf, ensuring the entity is live and
|
|
11
|
+
* non-component.
|
|
12
|
+
*
|
|
13
|
+
* `OnlyTerm` additionally subscribes to the **shape channel** of the world
|
|
14
|
+
* term so it is re-evaluated whenever *any* component is added to or removed
|
|
15
|
+
* from the entity — not just when one of the listed components changes
|
|
16
|
+
* membership. This is necessary because `only` requires `entity.components.size
|
|
17
|
+
* === componentEids.length`; adding an unlisted component must evict the entity
|
|
18
|
+
* even though the `AllTerm` child has not changed.
|
|
19
|
+
*/
|
|
20
|
+
export class OnlyTerm extends Term {
|
|
21
|
+
constructor(world, key, allTerm, worldTerm, _componentEids, onDestroy) {
|
|
22
|
+
super(world, key, "only", false, onDestroy);
|
|
23
|
+
this._componentEids = _componentEids;
|
|
24
|
+
this._setChildren([allTerm, worldTerm]);
|
|
25
|
+
if (worldTerm instanceof WorldTerm) {
|
|
26
|
+
this._addChildCleanup(worldTerm.subscribeShape(this));
|
|
27
|
+
}
|
|
28
|
+
this._initializeFromWorld();
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Build an `OnlyTerm` for the given component eid list.
|
|
32
|
+
*
|
|
33
|
+
* @param world - Owning world.
|
|
34
|
+
* @param componentEids - Numeric component type ids that must be present and
|
|
35
|
+
* comprise the entire component set.
|
|
36
|
+
* @param key - Canonical cache key.
|
|
37
|
+
* @param getTerm - Term canonicalizer / cache accessor.
|
|
38
|
+
* @param onDestroy - Called when the last consumer unsubscribes.
|
|
39
|
+
*/
|
|
40
|
+
static build(world, componentEids, key, getTerm, onDestroy) {
|
|
41
|
+
// Build the inner all-term for the component set, then the world leaf.
|
|
42
|
+
const allTerm = getTerm(world, componentEids);
|
|
43
|
+
const worldTerm = getTerm(world, true);
|
|
44
|
+
return new OnlyTerm(world, key, allTerm, worldTerm, componentEids, onDestroy);
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Shape-channel entry point: called directly by `WorldTerm` when the entity's
|
|
48
|
+
* component set changes.
|
|
49
|
+
*
|
|
50
|
+
* Overrides the base `onTermRefresh` to ensure a shape change always
|
|
51
|
+
* triggers a re-evaluation, regardless of which child emitted the hint.
|
|
52
|
+
* Without this override the base implementation would also call `_sync`,
|
|
53
|
+
* but the explicit override makes the intent clear and guards against future
|
|
54
|
+
* base-class changes.
|
|
55
|
+
*/
|
|
56
|
+
onTermRefresh(entity) {
|
|
57
|
+
this._sync(entity);
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* @internal React only to world-kind child refreshes.
|
|
61
|
+
*
|
|
62
|
+
* `OnlyTerm` cares about world-shape events (component add/remove) because
|
|
63
|
+
* the component count check in `_matches` can be violated by adding an
|
|
64
|
+
* unlisted component. Component-kind refreshes (data-only value changes) do
|
|
65
|
+
* not affect `only` membership and are intentionally ignored here.
|
|
66
|
+
*/
|
|
67
|
+
_onChildRefresh(entity, child) {
|
|
68
|
+
if (child.kind === "world") {
|
|
69
|
+
this._sync(entity);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
/** @internal */
|
|
73
|
+
_matches(entity) {
|
|
74
|
+
return (this._children[0].has(entity) &&
|
|
75
|
+
this._children[1].has(entity) &&
|
|
76
|
+
entity.components.size === this._componentEids.length);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=only_term.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"only_term.js","sourceRoot":"","sources":["../../src/terms/only_term.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAG5C;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,OAAO,QAAS,SAAQ,IAAI;IAChC,YACE,KAAY,EACZ,GAAW,EACX,OAAa,EACb,SAAe,EACE,cAAiC,EAClD,SAA+B;QAE/B,KAAK,CAAC,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,CAAC,CAAC;QAH3B,mBAAc,GAAd,cAAc,CAAmB;QAIlD,IAAI,CAAC,YAAY,CAAC,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC,CAAC;QACxC,IAAI,SAAS,YAAY,SAAS,EAAE,CAAC;YACnC,IAAI,CAAC,gBAAgB,CAAC,SAAS,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC,CAAC;QACxD,CAAC;QACD,IAAI,CAAC,oBAAoB,EAAE,CAAC;IAC9B,CAAC;IAED;;;;;;;;;OASG;IACI,MAAM,CAAC,KAAK,CACjB,KAAY,EACZ,aAAgC,EAChC,GAAW,EACX,OAAyB,EACzB,SAA+B;QAE/B,uEAAuE;QACvE,MAAM,OAAO,GAAG,OAAO,CAAC,KAAK,EAAE,aAAyB,CAAC,CAAC;QAC1D,MAAM,SAAS,GAAG,OAAO,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;QACvC,OAAO,IAAI,QAAQ,CAAC,KAAK,EAAE,GAAG,EAAE,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,SAAS,CAAC,CAAC;IAChF,CAAC;IAED;;;;;;;;;OASG;IACa,aAAa,CAAC,MAAc;QAC1C,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IACrB,CAAC;IAED;;;;;;;OAOG;IACgB,eAAe,CAAC,MAAc,EAAE,KAAW;QAC5D,IAAI,KAAK,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;YAC3B,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QACrB,CAAC;IACH,CAAC;IAED,gBAAgB;IACN,QAAQ,CAAC,MAAc;QAC/B,OAAO,CACL,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC;YAC7B,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC;YAC7B,MAAM,CAAC,UAAU,CAAC,IAAI,KAAK,IAAI,CAAC,cAAc,CAAC,MAAM,CACtD,CAAC;IACJ,CAAC;CACF"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { type EntityTestFunc, type QueryDSL } from "../dsl.js";
|
|
2
|
+
import type { Entity } from "../entity/index.js";
|
|
3
|
+
import type { World } from "../world/index.js";
|
|
4
|
+
import { Term } from "./term.js";
|
|
5
|
+
import type { getTerm as GetTermFn } from "./build.js";
|
|
6
|
+
/**
|
|
7
|
+
* Analysis summary derived from a collapsed `all`/`any` subtree.
|
|
8
|
+
*
|
|
9
|
+
* Produced by `_allAnyPrecheck` in `build.ts` and consumed by
|
|
10
|
+
* {@link PredicateTerm.buildMerged} to configure the unified class without
|
|
11
|
+
* first building — and then having to tear down — the composite child terms.
|
|
12
|
+
*/
|
|
13
|
+
export type MergedInfo = {
|
|
14
|
+
componentEids: ReadonlySet<number>;
|
|
15
|
+
dependsOnWorld: boolean;
|
|
16
|
+
containsOnly: boolean;
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* A reactive predicate term that covers two related use-cases under one class:
|
|
20
|
+
*
|
|
21
|
+
* - **Plain predicate** (`kind === "predicate"`): wraps a user-supplied
|
|
22
|
+
* `{ test, watch }` DSL node. The predicate is evaluated whenever a watched
|
|
23
|
+
* component changes; membership requires the world leaf to include the entity
|
|
24
|
+
* (i.e. it is not destroyed and not a `Component` instance).
|
|
25
|
+
*
|
|
26
|
+
* - **Merged / diamond-collapsed** (`kind === "merged"`): wraps a compiled
|
|
27
|
+
* predicate for an `all`/`any` subtree that was detected to contain a
|
|
28
|
+
* leaf-level diamond (the same leaf reachable via two paths). Collapsing it
|
|
29
|
+
* into a single compiled predicate keeps the live graph a strict tree and
|
|
30
|
+
* avoids glitch-inducing double propagation. The optional `_worldTerm` is
|
|
31
|
+
* present only when the subtree depends on world lifecycle, and
|
|
32
|
+
* `_usesShapeRefresh` is set when the subtree contains an `only(...)` node
|
|
33
|
+
* (which needs a shape-channel wakeup on component add/remove).
|
|
34
|
+
*
|
|
35
|
+
* Both variants are constructed via the static factory methods
|
|
36
|
+
* {@link buildPredicate} and {@link buildMerged}; the constructor is not
|
|
37
|
+
* public.
|
|
38
|
+
*/
|
|
39
|
+
export declare class PredicateTerm extends Term {
|
|
40
|
+
private readonly _predicate;
|
|
41
|
+
private readonly _worldTerm;
|
|
42
|
+
private readonly _usesShapeRefresh;
|
|
43
|
+
protected constructor(world: World, key: string, kind: "predicate" | "merged", predicate: EntityTestFunc, worldTerm: Term | undefined, watchedTerms: readonly Term[], usesShapeRefresh: boolean, onDestroy: (term: Term) => void);
|
|
44
|
+
/**
|
|
45
|
+
* Build a plain predicate term for a `{ test, watch }` DSL node.
|
|
46
|
+
*
|
|
47
|
+
* Subscribes to the world leaf plus any watched component leaves so the
|
|
48
|
+
* predicate is re-evaluated whenever watched component data changes.
|
|
49
|
+
*
|
|
50
|
+
* @param world - Owning world.
|
|
51
|
+
* @param dsl - DSL node with a `test` function and optional `watch` list.
|
|
52
|
+
* @param key - Canonical cache key for this term.
|
|
53
|
+
* @param getTerm - Term canonicalizer / cache accessor.
|
|
54
|
+
* @param onDestroy - Called when the last consumer unsubscribes.
|
|
55
|
+
*/
|
|
56
|
+
static buildPredicate(world: World, dsl: {
|
|
57
|
+
test: EntityTestFunc;
|
|
58
|
+
watch?: unknown;
|
|
59
|
+
}, key: string, getTerm: typeof GetTermFn, onDestroy: (term: Term) => void): PredicateTerm;
|
|
60
|
+
/**
|
|
61
|
+
* Build a merged (diamond-collapsed) predicate term for an `all`/`any`
|
|
62
|
+
* subtree.
|
|
63
|
+
*
|
|
64
|
+
* The predicate is compiled from the full subtree DSL via `_compile` so no
|
|
65
|
+
* composite child terms need to be constructed and then torn down. The
|
|
66
|
+
* `worldTerm` leaf is included only when the subtree depends on world
|
|
67
|
+
* lifecycle; shape-refresh subscription is added only when the subtree
|
|
68
|
+
* contains an `only(...)` node.
|
|
69
|
+
*
|
|
70
|
+
* @param world - Owning world.
|
|
71
|
+
* @param dsl - The original subtree DSL to compile into a predicate.
|
|
72
|
+
* @param key - Canonical cache key for this term.
|
|
73
|
+
* @param info - Pre-computed analysis of the subtree (from `_allAnyPrecheck`).
|
|
74
|
+
* @param getTerm - Term canonicalizer / cache accessor.
|
|
75
|
+
* @param onDestroy - Called when the last consumer unsubscribes.
|
|
76
|
+
*/
|
|
77
|
+
static buildMerged(world: World, dsl: QueryDSL, key: string, info: MergedInfo, getTerm: typeof GetTermFn, onDestroy: (term: Term) => void): PredicateTerm;
|
|
78
|
+
/** Direct shape-channel invalidation from `WorldTerm.subscribeShape`. */
|
|
79
|
+
onTermRefresh(entity: Entity): void;
|
|
80
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { _compile } from "../dsl.js";
|
|
2
|
+
import { Term } from "./term.js";
|
|
3
|
+
import { WorldTerm } from "./world_term.js";
|
|
4
|
+
/**
|
|
5
|
+
* A reactive predicate term that covers two related use-cases under one class:
|
|
6
|
+
*
|
|
7
|
+
* - **Plain predicate** (`kind === "predicate"`): wraps a user-supplied
|
|
8
|
+
* `{ test, watch }` DSL node. The predicate is evaluated whenever a watched
|
|
9
|
+
* component changes; membership requires the world leaf to include the entity
|
|
10
|
+
* (i.e. it is not destroyed and not a `Component` instance).
|
|
11
|
+
*
|
|
12
|
+
* - **Merged / diamond-collapsed** (`kind === "merged"`): wraps a compiled
|
|
13
|
+
* predicate for an `all`/`any` subtree that was detected to contain a
|
|
14
|
+
* leaf-level diamond (the same leaf reachable via two paths). Collapsing it
|
|
15
|
+
* into a single compiled predicate keeps the live graph a strict tree and
|
|
16
|
+
* avoids glitch-inducing double propagation. The optional `_worldTerm` is
|
|
17
|
+
* present only when the subtree depends on world lifecycle, and
|
|
18
|
+
* `_usesShapeRefresh` is set when the subtree contains an `only(...)` node
|
|
19
|
+
* (which needs a shape-channel wakeup on component add/remove).
|
|
20
|
+
*
|
|
21
|
+
* Both variants are constructed via the static factory methods
|
|
22
|
+
* {@link buildPredicate} and {@link buildMerged}; the constructor is not
|
|
23
|
+
* public.
|
|
24
|
+
*/
|
|
25
|
+
export class PredicateTerm extends Term {
|
|
26
|
+
constructor(world, key, kind, predicate, worldTerm, watchedTerms, usesShapeRefresh, onDestroy) {
|
|
27
|
+
super(world, key, kind, false, onDestroy);
|
|
28
|
+
this._predicate = predicate;
|
|
29
|
+
this._worldTerm = worldTerm;
|
|
30
|
+
this._usesShapeRefresh = usesShapeRefresh;
|
|
31
|
+
this._setChildren(worldTerm ? [worldTerm, ...watchedTerms] : watchedTerms);
|
|
32
|
+
if (usesShapeRefresh && worldTerm instanceof WorldTerm) {
|
|
33
|
+
this._addChildCleanup(worldTerm.subscribeShape(this));
|
|
34
|
+
}
|
|
35
|
+
this._initializeFromWorld();
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Build a plain predicate term for a `{ test, watch }` DSL node.
|
|
39
|
+
*
|
|
40
|
+
* Subscribes to the world leaf plus any watched component leaves so the
|
|
41
|
+
* predicate is re-evaluated whenever watched component data changes.
|
|
42
|
+
*
|
|
43
|
+
* @param world - Owning world.
|
|
44
|
+
* @param dsl - DSL node with a `test` function and optional `watch` list.
|
|
45
|
+
* @param key - Canonical cache key for this term.
|
|
46
|
+
* @param getTerm - Term canonicalizer / cache accessor.
|
|
47
|
+
* @param onDestroy - Called when the last consumer unsubscribes.
|
|
48
|
+
*/
|
|
49
|
+
static buildPredicate(world, dsl, key, getTerm, onDestroy) {
|
|
50
|
+
const watch = normalizeWatch(dsl.watch);
|
|
51
|
+
const worldTerm = getTerm(world, true);
|
|
52
|
+
const watchedTerms = watch.map((eid) => getTerm(world, eid));
|
|
53
|
+
return new PredicateTerm(world, key, "predicate", dsl.test, worldTerm, watchedTerms, false, onDestroy);
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Build a merged (diamond-collapsed) predicate term for an `all`/`any`
|
|
57
|
+
* subtree.
|
|
58
|
+
*
|
|
59
|
+
* The predicate is compiled from the full subtree DSL via `_compile` so no
|
|
60
|
+
* composite child terms need to be constructed and then torn down. The
|
|
61
|
+
* `worldTerm` leaf is included only when the subtree depends on world
|
|
62
|
+
* lifecycle; shape-refresh subscription is added only when the subtree
|
|
63
|
+
* contains an `only(...)` node.
|
|
64
|
+
*
|
|
65
|
+
* @param world - Owning world.
|
|
66
|
+
* @param dsl - The original subtree DSL to compile into a predicate.
|
|
67
|
+
* @param key - Canonical cache key for this term.
|
|
68
|
+
* @param info - Pre-computed analysis of the subtree (from `_allAnyPrecheck`).
|
|
69
|
+
* @param getTerm - Term canonicalizer / cache accessor.
|
|
70
|
+
* @param onDestroy - Called when the last consumer unsubscribes.
|
|
71
|
+
*/
|
|
72
|
+
static buildMerged(world, dsl, key, info, getTerm, onDestroy) {
|
|
73
|
+
const predicate = _compile(world, dsl);
|
|
74
|
+
const worldTerm = info.dependsOnWorld ? getTerm(world, true) : undefined;
|
|
75
|
+
const watchedTerms = [...info.componentEids].map((eid) => getTerm(world, eid));
|
|
76
|
+
return new PredicateTerm(world, key, "merged", predicate, worldTerm, watchedTerms, info.containsOnly, onDestroy);
|
|
77
|
+
}
|
|
78
|
+
/** Direct shape-channel invalidation from `WorldTerm.subscribeShape`. */
|
|
79
|
+
onTermRefresh(entity) {
|
|
80
|
+
this._sync(entity);
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* @internal React to a child term refresh.
|
|
84
|
+
*
|
|
85
|
+
* For the plain-predicate variant (`_usesShapeRefresh === false`) only
|
|
86
|
+
* component-kind children trigger a re-evaluation — shape changes on the
|
|
87
|
+
* world leaf are irrelevant. For the merged variant, world-kind refreshes
|
|
88
|
+
* are also relevant when the subtree contained an `only(...)` node (because
|
|
89
|
+
* `only` is sensitive to which components an entity currently has, not just
|
|
90
|
+
* whether a specific one is present).
|
|
91
|
+
*/
|
|
92
|
+
_onChildRefresh(entity, child) {
|
|
93
|
+
if (child.kind === "component" || (this._usesShapeRefresh && child.kind === "world")) {
|
|
94
|
+
this._sync(entity);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
/** @internal */
|
|
98
|
+
_matches(entity) {
|
|
99
|
+
return !entity._destroyed && (this._worldTerm?.has(entity) ?? true) && this._predicate(entity);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
/** @internal Deduplicate and sort a `watch` list of component eids. */
|
|
103
|
+
export function normalizeWatch(watch) {
|
|
104
|
+
if (watch === undefined) {
|
|
105
|
+
return [];
|
|
106
|
+
}
|
|
107
|
+
return [...new Set((watch instanceof Array ? watch : [watch]))].sort((a, b) => a - b);
|
|
108
|
+
}
|
|
109
|
+
//# sourceMappingURL=predicate_term.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"predicate_term.js","sourceRoot":"","sources":["../../src/terms/predicate_term.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { QueryDSL } from "../dsl.js";
|
|
2
|
+
import type { World } from "../world/index.js";
|
|
3
|
+
import { Term } from "./term.js";
|
|
4
|
+
import type { getTerm as GetTermFn } from "./build.js";
|
|
5
|
+
/**
|
|
6
|
+
* Composite term that matches entities whose relationship target satisfies a
|
|
7
|
+
* given inner predicate.
|
|
8
|
+
*
|
|
9
|
+
* An entity `e` matches when it carries the relationship component
|
|
10
|
+
* `_relationshipEid` **and** the target entity stored in that component
|
|
11
|
+
* satisfies `_innerTerm`.
|
|
12
|
+
*
|
|
13
|
+
* Children: `[relationshipTerm, innerTerm]`.
|
|
14
|
+
* - `children[0]` (`relationshipTerm`): the `ComponentTerm` for the
|
|
15
|
+
* relationship eid — fires when the relationship is added/removed from `e`.
|
|
16
|
+
* - `children[1]` (`innerTerm`): the term for the inner DSL — fires when the
|
|
17
|
+
* target entity transitions in or out.
|
|
18
|
+
*
|
|
19
|
+
* ## Non-standard event routing
|
|
20
|
+
*
|
|
21
|
+
* `TargetTerm` overrides `_onChildEvent` and `_onChildRefresh` to implement
|
|
22
|
+
* a **fan-out** strategy: when the inner term changes (the target enters or
|
|
23
|
+
* exits), all *sources* that point at that target via the relationship must be
|
|
24
|
+
* re-evaluated. This reverses the normal bottom-up signal direction. The
|
|
25
|
+
* fan-out is why `TargetTerm` subtrees are excluded from diamond collapsing —
|
|
26
|
+
* a plain compiled predicate cannot replicate this per-source walk.
|
|
27
|
+
*/
|
|
28
|
+
export declare class TargetTerm extends Term {
|
|
29
|
+
private readonly _relationshipEid;
|
|
30
|
+
private readonly _innerTerm;
|
|
31
|
+
constructor(world: World, key: string, _relationshipEid: number, relationshipTerm: Term, _innerTerm: Term, onDestroy: (term: Term) => void);
|
|
32
|
+
/**
|
|
33
|
+
* Build a `TargetTerm` for the given relationship eid and inner DSL.
|
|
34
|
+
*
|
|
35
|
+
* @param world - Owning world.
|
|
36
|
+
* @param relationshipEid - Numeric eid of the relationship component type.
|
|
37
|
+
* @param innerDSL - DSL expression the relationship target must satisfy.
|
|
38
|
+
* @param key - Canonical cache key.
|
|
39
|
+
* @param getTerm - Term canonicalizer / cache accessor.
|
|
40
|
+
* @param onDestroy - Called when the last consumer unsubscribes.
|
|
41
|
+
*/
|
|
42
|
+
static build(world: World, relationshipEid: number, innerDSL: QueryDSL, key: string, getTerm: typeof GetTermFn, onDestroy: (term: Term) => void): TargetTerm;
|
|
43
|
+
}
|