Skip to content

Commit 655d3a5

Browse files
committed
🔖 publish: bump
1 parent c24159d commit 655d3a5

File tree

4 files changed

+145
-2
lines changed

4 files changed

+145
-2
lines changed

packages/publish/README.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,11 +48,18 @@ const goblin = world.spawn(Position({ x: 10, y: 10 }), Velocity, Mesh)
4848
Queries fetch entities sharing traits (archetypes). Use them to batch update entities efficiently.
4949

5050
```js
51+
// updateEach mutates and writes back to stores
5152
// Run this in a loop
5253
world.query(Position, Velocity).updateEach(([position, velocity]) => {
5354
position.x += velocity.x * delta
5455
position.y += velocity.y * delta
5556
})
57+
58+
// For read-only operations with no mutation, use readEach
59+
const data = []
60+
world.query(Position, Velocity).readEach(([position, velocity]) => {
61+
data.push({ x: position.x, y: position.y })
62+
})
5663
```
5764

5865
### Use in your React components
@@ -416,6 +423,8 @@ const movingOrVisible = world.query(Or(Velocity, Renderable))
416423
417424
The `Added` modifier tracks all entities that have added the specified traits or relations since the last time the query was run. A new instance of the modifier must be created for tracking to be unique.
418425
426+
When multiple traits are passed to `Added` it uses logical `AND`. Only entities where **all** specified traits have been added will be returned.
427+
419428
```js
420429
import { createAdded } from 'koota'
421430

@@ -427,13 +436,21 @@ const newPositions = world.query(Added(Position))
427436
// Track entities that added a ChildOf relation
428437
const newChildren = world.query(Added(ChildOf))
429438

439+
// Track entities where BOTH Position AND Velocity were added
440+
const fullyAdded = world.query(Added(Position, Velocity))
441+
442+
// Track entities where EITHER Position OR Velocity was added
443+
const eitherAdded = world.query(Or(Added(Position), Added(Velocity)))
444+
430445
// After running the query, the Added modifier is reset
431446
```
432447
433448
#### Removed
434449
435450
The `Removed` modifier tracks all entities that have removed the specified traits or relations since the last time the query was run. This includes entities that have been destroyed. A new instance of the modifier must be created for tracking to be unique.
436451
452+
When multiple traits are passed to `Removed` it uses logical `AND`. Only entities where **all** specified traits have been removed will be returned.
453+
437454
```js
438455
import { createRemoved } from 'koota'
439456

@@ -445,13 +462,21 @@ const stoppedEntities = world.query(Removed(Velocity))
445462
// Track entities that removed a ChildOf relation
446463
const orphaned = world.query(Removed(ChildOf))
447464

465+
// Track entities where BOTH Position AND Velocity were removed
466+
const fullyRemoved = world.query(Removed(Position, Velocity))
467+
468+
// Track entities where EITHER Position OR Velocity was removed
469+
const eitherRemoved = world.query(Or(Removed(Position), Removed(Velocity)))
470+
448471
// After running the query, the Removed modifier is reset
449472
```
450473
451474
#### Changed
452475
453476
The `Changed` modifier tracks all entities that have had the specified traits or relation stores change since the last time the query was run. A new instance of the modifier must be created for tracking to be unique.
454477
478+
When multiple traits are passed to `Changed` it uses logical `AND`. Only entities where **all** specified traits have changed will be returned.
479+
455480
```js
456481
import { createChanged } from 'koota'
457482

@@ -463,6 +488,12 @@ const movedEntities = world.query(Changed(Position))
463488
// Track entities whose ChildOf relation data has changed
464489
const updatedChildren = world.query(Changed(ChildOf))
465490

491+
// Track entities where BOTH Position AND Velocity have changed
492+
const fullyUpdated = world.query(Changed(Position, Velocity))
493+
494+
// Track entities where EITHER Position OR Velocity has changed
495+
const eitherChanged = world.query(Or(Changed(Position), Changed(Velocity)))
496+
466497
// After running the query, the Changed modifier is reset
467498
```
468499

packages/publish/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "koota",
3-
"version": "0.6.4",
4-
"description": "🌎 Performant real-time state management for React and TypeScript",
3+
"version": "0.6.5",
4+
"description": "🌎 Performant real-time state management for TypeScript and React",
55
"license": "ISC",
66
"type": "module",
77
"main": "./src/index.ts",

packages/publish/tests/core/query-modifiers.test.ts

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
createWorld,
88
getStore,
99
Not,
10+
Or,
1011
trait,
1112
} from '../../dist';
1213

@@ -575,6 +576,98 @@ describe('Query modifiers', () => {
575576
expect(entity.get(Name)!.name).toBe('modified');
576577
});
577578

579+
it('should combine Or with Changed modifiers to match ANY changed trait', () => {
580+
const Changed = createChanged();
581+
582+
const entityA = world.spawn(Position, Foo);
583+
const entityB = world.spawn(Position, Foo);
584+
const entityC = world.spawn(Position, Foo);
585+
586+
// No changes yet
587+
let entities = world.query(Or(Changed(Position), Changed(Foo)));
588+
expect(entities.length).toBe(0);
589+
590+
// Change only Position on entityA
591+
entityA.changed(Position);
592+
entities = world.query(Or(Changed(Position), Changed(Foo)));
593+
expect(entities).toContain(entityA);
594+
expect(entities.length).toBe(1);
595+
596+
// Change only Foo on entityB
597+
entityB.changed(Foo);
598+
entities = world.query(Or(Changed(Position), Changed(Foo)));
599+
expect(entities).toContain(entityB);
600+
expect(entities.length).toBe(1);
601+
602+
// Change both on entityC - should still match
603+
entityC.changed(Position);
604+
entityC.changed(Foo);
605+
entities = world.query(Or(Changed(Position), Changed(Foo)));
606+
expect(entities).toContain(entityC);
607+
expect(entities.length).toBe(1);
608+
});
609+
610+
it('should combine Or with Added modifiers to match ANY added trait', () => {
611+
const Added = createAdded();
612+
613+
const entityA = world.spawn();
614+
const entityB = world.spawn();
615+
const entityC = world.spawn();
616+
617+
// No additions yet
618+
let entities = world.query(Or(Added(Position), Added(Foo)));
619+
expect(entities.length).toBe(0);
620+
621+
// Add only Position to entityA
622+
entityA.add(Position);
623+
entities = world.query(Or(Added(Position), Added(Foo)));
624+
expect(entities).toContain(entityA);
625+
expect(entities.length).toBe(1);
626+
627+
// Add only Foo to entityB
628+
entityB.add(Foo);
629+
entities = world.query(Or(Added(Position), Added(Foo)));
630+
expect(entities).toContain(entityB);
631+
expect(entities.length).toBe(1);
632+
633+
// Add both to entityC - should still match
634+
entityC.add(Position, Foo);
635+
entities = world.query(Or(Added(Position), Added(Foo)));
636+
expect(entities).toContain(entityC);
637+
expect(entities.length).toBe(1);
638+
});
639+
640+
it('should combine Or with Removed modifiers to match ANY removed trait', () => {
641+
const Removed = createRemoved();
642+
643+
const entityA = world.spawn(Position, Foo);
644+
const entityB = world.spawn(Position, Foo);
645+
const entityC = world.spawn(Position, Foo);
646+
647+
// No removals yet
648+
let entities = world.query(Or(Removed(Position), Removed(Foo)));
649+
expect(entities.length).toBe(0);
650+
651+
// Remove only Position from entityA
652+
entityA.remove(Position);
653+
entities = world.query(Or(Removed(Position), Removed(Foo)));
654+
expect(entities).toContain(entityA);
655+
expect(entities.length).toBe(1);
656+
657+
// Remove only Foo from entityB
658+
entityB.remove(Foo);
659+
entities = world.query(Or(Removed(Position), Removed(Foo)));
660+
expect(entities).toContain(entityB);
661+
expect(entities.length).toBe(1);
662+
663+
// Remove both from entityC - should still match
664+
entityC.remove(Position);
665+
entityC.remove(Foo);
666+
entities = world.query(Or(Removed(Position), Removed(Foo)));
667+
expect(entities).toContain(entityC);
668+
expect(entities.length).toBe(1);
669+
});
670+
578671
// @internal Tests internal implementation edge case with generation overflow
579672
it('[internal] should handle Changed modifier when trait registration causes generation overflow', () => {
580673
// Create a fresh world to control trait registration count

packages/publish/tests/core/query.test.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,25 @@ describe('Query', () => {
260260
expect(cb).toHaveBeenCalledTimes(9);
261261
});
262262

263+
it('should read trait data with readEach without modifying stores', () => {
264+
for (let i = 0; i < 5; i++) {
265+
world.spawn(Position({ x: i, y: i * 2 }), Name({ name: `Entity${i}` }));
266+
}
267+
268+
const results: any[] = [];
269+
world.query(Position, Name).readEach(([position, name], entity, index) => {
270+
results.push({
271+
x: position.x,
272+
name: name.name,
273+
index,
274+
});
275+
});
276+
277+
expect(results).toHaveLength(5);
278+
expect(results[0]).toEqual({ x: 0, name: 'Entity0', index: 0 });
279+
expect(results[2]).toEqual({ x: 2, name: 'Entity2', index: 2 });
280+
});
281+
263282
it('updateEach should return values in caller parameter order regardless of cache', () => {
264283
// Create entity with both traits
265284
world.spawn(Position({ x: 10, y: 20 }), Name({ name: 'test' }));

0 commit comments

Comments
 (0)