@vworlds/vecs 1.0.3 → 1.0.4

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 CHANGED
@@ -14,18 +14,20 @@ yarn add @vworlds/vecs
14
14
 
15
15
  | Concept | What it is |
16
16
  |---|---|
17
- | **World** | Central container. Owns all entities, runs all systems. |
17
+ | **World** | Central container. Owns all entities, runs all systems and queries. |
18
18
  | **Component** | A plain data class. Extend `Component` and attach instances to entities. |
19
19
  | **Entity** | An integer id with a set of components. Create via the world. |
20
- | **System** | Reactive logic. Declare which components you need; get called when things change. |
20
+ | **Query** | A reactive, always-updated set of entities that match a predicate. |
21
+ | **System** | A `Query` with per-tick runtime logic (phases, `update`, `each`, `run`). |
22
+ | **Filter** | A non-reactive, one-shot scan: walks all world entities on each `forEach` call. |
21
23
 
22
24
  ### Lifecycle in brief
23
25
 
24
26
  ```
25
- registerComponent() × N → system() × N → start() → progress() every frame
27
+ registerComponent() × N → system() / query() × N → start() → progress() every frame
26
28
  ```
27
29
 
28
- After `start()`, no new components or systems can be registered.
30
+ After `start()`, component registration is disabled. Systems and queries can still be created — standalone queries backfill existing matched entities immediately.
29
31
 
30
32
  ---
31
33
 
@@ -112,17 +114,12 @@ world.start(); // freeze registration, sort systems into phases
112
114
  // ─── Create entities ───────────────────────────────────────────────────────
113
115
 
114
116
  const bullet = world.createEntity();
115
- const pos = bullet.add(Position);
116
- pos.x = 0;
117
- pos.y = 0;
117
+ bullet.set(Position, { x: 0, y: 0 });
118
118
 
119
- const vel = bullet.add(Velocity);
120
- vel.vx = 5;
121
- vel.vy = 0;
119
+ const vel = bullet.set(Velocity, { vx: 5, vy: 0 });
122
120
  vel.modified(); // first update: notify Move system
123
121
 
124
- const hp = bullet.add(Health);
125
- hp.hp = 3;
122
+ const hp = bullet.set(Health, { hp: 3 });
126
123
  hp.modified();
127
124
 
128
125
  // ─── Game loop ─────────────────────────────────────────────────────────────
@@ -197,9 +194,54 @@ world.system("MySystem")
197
194
  .update(...)
198
195
  .exit(...);
199
196
 
200
- world.start(); // must be called once, after all systems are set up
197
+ world.start(); // distributes systems to phases, freezes component registration
201
198
  ```
202
199
 
200
+ #### Queries
201
+
202
+ A standalone `Query` is a reactive entity set without a phase or per-tick callbacks. Use it when you need the matched set kept up-to-date automatically — for example to enumerate scene nodes or find the nearest enemy.
203
+
204
+ ```ts
205
+ const enemies = world.query("Enemies")
206
+ .requires(Enemy, Health)
207
+ .enter((e) => console.log("enemy spawned", e.eid))
208
+ .exit((e) => console.log("enemy died", e.eid));
209
+
210
+ world.start();
211
+ // enemies.entities is kept up-to-date automatically
212
+
213
+ // Can also be created after start(); existing matched entities are backfilled:
214
+ const lateQuery = world.query("Walls").requires(Wall);
215
+ // lateQuery.entities immediately contains all current Wall entities
216
+ ```
217
+
218
+ #### Filters
219
+
220
+ A `Filter` is a non-reactive, one-shot scan. It holds no tracked entity set — each `forEach` call walks all world entities at that moment. Use it for ad-hoc lookups that don't need to stay live.
221
+
222
+ ```ts
223
+ // Entity only:
224
+ world.filter([Position]).forEach((e) => console.log(e.eid));
225
+
226
+ // With component injection:
227
+ world.filter([Position, Velocity])
228
+ .forEach([Position, Velocity], (e, [pos, vel]) => {
229
+ pos.x += vel.vx;
230
+ });
231
+
232
+ // Full DSL, with auto-deduced required components:
233
+ world.filter({ AND: [{ HAS: Position }, { HAS: Velocity }] })
234
+ .forEach([Position, Velocity], (e, [pos, vel]) => {
235
+ pos.x += vel.vx; // pos and vel are non-null — deduced from AND of HAS
236
+ });
237
+
238
+ // Manual type hint for queries the extractor can't see through:
239
+ world.filter({ OR: [Position, Velocity] }, [Position])
240
+ .forEach([Position], (e, [pos]) => pos.x);
241
+ ```
242
+
243
+ Unlike `Query`, a `Filter` requires no name, no `world.start()`, and no `destroy()` — create it anywhere and discard it freely.
244
+
203
245
  #### Phases
204
246
 
205
247
  ```ts
@@ -275,6 +317,7 @@ const e = world.createEntity();
275
317
  | `eid` | Unique numeric entity id. |
276
318
  | `world` | The `World` that owns this entity. |
277
319
  | `add(Class)` | Attach a component; returns the typed instance. Idempotent. |
320
+ | `set(Class, props)` | Like `add`, but also assigns the given partial properties onto the instance. Returns the typed instance. |
278
321
  | `get(Class)` | Return the component instance, or `undefined` if not present. |
279
322
  | `remove(Class)` | Detach a component (triggers `onRemove` hooks and `exit` callbacks). |
280
323
  | `destroy()` | Remove all components and unregister the entity. Recurses to children. |
@@ -436,7 +479,9 @@ Iterating `system.entities` after a phase run yields entities in the sorted orde
436
479
 
437
480
  #### `.track()`
438
481
 
439
- Enable entity tracking without an `each` callback — matched entities are exposed via `system.entities` as they enter and leave. `each` and `sort` imply `track` automatically; call this directly only when you need the set without a per-tick callback.
482
+ Enable entity tracking without an `each` callback — matched entities are exposed via `system.entities` (or `query.entities`) as they enter and leave. `each` and `sort` imply `track` automatically; call this directly only when you need the tracked set without a per-tick callback.
483
+
484
+ When called after `world.start()`, `track()` immediately backfills existing entities that satisfy the query predicate.
440
485
 
441
486
  #### `.run(callback)`
442
487
 
@@ -450,6 +495,82 @@ Called every tick when the system's phase runs, regardless of entity state. Use
450
495
 
451
496
  ---
452
497
 
498
+ ### Query
499
+
500
+ A standalone query is created via `world.query(name)` and configured through the same fluent builder API as `System` (`requires`, `query`, `enter`, `exit`, `sort`, `track`, `forEach`, `entities`). It has no phase and no per-tick callbacks.
501
+
502
+ ```ts
503
+ const projectiles = world.query("Projectiles")
504
+ .requires(Position, Velocity)
505
+ .sort([Position], ([a], [b]) => a.z - b.z)
506
+ .enter([Position], (e, [pos]) => { pos.x = spawnX; });
507
+
508
+ world.start();
509
+
510
+ // Anywhere in game code:
511
+ projectiles.forEach((e) => { /* ... */ });
512
+ console.log(projectiles.entities.size, "active projectiles");
513
+ ```
514
+
515
+ | Method | Description |
516
+ |---|---|
517
+ | `.requires(...components)` | Set the membership predicate and start tracking. |
518
+ | `.query(expr)` | Set the membership predicate using the {@link SystemQuery} DSL. |
519
+ | `.enter(callback)` / `.enter(inject, callback)` | Fires when an entity joins the query. |
520
+ | `.exit(callback)` / `.exit(inject, callback)` | Fires when an entity leaves the query. |
521
+ | `.sort(components, compare)` | Store matched entities in sorted order. |
522
+ | `.track()` | Enable tracking (implied by `sort`; backfills when called after `start`). |
523
+ | `.belongs(e)` | Returns `true` if the entity satisfies the predicate. |
524
+ | `.forEach(callback)` | Iterate all currently tracked entities (entity only). |
525
+ | `.forEach(components, callback)` | Iterate with component injection — same signature as `Filter.forEach`. |
526
+ | `.entities` | `ReadonlySet<Entity>` of all currently tracked entities. |
527
+ | `.destroy()` | Remove the query from the world and all entities. See below. |
528
+
529
+ #### `.destroy()`
530
+
531
+ Permanently removes a standalone query from the world. All entity references are silently purged (no exit callbacks fire), the tracked entity set is cleared, and the query's `world` reference is set to `undefined`. After this call, any use of the query object is **undefined behavior**.
532
+
533
+ ```ts
534
+ const q = world.query("Temporary").requires(Position);
535
+ // ... use q.entities ...
536
+ q.destroy(); // unregisters from world and all entities
537
+ ```
538
+
539
+ `System` does **not** support `destroy()` — calling it throws. Systems are owned by the world for the lifetime of the session. Use a standalone `Query` when you need a temporary reactive set.
540
+
541
+ Both `System` and `Query` share the same query DSL, enter/exit callbacks, sort, and `entities` set — `System` extends `Query` and layers phase execution on top.
542
+
543
+ ---
544
+
545
+ ### Filter
546
+
547
+ A `Filter` is created via `world.filter(dsl)` and provides a non-reactive `forEach`. It accepts the same [`QueryDSL`](#-requirescomponents-and-queryq) expressions as systems and queries.
548
+
549
+ ```ts
550
+ const f = world.filter([Position, Velocity]);
551
+ ```
552
+
553
+ | Method | Description |
554
+ |---|---|
555
+ | `.forEach(callback)` | Walk all world entities; invoke callback for each matching one. |
556
+ | `.forEach(components, callback)` | Same, with component injection and non-null types for required components. |
557
+
558
+ **Type inference** works the same way as for `requires()` on systems/queries: component classes extractable from the DSL (`HAS`, `HAS_ONLY`, plain arrays, and `AND` of those) are non-nullable in the callback tuple. Pass a `_guaranteed` second argument to `world.filter()` as a manual override when inference can't reach:
559
+
560
+ ```ts
561
+ // Auto-deduced — both non-null:
562
+ world.filter([Position, Velocity])
563
+ .forEach([Position, Velocity], (e, [pos, vel]) => { ... });
564
+
565
+ // Manual hint for OR / NOT / PARENT / custom function:
566
+ world.filter({ OR: [Position, Velocity] }, [Position])
567
+ .forEach([Position], (e, [pos]) => pos.x);
568
+ ```
569
+
570
+ A `Filter` holds no tracked set, makes no registration calls, and needs no `destroy()`.
571
+
572
+ ---
573
+
453
574
  ## Build & Test
454
575
 
455
576
  ```
package/dist/dsl.d.ts ADDED
@@ -0,0 +1,71 @@
1
+ import { Component, ComponentClassArray, ComponentClassOrType } from "./component.js";
2
+ import type { Entity } from "./entity.js";
3
+ import type { World } from "./world.js";
4
+ /** A function that tests whether a given entity belongs to a query. */
5
+ export type EntityTestFunc = (e: Entity) => boolean;
6
+ /**
7
+ * A composable query expression used to declare which entities a
8
+ * {@link Query} or {@link System} should track.
9
+ *
10
+ * Queries can be nested arbitrarily:
11
+ *
12
+ * ```ts
13
+ * // Entities that have Position AND (Sprite OR Container):
14
+ * world.system("render").query({
15
+ * AND: [Position, { OR: [Sprite, Container] }]
16
+ * });
17
+ *
18
+ * // Entities that have a parent with Player AND Container:
19
+ * world.system("attach").query({
20
+ * PARENT: { AND: [Player, Container] }
21
+ * });
22
+ * ```
23
+ *
24
+ * Short forms:
25
+ * - A single class or type id is equivalent to `{ HAS: [C] }`.
26
+ * - An array `[A, B]` is equivalent to `{ HAS: [A, B] }`.
27
+ * - Pass an {@link EntityTestFunc} directly for fully custom membership logic.
28
+ */
29
+ export type QueryDSL = ComponentClassArray | ComponentClassOrType | EntityTestFunc | {
30
+ HAS: ComponentClassArray | ComponentClassOrType;
31
+ } | {
32
+ HAS_ONLY: ComponentClassArray | ComponentClassOrType;
33
+ } | {
34
+ AND: readonly QueryDSL[];
35
+ } | {
36
+ OR: readonly QueryDSL[];
37
+ } | {
38
+ NOT: QueryDSL;
39
+ } | {
40
+ PARENT: QueryDSL;
41
+ };
42
+ export declare function HAS(world: World, ...components: ComponentClassArray): EntityTestFunc;
43
+ /**
44
+ * Resolves component nullability based on what was declared in `requires` (or
45
+ * the `_guaranteed` hint). Components in `R` are non-nullable; others are
46
+ * `InstanceType<C> | undefined`.
47
+ */
48
+ export type MaybeRequired<C, R extends (typeof Component)[]> = C extends typeof Component ? C extends R[number] ? InstanceType<C> : InstanceType<C> | undefined : never;
49
+ /**
50
+ * Statically extracts the component classes that are **guaranteed present** on
51
+ * every entity matched by a {@link QueryDSL} expression.
52
+ *
53
+ * Rules:
54
+ * - Plain class `C` → `[C]`
55
+ * - Plain array `[A, B]` → `[A, B]`
56
+ * - `{HAS: ...}` / `{HAS_ONLY: ...}` → recurse into the payload
57
+ * - `{AND: [q1, q2, ...]}` → concatenation of each branch's extraction
58
+ * - `{OR: ...}` / `{NOT: ...}` / `{PARENT: ...}` → `[]` (no guarantee)
59
+ * - `EntityTestFunc` / numeric type id → `[]` (opaque)
60
+ */
61
+ export type ExtractRequired<Q> = Q extends typeof Component ? [Q] : Q extends readonly (typeof Component)[] ? Q : Q extends {
62
+ HAS: infer H;
63
+ } ? ExtractRequired<H> : Q extends {
64
+ HAS_ONLY: infer H;
65
+ } ? ExtractRequired<H> : Q extends {
66
+ AND: infer A extends readonly QueryDSL[];
67
+ } ? ExtractAndChain<A> : [];
68
+ type ExtractAndChain<A extends readonly QueryDSL[]> = A extends readonly [infer First, ...infer Rest extends readonly QueryDSL[]] ? [...ExtractRequired<First>, ...ExtractAndChain<Rest>] : [];
69
+ /** Convert a {@link QueryDSL} expression into a runtime entity-test predicate. */
70
+ export declare function buildEntityTest(world: World, q: QueryDSL): EntityTestFunc;
71
+ export {};
package/dist/dsl.js ADDED
@@ -0,0 +1,58 @@
1
+ import { Component, calculateComponentBitmask, } from "./component.js";
2
+ export function HAS(world, ...components) {
3
+ const testBitmask = calculateComponentBitmask(components, world);
4
+ return (e) => e.componentBitmask.hasBitset(testBitmask);
5
+ }
6
+ function HAS_ONLY(world, ...components) {
7
+ const testBitmask = calculateComponentBitmask(components, world);
8
+ return (e) => e.componentBitmask.equal(testBitmask);
9
+ }
10
+ function NOT(func) {
11
+ return (e) => !func(e);
12
+ }
13
+ function AND(...funcs) {
14
+ return (e) => funcs.every((f) => f(e));
15
+ }
16
+ function OR(...funcs) {
17
+ return (e) => funcs.some((f) => f(e));
18
+ }
19
+ function PARENT(func) {
20
+ return (e) => (e.parent && func(e.parent)) || false;
21
+ }
22
+ /** Convert a {@link QueryDSL} expression into a runtime entity-test predicate. */
23
+ export function buildEntityTest(world, q) {
24
+ if (typeof q === "number" ||
25
+ (typeof q === "function" && q.prototype instanceof Component)) {
26
+ return HAS(world, q);
27
+ }
28
+ else if (typeof q === "function") {
29
+ return q;
30
+ }
31
+ if (q instanceof Array) {
32
+ return HAS(world, ...q);
33
+ }
34
+ if ("HAS" in q) {
35
+ return buildEntityTest(world, q.HAS);
36
+ }
37
+ if ("HAS_ONLY" in q) {
38
+ const v = q.HAS_ONLY;
39
+ if (v instanceof Array) {
40
+ return HAS_ONLY(world, ...v);
41
+ }
42
+ return HAS_ONLY(world, v);
43
+ }
44
+ if ("AND" in q) {
45
+ return AND(...q.AND.map((sq) => buildEntityTest(world, sq)));
46
+ }
47
+ if ("OR" in q) {
48
+ return OR(...q.OR.map((sq) => buildEntityTest(world, sq)));
49
+ }
50
+ if ("NOT" in q) {
51
+ return NOT(buildEntityTest(world, q.NOT));
52
+ }
53
+ if ("PARENT" in q) {
54
+ return PARENT(buildEntityTest(world, q.PARENT));
55
+ }
56
+ throw "Unrecognized query term";
57
+ }
58
+ //# sourceMappingURL=dsl.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dsl.js","sourceRoot":"","sources":["../src/dsl.ts"],"names":[],"mappings":"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"}
package/dist/entity.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { Component } from "./component.js";
2
2
  import type { World } from "./world.js";
3
- import { type System } from "./system.js";
3
+ import { type Query } from "./query.js";
4
4
  import { Events } from "./util/events.js";
5
5
  import { Bitset } from "./util/bitset.js";
6
6
  type EntityEvents = Events<{
@@ -33,12 +33,12 @@ export declare class Entity {
33
33
  private deletedComponents;
34
34
  /**
35
35
  * Bitmask representing the set of component types currently attached to this
36
- * entity. Used by the world to efficiently match entities against system
37
- * queries.
36
+ * entity. Used by the world to efficiently match entities against query
37
+ * predicates.
38
38
  */
39
39
  readonly componentBitmask: Bitset;
40
- private readonly systems;
41
- private readonly newSystems;
40
+ private readonly queries;
41
+ private readonly newQueries;
42
42
  /**
43
43
  * A free-form property bag that modules can use to associate arbitrary data
44
44
  * with an entity without registering a component.
@@ -86,6 +86,22 @@ export declare class Entity {
86
86
  * @param markAsModified - Whether to queue an update notification.
87
87
  */
88
88
  add(type: number, markAsModified?: boolean): Component;
89
+ /**
90
+ * Add a component of type `Class` (if not already present) and assign the
91
+ * provided properties onto the instance, then return it.
92
+ *
93
+ * @param Class - The component class to instantiate.
94
+ * @param props - Optional properties to assign onto the component instance.
95
+ * @returns The new (or existing) component instance with the given properties applied.
96
+ */
97
+ set<C extends typeof Component>(Class: C, props: Partial<InstanceType<C>>): InstanceType<C>;
98
+ /**
99
+ * Add a component by its numeric type id and assign the provided properties.
100
+ *
101
+ * @param type - Numeric component type id.
102
+ * @param props - Optional properties to assign onto the component instance.
103
+ */
104
+ set(type: number, props: Partial<Component>): Component;
89
105
  /**
90
106
  * Remove the component of the given class from this entity.
91
107
  *
@@ -102,7 +118,7 @@ export declare class Entity {
102
118
  * @param type - Numeric component type id.
103
119
  */
104
120
  remove(type: number): void;
105
- /** @internal Called by systems to deliver update notifications. */
121
+ /** @internal Called by queries to deliver update notifications. */
106
122
  _notifyModified(component: Component): void;
107
123
  /**
108
124
  * Retrieve the component of type `Class`, or `undefined` if not present.
@@ -124,13 +140,15 @@ export declare class Entity {
124
140
  */
125
141
  get events(): EntityEvents;
126
142
  /** @internal */
127
- _hasSystem(s: System): boolean;
143
+ _hasQuery(q: Query): boolean;
144
+ /** @internal Removes a query from this entity's tracking sets without firing any callbacks. */
145
+ _purgeQuery(q: Query): void;
128
146
  /** @internal */
129
- _addSystem(s: System): void;
147
+ _addQuery(q: Query): void;
130
148
  /** @internal */
131
- _removeSystem(s: System): void;
149
+ _removeQuery(q: Query): void;
132
150
  /** @internal */
133
- _updateSystems(): void;
151
+ _updateQueries(): void;
134
152
  /** `true` when the entity has no components attached. */
135
153
  get empty(): boolean;
136
154
  private _destroy;
package/dist/entity.js CHANGED
@@ -31,12 +31,12 @@ export class Entity {
31
31
  this.deletedComponents = new ArrayMap(); //maps deleted component types to Components
32
32
  /**
33
33
  * Bitmask representing the set of component types currently attached to this
34
- * entity. Used by the world to efficiently match entities against system
35
- * queries.
34
+ * entity. Used by the world to efficiently match entities against query
35
+ * predicates.
36
36
  */
37
37
  this.componentBitmask = new Bitset();
38
- this.systems = new Set();
39
- this.newSystems = [];
38
+ this.queries = new Set();
39
+ this.newQueries = [];
40
40
  /**
41
41
  * A free-form property bag that modules can use to associate arbitrary data
42
42
  * with an entity without registering a component.
@@ -71,6 +71,10 @@ export class Entity {
71
71
  this.world._queueUpdatedComponent(c);
72
72
  return c;
73
73
  }
74
+ set(typeOrClass, props) {
75
+ const c = this.add(typeOrClass);
76
+ return Object.assign(c, props);
77
+ }
74
78
  remove(typeOrClass) {
75
79
  const type = this.world.getComponentType(typeOrClass);
76
80
  const c = this.components.get(type);
@@ -81,10 +85,10 @@ export class Entity {
81
85
  this.world._notifyComponentRemoved(this, c);
82
86
  }
83
87
  }
84
- /** @internal Called by systems to deliver update notifications. */
88
+ /** @internal Called by queries to deliver update notifications. */
85
89
  _notifyModified(component) {
86
- this.systems.forEach((s) => {
87
- s.notifyModified(component);
90
+ this.queries.forEach((q) => {
91
+ q.notifyModified(component);
88
92
  });
89
93
  }
90
94
  /**
@@ -119,28 +123,35 @@ export class Entity {
119
123
  return this._events;
120
124
  }
121
125
  /** @internal */
122
- _hasSystem(s) {
123
- return this.systems.has(s);
126
+ _hasQuery(q) {
127
+ return this.queries.has(q);
128
+ }
129
+ /** @internal Removes a query from this entity's tracking sets without firing any callbacks. */
130
+ _purgeQuery(q) {
131
+ this.queries.delete(q);
132
+ const idx = this.newQueries.indexOf(q);
133
+ if (idx !== -1)
134
+ this.newQueries.splice(idx, 1);
124
135
  }
125
136
  /** @internal */
126
- _addSystem(s) {
127
- if (!this.systems.has(s)) {
128
- this.newSystems.push(s);
129
- s._enter(this);
137
+ _addQuery(q) {
138
+ if (!this.queries.has(q)) {
139
+ this.newQueries.push(q);
140
+ q._enter(this);
130
141
  }
131
142
  }
132
143
  /** @internal */
133
- _removeSystem(s) {
134
- if (this.systems.delete(s)) {
135
- s._exit(this);
144
+ _removeQuery(q) {
145
+ if (this.queries.delete(q)) {
146
+ q._exit(this);
136
147
  }
137
148
  }
138
149
  /** @internal */
139
- _updateSystems() {
140
- this.newSystems.forEach((s) => {
141
- this.systems.add(s);
150
+ _updateQueries() {
151
+ this.newQueries.forEach((q) => {
152
+ this.queries.add(q);
142
153
  });
143
- this.newSystems.length = 0;
154
+ this.newQueries.length = 0;
144
155
  }
145
156
  /** `true` when the entity has no components attached. */
146
157
  get empty() {
@@ -150,10 +161,10 @@ export class Entity {
150
161
  if (this.destroyed)
151
162
  return;
152
163
  this.destroyed = true;
153
- this.systems.forEach((s) => {
154
- s._exit(this);
164
+ this.queries.forEach((q) => {
165
+ q._exit(this);
155
166
  });
156
- this.systems.clear();
167
+ this.queries.clear();
157
168
  if (this._events) {
158
169
  this._events.emit("destroy");
159
170
  this._events.removeAllListeners("destroy");
@@ -1 +1 @@
1
- {"version":3,"file":"entity.js","sourceRoot":"","sources":["../src/entity.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"entity.js","sourceRoot":"","sources":["../src/entity.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAE/C,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC1C,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAI1C;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,OAAO,MAAM;IA0BjB;IACE,+CAA+C;IAC/B,KAAY;IAC5B,0DAA0D;IAC1C,GAAW;QAFX,UAAK,GAAL,KAAK,CAAO;QAEZ,QAAG,GAAH,GAAG,CAAQ;QA7BrB,eAAU,GAAG,IAAI,QAAQ,EAAa,CAAC,CAAC,oCAAoC;QAC5E,sBAAiB,GAAG,IAAI,QAAQ,EAAa,CAAC,CAAC,4CAA4C;QAEnG;;;;WAIG;QACa,qBAAgB,GAAG,IAAI,MAAM,EAAE,CAAC;QAC/B,YAAO,GAAG,IAAI,GAAG,EAAS,CAAC;QAC3B,eAAU,GAAY,EAAE,CAAC;QAE1C;;;WAGG;QACI,eAAU,GAAG,IAAI,GAAG,EAAe,CAAC;QAMpC,sBAAiB,GAAY,KAAK,CAAC;QAClC,cAAS,GAAG,KAAK,CAAC;IAOvB,CAAC;IAEJ;;;;;;OAMG;IACH,IAAW,QAAQ;QACjB,IAAI,CAAC,IAAI,CAAC,SAAS;YAAE,IAAI,CAAC,SAAS,GAAG,IAAI,GAAG,EAAU,CAAC;QACxD,OAAO,IAAI,CAAC,SAAS,CAAC;IACxB,CAAC;IA4BM,GAAG,CACR,WAAsC,EACtC,iBAA0B,IAAI;QAE9B,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,gBAAgB,CAAC,WAAW,CAAC,CAAC;QAEtD,IAAI,CAAC,GAAG,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAClC,IAAI,CAAC,EAAE;YACL,OAAO,CAAC,CAAC;SACV;QACD,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,sBAAsB,CAAC,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC;QAE1D,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;QAC7B,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAChC,IAAI,CAAC,KAAK,CAAC,qBAAqB,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;QAC1C,IAAI,cAAc;YAAE,IAAI,CAAC,KAAK,CAAC,sBAAsB,CAAC,CAAC,CAAC,CAAC;QAEzD,OAAO,CAAC,CAAC;IACX,CAAC;IAqBM,GAAG,CACR,WAAsC,EACtC,KAAyB;QAEzB,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,WAAkB,CAAC,CAAC;QACvC,OAAO,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;IACjC,CAAC;IAkBM,MAAM,CAAC,WAAsC;QAClD,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,gBAAgB,CAAC,WAAW,CAAC,CAAC;QACtD,MAAM,CAAC,GAAG,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACpC,IAAI,CAAC,EAAE;YACL,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;YAC7B,IAAI,CAAC,iBAAiB,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;YACpC,IAAI,CAAC,gBAAgB,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;YACnC,IAAI,CAAC,KAAK,CAAC,uBAAuB,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;SAC7C;IACH,CAAC;IAED,mEAAmE;IAC5D,eAAe,CAAC,SAAoB;QACzC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE;YACzB,CAAC,CAAC,cAAc,CAAC,SAAS,CAAC,CAAC;QAC9B,CAAC,CAAC,CAAC;IACL,CAAC;IAED;;;;;;;;OAQG;IACI,GAAG,CACR,WAAuB,EACvB,cAAuB,KAAK;QAE5B,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,gBAAgB,CAAC,WAAW,CAAC,CAAC;QAEtD,MAAM,CAAC,GAAG,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACpC,IAAI,CAAC,CAAC,IAAI,WAAW,EAAE;YACrB,OAAO,IAAI,CAAC,iBAAiB,CAAC,GAAG,CAAC,IAAI,CAAgC,CAAC;SACxE;QACD,OAAO,CAAgC,CAAC;IAC1C,CAAC;IAED;;;;;;;OAOG;IACH,IAAW,MAAM;QACf,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE;YACjB,IAAI,CAAC,OAAO,GAAG,IAAI,MAAM,EAAE,CAAC;SAC7B;QACD,OAAO,IAAI,CAAC,OAAO,CAAC;IACtB,CAAC;IAED,gBAAgB;IACT,SAAS,CAAC,CAAQ;QACvB,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;IAC7B,CAAC;IAED,+FAA+F;IACxF,WAAW,CAAC,CAAQ;QACzB,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;QACvB,MAAM,GAAG,GAAG,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;QACvC,IAAI,GAAG,KAAK,CAAC,CAAC;YAAE,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;IACjD,CAAC;IAED,gBAAgB;IACT,SAAS,CAAC,CAAQ;QACvB,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE;YACxB,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YACxB,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;SAChB;IACH,CAAC;IAED,gBAAgB;IACT,YAAY,CAAC,CAAQ;QAC1B,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE;YAC1B,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;SACf;IACH,CAAC;IAED,gBAAgB;IACT,cAAc;QACnB,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE;YAC5B,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QACtB,CAAC,CAAC,CAAC;QACH,IAAI,CAAC,UAAU,CAAC,MAAM,GAAG,CAAC,CAAC;IAC7B,CAAC;IAED,yDAAyD;IACzD,IAAW,KAAK;QACd,OAAO,IAAI,CAAC,UAAU,CAAC,IAAI,IAAI,CAAC,CAAC;IACnC,CAAC;IAEO,QAAQ;QACd,IAAI,IAAI,CAAC,SAAS;YAAE,OAAO;QAC3B,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;QACtB,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE;YACzB,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAChB,CAAC,CAAC,CAAC;QACH,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;QAErB,IAAI,IAAI,CAAC,OAAO,EAAE;YAChB,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;YAC7B,IAAI,CAAC,OAAO,CAAC,kBAAkB,CAAC,SAAS,CAAC,CAAC;SAC5C;IACH,CAAC;IAED;;;;;;OAMG;IACI,OAAO;QACZ,IAAI,CAAC,KAAK,CAAC,sBAAsB,CAAC,IAAI,CAAC,CAAC;QACxC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE;YAC9B,KAAK,CAAC,OAAO,EAAE,CAAC;QAClB,CAAC,CAAC,CAAC;QACH,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE,CAAC;QACtB,IAAI,IAAI,CAAC,MAAM,EAAE;YACf,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;YAClC,IAAI,CAAC,KAAK,CAAC,gBAAgB,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YACzC,IAAI,CAAC,MAAM,GAAG,SAAS,CAAC;SACzB;IACH,CAAC;IAED,gBAAgB;IACT,sBAAsB;QAC3B,IAAI,CAAC,iBAAiB,CAAC,KAAK,EAAE,CAAC;IACjC,CAAC;IAED;;;;;OAKG;IACI,gBAAgB,CAAC,QAAgC;QACtD,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IACpC,CAAC;IAED,oDAAoD;IAC7C,QAAQ;QACb,OAAO,SAAS,IAAI,CAAC,GAAG,EAAE,CAAC;IAC7B,CAAC;CACF"}
@@ -0,0 +1,59 @@
1
+ import { Component } from "./component.js";
2
+ import type { Entity } from "./entity.js";
3
+ import type { World } from "./world.js";
4
+ import { type MaybeRequired, type QueryDSL } from "./dsl.js";
5
+ /**
6
+ * A non-reactive, one-shot entity filter.
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.
12
+ *
13
+ * Create via {@link World.filter}:
14
+ *
15
+ * ```ts
16
+ * const f = world.filter([Position, Velocity]);
17
+ *
18
+ * // Entity only:
19
+ * f.forEach((e) => console.log(e.eid));
20
+ *
21
+ * // With component injection:
22
+ * f.forEach([Position, Velocity], (e, [pos, vel]) => {
23
+ * pos.x += vel.vx;
24
+ * });
25
+ * ```
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.
32
+ */
33
+ export declare class Filter<R extends (typeof Component)[] = []> {
34
+ private readonly world;
35
+ private readonly belongs;
36
+ constructor(world: World, dsl: QueryDSL);
37
+ /**
38
+ * Iterate all world entities and call `callback` for each one that matches
39
+ * the DSL this filter was created with.
40
+ *
41
+ * @param callback - Receives only the entity.
42
+ */
43
+ forEach(callback: (e: Entity) => void): void;
44
+ /**
45
+ * Iterate all world entities and call `callback` for each one that matches
46
+ * the DSL, with component injection.
47
+ *
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.
51
+ *
52
+ * @param components - Component classes to resolve from each matching entity.
53
+ * @param callback - Receives the entity and a tuple of resolved component
54
+ * instances.
55
+ */
56
+ forEach<J extends (typeof Component)[]>(components: readonly [...J], callback: (e: Entity, resolved: {
57
+ [K in keyof J]: MaybeRequired<J[K], R>;
58
+ }) => void): void;
59
+ }
package/dist/filter.js ADDED
@@ -0,0 +1,53 @@
1
+ import { buildEntityTest, } from "./dsl.js";
2
+ /**
3
+ * A non-reactive, one-shot entity filter.
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.
9
+ *
10
+ * Create via {@link World.filter}:
11
+ *
12
+ * ```ts
13
+ * const f = world.filter([Position, Velocity]);
14
+ *
15
+ * // Entity only:
16
+ * f.forEach((e) => console.log(e.eid));
17
+ *
18
+ * // With component injection:
19
+ * f.forEach([Position, Velocity], (e, [pos, vel]) => {
20
+ * pos.x += vel.vx;
21
+ * });
22
+ * ```
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.
29
+ */
30
+ export class Filter {
31
+ constructor(world, dsl) {
32
+ this.world = world;
33
+ this.belongs = buildEntityTest(world, dsl);
34
+ }
35
+ forEach(componentsOrCallback, callback) {
36
+ if (typeof componentsOrCallback === "function") {
37
+ this.world._forEachEntity((e) => {
38
+ if (this.belongs(e))
39
+ componentsOrCallback(e);
40
+ });
41
+ }
42
+ else {
43
+ const types = componentsOrCallback.map((C) => this.world.getComponentType(C));
44
+ this.world._forEachEntity((e) => {
45
+ if (!this.belongs(e))
46
+ return;
47
+ const resolved = types.map((t) => e.get(t));
48
+ callback(e, resolved);
49
+ });
50
+ }
51
+ }
52
+ }
53
+ //# sourceMappingURL=filter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"filter.js","sourceRoot":"","sources":["../src/filter.ts"],"names":[],"mappings":"AAGA,OAAO,EACL,eAAe,GAIhB,MAAM,UAAU,CAAC;AAElB;;;;;;;;;;;;;;;;;;;;;;;;;;;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;IA8BM,OAAO,CACZ,oBAEyB,EACzB,QAGS;QAET,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;oBAAE,oBAAoB,CAAC,CAAC,CAAC,CAAC;YAC/C,CAAC,CAAC,CAAC;SACJ;aAAM;YACL,MAAM,KAAK,GAAG,oBAAoB,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAC3C,IAAI,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAC/B,CAAC;YACF,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC,CAAC,EAAE,EAAE;gBAC9B,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC;oBAAE,OAAO;gBAC7B,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"}
package/dist/index.d.ts CHANGED
@@ -1,5 +1,7 @@
1
1
  export { type System } from "./system.js";
2
+ export { Query } from "./query.js";
2
3
  export { World } from "./world.js";
4
+ export { Filter } from "./filter.js";
3
5
  export { Component, type ComponentMeta } from "./component.js";
4
6
  export { type Entity } from "./entity.js";
5
7
  export { type IPhase } from "./phase.js";