@vworlds/vecs 1.0.9 → 1.0.11

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 (46) hide show
  1. package/.husky/pre-commit +1 -0
  2. package/README.md +218 -229
  3. package/dist/command.d.ts +1 -0
  4. package/dist/command.js +2 -0
  5. package/dist/command.js.map +1 -0
  6. package/dist/component.d.ts +51 -59
  7. package/dist/component.js +31 -25
  8. package/dist/component.js.map +1 -1
  9. package/dist/dsl.d.ts +34 -26
  10. package/dist/dsl.js +46 -20
  11. package/dist/dsl.js.map +1 -1
  12. package/dist/entity.d.ts +110 -127
  13. package/dist/entity.js +323 -164
  14. package/dist/entity.js.map +1 -1
  15. package/dist/filter.d.ts +31 -23
  16. package/dist/filter.js +41 -32
  17. package/dist/filter.js.map +1 -1
  18. package/dist/index.d.ts +1 -1
  19. package/dist/package.json +3 -1
  20. package/dist/phase.d.ts +5 -28
  21. package/dist/phase.js +11 -10
  22. package/dist/phase.js.map +1 -1
  23. package/dist/query.d.ts +128 -94
  24. package/dist/query.js +254 -145
  25. package/dist/query.js.map +1 -1
  26. package/dist/system.d.ts +64 -128
  27. package/dist/system.js +156 -149
  28. package/dist/system.js.map +1 -1
  29. package/dist/util/array_map.d.ts +4 -55
  30. package/dist/util/array_map.js +35 -37
  31. package/dist/util/array_map.js.map +1 -1
  32. package/dist/util/bitset.d.ts +40 -50
  33. package/dist/util/bitset.js +76 -62
  34. package/dist/util/bitset.js.map +1 -1
  35. package/dist/util/events.d.ts +14 -18
  36. package/dist/util/events.js +24 -3
  37. package/dist/util/events.js.map +1 -1
  38. package/dist/util/ordered_set.d.ts +1 -17
  39. package/dist/util/ordered_set.js +74 -25
  40. package/dist/util/ordered_set.js.map +1 -1
  41. package/dist/world.d.ts +222 -201
  42. package/dist/world.js +394 -323
  43. package/dist/world.js.map +1 -1
  44. package/eslint-rules/internal-underscore.js +60 -0
  45. package/eslint.config.js +5 -0
  46. package/package.json +3 -1
package/dist/filter.d.ts CHANGED
@@ -5,49 +5,57 @@ import { type MaybeRequired, type QueryDSL } from "./dsl.js";
5
5
  /**
6
6
  * A non-reactive, one-shot entity filter.
7
7
  *
8
- * Unlike {@link Query}, a `Filter` holds no tracked entity set and registers
9
- * nothing with the world. Every {@link forEach} call walks all world entities
10
- * and invokes the callback for those that match the predicate captured at
11
- * construction time.
8
+ * Unlike {@link Query} a `Filter` keeps no tracked entity set and registers
9
+ * nothing on the world. Each call to {@link forEach} walks all current world
10
+ * entities and invokes the callback on those that match the predicate captured
11
+ * at construction time.
12
12
  *
13
- * Create via {@link World.filter}:
13
+ * Create one through {@link World.filter}:
14
14
  *
15
15
  * ```ts
16
16
  * const f = world.filter([Position, Velocity]);
17
17
  *
18
- * // Entity only:
18
+ * // Iterate matching entities:
19
19
  * f.forEach((e) => console.log(e.eid));
20
20
  *
21
- * // With component injection:
21
+ * // ...or with component injection:
22
22
  * f.forEach([Position, Velocity], (e, [pos, vel]) => {
23
23
  * pos.x += vel.vx;
24
24
  * });
25
25
  * ```
26
26
  *
27
- * ### Type parameter `R`
28
- * Tracks which component classes are guaranteed present on every matched
29
- * entity inferred automatically from the DSL by {@link World.filter}, or
30
- * supplied manually via the optional `_guaranteed` argument. Components in `R`
31
- * appear as non-nullable in `forEach` callback tuples.
27
+ * `forEach` runs the callback inside a {@link World.defer | deferred scope}, so
28
+ * mutations made by the callback are batched and become visible after iteration
29
+ * finishes. Nesting a `forEach` inside an already-deferred block (a system, a
30
+ * `Query.forEach`, an outer `defer`) inherits the outer scope and does not
31
+ * drain on exit.
32
+ *
33
+ * @typeParam R - Component classes guaranteed present on every matched entity.
34
+ * Inferred from the DSL by {@link World.filter} when possible, or supplied
35
+ * manually via the `_guaranteed` argument. Components in `R` are non-nullable
36
+ * in `forEach` callback tuples.
32
37
  */
33
38
  export declare class Filter<R extends (typeof Component)[] = []> {
34
- private readonly world;
35
- private readonly belongs;
36
- constructor(world: World, dsl: QueryDSL);
39
+ /** World this filter reads entities from. */
40
+ readonly world: World;
41
+ private readonly _belongs;
42
+ constructor(
43
+ /** World this filter reads entities from. */
44
+ world: World, dsl: QueryDSL);
37
45
  /**
38
- * Iterate all world entities and call `callback` for each one that matches
39
- * the DSL this filter was created with.
46
+ * Walk all current world entities and call `callback` for each one that
47
+ * satisfies the filter's DSL.
40
48
  *
41
- * @param callback - Receives only the entity.
49
+ * @param callback - Receives only the matching entity.
42
50
  */
43
51
  forEach(callback: (e: Entity) => void): void;
44
52
  /**
45
- * Iterate all world entities and call `callback` for each one that matches
46
- * the DSL, with component injection.
53
+ * Walk all current world entities, call `callback` for each one that
54
+ * satisfies the filter's DSL, and inject the requested component instances.
47
55
  *
48
- * Components declared via {@link World.filter}'s DSL (or `_guaranteed`) are
49
- * non-nullable in the resolved tuple; any other requested component may be
50
- * `undefined` if the entity lacks it.
56
+ * Components covered by the filter's DSL or `_guaranteed` hint are
57
+ * non-nullable in the resolved tuple; any other component class may be
58
+ * `undefined` if the entity does not have it.
51
59
  *
52
60
  * @param components - Component classes to resolve from each matching entity.
53
61
  * @param callback - Receives the entity and a tuple of resolved component
package/dist/filter.js CHANGED
@@ -1,55 +1,64 @@
1
- import { buildEntityTest } from "./dsl.js";
1
+ import { _buildEntityTest } from "./dsl.js";
2
2
  /**
3
3
  * A non-reactive, one-shot entity filter.
4
4
  *
5
- * Unlike {@link Query}, a `Filter` holds no tracked entity set and registers
6
- * nothing with the world. Every {@link forEach} call walks all world entities
7
- * and invokes the callback for those that match the predicate captured at
8
- * construction time.
5
+ * Unlike {@link Query} a `Filter` keeps no tracked entity set and registers
6
+ * nothing on the world. Each call to {@link forEach} walks all current world
7
+ * entities and invokes the callback on those that match the predicate captured
8
+ * at construction time.
9
9
  *
10
- * Create via {@link World.filter}:
10
+ * Create one through {@link World.filter}:
11
11
  *
12
12
  * ```ts
13
13
  * const f = world.filter([Position, Velocity]);
14
14
  *
15
- * // Entity only:
15
+ * // Iterate matching entities:
16
16
  * f.forEach((e) => console.log(e.eid));
17
17
  *
18
- * // With component injection:
18
+ * // ...or with component injection:
19
19
  * f.forEach([Position, Velocity], (e, [pos, vel]) => {
20
20
  * pos.x += vel.vx;
21
21
  * });
22
22
  * ```
23
23
  *
24
- * ### Type parameter `R`
25
- * Tracks which component classes are guaranteed present on every matched
26
- * entity inferred automatically from the DSL by {@link World.filter}, or
27
- * supplied manually via the optional `_guaranteed` argument. Components in `R`
28
- * appear as non-nullable in `forEach` callback tuples.
24
+ * `forEach` runs the callback inside a {@link World.defer | deferred scope}, so
25
+ * mutations made by the callback are batched and become visible after iteration
26
+ * finishes. Nesting a `forEach` inside an already-deferred block (a system, a
27
+ * `Query.forEach`, an outer `defer`) inherits the outer scope and does not
28
+ * drain on exit.
29
+ *
30
+ * @typeParam R - Component classes guaranteed present on every matched entity.
31
+ * Inferred from the DSL by {@link World.filter} when possible, or supplied
32
+ * manually via the `_guaranteed` argument. Components in `R` are non-nullable
33
+ * in `forEach` callback tuples.
29
34
  */
30
35
  export class Filter {
31
- constructor(world, dsl) {
36
+ constructor(
37
+ /** World this filter reads entities from. */
38
+ world, dsl) {
32
39
  this.world = world;
33
- this.belongs = buildEntityTest(world, dsl);
40
+ this._belongs = _buildEntityTest(world, dsl);
34
41
  }
35
42
  forEach(componentsOrCallback, callback) {
36
- if (typeof componentsOrCallback === "function") {
37
- this.world._forEachEntity((e) => {
38
- if (this.belongs(e)) {
39
- componentsOrCallback(e);
40
- }
41
- });
42
- }
43
- else {
44
- const types = componentsOrCallback.map((C) => this.world.getComponentType(C));
45
- this.world._forEachEntity((e) => {
46
- if (!this.belongs(e)) {
47
- return;
48
- }
49
- const resolved = types.map((t) => e.get(t));
50
- callback(e, resolved);
51
- });
52
- }
43
+ this.world.defer(() => {
44
+ if (typeof componentsOrCallback === "function") {
45
+ this.world.entities.forEach((e) => {
46
+ if (this._belongs(e)) {
47
+ componentsOrCallback(e);
48
+ }
49
+ });
50
+ }
51
+ else {
52
+ const types = componentsOrCallback.map((C) => this.world.getComponentType(C));
53
+ this.world.entities.forEach((e) => {
54
+ if (!this._belongs(e)) {
55
+ return;
56
+ }
57
+ const resolved = types.map((t) => e.get(t));
58
+ callback(e, resolved);
59
+ });
60
+ }
61
+ });
53
62
  }
54
63
  }
55
64
  //# sourceMappingURL=filter.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"filter.js","sourceRoot":"","sources":["../src/filter.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,eAAe,EAA0D,MAAM,UAAU,CAAC;AAEnG;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,MAAM,OAAO,MAAM;IAGjB,YACmB,KAAY,EAC7B,GAAa;QADI,UAAK,GAAL,KAAK,CAAO;QAG7B,IAAI,CAAC,OAAO,GAAG,eAAe,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;IAC7C,CAAC;IA2BM,OAAO,CACZ,oBAA6D,EAC7D,QAAoF;QAEpF,IAAI,OAAO,oBAAoB,KAAK,UAAU,EAAE;YAC9C,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC,CAAC,EAAE,EAAE;gBAC9B,IAAI,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE;oBACnB,oBAAoB,CAAC,CAAC,CAAC,CAAC;iBACzB;YACH,CAAC,CAAC,CAAC;SACJ;aAAM;YACL,MAAM,KAAK,GAAG,oBAAoB,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAAC;YAC9E,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC,CAAC,EAAE,EAAE;gBAC9B,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE;oBACpB,OAAO;iBACR;gBACD,MAAM,QAAQ,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;gBAC5C,QAAS,CAAC,CAAC,EAAE,QAAe,CAAC,CAAC;YAChC,CAAC,CAAC,CAAC;SACJ;IACH,CAAC;CACF"}
1
+ {"version":3,"file":"filter.js","sourceRoot":"","sources":["../src/filter.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,gBAAgB,EAA0D,MAAM,UAAU,CAAC;AAEpG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,MAAM,OAAO,MAAM;IAGjB;IACE,6CAA6C;IAC7B,KAAY,EAC5B,GAAa;QADG,UAAK,GAAL,KAAK,CAAO;QAG5B,IAAI,CAAC,QAAQ,GAAG,gBAAgB,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;IAC/C,CAAC;IA2BM,OAAO,CACZ,oBAA6D,EAC7D,QAAoF;QAEpF,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,EAAE;YACpB,IAAI,OAAO,oBAAoB,KAAK,UAAU,EAAE;gBAC9C,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE;oBAChC,IAAI,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE;wBACpB,oBAAoB,CAAC,CAAC,CAAC,CAAC;qBACzB;gBACH,CAAC,CAAC,CAAC;aACJ;iBAAM;gBACL,MAAM,KAAK,GAAG,oBAAoB,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAAC;gBAC9E,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE;oBAChC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE;wBACrB,OAAO;qBACR;oBACD,MAAM,QAAQ,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;oBAC5C,QAAS,CAAC,CAAC,EAAE,QAAe,CAAC,CAAC;gBAChC,CAAC,CAAC,CAAC;aACJ;QACH,CAAC,CAAC,CAAC;IACL,CAAC;CACF"}
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export { type System } from "./system.js";
1
+ export { type System, type SystemQuery } from "./system.js";
2
2
  export { Query } from "./query.js";
3
3
  export { World } from "./world.js";
4
4
  export { Filter } from "./filter.js";
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vworlds/vecs",
3
- "version": "1.0.9",
3
+ "version": "1.0.11",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "type": "module",
@@ -12,6 +12,8 @@
12
12
  "test": "vitest run",
13
13
  "test:watch": "vitest",
14
14
  "lint": "eslint src/ tests/",
15
+ "typecheck": "tsc --noEmit",
16
+ "format:check": "prettier --check \"**/*.{ts,md}\"",
15
17
  "prepare": "husky"
16
18
  },
17
19
  "devDependencies": {
package/dist/phase.d.ts CHANGED
@@ -1,32 +1,9 @@
1
- import { type System } from "./system.js";
2
1
  import { type World } from "./world.js";
3
2
  /**
4
- * A named, ordered bucket of {@link System | systems} within the world's
5
- * update pipeline.
3
+ * Public interface for a pipeline phase, returned by {@link World.addPhase}.
6
4
  *
7
- * Created internally by {@link World.addPhase}. The systems in a phase run in
8
- * the order they were registered. Between each system run the world flushes
9
- * pending archetype changes, so `enter` / `exit` callbacks are always
10
- * delivered before the next system executes.
11
- *
12
- * @internal The concrete class is not part of the public API. Use
13
- * {@link IPhase} to refer to phases in user code.
14
- */
15
- export declare class Phase {
16
- /** Name used to look up the phase in the pipeline. */
17
- readonly name: string;
18
- world: World;
19
- /** Systems that belong to this phase, in execution order. */
20
- systems: System[];
21
- constructor(
22
- /** Name used to look up the phase in the pipeline. */
23
- name: string, world: World);
24
- }
25
- /**
26
- * Public interface for a pipeline phase returned by {@link World.addPhase}.
27
- *
28
- * Pass an `IPhase` to {@link System.phase} to assign a system to that phase,
29
- * or to {@link World.runPhase} to execute it:
5
+ * Pass an `IPhase` to {@link System.phase} to assign a system to the phase, or
6
+ * to {@link World.runPhase} to execute the phase:
30
7
  *
31
8
  * ```ts
32
9
  * const preUpdate = world.addPhase("preupdate");
@@ -40,8 +17,8 @@ export declare class Phase {
40
17
  * ```
41
18
  */
42
19
  export interface IPhase {
43
- /** The name this phase was registered under. */
20
+ /** Name this phase was registered under. */
44
21
  get name(): string;
45
- /** The world that owns this phase. */
22
+ /** World that owns this phase. */
46
23
  get world(): World;
47
24
  }
package/dist/phase.js CHANGED
@@ -1,22 +1,23 @@
1
1
  /**
2
- * A named, ordered bucket of {@link System | systems} within the world's
3
- * update pipeline.
2
+ * Concrete implementation of {@link IPhase}: a named, ordered bucket of
3
+ * {@link System | systems} within a world's update pipeline.
4
4
  *
5
- * Created internally by {@link World.addPhase}. The systems in a phase run in
6
- * the order they were registered. Between each system run the world flushes
7
- * pending archetype changes, so `enter` / `exit` callbacks are always
8
- * delivered before the next system executes.
5
+ * Created by {@link World.addPhase}. Systems run in the order they were added
6
+ * to the phase. Between systems the world drains pending commands so each
7
+ * system observes a consistent view of the world.
9
8
  *
10
- * @internal The concrete class is not part of the public API. Use
11
- * {@link IPhase} to refer to phases in user code.
9
+ * @internal The class itself is not part of the public API; user code should
10
+ * refer to phases via {@link IPhase}.
12
11
  */
13
12
  export class Phase {
14
13
  constructor(
15
14
  /** Name used to look up the phase in the pipeline. */
16
- name, world) {
15
+ name,
16
+ /** World that owns this phase. */
17
+ world) {
17
18
  this.name = name;
18
19
  this.world = world;
19
- /** Systems that belong to this phase, in execution order. */
20
+ /** Systems registered in this phase, in execution order. */
20
21
  this.systems = [];
21
22
  }
22
23
  }
package/dist/phase.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"phase.js","sourceRoot":"","sources":["../src/phase.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;GAWG;AACH,MAAM,OAAO,KAAK;IAIhB;IACE,sDAAsD;IACtC,IAAY,EACrB,KAAY;QADH,SAAI,GAAJ,IAAI,CAAQ;QACrB,UAAK,GAAL,KAAK,CAAO;QANrB,6DAA6D;QACtD,YAAO,GAAa,EAAE,CAAC;IAM3B,CAAC;CACL"}
1
+ {"version":3,"file":"phase.js","sourceRoot":"","sources":["../src/phase.ts"],"names":[],"mappings":"AA2BA;;;;;;;;;;GAUG;AACH,MAAM,OAAO,KAAK;IAIhB;IACE,sDAAsD;IACtC,IAAY;IAC5B,kCAAkC;IAClB,KAAY;QAFZ,SAAI,GAAJ,IAAI,CAAQ;QAEZ,UAAK,GAAL,KAAK,CAAO;QAP9B,4DAA4D;QACrD,YAAO,GAAa,EAAE,CAAC;IAO3B,CAAC;CACL"}
package/dist/query.d.ts CHANGED
@@ -3,81 +3,87 @@ import type { Entity } from "./entity.js";
3
3
  import { type World } from "./world.js";
4
4
  import { type EntityTestFunc, type QueryDSL, type MaybeRequired } from "./dsl.js";
5
5
  export type { EntityTestFunc, QueryDSL, MaybeRequired };
6
- type EntityCallback = (e: Entity) => void;
6
+ /** Component class, or `{ parent: ComponentClass }` to resolve from the entity's parent. */
7
7
  type ComponentOrParent = typeof Component | {
8
8
  parent: typeof Component;
9
9
  };
10
+ /** Resolves the component instance type for one element of a `ComponentOrParent` tuple. */
10
11
  type ComponentInstance<T> = T extends {
11
12
  parent: typeof Component;
12
13
  } ? InstanceType<T["parent"]> : T extends typeof Component ? InstanceType<T> : never;
13
14
  /**
14
- * A reactive, always-updated list of entities that match a given query
15
- * expression.
15
+ * A reactive, always-up-to-date set of entities matching a {@link QueryDSL}
16
+ * predicate.
16
17
  *
17
- * `Query` tracks every entity whose component set satisfies the predicate set
18
- * via {@link requires} or {@link query}. Entry and exit callbacks fire
19
- * automatically as entities gain or lose matching components. The tracked set
20
- * is exposed via {@link entities} and {@link forEach}.
18
+ * `Query` listens to entity / component mutations through the world's command
19
+ * queue and tracks which entities currently satisfy its predicate. It fires
20
+ * `enter`, `exit`, and `update` callbacks as the matched set changes. The
21
+ * tracked set is exposed via {@link entities} and {@link forEach}.
21
22
  *
22
- * {@link System} extends `Query` and adds per-tick runtime concerns
23
- * (`update`, `each`, `run`). Use `Query` directly when you only need the
24
- * reactive entity set without pipeline integration.
23
+ * Callbacks fire **synchronously** when the world routes a command — so
24
+ * mutations made inside one of these callbacks are themselves observed
25
+ * immediately by other queries / systems.
25
26
  *
26
- * ### Type parameter `R`
27
- * Tracks which component classes are "required" (declared via {@link requires}
28
- * or the `_guaranteed` hint of {@link query}). Those components appear as
29
- * non-nullable in {@link sort} callbacks.
27
+ * {@link System} extends `Query` and queues callbacks into an inbox replayed
28
+ * during `_run` instead of firing them immediately. Use `Query` directly when
29
+ * you want a reactive entity set without pipeline integration.
30
+ *
31
+ * @typeParam R - Component classes guaranteed present on every matched entity
32
+ * (declared via {@link requires} or the `_guaranteed` hint of {@link query}).
33
+ * Components in `R` appear as non-nullable in {@link sort}, {@link forEach},
34
+ * and {@link update} callback tuples.
30
35
  */
31
36
  export declare class Query<R extends (typeof Component)[] = []> {
32
- /** Unique name for this query, used in logs and debug output. */
37
+ /** Unique display name; appears in logs and debug output. */
33
38
  readonly name: string;
34
- /** The world that owns this query. */
39
+ /** World that owns this query. */
35
40
  readonly world: World;
36
- protected _entities: Set<Entity> | undefined;
37
- protected _enterCallback: EntityCallback[];
38
- protected _exitCallback: EntityCallback[];
39
- protected _belongs: EntityTestFunc;
40
- protected hasQuery: boolean;
41
41
  constructor(
42
- /** Unique name for this query, used in logs and debug output. */
42
+ /** Unique display name; appears in logs and debug output. */
43
43
  name: string,
44
- /** The world that owns this query. */
44
+ /** World that owns this query. */
45
45
  world: World, track?: boolean);
46
+ /**
47
+ * Read-only view of the entities currently tracked by this query.
48
+ *
49
+ * Populated as entities enter and removed as they exit. Empty unless
50
+ * tracking is enabled — that is the default for standalone queries created
51
+ * via {@link World.query}, but {@link System} requires an explicit
52
+ * {@link track}, {@link sort}, or `each` call.
53
+ */
54
+ get entities(): ReadonlySet<Entity>;
46
55
  /** Returns the query name. */
47
56
  toString(): string;
48
57
  /**
49
58
  * Enable entity tracking: matched entities are inserted into {@link entities}
50
59
  * as they enter and removed as they exit.
51
60
  *
52
- * Idempotent. When called after {@link World.start}, immediately backfills all
53
- * existing entities that currently satisfy the query predicate.
61
+ * Idempotent. When called after {@link World.start}, immediately backfills
62
+ * every existing entity that satisfies the current predicate.
54
63
  *
55
- * @returns `this` for chaining.
64
+ * @returns This query, for chaining.
56
65
  */
57
66
  track(): this;
58
- private backfill;
67
+ /** Returns `true` when `e` satisfies this query's predicate. */
68
+ belongs(e: Entity): boolean;
59
69
  /**
60
- * Read-only view of the entities currently tracked by this query.
70
+ * Iterate every entity currently tracked by this query.
61
71
  *
62
- * Populated as entities enter (and removed as they exit). Empty unless
63
- * tracking is enabled (default for standalone queries; requires an explicit
64
- * {@link track} call on {@link System | systems}).
65
- */
66
- get entities(): ReadonlySet<Entity>;
67
- /**
68
- * Iterate over every entity currently tracked by this query.
72
+ * Mutations made by `callback` are buffered into the world command queue
73
+ * and only become visible after iteration finishes. Nested iteration inside
74
+ * an already-deferred context (a system, an outer `forEach`) inherits the
75
+ * outer scope and does not drain on exit.
69
76
  *
70
- * @param callback - Called once per tracked entity, in insertion order
77
+ * @param callback - Invoked once per tracked entity, in insertion order
71
78
  * (or sort order when {@link sort} is configured).
72
79
  */
73
80
  forEach(callback: (e: Entity) => void): void;
74
81
  /**
75
- * Iterate over every entity currently tracked by this query, with component
76
- * injection.
82
+ * Iterate every tracked entity with component injection.
77
83
  *
78
- * Components declared via {@link requires} (or the `_guaranteed` hint of
79
- * {@link query}) are non-nullable in the resolved tuple; any other requested
80
- * component may be `undefined` if the entity lacks it.
84
+ * Components covered by {@link requires} (or the `_guaranteed` hint of
85
+ * {@link query}) are non-nullable in the resolved tuple; any other
86
+ * requested component may be `undefined` if the entity lacks it.
81
87
  *
82
88
  * @param components - Component classes to resolve from each entity.
83
89
  * @param callback - Receives the entity and a tuple of resolved component
@@ -86,22 +92,14 @@ export declare class Query<R extends (typeof Component)[] = []> {
86
92
  forEach<J extends (typeof Component)[]>(components: readonly [...J], callback: (e: Entity, resolved: {
87
93
  [K in keyof J]: MaybeRequired<J[K], R>;
88
94
  }) => void): void;
89
- /** Returns `true` if the entity satisfies this query's predicate. */
90
- belongs(e: Entity): boolean;
91
- /** Hook for subclasses — called when a component on an entity in this query changes. */
92
- notifyModified(_c: Component): void;
93
- /** @internal Fires enter callbacks and adds entity to the tracked set. */
94
- _enter(e: Entity): void;
95
- /** @internal Fires exit callbacks and removes entity from the tracked set. */
96
- _exit(e: Entity): void;
97
95
  /**
98
- * Register a callback that fires when an entity **enters** this query
99
- * (i.e. first satisfies the predicate) with injected components.
96
+ * Register a callback invoked when an entity **enters** this query (i.e.
97
+ * first satisfies the predicate), with injected components.
100
98
  *
101
99
  * @param inject - Ordered list of component classes (or `{ parent: C }`) to
102
100
  * resolve from the entering entity and pass to `callback`.
103
101
  * @param callback - Receives the entity and the resolved component tuple.
104
- * @returns `this` for chaining.
102
+ * @returns This query, for chaining.
105
103
  *
106
104
  * @example
107
105
  * ```ts
@@ -115,44 +113,81 @@ export declare class Query<R extends (typeof Component)[] = []> {
115
113
  [K in keyof J]: ComponentInstance<J[K]>;
116
114
  }) => void): this;
117
115
  /**
118
- * Register a callback that fires when an entity enters this query.
116
+ * Register an `enter` callback without component injection.
119
117
  *
120
- * @param callback - Receives only the entity (no injection).
121
- * @returns `this` for chaining.
118
+ * @param callback - Receives only the entering entity.
119
+ * @returns This query, for chaining.
122
120
  */
123
121
  enter(callback: (e: Entity) => void): this;
124
122
  /**
125
- * Register a callback that fires when an entity **exits** this query
126
- * (its components no longer satisfy the predicate, or it was destroyed) with
123
+ * Register a callback invoked when an entity **exits** this query (its
124
+ * archetype no longer satisfies the predicate, or it was destroyed), with
127
125
  * injected components.
128
126
  *
129
- * Components that were just removed are still accessible via `get_deleted`
130
- * semantics the injected tuple includes them even though they are no
131
- * longer in the entity's active component set.
127
+ * Components removed in the same frame as the exit are still resolvable
128
+ * because the runtime snapshots them at routing time.
132
129
  *
133
130
  * @param inject - Component classes to resolve and inject.
134
131
  * @param callback - Receives the entity and the resolved component tuple.
135
- * @returns `this` for chaining.
132
+ * @returns This query, for chaining.
136
133
  */
137
134
  exit<J extends ComponentOrParent[]>(inject: readonly [...J], callback: (e: Entity, injected: {
138
135
  [K in keyof J]: ComponentInstance<J[K]>;
139
136
  }) => void): this;
140
137
  /**
141
- * Register a callback that fires when an entity exits this query.
138
+ * Register an `exit` callback without component injection.
142
139
  *
143
- * @param callback - Receives only the entity.
144
- * @returns `this` for chaining.
140
+ * @param callback - Receives only the exiting entity.
141
+ * @returns This query, for chaining.
145
142
  */
146
143
  exit(callback: (e: Entity) => void): this;
147
144
  /**
148
- * Enable sorted entity tracking: matched entities are stored in insertion
149
- * order determined by `compare`, which receives a tuple of resolved
150
- * component instances for each pair of entities being ordered.
145
+ * Register a callback invoked when a component of `ComponentClass` is
146
+ * modified on a tracked entity.
147
+ *
148
+ * On a {@link Query} the callback fires **immediately** when the world
149
+ * routes the corresponding `Set` / `Modified` command. On a {@link System}
150
+ * the event is buffered in the inbox and the callback fires during the
151
+ * system's next `_run`.
152
+ *
153
+ * If no other predicate has been set on this query, the watchlist
154
+ * automatically expands so `ComponentClass` is implicitly required (the
155
+ * predicate becomes a `HAS` of every watched type).
156
+ *
157
+ * @param ComponentClass - Component class to watch.
158
+ * @param callback - Receives the modified component instance.
159
+ * @returns This query, for chaining.
160
+ *
161
+ * @example
162
+ * ```ts
163
+ * world.system("RenderPosition")
164
+ * .update(Position, (pos) => sprite.setPosition(pos.x, pos.y));
165
+ * ```
166
+ */
167
+ update<C extends typeof Component>(ComponentClass: C, callback: (c: InstanceType<C>) => void): this;
168
+ /**
169
+ * Like {@link update}, but with extra components injected from the same
170
+ * entity.
171
+ *
172
+ * @param ComponentClass - Component class to watch.
173
+ * @param inject - Additional component classes to resolve from the entity.
174
+ * @param callback - Receives the modified component and the injected tuple.
175
+ * @returns This query, for chaining.
176
+ */
177
+ update<C extends typeof Component, J extends (typeof Component)[]>(ComponentClass: C, inject: readonly [...J], callback: (c: InstanceType<C>, injected: {
178
+ [K in keyof J]: MaybeRequired<J[K], R>;
179
+ }) => void): this;
180
+ /**
181
+ * Switch the tracked set to a sorted ordering: matched entities are stored
182
+ * in the position determined by `compare`, which receives a tuple of
183
+ * resolved component instances for each pair being ordered.
184
+ *
185
+ * Implies {@link track}.
151
186
  *
152
187
  * @param components - Component classes to resolve and pass to `compare`.
153
- * @param compare - Returns a negative number, zero, or positive number when
154
- * `a` should sort before, equal to, or after `b`.
155
- * @returns `this` for chaining.
188
+ * @param compare - Negative when `a` should sort before `b`, zero for
189
+ * equality, positive when `a` should sort after `b`.
190
+ * @returns This query, for chaining.
156
191
  *
157
192
  * @example
158
193
  * ```ts
@@ -167,17 +202,18 @@ export declare class Query<R extends (typeof Component)[] = []> {
167
202
  [K in keyof J]: MaybeRequired<J[K], R>;
168
203
  }) => number): this;
169
204
  /**
170
- * Set the entity membership predicate using the {@link QueryDSL} DSL.
205
+ * Set the entity-membership predicate using a {@link QueryDSL} expression.
171
206
  *
172
- * Replaces any previous predicate. The optional `guaranteed` tuple is a
173
- * pure type-level hint: it tells {@link sort} callbacks which components are
174
- * guaranteed present on every matched entity, eliminating `| undefined` from
175
- * those positions. It has no effect at runtime.
207
+ * Replaces any previous predicate. The optional `_guaranteed` tuple is a
208
+ * pure type-level hint: it tells {@link sort} / {@link forEach} /
209
+ * {@link update} callbacks which components are guaranteed present on every
210
+ * matched entity, eliminating `| undefined` from those positions. It has no
211
+ * effect at runtime.
176
212
  *
177
- * @param q - A {@link QueryDSL} expression.
213
+ * @param q - Query expression.
178
214
  * @param _guaranteed - Component classes guaranteed present on every matched
179
215
  * entity (type hint only — not validated at runtime).
180
- * @returns `this` for chaining.
216
+ * @returns This query, retyped with the guaranteed tuple as its `R`.
181
217
  *
182
218
  * @example
183
219
  * ```ts
@@ -190,28 +226,26 @@ export declare class Query<R extends (typeof Component)[] = []> {
190
226
  */
191
227
  query<T extends (typeof Component)[] = []>(q: QueryDSL, _guaranteed?: readonly [...T]): Query<T>;
192
228
  /**
193
- * Remove this query from the world and all entities.
229
+ * Shorthand for `query([...components])`: track entities that have **all**
230
+ * of the listed component types.
194
231
  *
195
- * Every entity that currently belongs to this query has the query silently
196
- * removed (no exit callbacks are fired). After this call the query is
197
- * unregistered from its world and `world` is set to `undefined` by force.
232
+ * Equivalent to `query({ HAS: components })`. Unlike `query`, the listed
233
+ * components are also recorded in the type parameter `R`, so {@link sort}
234
+ * and {@link forEach} callbacks see them as non-nullable.
198
235
  *
199
- * Calling any method on the query after `destroy()` is **undefined behavior**.
236
+ * @param components - Component classes to require.
237
+ * @returns This query, retyped with the required tuple as its `R`.
200
238
  */
201
- destroy(): void;
239
+ requires<T extends (typeof Component)[]>(...components: [...T]): Query<T>;
202
240
  /**
203
- * Shorthand for `query([...components])` tracks entities that have
204
- * **all** of the listed component types.
241
+ * Permanently remove this query from the world.
205
242
  *
206
- * Equivalent to `query({ HAS: components })`. Unlike `query`, passing
207
- * component classes here also informs the types of {@link sort} callbacks:
208
- * listed components will be non-nullable in those tuples.
243
+ * Every entity that currently belongs to this query has the membership
244
+ * silently purged (no `exit` callbacks fire), the tracked set is cleared,
245
+ * and the `world` reference is forced to `undefined`. Calling any method on
246
+ * the query afterwards is **undefined behavior**.
209
247
  *
210
- * @param components - One or more component classes.
211
- * @returns `this` for chaining.
248
+ * Not supported on {@link System} calling it on a system throws.
212
249
  */
213
- requires<T extends (typeof Component)[]>(...components: [...T]): Query<T>;
214
- private getComponent;
215
- private getInjected;
216
- private mapInjectedClassToTypes;
250
+ destroy(): void;
217
251
  }