Releases: pmndrs/koota
v0.5.0 - Skywind Coastlines
A minor version bump means a breaking change, so what happened? Before the traits schemas were vague about using nested data structures and it was valid to add an object literal as property. However, this had unexpected results as this same object would be copied into all traits. Intuitively you might think a nested store would be created, but it turns out this makes data operations exponentially more complex and avoiding non-scalars is often considered the first rule of normalizing data.
// ❌ Arrays and objects are not allowed in trait schemas
const Inventory = trait({
items: [],
vec3: { x: 0, y: 0, z: 0}
max: 10,
})
// ✅ Use a callback initializer for arrays and objects
const Inventory = trait({
items: () => [],
vec3: () => ({ x: 0, y: 0, z: 0})
max: 10,
})
This breaking change should affect very few users, but all the same it is breaking. In addition to some fixes and internal cleanup we also have wildcard relation removal.
player.add(Likes(apple))
player.add(Likes(banana))
// Remove all Likes relations
player.remove(Likes('*'))
player.has(apple) // false
player.has(banana) // false
What's Changed
- feat: constrain trait schemas to no longer accept object or array literals by @krispya in #141
- feat: remove all relation targets with a wildcard by @krispya in #133
- fix: world set types by @krispya in #136
- chore: update CI node version by @krispya in #137
- chore: update three-stdlib fixing examples by @ospira in #140
New Contributors
Full Changelog: v0.4.4...v0.5.0
v0.4.4 - Elephant Gun
I really love beautiful code. I thrive in an organized space and strive for my code to come out like a haiku, the APIs like finishing a sentence. However, performant code is often ugly and adds complexity to the mental model. For example, inlining functions is a simple way to improve performance on critical pathways that are called many times a second. Each function incurs some overhead, either in its actual execution on the machine or in the JIT compiler that needs to analyze and inline the code at runtime. At the same time, inlining functions makes it harder to maintain the code (it is in multiple places) and is also just ugly!
My ahem passion for an aesthetic dev experience drove me to write the experimental esbuild-plugin-inline-functions
. This lets me write simple, legible functions but guarantee they are inlined when transpiled for the final build. To learn more about how it works, check out the repo.
While I was improving the dev experience of working on Koota, I could not stop gardening and updated all of the tooling with a monorepo layout I am testing for larger projects. It sparks joy.
But with the headline feature out of the way, here is what actually impacts users 😉 :
getStore
is now exported. Use it to get the store. For low level use only! See docs for more info.IsExcluded
is now exported. This tag makes an entity excluded from all queries.unpackEntity
is now exported. Use this function to unpack the entity into its entity ID, generation and world ID. See docs for more info.- The entity generation can now be accessed with
entity.generation()
. - The new linter caught some rerender edge cases in
useTraitEffect
anduseQuery
that I fixed. - Builds are now streamlined with automated inlining improving performance marginally.
- Schemas now support
BigInt
as primitive type. If you have never seen the1n
syntax, check it out!
Note: What happened v0.4.3? I messed up publishing it, so we skipped to v0.4.4. Whoops! 🤫
What's Changed
- chore(core): Add missing exports by @krispya in #110
- feat(core): Entity generation method by @krispya in #118
- feat: Use inline plugin by @krispya in #46
- chore: Migrate to pnpm catalogs by @krispya in #122
- chore: Switch to Oxlint by @krispya in #123
- fix(core): Make array creation explicit by @krispya in #124
- fix(react): useTraitEffect no longer rerenders if callback is an arrow function by @krispya in #125
- fix(react): Make useQuery rerenders more stable by @krispya in #126
- chore: Remove dev package and simplify dev workflow by @krispya in #127
- feat(core): Support BigInt in schemas by @krispya in #129
Full Changelog: v0.4.2...v0.4.4
v0.4.2 - Jumpscare
A bushel of fixes here!
world.reset()
will no longer break if entities have exclusive relations.- Tracking modifiers, including
Changed
, have various bugs fixed related to the tracking not being properly cleared after being read. This fixes in particular using multiple tracking modifiers in the same query.
What's Changed
- Fix typos in README (no queryable → not queryable, enttiy → entity) by @pietdaniel in #112
- fix: ensure entities are only destroyed when present by @wrangelvid in #111
- fix: only update a Changed query when the tracked trait is changed by @r04423 in #116
- Reset bit masks after executing tracking queries by @r04423 in #117
New Contributors
- @pietdaniel made their first contribution in #112
- @wrangelvid made their first contribution in #111
- @r04423 made their first contribution in #116
Full Changelog: v0.4.1...v0.4.2
v0.4.1 - Metro Phenomenon
This release fixes two bugs. The first is simple, cached queries will now return a proper query result. Some refactoring will follow to make this kind of mistake harder to do in the future.
The second enables Koota to work with React Native when bundled with Metro. It turns out that Metro removes all use strict
directives, opting JS files into so-called "sloppy mode" if they are not invoking modules. The causes number primitives to be coerced into objects and destroys the internals of Koota. We now force all apps using Koota to be in strict mode. I'm sorry, we need to enforce updates to the language from 16 years ago. You can read more here.
What's Changed
- 🐛 core: fix cached queries not returning query results by @krispya in #100
- fix: emit "use strict" everywhere to thwart bad bundlers by @thejustinwalsh in #109
- Add failing test case for use query result by @itsdouges in #99
New Contributors
- @itsdouges made their first contribution in #99
- @thejustinwalsh made their first contribution in #109
Full Changelog: v0.4.0...v0.4.1
v0.4.0 - Have Heaven
I realized that the implementation of onAdd
and onRemove
was mixing two ideas together. One is trait events, when a trait is added, removed of changed on an entity. Another is query events, when a query has an entity added or removed from it. These are now two distinct events bringing a breaking change.
onAdd
triggers whenentity.add()
is called after the initial value has been set on the trait.onRemove
triggers whenentity.remove()
is called, but before any data has been removed.onChange
triggers when an entity's trait value has been set withentity.set()
or when it is manually flagged withentity.changed()
.
// Subscribe to Position additions
const unsub = world.onAdd(Position, (entity) => {
console.log(`Entity ${entity} added position`);
});
// Subscribe to Position removals
const unsub = world.onRemove(Position, (entity) => {
console.log(`Entity ${entity} removed position`);
});
// Subscribe to Position changes
const unsub = world.onChange(Position, (entity) => {
console.log(`Entity ${entity} changed position`);
});
And then we have query events which trigger when an entity is added or removed from a query with no guarantees about data. This is stream of all modifications to a query, not to the addition or removal of traits to an entity. You can think of this an internal hook for advanced features that require observing queries like our useQuery
hook.
// Subscribe to add or remove query events
// This triggers whenever a query is updated
// Return unsub function
const unsub = world.onQueryAdd([Position, Velocity], (entity) => {})
const unsub = world.onQueryRemove([Position, Velocity], (entity) => {})
What's Changed
- 💥 core: onAdd is now a trait event, former onAdd is renamed onQueryAdd
- 🐛 core: fix queryFirst types
- 🐛 react: fix useTraitEffect, add tests
v0.3.1 - Palisades
This mostly fixes a few bugs that have been reported but also removes the reset options added in the previous version. They did not work as expected and were creating complexity where it was not needed.
What's Changed
- 🐛 Fix world reset reactivity by @krispya in #75
- 🏷️ core: improve action types by @krispya in #77
- 🐛 core: trait generic type now requires the schema matches by @krispya in #84
- 🐛 core: system traits get properly registered with new worlds by @krispya in #85
- 🐛 core: tracked traits were removed prematurely by @krispya in #88
Full Changelog: v0.3.0...v0.3.1
v0.3.0 - Shame, Shame
This release has a minor breaking change for the React API where I removed the default world. WorldProvider
is required now. It did not help as much as I thought it would and allowed for subtle bugs where it was easy to forget that a ghost world was created. Otherwise, we have a laundry list of updates. Some highlights are:
useQuery
is now more stable for React causing it to hit the fast path more often.- Worlds are now side-effect free and can be GCed without worry.
- A
sort
method has been added to the query result interface. - Getting queries with a cache key now results is proper typing.
What's Changed
- 🏷️ core: add sort method to QueryResult type by @krispya in #64
- ✨ core: add sort method to query result by @krispya in #65
- 🏷️ core: fix cache query types by @krispya in #68
- ✨ react: sort useQuery results for better stability by @krispya in #69
- ✨ core: world is now GC safe by @krispya in #71
- 💥 react: remove default world by @krispya in #72
- ✨ Add world reset options by @krispya in #74
Full Changelog: v0.2.3...v0.3.0
v0.2.3 - Trust
This release fixes some bugs and adds a new method for entities for checking if they are alive.
entity.isAlive() // ture
entity.destroy()
entity.isAlive() // false
What's Changed
- 🐛 core: query all removes destroyed entities by @krispya in #61
- ✨ core: add isAlive method to entity by @krispya in #59
- 🐛 core: query version was not incrementing on remove by @krispya in #60
- ♻️ core: replace entity methods with functions internally by @krispya in #62
Full Changelog: v0.2.1...v0.2.3
v0.2.1 - Lemon Days
This release simply makes Koota compatible to install with React 18 or 19.
What's Changed
Full Changelog: v0.2.0...v0.2.1
v0.2.0 - Words Without Sound
This release includes breaking changes so we are bumping to v0.2.0.
Querying the world with empty parameters now returns all queryable entities.
Previously querying a world with empty parameters (ie world.query()
) would give empty results. But there was two problems, one this doesn't really follow set theory where the empty set is the set of all entities, but also there was no public API for getting all queryable entities. What we mean by queryable entities? Some entities are system level and cannot be queried, but will still show up in world.entities
. This is any entity with the IsExcluded
trait and includes entities that represent the world and any other abstract entity that comes up for future features. This will exclude these entities. Thank you @wrangelvid and @DanAmador for your help here!
world.spawn(Position)
world.entities.length // Is 2 since a system entity is create for world
world.query().length // Is 1 since the system entity is excluded
useQuery
is immutable and more stable
Previously, useQuery
returned a memoized array that was mutated with rerenders being triggered. However, this led to subtle bugs as it broke React's rules about immutable state so we fixed it. useQuery
now returns a new array result each time it rerenders and effects will work as expected. Also, it will not longer rerender twice on first call even if the entities does not change.
What's Changed
- 💥 core: queries with empty params return all queryable entities by @krispya in #45
- 🔧 🐛 Peer dependencies are properly optional, including types by @krispya in #48
- Update README.md by @stephencorwin in #51
- 🐛 react: useQuery was unstable with the same params by @krispya in #52
- Fix relation store get type with get method by @krispya in #56
- Make
useQuery
immutable and more stable by @krispya in #55
New Contributors
- @stephencorwin made their first contribution in #51
- @wrangelvid
Full Changelog: v0.1.12...v0.2.0