@vworlds/vecs 1.0.5 → 1.0.7

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.
@@ -0,0 +1 @@
1
+ npx lint-staged
package/.prettierrc ADDED
@@ -0,0 +1,7 @@
1
+ {
2
+ "semi": true,
3
+ "singleQuote": false,
4
+ "tabWidth": 2,
5
+ "trailingComma": "es5",
6
+ "printWidth": 100
7
+ }
package/README.md CHANGED
@@ -12,14 +12,14 @@ yarn add @vworlds/vecs
12
12
 
13
13
  ## Concepts
14
14
 
15
- | Concept | What it is |
16
- |---|---|
17
- | **World** | Central container. Owns all entities, runs all systems and queries. |
18
- | **Component** | A plain data class. Extend `Component` and attach instances to entities. |
19
- | **Entity** | An integer id with a set of components. Create via the world. |
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. |
15
+ | Concept | What it is |
16
+ | ------------------------ | ------------------------------------------------------------------------------- |
17
+ | **World** | Central container. Owns all entities, runs all systems and queries. |
18
+ | **Component** | A plain data class. Extend `Component` and attach instances to entities. |
19
+ | **Entity** | An integer id with a set of components. Create via the world. |
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. |
23
23
  | **Exclusive components** | A group of components where at most one may be present on any entity at a time. |
24
24
 
25
25
  ### Lifecycle in brief
@@ -117,11 +117,9 @@ world.start(); // freeze registration, sort systems into phases
117
117
  const bullet = world.createEntity();
118
118
  bullet.set(Position, { x: 0, y: 0 });
119
119
 
120
- const vel = bullet.set(Velocity, { vx: 5, vy: 0 });
121
- vel.modified(); // first update: notify Move system
120
+ const vel = bullet.set(Velocity, { vx: 5, vy: 0 }).get(Velocity)!;
122
121
 
123
- const hp = bullet.set(Health, { hp: 3 });
124
- hp.modified();
122
+ const hp = bullet.set(Health, { hp: 3 }).get(Health)!;
125
123
 
126
124
  // ─── Game loop ─────────────────────────────────────────────────────────────
127
125
 
@@ -220,10 +218,11 @@ world.start(); // distributes systems to phases, freezes component registration
220
218
  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.
221
219
 
222
220
  ```ts
223
- const enemies = world.query("Enemies")
221
+ const enemies = world
222
+ .query("Enemies")
224
223
  .requires(Enemy, Health)
225
224
  .enter((e) => console.log("enemy spawned", e.eid))
226
- .exit((e) => console.log("enemy died", e.eid));
225
+ .exit((e) => console.log("enemy died", e.eid));
227
226
 
228
227
  world.start();
229
228
  // enemies.entities is kept up-to-date automatically
@@ -242,20 +241,19 @@ A `Filter` is a non-reactive, one-shot scan. It holds no tracked entity set —
242
241
  world.filter([Position]).forEach((e) => console.log(e.eid));
243
242
 
244
243
  // With component injection:
245
- world.filter([Position, Velocity])
246
- .forEach([Position, Velocity], (e, [pos, vel]) => {
247
- pos.x += vel.vx;
248
- });
244
+ world.filter([Position, Velocity]).forEach([Position, Velocity], (e, [pos, vel]) => {
245
+ pos.x += vel.vx;
246
+ });
249
247
 
250
248
  // Full DSL, with auto-deduced required components:
251
- world.filter({ AND: [{ HAS: Position }, { HAS: Velocity }] })
249
+ world
250
+ .filter({ AND: [{ HAS: Position }, { HAS: Velocity }] })
252
251
  .forEach([Position, Velocity], (e, [pos, vel]) => {
253
252
  pos.x += vel.vx; // pos and vel are non-null — deduced from AND of HAS
254
253
  });
255
254
 
256
255
  // Manual type hint for queries the extractor can't see through:
257
- world.filter({ OR: [Position, Velocity] }, [Position])
258
- .forEach([Position], (e, [pos]) => pos.x);
256
+ world.filter({ OR: [Position, Velocity] }, [Position]).forEach([Position], (e, [pos]) => pos.x);
259
257
  ```
260
258
 
261
259
  Unlike `Query`, a `Filter` requires no name, no `world.start()`, and no `destroy()` — create it anywhere and discard it freely.
@@ -265,16 +263,16 @@ Unlike `Query`, a `Filter` requires no name, no `world.start()`, and no `destroy
265
263
  ```ts
266
264
  // Declare phases in the order they should run each frame:
267
265
  const preUpdate = world.addPhase("preupdate");
268
- const update = world.addPhase("update");
269
- const send = world.addPhase("send");
266
+ const update = world.addPhase("update");
267
+ const send = world.addPhase("send");
270
268
 
271
269
  // Each frame, run all phases in registration order:
272
270
  world.progress(Date.now(), deltaMs);
273
271
 
274
272
  // Or drive individual phases manually:
275
273
  world.runPhase(preUpdate, Date.now(), deltaMs);
276
- world.runPhase(update, Date.now(), deltaMs);
277
- world.runPhase(send, Date.now(), deltaMs);
274
+ world.runPhase(update, Date.now(), deltaMs);
275
+ world.runPhase(send, Date.now(), deltaMs);
278
276
  ```
279
277
 
280
278
  Systems with no explicit phase go into a built-in `"update"` phase.
@@ -284,10 +282,11 @@ Systems with no explicit phase go into a built-in `"update"` phase.
284
282
  A hook is a shorthand for reacting to a single component's lifecycle without writing a full system:
285
283
 
286
284
  ```ts
287
- world.hook(Sprite)
288
- .onAdd((sprite) => sprite.initialize(scene))
285
+ world
286
+ .hook(Sprite)
287
+ .onAdd((sprite) => sprite.initialize(scene))
289
288
  .onRemove((sprite) => sprite.destroy())
290
- .onSet((sprite) => sprite.syncToScene());
289
+ .onSet((sprite) => sprite.syncToScene());
291
290
  ```
292
291
 
293
292
  `onSet` fires whenever `component.modified()` is called.
@@ -308,19 +307,23 @@ class Position extends Component {
308
307
 
309
308
  world.registerComponent(Position);
310
309
 
311
- const pos = entity.add(Position);
310
+ entity.add(Position);
311
+ const pos = entity.get(Position)!;
312
312
  pos.x = 100;
313
313
  pos.modified(); // tell the world this component changed
314
+
315
+ // Alternatively:
316
+ entity.set(Position, { x: 100 });
314
317
  ```
315
318
 
316
319
  Every component instance exposes:
317
320
 
318
- | Property / Method | Description |
319
- |---|---|
320
- | `entity` | The `Entity` this component belongs to. |
321
- | `meta` | `ComponentMeta` — holds the type id, name, and bitset pointer. |
322
- | `type` | Numeric type id (shorthand for `meta.type`). |
323
- | `modified()` | Queue an `onSet` / `update` notification. Call after mutating fields. |
321
+ | Property / Method | Description |
322
+ | ----------------- | --------------------------------------------------------------------- |
323
+ | `entity` | The `Entity` this component belongs to. |
324
+ | `meta` | `ComponentMeta` — holds the type id, name, and bitset pointer. |
325
+ | `type` | Numeric type id (shorthand for `meta.type`). |
326
+ | `modified()` | Queue an `onSet` / `update` notification. Call after mutating fields. |
324
327
 
325
328
  ---
326
329
 
@@ -330,21 +333,34 @@ Every component instance exposes:
330
333
  const e = world.createEntity();
331
334
  ```
332
335
 
333
- | Property / Method | Description |
334
- |---|---|
335
- | `eid` | Unique numeric entity id. |
336
- | `world` | The `World` that owns this entity. |
337
- | `add(Class)` | Attach a component; returns the typed instance. Idempotent. |
338
- | `set(Class, props)` | Like `add`, but also assigns the given partial properties onto the instance. Returns the typed instance. |
339
- | `get(Class)` | Return the component instance, or `undefined` if not present. |
340
- | `remove(Class)` | Detach a component (triggers `onRemove` hooks and `exit` callbacks). |
341
- | `destroy()` | Remove all components and unregister the entity. Recurses to children. |
342
- | `empty` | `true` when no components are attached. |
343
- | `forEachComponent(cb)` | Iterate over all attached components. |
344
- | `parent` | Parent entity in the scene hierarchy, or `undefined`. |
345
- | `children` | `Set<Entity>` of direct children (lazy, created on first access). |
346
- | `events` | Typed event emitter. Currently emits `"destroy"` before teardown. |
347
- | `properties` | `Map<string, any>` free-form bag for module-level bookkeeping. |
336
+ | Property / Method | Description |
337
+ | ---------------------- | ------------------------------------------------------------------------------------------------------------- |
338
+ | `eid` | Unique numeric entity id. |
339
+ | `world` | The `World` that owns this entity. |
340
+ | `add(Class)` | Attach a component and return the entity for chaining. Idempotent. |
341
+ | `set(Class, props)` | Like `add`, but also assigns the given partial properties onto the instance. Returns the entity for chaining. |
342
+ | `modified(component)` | Queue an `onSet` / `update` notification for the component. Returns the entity for chaining. |
343
+ | `get(Class)` | Return the component instance, or `undefined` if not present. |
344
+ | `remove(Class)` | Detach a component (triggers `onRemove` hooks and `exit` callbacks). |
345
+ | `destroy()` | Remove all components and unregister the entity. Recurses to children. |
346
+ | `empty` | `true` when no components are attached. |
347
+ | `forEachComponent(cb)` | Iterate over all attached components. |
348
+ | `parent` | Parent entity in the scene hierarchy, or `undefined`. |
349
+ | `children` | `Set<Entity>` of direct children (lazy, created on first access). |
350
+ | `events` | Typed event emitter. Currently emits `"destroy"` before teardown. |
351
+ | `properties` | `Map<string, any>` free-form bag for module-level bookkeeping. |
352
+
353
+ `entity.modified(c)` is equivalent to `c.modified()` but returns the entity, making it usable in a method chain:
354
+
355
+ ```ts
356
+ // Mutate fields then signal the change inline:
357
+ const vel = entity.get(Velocity)!;
358
+ vel.vx += accel;
359
+ entity.modified(vel); // same effect as vel.modified(), returns entity
360
+
361
+ // Or in a chain — add without initial notification, then notify later:
362
+ entity.add(Position, false).modified(entity.get(Position)!);
363
+ ```
348
364
 
349
365
  #### Parent–child hierarchy
350
366
 
@@ -385,15 +401,15 @@ Declare which entities the system should track:
385
401
 
386
402
  **Query operators:**
387
403
 
388
- | Operator | Meaning |
389
- |---|---|
390
- | `{ HAS: [A, B] }` | Entity has all of A and B |
404
+ | Operator | Meaning |
405
+ | ---------------------- | ---------------------------------------- |
406
+ | `{ HAS: [A, B] }` | Entity has all of A and B |
391
407
  | `{ HAS_ONLY: [A, B] }` | Entity has exactly A and B, nothing else |
392
- | `{ AND: [q1, q2] }` | Both sub-queries must match |
393
- | `{ OR: [q1, q2] }` | Either sub-query matches |
394
- | `{ NOT: q }` | Sub-query must not match |
395
- | `{ PARENT: q }` | Entity's parent matches q |
396
- | An array `[A, B]` | Shorthand for `HAS: [A, B]` |
408
+ | `{ AND: [q1, q2] }` | Both sub-queries must match |
409
+ | `{ OR: [q1, q2] }` | Either sub-query matches |
410
+ | `{ NOT: q }` | Sub-query must not match |
411
+ | `{ PARENT: q }` | Entity's parent matches q |
412
+ | An array `[A, B]` | Shorthand for `HAS: [A, B]` |
397
413
 
398
414
  **Type inference:** `requires()` records the listed classes as a type parameter on the system. Callbacks in `.sort()`, `.each()`, and `.update()` inject then treat those components as non-nullable — no `!` needed. For complex `query()` expressions the type system cannot introspect, pass a second argument as an explicit hint:
399
415
 
@@ -485,7 +501,8 @@ Enable sorted entity tracking. Matched entities are stored in an ordered set who
485
501
  Components declared via `requires()` are non-null in the compare callback.
486
502
 
487
503
  ```ts
488
- world.system("Render")
504
+ world
505
+ .system("Render")
489
506
  .requires(Position, Sprite)
490
507
  .sort([Position], ([posA], [posB]) => posA.z - posB.z)
491
508
  .each([Position, Sprite], (e, [pos, sprite]) => {
@@ -518,31 +535,36 @@ Called every tick when the system's phase runs, regardless of entity state. Use
518
535
  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.
519
536
 
520
537
  ```ts
521
- const projectiles = world.query("Projectiles")
538
+ const projectiles = world
539
+ .query("Projectiles")
522
540
  .requires(Position, Velocity)
523
541
  .sort([Position], ([a], [b]) => a.z - b.z)
524
- .enter([Position], (e, [pos]) => { pos.x = spawnX; });
542
+ .enter([Position], (e, [pos]) => {
543
+ pos.x = spawnX;
544
+ });
525
545
 
526
546
  world.start();
527
547
 
528
548
  // Anywhere in game code:
529
- projectiles.forEach((e) => { /* ... */ });
549
+ projectiles.forEach((e) => {
550
+ /* ... */
551
+ });
530
552
  console.log(projectiles.entities.size, "active projectiles");
531
553
  ```
532
554
 
533
- | Method | Description |
534
- |---|---|
535
- | `.requires(...components)` | Set the membership predicate and start tracking. |
536
- | `.query(expr)` | Set the membership predicate using the {@link SystemQuery} DSL. |
537
- | `.enter(callback)` / `.enter(inject, callback)` | Fires when an entity joins the query. |
538
- | `.exit(callback)` / `.exit(inject, callback)` | Fires when an entity leaves the query. |
539
- | `.sort(components, compare)` | Store matched entities in sorted order. |
540
- | `.track()` | Enable tracking (implied by `sort`; backfills when called after `start`). |
541
- | `.belongs(e)` | Returns `true` if the entity satisfies the predicate. |
542
- | `.forEach(callback)` | Iterate all currently tracked entities (entity only). |
543
- | `.forEach(components, callback)` | Iterate with component injection — same signature as `Filter.forEach`. |
544
- | `.entities` | `ReadonlySet<Entity>` of all currently tracked entities. |
545
- | `.destroy()` | Remove the query from the world and all entities. See below. |
555
+ | Method | Description |
556
+ | ----------------------------------------------- | ------------------------------------------------------------------------- |
557
+ | `.requires(...components)` | Set the membership predicate and start tracking. |
558
+ | `.query(expr)` | Set the membership predicate using the {@link SystemQuery} DSL. |
559
+ | `.enter(callback)` / `.enter(inject, callback)` | Fires when an entity joins the query. |
560
+ | `.exit(callback)` / `.exit(inject, callback)` | Fires when an entity leaves the query. |
561
+ | `.sort(components, compare)` | Store matched entities in sorted order. |
562
+ | `.track()` | Enable tracking (implied by `sort`; backfills when called after `start`). |
563
+ | `.belongs(e)` | Returns `true` if the entity satisfies the predicate. |
564
+ | `.forEach(callback)` | Iterate all currently tracked entities (entity only). |
565
+ | `.forEach(components, callback)` | Iterate with component injection — same signature as `Filter.forEach`. |
566
+ | `.entities` | `ReadonlySet<Entity>` of all currently tracked entities. |
567
+ | `.destroy()` | Remove the query from the world and all entities. See below. |
546
568
 
547
569
  #### `.destroy()`
548
570
 
@@ -568,9 +590,9 @@ A `Filter` is created via `world.filter(dsl)` and provides a non-reactive `forEa
568
590
  const f = world.filter([Position, Velocity]);
569
591
  ```
570
592
 
571
- | Method | Description |
572
- |---|---|
573
- | `.forEach(callback)` | Walk all world entities; invoke callback for each matching one. |
593
+ | Method | Description |
594
+ | -------------------------------- | -------------------------------------------------------------------------- |
595
+ | `.forEach(callback)` | Walk all world entities; invoke callback for each matching one. |
574
596
  | `.forEach(components, callback)` | Same, with component injection and non-null types for required components. |
575
597
 
576
598
  **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:
@@ -65,9 +65,12 @@ export declare class ComponentMeta implements Hook<Component> {
65
65
  readonly componentName: string;
66
66
  /** Pre-computed bit-pointer into the entity archetype {@link Bitset}. */
67
67
  readonly bitPtr: BitPtr;
68
- private onAddHandler;
69
- private onRemoveHandler;
70
- private onSetHandler;
68
+ /** @internal */
69
+ _onAddHandler: ((c: Component) => void) | undefined;
70
+ /** @internal */
71
+ _onRemoveHandler: ((c: Component) => void) | undefined;
72
+ /** @internal */
73
+ _onSetHandler: ((c: Component) => void) | undefined;
71
74
  /**
72
75
  * Type ids of components that cannot coexist with this one on the same entity.
73
76
  * Set via {@link World.setExclusiveComponents}. `undefined` means no restrictions.
@@ -97,9 +100,7 @@ export type ComponentClassArray = ComponentClassOrType[];
97
100
  * }
98
101
  *
99
102
  * world.registerComponent(Position);
100
- * const pos = entity.add(Position);
101
- * pos.x = 100;
102
- * pos.modified(); // notify watching systems
103
+ * entity.set(Position, { x: 100 });
103
104
  * ```
104
105
  *
105
106
  * A component instance is always bound to a single entity and is created by
@@ -110,7 +111,8 @@ export declare class Component {
110
111
  readonly entity: Entity;
111
112
  /** Registration metadata (type id, name, bit-pointer). */
112
113
  readonly meta: ComponentMeta;
113
- private dirty;
114
+ /** @internal */
115
+ _dirty: boolean;
114
116
  constructor(
115
117
  /** The entity this component belongs to. */
116
118
  entity: Entity,
package/dist/component.js CHANGED
@@ -23,17 +23,17 @@ export class ComponentMeta {
23
23
  }
24
24
  /** @inheritdoc */
25
25
  onAdd(handler) {
26
- this.onAddHandler = handler;
26
+ this._onAddHandler = handler;
27
27
  return this;
28
28
  }
29
29
  /** @inheritdoc */
30
30
  onRemove(handler) {
31
- this.onRemoveHandler = handler;
31
+ this._onRemoveHandler = handler;
32
32
  return this;
33
33
  }
34
34
  /** @inheritdoc */
35
35
  onSet(handler) {
36
- this.onSetHandler = handler;
36
+ this._onSetHandler = handler;
37
37
  return this;
38
38
  }
39
39
  }
@@ -49,9 +49,7 @@ export class ComponentMeta {
49
49
  * }
50
50
  *
51
51
  * world.registerComponent(Position);
52
- * const pos = entity.add(Position);
53
- * pos.x = 100;
54
- * pos.modified(); // notify watching systems
52
+ * entity.set(Position, { x: 100 });
55
53
  * ```
56
54
  *
57
55
  * A component instance is always bound to a single entity and is created by
@@ -65,7 +63,8 @@ export class Component {
65
63
  meta) {
66
64
  this.entity = entity;
67
65
  this.meta = meta;
68
- this.dirty = false;
66
+ /** @internal */
67
+ this._dirty = false;
69
68
  }
70
69
  /** Numeric type id — shorthand for `this.meta.type`. */
71
70
  get type() {
@@ -83,7 +82,7 @@ export class Component {
83
82
  * fields to ensure systems react to the new values.
84
83
  */
85
84
  modified() {
86
- this.entity.world._queueUpdatedComponent(this);
85
+ this.entity.modified(this);
87
86
  }
88
87
  /** Returns the component's registered name, e.g. `"Position"`. */
89
88
  toString() {
@@ -1 +1 @@
1
- {"version":3,"file":"component.js","sourceRoot":"","sources":["../src/component.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAoDlD;;;;;;;;;GASG;AACH,MAAM,OAAO,aAAa;IAkBxB,YAAY,KAAuB,EAAE,IAAY,EAAE,aAAqB;QANxE;;;WAGG;QACI,cAAS,GAAyB,SAAS,CAAC;QAGjD,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,aAAa,GAAG,aAAa,CAAC;QACnC,IAAI,CAAC,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,CAAC;IACjC,CAAC;IAED,kBAAkB;IACX,KAAK,CAAC,OAA+B;QAC1C,IAAI,CAAC,YAAY,GAAG,OAAO,CAAC;QAC5B,OAAO,IAAI,CAAC;IACd,CAAC;IAED,kBAAkB;IACX,QAAQ,CAAC,OAA+B;QAC7C,IAAI,CAAC,eAAe,GAAG,OAAO,CAAC;QAC/B,OAAO,IAAI,CAAC;IACd,CAAC;IAED,kBAAkB;IACX,KAAK,CAAC,OAA+B;QAC1C,IAAI,CAAC,YAAY,GAAG,OAAO,CAAC;QAC5B,OAAO,IAAI,CAAC;IACd,CAAC;CACF;AAQD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,OAAO,SAAS;IAGpB;IACE,4CAA4C;IAC5B,MAAc;IAC9B,0DAA0D;IAC1C,IAAmB;QAFnB,WAAM,GAAN,MAAM,CAAQ;QAEd,SAAI,GAAJ,IAAI,CAAe;QAN7B,UAAK,GAAY,KAAK,CAAC;IAO5B,CAAC;IAEJ,wDAAwD;IACxD,IAAW,IAAI;QACb,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC;IACxB,CAAC;IAED,mEAAmE;IACnE,IAAW,MAAM;QACf,OAAO,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC;IAC1B,CAAC;IAED;;;;;;OAMG;IACI,QAAQ;QACb,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,sBAAsB,CAAC,IAAI,CAAC,CAAC;IACjD,CAAC;IAED,kEAAkE;IAC3D,QAAQ;QACb,OAAO,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC;IACjC,CAAC;CACF;AAED;;;;;GAKG;AACH,MAAM,UAAU,yBAAyB,CACvC,OAA4B,EAC5B,KAAY;IAEZ,MAAM,OAAO,GAAG,IAAI,MAAM,EAAE,CAAC;IAC7B,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE;QACpB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAAC;IACzC,CAAC,CAAC,CAAC;IACH,OAAO,OAAO,CAAC;AACjB,CAAC"}
1
+ {"version":3,"file":"component.js","sourceRoot":"","sources":["../src/component.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAoDlD;;;;;;;;;GASG;AACH,MAAM,OAAO,aAAa;IAqBxB,YAAY,KAAuB,EAAE,IAAY,EAAE,aAAqB;QANxE;;;WAGG;QACI,cAAS,GAAyB,SAAS,CAAC;QAGjD,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,aAAa,GAAG,aAAa,CAAC;QACnC,IAAI,CAAC,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,CAAC;IACjC,CAAC;IAED,kBAAkB;IACX,KAAK,CAAC,OAA+B;QAC1C,IAAI,CAAC,aAAa,GAAG,OAAO,CAAC;QAC7B,OAAO,IAAI,CAAC;IACd,CAAC;IAED,kBAAkB;IACX,QAAQ,CAAC,OAA+B;QAC7C,IAAI,CAAC,gBAAgB,GAAG,OAAO,CAAC;QAChC,OAAO,IAAI,CAAC;IACd,CAAC;IAED,kBAAkB;IACX,KAAK,CAAC,OAA+B;QAC1C,IAAI,CAAC,aAAa,GAAG,OAAO,CAAC;QAC7B,OAAO,IAAI,CAAC;IACd,CAAC;CACF;AAQD;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,OAAO,SAAS;IAIpB;IACE,4CAA4C;IAC5B,MAAc;IAC9B,0DAA0D;IAC1C,IAAmB;QAFnB,WAAM,GAAN,MAAM,CAAQ;QAEd,SAAI,GAAJ,IAAI,CAAe;QAPrC,gBAAgB;QACT,WAAM,GAAY,KAAK,CAAC;IAO5B,CAAC;IAEJ,wDAAwD;IACxD,IAAW,IAAI;QACb,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC;IACxB,CAAC;IAED,mEAAmE;IACnE,IAAW,MAAM;QACf,OAAO,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC;IAC1B,CAAC;IAED;;;;;;OAMG;IACI,QAAQ;QACb,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;IAC7B,CAAC;IAED,kEAAkE;IAC3D,QAAQ;QACb,OAAO,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC;IACjC,CAAC;CACF;AAED;;;;;GAKG;AACH,MAAM,UAAU,yBAAyB,CAAC,OAA4B,EAAE,KAAY;IAClF,MAAM,OAAO,GAAG,IAAI,MAAM,EAAE,CAAC;IAC7B,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE;QACpB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAAC;IACzC,CAAC,CAAC,CAAC;IACH,OAAO,OAAO,CAAC;AACjB,CAAC"}
package/dist/dsl.d.ts CHANGED
@@ -65,7 +65,10 @@ export type ExtractRequired<Q> = Q extends typeof Component ? [Q] : Q extends re
65
65
  } ? ExtractRequired<H> : Q extends {
66
66
  AND: infer A extends readonly QueryDSL[];
67
67
  } ? ExtractAndChain<A> : [];
68
- type ExtractAndChain<A extends readonly QueryDSL[]> = A extends readonly [infer First, ...infer Rest extends readonly QueryDSL[]] ? [...ExtractRequired<First>, ...ExtractAndChain<Rest>] : [];
68
+ type ExtractAndChain<A extends readonly QueryDSL[]> = A extends readonly [
69
+ infer First,
70
+ ...infer Rest extends readonly QueryDSL[]
71
+ ] ? [...ExtractRequired<First>, ...ExtractAndChain<Rest>] : [];
69
72
  /** Convert a {@link QueryDSL} expression into a runtime entity-test predicate. */
70
73
  export declare function buildEntityTest(world: World, q: QueryDSL): EntityTestFunc;
71
74
  export {};
package/dist/dsl.js CHANGED
@@ -21,8 +21,7 @@ function PARENT(func) {
21
21
  }
22
22
  /** Convert a {@link QueryDSL} expression into a runtime entity-test predicate. */
23
23
  export function buildEntityTest(world, q) {
24
- if (typeof q === "number" ||
25
- (typeof q === "function" && q.prototype instanceof Component)) {
24
+ if (typeof q === "number" || (typeof q === "function" && q.prototype instanceof Component)) {
26
25
  return HAS(world, q);
27
26
  }
28
27
  else if (typeof q === "function") {
package/dist/dsl.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"dsl.js","sourceRoot":"","sources":["../src/dsl.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"dsl.js","sourceRoot":"","sources":["../src/dsl.ts"],"names":[],"mappings":"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"}
package/dist/entity.d.ts CHANGED
@@ -16,9 +16,7 @@ type EntityEvents = Events<{
16
16
  *
17
17
  * ```ts
18
18
  * const e = world.createEntity();
19
- * const pos = e.add(Position);
20
- * pos.x = 100;
21
- * pos.modified();
19
+ * e.set(Position, { x: 100 });
22
20
  * ```
23
21
  *
24
22
  * Entities support a parent–child hierarchy. When a parent is destroyed its
@@ -47,7 +45,8 @@ export declare class Entity {
47
45
  _events: EntityEvents;
48
46
  /** Parent entity in the scene hierarchy, or `undefined` if root. */
49
47
  parent: Entity | undefined;
50
- private _children;
48
+ /** @internal */
49
+ _children: Set<Entity> | undefined;
51
50
  _archetypeChanged: boolean;
52
51
  private destroyed;
53
52
  constructor(
@@ -64,6 +63,17 @@ export declare class Entity {
64
63
  */
65
64
  get children(): Set<Entity>;
66
65
  private getComponentInstance;
66
+ /**
67
+ * Queue an `onSet` / `update` notification for the given component and
68
+ * return the entity for chaining.
69
+ *
70
+ * Equivalent to `component.modified()`, but usable inside an entity method
71
+ * chain (e.g. after `add` or `set`).
72
+ *
73
+ * @param c - The component instance whose data changed.
74
+ * @returns This entity, for chaining.
75
+ */
76
+ modified<C extends typeof Component>(c: InstanceType<C>): Entity;
67
77
  /**
68
78
  * Add a component of type `Class` to this entity and return the instance.
69
79
  *
@@ -78,7 +88,29 @@ export declare class Entity {
78
88
  * @returns The new (or existing) component instance, typed as
79
89
  * `InstanceType<Class>`.
80
90
  */
81
- add<C extends typeof Component>(Class: C, markAsModified?: boolean): InstanceType<C>;
91
+ _add<C extends typeof Component>(Class: C, markAsModified?: boolean): InstanceType<C>;
92
+ /**
93
+ * Add a component by its numeric type id.
94
+ *
95
+ * @param type - Numeric component type id (as returned by
96
+ * {@link World.getComponentType}).
97
+ * @param markAsModified - Whether to queue an update notification.
98
+ */
99
+ _add(type: number, markAsModified?: boolean): Component;
100
+ /**
101
+ * Add a component of type `Class` to this entity and return the entity.
102
+ *
103
+ * If the component is already present the existing instance is returned and
104
+ * no callback is fired. Pass `markAsModified = false` to suppress the
105
+ * initial `onSet` / `update` notification (useful when bulk-loading
106
+ * network snapshots before systems are running).
107
+ *
108
+ * @param Class - The component class to instantiate.
109
+ * @param markAsModified - Whether to immediately queue an `update`
110
+ * notification. Defaults to `true`.
111
+ * @returns This entity, for chaining.
112
+ */
113
+ add<C extends typeof Component>(Class: C, markAsModified?: boolean): Entity;
82
114
  /**
83
115
  * Add a component by its numeric type id.
84
116
  *
@@ -86,23 +118,24 @@ export declare class Entity {
86
118
  * {@link World.getComponentType}).
87
119
  * @param markAsModified - Whether to queue an update notification.
88
120
  */
89
- add(type: number, markAsModified?: boolean): Component;
121
+ add(type: number, markAsModified?: boolean): Entity;
90
122
  /**
91
- * Add a component of type `Class` (if not already present) and assign the
92
- * provided properties onto the instance, then return it.
123
+ * Add a component of type `Class` (if not already present), assign the
124
+ * provided properties onto the instance, and return the entity for chaining.
93
125
  *
94
126
  * @param Class - The component class to instantiate.
95
- * @param props - Optional properties to assign onto the component instance.
96
- * @returns The new (or existing) component instance with the given properties applied.
127
+ * @param props - Properties to assign onto the component instance.
128
+ * @returns This entity, for chaining.
97
129
  */
98
- set<C extends typeof Component>(Class: C, props: Partial<InstanceType<C>>): InstanceType<C>;
130
+ set<C extends typeof Component>(Class: C, props: Partial<InstanceType<C>>): Entity;
99
131
  /**
100
- * Add a component by its numeric type id and assign the provided properties.
132
+ * Add a component by its numeric type id, assign the provided properties,
133
+ * and return the entity for chaining.
101
134
  *
102
135
  * @param type - Numeric component type id.
103
- * @param props - Optional properties to assign onto the component instance.
136
+ * @param props - Properties to assign onto the component instance.
104
137
  */
105
- set(type: number, props: Partial<Component>): Component;
138
+ set(type: number, props: Partial<Component>): Entity;
106
139
  /**
107
140
  * Remove the component of the given class from this entity.
108
141
  *
@@ -152,7 +185,8 @@ export declare class Entity {
152
185
  _updateQueries(): void;
153
186
  /** `true` when the entity has no components attached. */
154
187
  get empty(): boolean;
155
- private _destroy;
188
+ /** @internal */
189
+ _destroy(): void;
156
190
  /**
157
191
  * Destroy this entity and recursively destroy all of its children.
158
192
  *