aiecsjs 0.5.1 → 0.5.3
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 +67 -30
- package/README_ZHTW.md +67 -30
- package/api.json +31 -2
- package/dist/chunk-COORLIYF.js +2 -0
- package/dist/chunk-COORLIYF.js.map +1 -0
- package/dist/{chunk-7O3CM4D2.cjs → chunk-D4VGTV76.cjs} +2 -2
- package/dist/{chunk-7O3CM4D2.cjs.map → chunk-D4VGTV76.cjs.map} +1 -1
- package/dist/{chunk-QC25WDOZ.js → chunk-FZWRVGAA.js} +2 -2
- package/dist/{chunk-QC25WDOZ.js.map → chunk-FZWRVGAA.js.map} +1 -1
- package/dist/chunk-NCYUF4YW.cjs +2 -0
- package/dist/chunk-NCYUF4YW.cjs.map +1 -0
- package/dist/{chunk-BUFJWGRQ.js → chunk-UAJNL4VY.js} +2 -2
- package/dist/{chunk-BUFJWGRQ.js.map → chunk-UAJNL4VY.js.map} +1 -1
- package/dist/{chunk-I2SPFVXH.cjs → chunk-XBCASW7K.cjs} +2 -2
- package/dist/{chunk-I2SPFVXH.cjs.map → chunk-XBCASW7K.cjs.map} +1 -1
- package/dist/commands.cjs +1 -1
- package/dist/commands.js +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +18 -2
- package/dist/index.d.ts +18 -2
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/observers.cjs +1 -1
- package/dist/observers.js +1 -1
- package/dist/relations.cjs +1 -1
- package/dist/relations.js +1 -1
- package/dist/serialize.cjs +1 -1
- package/dist/serialize.js +1 -1
- package/dist/worker.cjs +1 -1
- package/dist/worker.js +1 -1
- package/llms-full.txt +85 -32
- package/package.json +1 -1
- package/dist/chunk-I726BP65.cjs +0 -2
- package/dist/chunk-I726BP65.cjs.map +0 -1
- package/dist/chunk-LU55S4LK.js +0 -2
- package/dist/chunk-LU55S4LK.js.map +0 -1
package/llms-full.txt
CHANGED
|
@@ -25,7 +25,7 @@ Part of the [ai\*js micro-runtime ecosystem](https://github.com/yshengliao) —
|
|
|
25
25
|
aiecsjs uses **archetype tables with TypedArray columns** and **bitmask queries** — the same architecture that powers piecs and wolf-ecs at the top of public benchmarks. Its API is **functional and tree-shakable**, composed with `pipe()`. Components support both Structure-of-Arrays (SoA) and Array-of-Structures (AoS) layouts. Since 0.3, `EntityId` packs index + generation into a single 32-bit number; the ABA-safe `EntityRef` API shipped in 0.3.0.
|
|
26
26
|
|
|
27
27
|
```ts
|
|
28
|
-
import { createWorld, createEntity, defineComponent, defineQuery, pipe,
|
|
28
|
+
import { createWorld, createEntity, addComponent, defineComponent, defineQuery, pipe, forEachEntityIndexed, Types } from 'aiecsjs'
|
|
29
29
|
|
|
30
30
|
const Position = defineComponent({ x: Types.f32, y: Types.f32 })
|
|
31
31
|
const Velocity = defineComponent({ x: Types.f32, y: Types.f32 })
|
|
@@ -36,12 +36,14 @@ addComponent(world, eid, Position, { x: 0, y: 0 })
|
|
|
36
36
|
addComponent(world, eid, Velocity, { x: 1, y: 2 })
|
|
37
37
|
|
|
38
38
|
const movers = defineQuery([Position, Velocity])
|
|
39
|
-
const movement = (w, dt) => {
|
|
39
|
+
const movement = (w, dt) => { forEachEntityIndexed(w, movers, (e, i, pos, vel) => { pos.x[i] += vel.x[i] * dt; pos.y[i] += vel.y[i] * dt }); return w }
|
|
40
40
|
|
|
41
41
|
pipe(movement)(world, 1/60)
|
|
42
42
|
```
|
|
43
43
|
|
|
44
|
-
> **
|
|
44
|
+
> **Use `forEachEntityIndexed` for column iteration.** Its callback is `(e, i, ...cols)`: `e` is the packed `EntityId` (use it directly for `destroyEntity`, `hasComponent`, `getComponent`, command buffers), and `i` is the **safe column subscript** — index every SoA column with it (`pos.x[i]`). The packed `e` is **not** a column index: it only equals the index until a slot is recycled (generation 0); after any `destroyEntity` recycles a slot, `e !== i` and indexing a column with `e` reads the wrong slot (out of bounds → `undefined`/`NaN`). `forEachEntityIndexed` hands you the correct `i` so this footgun is closed in code. Use `forEachEntity` when you only need the `EntityId` (and index columns via `getEntityIndex(e)` if you must).
|
|
45
|
+
|
|
46
|
+
> **Status: experimental (v0.5.x).** The API surface in `STABILITY.md` is committed for the 0.x line, but expect adjustments. A stable 1.0 freeze is targeted after community feedback.
|
|
45
47
|
|
|
46
48
|
## Table of contents
|
|
47
49
|
|
|
@@ -144,7 +146,7 @@ Peer requirements: **Node 18+** (for ESM and structured-clone WebStreams), **Typ
|
|
|
144
146
|
import {
|
|
145
147
|
createWorld, createEntity, destroyEntity,
|
|
146
148
|
defineComponent, addComponent, removeComponent,
|
|
147
|
-
defineQuery,
|
|
149
|
+
defineQuery, forEachEntityIndexed, pipe, Types,
|
|
148
150
|
} from 'aiecsjs'
|
|
149
151
|
import { createLoop } from 'aiecsjs/loop'
|
|
150
152
|
|
|
@@ -165,17 +167,17 @@ const movers = defineQuery([Position, Velocity])
|
|
|
165
167
|
const decaying = defineQuery([Lifetime])
|
|
166
168
|
|
|
167
169
|
const movementSystem = (w, dt) => {
|
|
168
|
-
|
|
169
|
-
pos.x[
|
|
170
|
-
pos.y[
|
|
170
|
+
forEachEntityIndexed(w, movers, (e, i, pos, vel) => {
|
|
171
|
+
pos.x[i] += vel.x[i] * dt // `i` is the safe column subscript
|
|
172
|
+
pos.y[i] += vel.y[i] * dt
|
|
171
173
|
})
|
|
172
174
|
return w
|
|
173
175
|
}
|
|
174
176
|
|
|
175
177
|
const lifetimeSystem = (w, dt) => {
|
|
176
|
-
|
|
177
|
-
life.remaining[
|
|
178
|
-
if (life.remaining[
|
|
178
|
+
forEachEntityIndexed(w, decaying, (e, i, life) => {
|
|
179
|
+
life.remaining[i] -= dt
|
|
180
|
+
if (life.remaining[i] <= 0) destroyEntity(w, e) // destroyEntity takes the packed `e`
|
|
179
181
|
})
|
|
180
182
|
return w
|
|
181
183
|
}
|
|
@@ -189,7 +191,7 @@ That's a complete simulation: 100 particles drifting until each one's lifetime e
|
|
|
189
191
|
|
|
190
192
|
## Core Concepts
|
|
191
193
|
|
|
192
|
-
**Entity.** A versioned 32-bit ID. The low bits are the entity index; the high bits are a generation counter that bumps when the ID is recycled. This prevents the "I cached a reference to entity 42 but now entity 42 is something else" class of bug. Default split is 24 index bits + 8 generation bits (≈ 16M entities × 256 recycles each).
|
|
194
|
+
**Entity.** A versioned 32-bit ID. The low bits are the entity index; the high bits are a generation counter that bumps when the ID is recycled. This prevents the "I cached a reference to entity 42 but now entity 42 is something else" class of bug. Default split is 24 index bits + 8 generation bits (≈ 16M entities × 256 recycles each). Because the packed ID is **not** the column index once a slot is recycled, prefer **`forEachEntityIndexed((e, i, ...cols) => …)`** for column iteration — it hands you the masked index `i` (the safe SoA subscript, `pos.x[i]`) alongside the packed `e`. For the raw `forEachEntity` form, derive the index with `getEntityIndex(e)`.
|
|
193
195
|
|
|
194
196
|
**Component.** A data type attached to entities. Two flavours:
|
|
195
197
|
- **SoA (Structure of Arrays)** — declared with `defineComponent({ x: Types.f32, y: Types.f32 })`. Each field becomes a TypedArray column indexed by entity ID. Best for hot, numeric data.
|
|
@@ -243,16 +245,46 @@ if (entityExists(world, eid)) {
|
|
|
243
245
|
|
|
244
246
|
```ts
|
|
245
247
|
const moveSystem = (world: World, dt: number) => {
|
|
246
|
-
|
|
247
|
-
pos.x[
|
|
248
|
-
pos.y[
|
|
248
|
+
forEachEntityIndexed(world, defineQuery([Position, Velocity]), (e, i, pos, vel) => {
|
|
249
|
+
pos.x[i] += vel.x[i] * dt // `i` is the safe column subscript
|
|
250
|
+
pos.y[i] += vel.y[i] * dt
|
|
249
251
|
})
|
|
250
252
|
return world
|
|
251
253
|
}
|
|
252
254
|
```
|
|
253
255
|
|
|
256
|
+
**Prefer `forEachEntityIndexed` for column iteration.** Its callback is `(e, i, ...cols)`: index every SoA column with `i` (the masked slot index), and pass the packed `e` to entity operations (`destroyEntity`, `hasComponent`, command buffers). The yielded `i` is always the correct subscript, even after a slot is recycled — so you never hand-mask or call `getEntityIndex` in the loop. Use **`forEachEntity`** when you only need the `EntityId` (its first argument is a packed `EntityId`, not a column index; derive the subscript with `getEntityIndex(e)` if you must index columns). See [Core Concepts → Entity](#core-concepts) for why `e !== i` once a slot is recycled.
|
|
257
|
+
|
|
254
258
|
Hoist `defineQuery(...)` calls out of the hot loop — the same query object is returned for the same component set, but the lookup still costs a hash.
|
|
255
259
|
|
|
260
|
+
### Tags in mixed queries
|
|
261
|
+
|
|
262
|
+
Every component named in `defineQuery([...])` — **including tags** — occupies one callback slot, in declaration order. A tag has no storage, so its slot is passed as the literal `true`:
|
|
263
|
+
|
|
264
|
+
```ts
|
|
265
|
+
const Frozen = defineTag()
|
|
266
|
+
const q = defineQuery([Frozen, Position]) // tag first
|
|
267
|
+
|
|
268
|
+
// ❌ Wrong: `pos` binds to the tag slot (`true`); `pos.x` throws.
|
|
269
|
+
forEachEntityIndexed(world, q, (e, i, pos) => { /* pos === true */ })
|
|
270
|
+
|
|
271
|
+
// ✅ Correct: account for every slot. Tag arrives as `true`.
|
|
272
|
+
forEachEntityIndexed(world, q, (e, i, _frozen, pos) => {
|
|
273
|
+
pos.x[i] += 1
|
|
274
|
+
})
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
**Recommendation: put tags last** so data columns come first and the trailing `true` slots are easy to ignore:
|
|
278
|
+
|
|
279
|
+
```ts
|
|
280
|
+
const q = defineQuery([Position, Velocity, Frozen]) // tags last
|
|
281
|
+
forEachEntityIndexed(world, q, (e, i, pos, vel /* , _frozen */) => {
|
|
282
|
+
pos.x[i] += vel.x[i]
|
|
283
|
+
})
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
(Tags still filter membership either way — ordering only affects the callback's argument layout. The column-argument list is `any`-typed, so a mismatched binding is a runtime error, not a compile error.)
|
|
287
|
+
|
|
256
288
|
### Composing with pipe and createLoop
|
|
257
289
|
|
|
258
290
|
```ts
|
|
@@ -288,6 +320,8 @@ const reapSystem = (world) => {
|
|
|
288
320
|
|
|
289
321
|
`enterQuery` yields only entities that newly match this frame; `exitQuery` yields only entities that left. Both are computed incrementally during structural changes — there's no per-frame scan.
|
|
290
322
|
|
|
323
|
+
> **Define reactive queries at module scope** (as above), before any update runs. A reactive query's enter/exit buffer is **armed lazily** — the world starts tracking a query's transitions the first time that query (or its `enterQuery`/`exitQuery` view) participates in a structural change or is read. Create/destroy events that happen *before* the first registration are not retroactively captured. Module-scope definitions register the query at import time, so the very first `tick` already observes transitions; queries created lazily inside a system may miss the events from the frame they were introduced.
|
|
324
|
+
|
|
291
325
|
### Observers
|
|
292
326
|
|
|
293
327
|
```ts
|
|
@@ -323,8 +357,8 @@ import { withCommandBuffer } from 'aiecsjs/commands'
|
|
|
323
357
|
const damageSystem = (world) => {
|
|
324
358
|
const dying = defineQuery([Health])
|
|
325
359
|
withCommandBuffer(world, (cb) => {
|
|
326
|
-
|
|
327
|
-
if (health.hp[
|
|
360
|
+
forEachEntityIndexed(world, dying, (e, i, health) => {
|
|
361
|
+
if (health.hp[i] <= 0) cb.destroy(e) // index the column with `i`, queue the packed `e`
|
|
328
362
|
})
|
|
329
363
|
}) // auto-flushes here
|
|
330
364
|
return world
|
|
@@ -426,6 +460,7 @@ const Types = { i8, u8, i16, u16, i32, u32, f32, f64, eid, bool } as const
|
|
|
426
460
|
| `defineQuery` | `(components: ComponentLike[] \| QueryDescriptor) => Query` | stable |
|
|
427
461
|
| `runQuery` | `(world: World, q: Query) => readonly EntityId[]` | stable |
|
|
428
462
|
| `forEachEntity` | `<Q>(world, q: Q, fn: (eid, ...cols) => void) => void` | stable |
|
|
463
|
+
| `forEachEntityIndexed` | `<Q>(world, q: Q, fn: (eid, i, ...cols) => void) => void` | stable |
|
|
429
464
|
| `iterQuery` | `(world, q) => IterableIterator<EntityId>` | stable |
|
|
430
465
|
| `enterQuery` | `(q: Query) => Query` | stable |
|
|
431
466
|
| `exitQuery` | `(q: Query) => Query` | stable |
|
|
@@ -535,7 +570,7 @@ A query for `(Position, Velocity)` matches archetypes 2 and 3 and walks each lin
|
|
|
535
570
|
### Reproducible micro-benchmark
|
|
536
571
|
|
|
537
572
|
```ts
|
|
538
|
-
import { createWorld, createEntity, addComponent, defineComponent, defineQuery,
|
|
573
|
+
import { createWorld, createEntity, addComponent, defineComponent, defineQuery, forEachEntityIndexed, Types } from 'aiecsjs'
|
|
539
574
|
|
|
540
575
|
const Position = defineComponent({ x: Types.f32, y: Types.f32 })
|
|
541
576
|
const Velocity = defineComponent({ x: Types.f32, y: Types.f32 })
|
|
@@ -550,8 +585,8 @@ for (let i = 0; i < 100_000; i++) {
|
|
|
550
585
|
const movers = defineQuery([Position, Velocity])
|
|
551
586
|
const start = performance.now()
|
|
552
587
|
for (let frame = 0; frame < 1000; frame++) {
|
|
553
|
-
|
|
554
|
-
pos.x[
|
|
588
|
+
forEachEntityIndexed(world, movers, (e, i, pos, vel) => {
|
|
589
|
+
pos.x[i] += vel.x[i]; pos.y[i] += vel.y[i]
|
|
555
590
|
})
|
|
556
591
|
}
|
|
557
592
|
console.log('ms per frame:', (performance.now() - start) / 1000)
|
|
@@ -592,18 +627,18 @@ worker.postMessage({ buffer, meta: transferableSnapshot(world).meta })
|
|
|
592
627
|
|
|
593
628
|
```ts
|
|
594
629
|
// sim-worker.ts
|
|
595
|
-
import { adoptSnapshot, defineComponent, defineQuery,
|
|
630
|
+
import { adoptSnapshot, defineComponent, defineQuery, forEachEntityIndexed, Types } from 'aiecsjs'
|
|
596
631
|
|
|
597
632
|
const Position = defineComponent({ x: Types.f32, y: Types.f32 })
|
|
598
633
|
const Velocity = defineComponent({ x: Types.f32, y: Types.f32 })
|
|
599
634
|
|
|
600
|
-
self.onmessage = (
|
|
601
|
-
const world = adoptSnapshot(
|
|
635
|
+
self.onmessage = (msg) => {
|
|
636
|
+
const world = adoptSnapshot(msg.data)
|
|
602
637
|
const movers = defineQuery([Position, Velocity])
|
|
603
638
|
setInterval(() => {
|
|
604
|
-
|
|
605
|
-
pos.x[
|
|
606
|
-
pos.y[
|
|
639
|
+
forEachEntityIndexed(world, movers, (e, i, pos, vel) => {
|
|
640
|
+
pos.x[i] += vel.x[i]
|
|
641
|
+
pos.y[i] += vel.y[i]
|
|
607
642
|
})
|
|
608
643
|
}, 16)
|
|
609
644
|
}
|
|
@@ -750,7 +785,7 @@ This section is designed to be loaded as context by AI coding assistants. The sa
|
|
|
750
785
|
**1. Spawn-and-move**
|
|
751
786
|
|
|
752
787
|
```ts
|
|
753
|
-
import { createWorld, createEntity, addComponent, defineComponent, defineQuery,
|
|
788
|
+
import { createWorld, createEntity, addComponent, defineComponent, defineQuery, forEachEntityIndexed, pipe, Types } from 'aiecsjs'
|
|
754
789
|
|
|
755
790
|
const Position = defineComponent({ x: Types.f32, y: Types.f32 })
|
|
756
791
|
const Velocity = defineComponent({ x: Types.f32, y: Types.f32 })
|
|
@@ -764,7 +799,7 @@ for (let i = 0; i < 1000; i++) {
|
|
|
764
799
|
|
|
765
800
|
const movers = defineQuery([Position, Velocity])
|
|
766
801
|
const move = (w, dt) => {
|
|
767
|
-
|
|
802
|
+
forEachEntityIndexed(w, movers, (e, i, p, v) => { p.x[i] += v.x[i] * dt; p.y[i] += v.y[i] * dt })
|
|
768
803
|
return w
|
|
769
804
|
}
|
|
770
805
|
pipe(move)(world, 0.016)
|
|
@@ -843,7 +878,7 @@ ws.onmessage = (e) => rx.apply(remoteWorld, new Uint8Array(e.data))
|
|
|
843
878
|
- `defineQuery(X)` returns the same `Query` object for the same component set in the same module.
|
|
844
879
|
- Entity ID `0` is reserved. `createEntity` never returns `0`.
|
|
845
880
|
- `VERSION` exported from `'aiecsjs'` equals the published npm version.
|
|
846
|
-
- SoA columns are TypedArrays
|
|
881
|
+
- SoA columns are TypedArrays indexed by the entity **index** (always in `[0, getWorldCapacity(world))`), never by the packed `eid` — the two differ once a slot is recycled. `forEachEntityIndexed` yields this index as its `i` argument (correct for any world layout). For the raw `forEachEntity` form on the default world layout (`indexBits: 24`) the index is `getEntityIndex(eid)`; for a world created with a non-default `indexBits`, `getEntityIndex` uses the default 24-bit mask and is **not** the column index — prefer `forEachEntityIndexed` (or `EntityRef` + `deref`) instead.
|
|
847
882
|
- Component identity is **global** (created by `defineComponent`), but each component's storage is **per-world**.
|
|
848
883
|
|
|
849
884
|
### Glossary
|
|
@@ -883,14 +918,16 @@ When you generate code that uses aiecsjs, include this comment at the top of the
|
|
|
883
918
|
### Known LLM gotchas
|
|
884
919
|
|
|
885
920
|
- **aiecsjs is NOT bitECS.** Argument order for `addComponent` differs: aiecsjs uses `(world, eid, Component, init?)`; bitECS uses `(world, Component, eid)`.
|
|
886
|
-
-
|
|
921
|
+
- **Index SoA columns with the masked index, NOT the packed `EntityId`.** The footgun is closed in code: prefer **`forEachEntityIndexed(w, q, (e, i, ...cols) => …)`**, which yields the masked slot index `i` (the correct subscript: `pos.x[i]`) alongside the packed `e`. The packed `e` is **not** a column index — it only equals the index while no slot has been recycled (generation 0); after any `destroyEntity` it reads the wrong/out-of-bounds slot. With the raw `forEachEntity` form, derive the index yourself: hoist `const i = getEntityIndex(e)` once per callback. Either way, use `e` for entity operations (`destroyEntity(w, e)`, `hasComponent`, `getComponent`, command buffers).
|
|
922
|
+
- **A tag occupies a callback slot too.** `defineQuery([Tag, Position])` calls the callback as `(e, i, true, posCols)` under `forEachEntityIndexed` (`(e, true, posCols)` under `forEachEntity`) — the tag is passed as the literal `true`. Put tags **last** in the array (`defineQuery([Position, Tag])`) so data columns come first; or accept and ignore the trailing `true`. Binding `pos` to a tag slot makes `pos === true`, and `pos.x` throws.
|
|
923
|
+
- **`forEachEntity` / `forEachEntityIndexed` are the fast path.** `runQuery` allocates an array; `for...of iterQuery(...)` allocates an iterator. In hot loops, use `forEachEntityIndexed` (or `forEachEntity` when you only need the `EntityId`).
|
|
887
924
|
- **`defineObjectComponent` factory runs ONCE at definition**, not per entity. Mutate the entity's instance via `setComponent` / `getComponent`.
|
|
888
925
|
- **The component reference is the storage handle.** `Position` is not a constructor — it's a value object that aiecsjs uses to address the right archetype columns.
|
|
889
926
|
|
|
890
927
|
## FAQ
|
|
891
928
|
|
|
892
929
|
**Q: Is aiecsjs production-ready?**
|
|
893
|
-
A: Not yet. 0.
|
|
930
|
+
A: Not yet. 0.x is experimental. The API surface in `STABILITY.md` is the working contract; expect bug fixes. Target 1.0 is post-implementation hardening.
|
|
894
931
|
|
|
895
932
|
**Q: Can I use class instances as components?**
|
|
896
933
|
A: Yes, with `defineObjectComponent`. But AoS components are main-thread only and slower than SoA in iteration.
|
|
@@ -941,7 +978,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
|
941
978
|
|
|
942
979
|
## [Unreleased]
|
|
943
980
|
|
|
944
|
-
### Planned
|
|
981
|
+
### Planned
|
|
945
982
|
|
|
946
983
|
- Add `pipeAsync` for async system composition.
|
|
947
984
|
- Doc-test harness so README code blocks are mechanically verified.
|
|
@@ -954,6 +991,22 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
|
954
991
|
65 536 entities × 65 536 generations). See test
|
|
955
992
|
[tests/ref.test.ts](./tests/ref.test.ts) `generation wrap` describe block.
|
|
956
993
|
|
|
994
|
+
## [0.5.3] - 2026-06-05
|
|
995
|
+
|
|
996
|
+
### Added
|
|
997
|
+
|
|
998
|
+
- Add `forEachEntityIndexed` yielding the masked index alongside the EntityId; closes the A1 packed-EntityId footgun in code (safe column iteration is now the default; `forEachEntity` unchanged).
|
|
999
|
+
|
|
1000
|
+
### Docs
|
|
1001
|
+
|
|
1002
|
+
- **Correctness clarification — `forEachEntity`'s `e` is a packed `EntityId`, not a column index.** The column-iteration docs/examples now lead with `forEachEntityIndexed((e, i, ...cols) => …)`, indexing SoA columns with the yielded safe index `i` (`pos.x[i]`) — no manual masking (`README.md`, `README_ZHTW.md`, `docs/MIGRATION.md`, `docs/MIGRATION_ZHTW.md`, regenerated `llms-full.txt`). `forEachEntity` is documented as the form to use when you only need the `EntityId`; for that raw form, derive the subscript with `getEntityIndex(e)`. The old `pos.x[e]` pattern only worked while generation 0 (`e === getEntityIndex(e)`); after any `destroyEntity` recycles a slot it read an out-of-bounds column slot. Added a recycle regression test proving both the bug and the fix; `e`'s packed semantics are intentionally unchanged (it is still what you pass to `destroyEntity`/`hasComponent`/command buffers). Corrected the 0.1.0 storage note that implied `Position.x[eid]` indexes by the packed id.
|
|
1003
|
+
- **A2 (tag slot footgun).** Documented that each tag in `defineQuery([...])` occupies a callback slot passed as `true`, with a correct mixed-query example; recommend placing tags last so data columns come first. Docs-only — the callback arity is intentionally unchanged (changing it would shift data params for code that correctly writes `(e, _tag, pos)`; deferred to a future major). Column arguments remain `any`-typed.
|
|
1004
|
+
- **A3 (reactive-query lazy arm).** Documented that enter/exit buffers are armed lazily on first registration/read, and recommend defining reactive queries at module scope so the first `tick` already observes transitions.
|
|
1005
|
+
|
|
1006
|
+
### Fixed
|
|
1007
|
+
|
|
1008
|
+
- Synced the embedded `VERSION` constant (`src/version.ts`) to `0.5.2` to match `package.json` — the release gate asserts they are equal (`tests/utils.test.ts`). No API change; `dist` differs from 0.5.1 only by this version stamp.
|
|
1009
|
+
|
|
957
1010
|
## [0.5.1] - 2026-06-02
|
|
958
1011
|
|
|
959
1012
|
### Fixed
|
|
@@ -1213,7 +1266,7 @@ The "documentation honesty + test backstop" release. No new public APIs; this is
|
|
|
1213
1266
|
|
|
1214
1267
|
### Implementation notes
|
|
1215
1268
|
|
|
1216
|
-
- **Storage**: world-level TypedArray columns per SoA component field, sized to world capacity. Archetypes track entity membership (a `Uint32Array entities[]`) but do not own column data. This makes archetype migration O(1) and lets `Position.x[eid]`
|
|
1269
|
+
- **Storage**: world-level TypedArray columns per SoA component field, sized to world capacity. Archetypes track entity membership (a `Uint32Array entities[]`) but do not own column data. This makes archetype migration O(1) and lets columns be indexed by the entity **index** (`getEntityIndex(eid)`) directly, without per-archetype indirection. (In 0.1 `EntityId` *was* the index, so `Position.x[eid]` worked literally; since 0.3 packs a generation into the id, hot-loop code must index with `Position.x[getEntityIndex(eid)]` — the raw packed id is no longer the column offset.) Trade-off: iteration over archetypes reads columns at potentially non-contiguous offsets; for hot data this stays in L1.
|
|
1217
1270
|
- **EntityId is unversioned in 0.1**: `EntityId` is the entity index. Generation is tracked internally for slot reuse but not encoded in the ID. `getEntityIndex` / `getEntityGeneration` / `packEntity` are identity helpers. ABA-safe references via a separate `EntityRef` type are planned for 0.2.
|
|
1218
1271
|
- **Bitmask queries**: multi-word Uint32 masks, default 8 words (256 components). Per-world bit allocation, global component identity.
|
|
1219
1272
|
- **Worker / SAB**: 0.1 implements snapshot-copy semantics (serialize-into-SAB on send, deserialize-on-adopt) rather than true shared-memory column aliasing. The API surface matches the documented contract; true shared columns ship in 0.2.
|
package/package.json
CHANGED
package/dist/chunk-I726BP65.cjs
DELETED
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
'use strict';var chunk7O3CM4D2_cjs=require('./chunk-7O3CM4D2.cjs');var C=1,f=new Map;function _(n){let e=(n.all??[]).map(r=>r.__id).sort((r,o)=>r-o).join("-"),t=(n.any??[]).map(r=>r.__id).sort((r,o)=>r-o).join("-"),i=(n.none??[]).map(r=>r.__id).sort((r,o)=>r-o).join("-");return `A${e}|Y${t}|N${i}`}function q(n){let e=Array.isArray(n)?{all:n}:n;for(let o of [...e.all??[],...e.any??[],...e.none??[]])if(!o||typeof o!="object"||typeof o.__id!="number")throw new TypeError("aiecsjs: defineQuery received a non-component value");let t=_(e),i=f.get(t);if(i)return i;let r={id:C++,mask:[],all:(e.all??[]).map(o=>o.__id),any:(e.any??[]).map(o=>o.__id),none:(e.none??[]).map(o=>o.__id),columnViewCache:[...e.all??[],...e.any??[]],reactiveKind:"normal",sourceQueryId:-1,sourceQuery:null};return f.set(t,r),r}function z(n){let e=n,t=`enter:${e.id}`,i=f.get(t);if(i)return i;let r={id:C++,mask:[],all:e.all,any:e.any,none:e.none,columnViewCache:e.columnViewCache,reactiveKind:"enter",sourceQueryId:e.id,sourceQuery:e};return f.set(t,r),r}function T(n){let e=n,t=`exit:${e.id}`,i=f.get(t);if(i)return i;let r={id:C++,mask:[],all:e.all,any:e.any,none:e.none,columnViewCache:e.columnViewCache,reactiveKind:"exit",sourceQueryId:e.id,sourceQuery:e};return f.set(t,r),r}function p(n,e){if(n.queries[e.id]===e&&n.queryMasks.has(e.id))return;let t=n.options.maskWordCount,i=chunk7O3CM4D2_cjs.b(t),r=chunk7O3CM4D2_cjs.b(t),o=chunk7O3CM4D2_cjs.b(t);for(let a of e.all){let u=b(a),d=chunk7O3CM4D2_cjs.p(n,u);chunk7O3CM4D2_cjs.c(i,d);}for(let a of e.any){let u=b(a),d=chunk7O3CM4D2_cjs.p(n,u);chunk7O3CM4D2_cjs.c(r,d);}for(let a of e.none){let u=b(a),d=chunk7O3CM4D2_cjs.p(n,u);chunk7O3CM4D2_cjs.c(o,d);}let c={withMask:i,anyMask:r,noneMask:o,anyHasBits:!chunk7O3CM4D2_cjs.d(r)};n.queryMasks.set(e.id,c),n.queries[e.id]=e,n.queryArchetypeCache[e.id]=null,n.queryArchetypeStamp[e.id]=-1;let s=[...chunk7O3CM4D2_cjs.f(i),...chunk7O3CM4D2_cjs.f(r),...chunk7O3CM4D2_cjs.f(o)];for(let a of s){let u=n.bitToQueries.get(a);u||(u=new Set,n.bitToQueries.set(a,u)),u.add(e.id);}e.reactiveKind!=="normal"&&(n.reactiveBuffers.has(e.id)||n.reactiveBuffers.set(e.id,{entered:[],exited:[]}),e.sourceQuery&&p(n,e.sourceQuery));}function v(n,e){if(p(n,e),n.queryArchetypeCache[e.id]&&n.queryArchetypeStamp[e.id]===n.queryVersion)return n.queryArchetypeCache[e.id];let t=n.queryMasks.get(e.id),i=n.options.maskWordCount,r=[];for(let o=0;o<n.archetypes.length;o++){let c=n.archetypes[o];chunk7O3CM4D2_cjs.e(c.mask,t.withMask,t.anyMask,t.noneMask,t.anyHasBits,i)&&r.push(o);}return n.queryArchetypeCache[e.id]=r,n.queryArchetypeStamp[e.id]=n.queryVersion,r}function $(n,e){let t=chunk7O3CM4D2_cjs.i(n),r=v(t,e),o=[];for(let c of r){let s=t.archetypes[c];o.push({id:s.id,mask:Array.from(s.mask),size:s.size});}return o}function D(n,e){let t=chunk7O3CM4D2_cjs.i(n),i=e,r=[];if(i.reactiveKind==="enter"){let c=t.reactiveBuffers.get(i.id);if(c){for(let s of c.entered)r.push(s);c.entered.length=0;}return r}if(i.reactiveKind==="exit"){let c=t.reactiveBuffers.get(i.id);if(c){for(let s of c.exited)r.push(s);c.exited.length=0;}return r}let o=v(t,i);for(let c of o){let s=t.archetypes[c];for(let a=0;a<s.size;a++)r.push(s.entities[a]);}return r}function*H(n,e){let t=chunk7O3CM4D2_cjs.i(n),i=e;if(i.reactiveKind!=="normal"){let o=t.reactiveBuffers.get(i.id);if(!o)return;let c=i.reactiveKind==="enter"?o.entered:o.exited;for(let s of c)yield s;c.length=0;return}let r=v(t,i);for(let o of r){let c=t.archetypes[o];for(let s=0;s<c.size;s++)yield c.entities[s];}}function L(n,e,t){let i=chunk7O3CM4D2_cjs.i(n),r=e;if(r.reactiveKind!=="normal"){let s=i.reactiveBuffers.get(r.id);if(!s)return;let a=r.reactiveKind==="enter"?s.entered:s.exited;if(a.length===0)return;let u=w(i,r);for(let d=0;d<a.length;d++){let y=a[d];x(t,y,u);}a.length=0;return}p(i,r);let o=v(i,r),c=w(i,r);for(let s of o){let a=i.archetypes[s],u=a.entities,d=a.size;for(let y=0;y<d;y++)x(t,u[y],c);}}function x(n,e,t){switch(t.length){case 0:n(e);break;case 1:n(e,t[0]);break;case 2:n(e,t[0],t[1]);break;case 3:n(e,t[0],t[1],t[2]);break;case 4:n(e,t[0],t[1],t[2],t[3]);break;case 5:n(e,t[0],t[1],t[2],t[3],t[4]);break;default:n(e,...t);}}function w(n,e){let t=[];for(let i of e.columnViewCache){let r=chunk7O3CM4D2_cjs.I(i),o=n.componentBitFor.get(r.id);if(o===void 0){t.push(void 0);continue}let c=n.componentStorageByBit[o];if(!c){t.push(void 0);continue}c.kind==="soa"?t.push(c.soa):c.kind==="aos"?t.push(c.aos):t.push(true);}return t}function F(n,e,t,i,r){for(let s of f.values())s.reactiveKind!=="normal"&&s.sourceQuery&&n.queries[s.sourceQuery.id]!==s.sourceQuery&&p(n,s.sourceQuery);let o=n.bitToQueries.get(t);if(!o)return;let c=n.options.maskWordCount;for(let s of o){let a=n.queries[s];if(!a||a.reactiveKind!=="normal")continue;let u=n.queryMasks.get(s);if(!u)continue;let d=chunk7O3CM4D2_cjs.e(i,u.withMask,u.anyMask,u.noneMask,u.anyHasBits,c),y=chunk7O3CM4D2_cjs.e(r,u.withMask,u.anyMask,u.noneMask,u.anyHasBits,c);!d&&y?E(n,s,"enter",e):d&&!y&&E(n,s,"exit",e);}}function E(n,e,t,i){for(let r of f.values()){if(r.sourceQueryId!==e||r.reactiveKind!==t)continue;p(n,r);let o=A(n,r.id);t==="enter"?o.entered.push(i):o.exited.push(i);}}function A(n,e){let t=n.reactiveBuffers.get(e);return t||(t={entered:[],exited:[]},n.reactiveBuffers.set(e,t)),t}function b(n){let e=W(n);if(!e)throw new Error(`aiecsjs: component id ${n} not registered`);return e}var g=null;function O(n){g=n;}function W(n){return g?g(n):void 0}exports.a=q;exports.b=z;exports.c=T;exports.d=$;exports.e=D;exports.f=H;exports.g=L;exports.h=F;exports.i=O;//# sourceMappingURL=chunk-I726BP65.cjs.map
|
|
2
|
-
//# sourceMappingURL=chunk-I726BP65.cjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/internal/query.ts"],"names":["nextQueryId","moduleQueryCache","descKey","d","all","c","a","b","any","none","defineQuery","arg","desc","key","cached","q","enterQuery","query","reactive","exitQuery","ensureQueryRegistered","state","ww","withMask","createMask","anyMask","noneMask","compId","info","getComponentInfoById","bit","getOrRegisterComponentBit","setBit","bundle","isMaskZero","involvedBits","listBits","s","getQueryArchetypes","words","list","i","arch","matches","queryArchetypes","world","getWorldState","ids","out","id","runQuery","buf","e","archIds","r","iterQuery","src","forEachEntity","fn","cols","buildColumnViews","callWithCols","ents","n","eid","comp","getComponentInfo","storage","recordEntityMaskChange","changedBit","prevMask","nextMask","involved","qid","wasMatch","isMatch","pushReactive","queryId","kind","ensureReactiveBuffer","componentId","lazyGetComponentInfo","_getComponentInfoFn","registerComponentLookup"],"mappings":"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","file":"chunk-I726BP65.cjs","sourcesContent":["import { createMask, isMaskZero, listBits, matches, setBit } from './bitmask.js'\nimport { getComponentInfo } from './component.js'\nimport type {\n Archetype,\n ComponentInfo,\n ComponentLike,\n EntityId,\n Query,\n QueryDescriptor,\n QueryInternal,\n QueryMaskBundle,\n ReactiveBuffer,\n World,\n WorldState,\n} from './types.js'\nimport {\n getOrRegisterComponentBit,\n getWorldState,\n readEntityMask,\n tryGetComponentBit,\n} from './world.js'\n\nlet nextQueryId = 1\nconst moduleQueryCache = new Map<string, QueryInternal>()\n\nfunction descKey(d: QueryDescriptor): string {\n const all = (d.all ?? [])\n .map((c) => c.__id)\n .sort((a, b) => a - b)\n .join('-')\n const any = (d.any ?? [])\n .map((c) => c.__id)\n .sort((a, b) => a - b)\n .join('-')\n const none = (d.none ?? [])\n .map((c) => c.__id)\n .sort((a, b) => a - b)\n .join('-')\n return `A${all}|Y${any}|N${none}`\n}\n\nexport function defineQuery(arg: ComponentLike[] | QueryDescriptor): Query {\n const desc: QueryDescriptor = Array.isArray(arg) ? { all: arg } : arg\n\n // Validate all members are components\n for (const c of [...(desc.all ?? []), ...(desc.any ?? []), ...(desc.none ?? [])]) {\n if (!c || typeof c !== 'object' || typeof (c as any).__id !== 'number') {\n throw new TypeError('aiecsjs: defineQuery received a non-component value')\n }\n }\n\n const key = descKey(desc)\n const cached = moduleQueryCache.get(key)\n if (cached) return cached\n\n const q: QueryInternal = {\n id: nextQueryId++,\n mask: [],\n all: (desc.all ?? []).map((c) => c.__id),\n any: (desc.any ?? []).map((c) => c.__id),\n none: (desc.none ?? []).map((c) => c.__id),\n columnViewCache: [...(desc.all ?? []), ...(desc.any ?? [])],\n reactiveKind: 'normal',\n sourceQueryId: -1,\n sourceQuery: null,\n }\n moduleQueryCache.set(key, q)\n return q\n}\n\nexport function enterQuery(query: Query): Query {\n const q = query as QueryInternal\n const key = `enter:${q.id}`\n const cached = moduleQueryCache.get(key)\n if (cached) return cached\n const reactive: QueryInternal = {\n id: nextQueryId++,\n mask: [],\n all: q.all,\n any: q.any,\n none: q.none,\n columnViewCache: q.columnViewCache,\n reactiveKind: 'enter',\n sourceQueryId: q.id,\n sourceQuery: q,\n }\n moduleQueryCache.set(key, reactive)\n return reactive\n}\n\nexport function exitQuery(query: Query): Query {\n const q = query as QueryInternal\n const key = `exit:${q.id}`\n const cached = moduleQueryCache.get(key)\n if (cached) return cached\n const reactive: QueryInternal = {\n id: nextQueryId++,\n mask: [],\n all: q.all,\n any: q.any,\n none: q.none,\n columnViewCache: q.columnViewCache,\n reactiveKind: 'exit',\n sourceQueryId: q.id,\n sourceQuery: q,\n }\n moduleQueryCache.set(key, reactive)\n return reactive\n}\n\n// --- Per-world query setup ---\n\nfunction ensureQueryRegistered(state: WorldState, q: QueryInternal): void {\n if (state.queries[q.id] === q && state.queryMasks.has(q.id)) return\n\n // Build per-world bitmasks (this may register new bits)\n const ww = state.options.maskWordCount\n const withMask = createMask(ww)\n const anyMask = createMask(ww)\n const noneMask = createMask(ww)\n for (const compId of q.all) {\n const info = getComponentInfoById(compId)\n const bit = getOrRegisterComponentBit(state, info)\n setBit(withMask, bit)\n }\n for (const compId of q.any) {\n const info = getComponentInfoById(compId)\n const bit = getOrRegisterComponentBit(state, info)\n setBit(anyMask, bit)\n }\n for (const compId of q.none) {\n const info = getComponentInfoById(compId)\n const bit = getOrRegisterComponentBit(state, info)\n setBit(noneMask, bit)\n }\n\n const bundle: QueryMaskBundle = {\n withMask,\n anyMask,\n noneMask,\n anyHasBits: !isMaskZero(anyMask),\n }\n state.queryMasks.set(q.id, bundle)\n\n state.queries[q.id] = q\n state.queryArchetypeCache[q.id] = null\n state.queryArchetypeStamp[q.id] = -1\n\n // Build bit → queries index for fast reactive lookup\n const involvedBits: number[] = [\n ...listBits(withMask),\n ...listBits(anyMask),\n ...listBits(noneMask),\n ]\n for (const b of involvedBits) {\n let s = state.bitToQueries.get(b)\n if (!s) {\n s = new Set<number>()\n state.bitToQueries.set(b, s)\n }\n s.add(q.id)\n }\n\n // Initialize reactive buffer for enter/exit queries\n if (q.reactiveKind !== 'normal') {\n if (!state.reactiveBuffers.has(q.id)) {\n state.reactiveBuffers.set(q.id, { entered: [], exited: [] })\n }\n // Also register the source query so recordEntityMaskChange will see it\n if (q.sourceQuery) ensureQueryRegistered(state, q.sourceQuery)\n }\n}\n\nfunction getQueryArchetypes(state: WorldState, q: QueryInternal): number[] {\n ensureQueryRegistered(state, q)\n if (state.queryArchetypeCache[q.id] && state.queryArchetypeStamp[q.id] === state.queryVersion) {\n return state.queryArchetypeCache[q.id]!\n }\n const bundle = state.queryMasks.get(q.id)!\n const words = state.options.maskWordCount\n const list: number[] = []\n for (let i = 0; i < state.archetypes.length; i++) {\n const arch = state.archetypes[i]!\n if (\n matches(arch.mask, bundle.withMask, bundle.anyMask, bundle.noneMask, bundle.anyHasBits, words)\n ) {\n list.push(i)\n }\n }\n state.queryArchetypeCache[q.id] = list\n state.queryArchetypeStamp[q.id] = state.queryVersion\n return list\n}\n\nexport function queryArchetypes(world: World, query: Query): readonly Archetype[] {\n const state = getWorldState(world)\n const q = query as QueryInternal\n const ids = getQueryArchetypes(state, q)\n const out: Archetype[] = []\n for (const id of ids) {\n const arch = state.archetypes[id]!\n out.push({ id: arch.id, mask: Array.from(arch.mask), size: arch.size })\n }\n return out\n}\n\nexport function runQuery(world: World, query: Query): readonly EntityId[] {\n const state = getWorldState(world)\n const q = query as QueryInternal\n const out: EntityId[] = []\n if (q.reactiveKind === 'enter') {\n const buf = state.reactiveBuffers.get(q.id)\n if (buf) {\n for (const e of buf.entered) out.push(e as EntityId)\n buf.entered.length = 0\n }\n return out\n }\n if (q.reactiveKind === 'exit') {\n const buf = state.reactiveBuffers.get(q.id)\n if (buf) {\n for (const e of buf.exited) out.push(e as EntityId)\n buf.exited.length = 0\n }\n return out\n }\n const archIds = getQueryArchetypes(state, q)\n for (const id of archIds) {\n const arch = state.archetypes[id]!\n for (let r = 0; r < arch.size; r++) {\n out.push(arch.entities[r] as EntityId)\n }\n }\n return out\n}\n\nexport function* iterQuery(world: World, query: Query): IterableIterator<EntityId> {\n const state = getWorldState(world)\n const q = query as QueryInternal\n if (q.reactiveKind !== 'normal') {\n const buf = state.reactiveBuffers.get(q.id)\n if (!buf) return\n const src = q.reactiveKind === 'enter' ? buf.entered : buf.exited\n for (const e of src) yield e as EntityId\n src.length = 0\n return\n }\n const archIds = getQueryArchetypes(state, q)\n for (const id of archIds) {\n const arch = state.archetypes[id]!\n for (let r = 0; r < arch.size; r++) {\n yield arch.entities[r] as EntityId\n }\n }\n}\n\nexport function forEachEntity(\n world: World,\n query: Query,\n fn: (eid: EntityId, ...cols: any[]) => void,\n): void {\n const state = getWorldState(world)\n const q = query as QueryInternal\n\n if (q.reactiveKind !== 'normal') {\n const buf = state.reactiveBuffers.get(q.id)\n if (!buf) return\n const src = q.reactiveKind === 'enter' ? buf.entered : buf.exited\n if (src.length === 0) return\n const cols = buildColumnViews(state, q)\n for (let i = 0; i < src.length; i++) {\n const e = src[i] as EntityId\n callWithCols(fn, e, cols)\n }\n src.length = 0\n return\n }\n\n ensureQueryRegistered(state, q)\n const archIds = getQueryArchetypes(state, q)\n const cols = buildColumnViews(state, q)\n for (const id of archIds) {\n const arch = state.archetypes[id]!\n const ents = arch.entities\n const n = arch.size\n for (let r = 0; r < n; r++) {\n callWithCols(fn, ents[r] as EntityId, cols)\n }\n }\n}\n\nfunction callWithCols(\n fn: (eid: EntityId, ...cols: any[]) => void,\n eid: EntityId,\n cols: any[],\n): void {\n // Specialise for low arities to avoid spread allocation.\n switch (cols.length) {\n case 0:\n fn(eid)\n break\n case 1:\n fn(eid, cols[0])\n break\n case 2:\n fn(eid, cols[0], cols[1])\n break\n case 3:\n fn(eid, cols[0], cols[1], cols[2])\n break\n case 4:\n fn(eid, cols[0], cols[1], cols[2], cols[3])\n break\n case 5:\n fn(eid, cols[0], cols[1], cols[2], cols[3], cols[4])\n break\n default:\n fn(eid, ...cols)\n }\n}\n\nfunction buildColumnViews(state: WorldState, q: QueryInternal): any[] {\n const out: any[] = []\n for (const comp of q.columnViewCache) {\n const info = getComponentInfo(comp)\n const bit = state.componentBitFor.get(info.id)\n if (bit === undefined) {\n out.push(undefined)\n continue\n }\n const storage = state.componentStorageByBit[bit]\n if (!storage) {\n out.push(undefined)\n continue\n }\n if (storage.kind === 'soa') out.push(storage.soa)\n else if (storage.kind === 'aos') out.push(storage.aos)\n else out.push(true) // tag\n }\n return out\n}\n\n// --- Reactive query updates (called from migration code) ---\n\nexport function recordEntityMaskChange(\n state: WorldState,\n eid: EntityId,\n changedBit: number,\n prevMask: Uint32Array,\n nextMask: Uint32Array,\n): void {\n // Lazy-register any reactive queries (and their sources) so we can correctly\n // determine match transitions even if the user only ever called enterQuery/exitQuery.\n for (const q of moduleQueryCache.values()) {\n if (q.reactiveKind === 'normal') continue\n if (q.sourceQuery && state.queries[q.sourceQuery.id] !== q.sourceQuery) {\n ensureQueryRegistered(state, q.sourceQuery)\n }\n }\n\n const involved = state.bitToQueries.get(changedBit)\n if (!involved) return\n const words = state.options.maskWordCount\n for (const qid of involved) {\n const q = state.queries[qid]\n if (!q) continue\n if (q.reactiveKind !== 'normal') continue\n const bundle = state.queryMasks.get(qid)\n if (!bundle) continue\n const wasMatch = matches(\n prevMask,\n bundle.withMask,\n bundle.anyMask,\n bundle.noneMask,\n bundle.anyHasBits,\n words,\n )\n const isMatch = matches(\n nextMask,\n bundle.withMask,\n bundle.anyMask,\n bundle.noneMask,\n bundle.anyHasBits,\n words,\n )\n if (!wasMatch && isMatch) {\n pushReactive(state, qid, 'enter', eid)\n } else if (wasMatch && !isMatch) {\n pushReactive(state, qid, 'exit', eid)\n }\n }\n}\n\nfunction pushReactive(\n state: WorldState,\n queryId: number,\n kind: 'enter' | 'exit',\n eid: EntityId,\n): void {\n // Walk the module cache (not just state.queries) so reactive variants that\n // haven't been registered with this world yet still receive events.\n for (const r of moduleQueryCache.values()) {\n if (r.sourceQueryId !== queryId) continue\n if (r.reactiveKind !== kind) continue\n // Lazily register the reactive query in this world so subsequent reads can find it\n ensureQueryRegistered(state, r)\n const buf = ensureReactiveBuffer(state, r.id)\n if (kind === 'enter') buf.entered.push(eid as number)\n else buf.exited.push(eid as number)\n }\n}\n\nfunction ensureReactiveBuffer(state: WorldState, qid: number): ReactiveBuffer {\n let buf = state.reactiveBuffers.get(qid)\n if (!buf) {\n buf = { entered: [], exited: [] }\n state.reactiveBuffers.set(qid, buf)\n }\n return buf\n}\n\n// --- Helpers ---\n\nfunction getComponentInfoById(componentId: number): ComponentInfo {\n // We need access to the component registry's lookup by id. Re-import dynamically to avoid cycles.\n const info = lazyGetComponentInfo(componentId)\n if (!info) throw new Error(`aiecsjs: component id ${componentId} not registered`)\n return info\n}\n\nlet _getComponentInfoFn: ((id: number) => ComponentInfo | undefined) | null = null\nexport function registerComponentLookup(fn: (id: number) => ComponentInfo | undefined): void {\n _getComponentInfoFn = fn\n}\nfunction lazyGetComponentInfo(id: number): ComponentInfo | undefined {\n return _getComponentInfoFn ? _getComponentInfoFn(id) : undefined\n}\n\nexport function _resetQueryRegistry_FOR_TESTS_ONLY(): void {\n moduleQueryCache.clear()\n nextQueryId = 1\n}\n"]}
|
package/dist/chunk-LU55S4LK.js
DELETED
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
import {i,I,e,b as b$1,p as p$1,c,d,f as f$1}from'./chunk-BUFJWGRQ.js';var C=1,f=new Map;function _(n){let e=(n.all??[]).map(r=>r.__id).sort((r,o)=>r-o).join("-"),t=(n.any??[]).map(r=>r.__id).sort((r,o)=>r-o).join("-"),i=(n.none??[]).map(r=>r.__id).sort((r,o)=>r-o).join("-");return `A${e}|Y${t}|N${i}`}function q(n){let e=Array.isArray(n)?{all:n}:n;for(let o of [...e.all??[],...e.any??[],...e.none??[]])if(!o||typeof o!="object"||typeof o.__id!="number")throw new TypeError("aiecsjs: defineQuery received a non-component value");let t=_(e),i=f.get(t);if(i)return i;let r={id:C++,mask:[],all:(e.all??[]).map(o=>o.__id),any:(e.any??[]).map(o=>o.__id),none:(e.none??[]).map(o=>o.__id),columnViewCache:[...e.all??[],...e.any??[]],reactiveKind:"normal",sourceQueryId:-1,sourceQuery:null};return f.set(t,r),r}function z(n){let e=n,t=`enter:${e.id}`,i=f.get(t);if(i)return i;let r={id:C++,mask:[],all:e.all,any:e.any,none:e.none,columnViewCache:e.columnViewCache,reactiveKind:"enter",sourceQueryId:e.id,sourceQuery:e};return f.set(t,r),r}function T(n){let e=n,t=`exit:${e.id}`,i=f.get(t);if(i)return i;let r={id:C++,mask:[],all:e.all,any:e.any,none:e.none,columnViewCache:e.columnViewCache,reactiveKind:"exit",sourceQueryId:e.id,sourceQuery:e};return f.set(t,r),r}function p(n,e){if(n.queries[e.id]===e&&n.queryMasks.has(e.id))return;let t=n.options.maskWordCount,i=b$1(t),r=b$1(t),o=b$1(t);for(let a of e.all){let u=b(a),d=p$1(n,u);c(i,d);}for(let a of e.any){let u=b(a),d=p$1(n,u);c(r,d);}for(let a of e.none){let u=b(a),d=p$1(n,u);c(o,d);}let c$1={withMask:i,anyMask:r,noneMask:o,anyHasBits:!d(r)};n.queryMasks.set(e.id,c$1),n.queries[e.id]=e,n.queryArchetypeCache[e.id]=null,n.queryArchetypeStamp[e.id]=-1;let s=[...f$1(i),...f$1(r),...f$1(o)];for(let a of s){let u=n.bitToQueries.get(a);u||(u=new Set,n.bitToQueries.set(a,u)),u.add(e.id);}e.reactiveKind!=="normal"&&(n.reactiveBuffers.has(e.id)||n.reactiveBuffers.set(e.id,{entered:[],exited:[]}),e.sourceQuery&&p(n,e.sourceQuery));}function v(n,e$1){if(p(n,e$1),n.queryArchetypeCache[e$1.id]&&n.queryArchetypeStamp[e$1.id]===n.queryVersion)return n.queryArchetypeCache[e$1.id];let t=n.queryMasks.get(e$1.id),i=n.options.maskWordCount,r=[];for(let o=0;o<n.archetypes.length;o++){let c=n.archetypes[o];e(c.mask,t.withMask,t.anyMask,t.noneMask,t.anyHasBits,i)&&r.push(o);}return n.queryArchetypeCache[e$1.id]=r,n.queryArchetypeStamp[e$1.id]=n.queryVersion,r}function $(n,e){let t=i(n),r=v(t,e),o=[];for(let c of r){let s=t.archetypes[c];o.push({id:s.id,mask:Array.from(s.mask),size:s.size});}return o}function D(n,e){let t=i(n),i$1=e,r=[];if(i$1.reactiveKind==="enter"){let c=t.reactiveBuffers.get(i$1.id);if(c){for(let s of c.entered)r.push(s);c.entered.length=0;}return r}if(i$1.reactiveKind==="exit"){let c=t.reactiveBuffers.get(i$1.id);if(c){for(let s of c.exited)r.push(s);c.exited.length=0;}return r}let o=v(t,i$1);for(let c of o){let s=t.archetypes[c];for(let a=0;a<s.size;a++)r.push(s.entities[a]);}return r}function*H(n,e){let t=i(n),i$1=e;if(i$1.reactiveKind!=="normal"){let o=t.reactiveBuffers.get(i$1.id);if(!o)return;let c=i$1.reactiveKind==="enter"?o.entered:o.exited;for(let s of c)yield s;c.length=0;return}let r=v(t,i$1);for(let o of r){let c=t.archetypes[o];for(let s=0;s<c.size;s++)yield c.entities[s];}}function L(n,e,t){let i$1=i(n),r=e;if(r.reactiveKind!=="normal"){let s=i$1.reactiveBuffers.get(r.id);if(!s)return;let a=r.reactiveKind==="enter"?s.entered:s.exited;if(a.length===0)return;let u=w(i$1,r);for(let d=0;d<a.length;d++){let y=a[d];x(t,y,u);}a.length=0;return}p(i$1,r);let o=v(i$1,r),c=w(i$1,r);for(let s of o){let a=i$1.archetypes[s],u=a.entities,d=a.size;for(let y=0;y<d;y++)x(t,u[y],c);}}function x(n,e,t){switch(t.length){case 0:n(e);break;case 1:n(e,t[0]);break;case 2:n(e,t[0],t[1]);break;case 3:n(e,t[0],t[1],t[2]);break;case 4:n(e,t[0],t[1],t[2],t[3]);break;case 5:n(e,t[0],t[1],t[2],t[3],t[4]);break;default:n(e,...t);}}function w(n,e){let t=[];for(let i of e.columnViewCache){let r=I(i),o=n.componentBitFor.get(r.id);if(o===void 0){t.push(void 0);continue}let c=n.componentStorageByBit[o];if(!c){t.push(void 0);continue}c.kind==="soa"?t.push(c.soa):c.kind==="aos"?t.push(c.aos):t.push(true);}return t}function F(n,e$1,t,i,r){for(let s of f.values())s.reactiveKind!=="normal"&&s.sourceQuery&&n.queries[s.sourceQuery.id]!==s.sourceQuery&&p(n,s.sourceQuery);let o=n.bitToQueries.get(t);if(!o)return;let c=n.options.maskWordCount;for(let s of o){let a=n.queries[s];if(!a||a.reactiveKind!=="normal")continue;let u=n.queryMasks.get(s);if(!u)continue;let d=e(i,u.withMask,u.anyMask,u.noneMask,u.anyHasBits,c),y=e(r,u.withMask,u.anyMask,u.noneMask,u.anyHasBits,c);!d&&y?E(n,s,"enter",e$1):d&&!y&&E(n,s,"exit",e$1);}}function E(n,e,t,i){for(let r of f.values()){if(r.sourceQueryId!==e||r.reactiveKind!==t)continue;p(n,r);let o=A(n,r.id);t==="enter"?o.entered.push(i):o.exited.push(i);}}function A(n,e){let t=n.reactiveBuffers.get(e);return t||(t={entered:[],exited:[]},n.reactiveBuffers.set(e,t)),t}function b(n){let e=W(n);if(!e)throw new Error(`aiecsjs: component id ${n} not registered`);return e}var g=null;function O(n){g=n;}function W(n){return g?g(n):void 0}export{q as a,z as b,T as c,$ as d,D as e,H as f,L as g,F as h,O as i};//# sourceMappingURL=chunk-LU55S4LK.js.map
|
|
2
|
-
//# sourceMappingURL=chunk-LU55S4LK.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/internal/query.ts"],"names":["nextQueryId","moduleQueryCache","descKey","d","all","c","a","b","any","none","defineQuery","arg","desc","key","cached","q","enterQuery","query","reactive","exitQuery","ensureQueryRegistered","state","ww","withMask","createMask","anyMask","noneMask","compId","info","getComponentInfoById","bit","getOrRegisterComponentBit","setBit","bundle","isMaskZero","involvedBits","listBits","s","getQueryArchetypes","words","list","i","arch","matches","queryArchetypes","world","getWorldState","ids","out","id","runQuery","buf","e","archIds","r","iterQuery","src","forEachEntity","fn","cols","buildColumnViews","callWithCols","ents","n","eid","comp","getComponentInfo","storage","recordEntityMaskChange","changedBit","prevMask","nextMask","involved","qid","wasMatch","isMatch","pushReactive","queryId","kind","ensureReactiveBuffer","componentId","lazyGetComponentInfo","_getComponentInfoFn","registerComponentLookup"],"mappings":"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","file":"chunk-LU55S4LK.js","sourcesContent":["import { createMask, isMaskZero, listBits, matches, setBit } from './bitmask.js'\nimport { getComponentInfo } from './component.js'\nimport type {\n Archetype,\n ComponentInfo,\n ComponentLike,\n EntityId,\n Query,\n QueryDescriptor,\n QueryInternal,\n QueryMaskBundle,\n ReactiveBuffer,\n World,\n WorldState,\n} from './types.js'\nimport {\n getOrRegisterComponentBit,\n getWorldState,\n readEntityMask,\n tryGetComponentBit,\n} from './world.js'\n\nlet nextQueryId = 1\nconst moduleQueryCache = new Map<string, QueryInternal>()\n\nfunction descKey(d: QueryDescriptor): string {\n const all = (d.all ?? [])\n .map((c) => c.__id)\n .sort((a, b) => a - b)\n .join('-')\n const any = (d.any ?? [])\n .map((c) => c.__id)\n .sort((a, b) => a - b)\n .join('-')\n const none = (d.none ?? [])\n .map((c) => c.__id)\n .sort((a, b) => a - b)\n .join('-')\n return `A${all}|Y${any}|N${none}`\n}\n\nexport function defineQuery(arg: ComponentLike[] | QueryDescriptor): Query {\n const desc: QueryDescriptor = Array.isArray(arg) ? { all: arg } : arg\n\n // Validate all members are components\n for (const c of [...(desc.all ?? []), ...(desc.any ?? []), ...(desc.none ?? [])]) {\n if (!c || typeof c !== 'object' || typeof (c as any).__id !== 'number') {\n throw new TypeError('aiecsjs: defineQuery received a non-component value')\n }\n }\n\n const key = descKey(desc)\n const cached = moduleQueryCache.get(key)\n if (cached) return cached\n\n const q: QueryInternal = {\n id: nextQueryId++,\n mask: [],\n all: (desc.all ?? []).map((c) => c.__id),\n any: (desc.any ?? []).map((c) => c.__id),\n none: (desc.none ?? []).map((c) => c.__id),\n columnViewCache: [...(desc.all ?? []), ...(desc.any ?? [])],\n reactiveKind: 'normal',\n sourceQueryId: -1,\n sourceQuery: null,\n }\n moduleQueryCache.set(key, q)\n return q\n}\n\nexport function enterQuery(query: Query): Query {\n const q = query as QueryInternal\n const key = `enter:${q.id}`\n const cached = moduleQueryCache.get(key)\n if (cached) return cached\n const reactive: QueryInternal = {\n id: nextQueryId++,\n mask: [],\n all: q.all,\n any: q.any,\n none: q.none,\n columnViewCache: q.columnViewCache,\n reactiveKind: 'enter',\n sourceQueryId: q.id,\n sourceQuery: q,\n }\n moduleQueryCache.set(key, reactive)\n return reactive\n}\n\nexport function exitQuery(query: Query): Query {\n const q = query as QueryInternal\n const key = `exit:${q.id}`\n const cached = moduleQueryCache.get(key)\n if (cached) return cached\n const reactive: QueryInternal = {\n id: nextQueryId++,\n mask: [],\n all: q.all,\n any: q.any,\n none: q.none,\n columnViewCache: q.columnViewCache,\n reactiveKind: 'exit',\n sourceQueryId: q.id,\n sourceQuery: q,\n }\n moduleQueryCache.set(key, reactive)\n return reactive\n}\n\n// --- Per-world query setup ---\n\nfunction ensureQueryRegistered(state: WorldState, q: QueryInternal): void {\n if (state.queries[q.id] === q && state.queryMasks.has(q.id)) return\n\n // Build per-world bitmasks (this may register new bits)\n const ww = state.options.maskWordCount\n const withMask = createMask(ww)\n const anyMask = createMask(ww)\n const noneMask = createMask(ww)\n for (const compId of q.all) {\n const info = getComponentInfoById(compId)\n const bit = getOrRegisterComponentBit(state, info)\n setBit(withMask, bit)\n }\n for (const compId of q.any) {\n const info = getComponentInfoById(compId)\n const bit = getOrRegisterComponentBit(state, info)\n setBit(anyMask, bit)\n }\n for (const compId of q.none) {\n const info = getComponentInfoById(compId)\n const bit = getOrRegisterComponentBit(state, info)\n setBit(noneMask, bit)\n }\n\n const bundle: QueryMaskBundle = {\n withMask,\n anyMask,\n noneMask,\n anyHasBits: !isMaskZero(anyMask),\n }\n state.queryMasks.set(q.id, bundle)\n\n state.queries[q.id] = q\n state.queryArchetypeCache[q.id] = null\n state.queryArchetypeStamp[q.id] = -1\n\n // Build bit → queries index for fast reactive lookup\n const involvedBits: number[] = [\n ...listBits(withMask),\n ...listBits(anyMask),\n ...listBits(noneMask),\n ]\n for (const b of involvedBits) {\n let s = state.bitToQueries.get(b)\n if (!s) {\n s = new Set<number>()\n state.bitToQueries.set(b, s)\n }\n s.add(q.id)\n }\n\n // Initialize reactive buffer for enter/exit queries\n if (q.reactiveKind !== 'normal') {\n if (!state.reactiveBuffers.has(q.id)) {\n state.reactiveBuffers.set(q.id, { entered: [], exited: [] })\n }\n // Also register the source query so recordEntityMaskChange will see it\n if (q.sourceQuery) ensureQueryRegistered(state, q.sourceQuery)\n }\n}\n\nfunction getQueryArchetypes(state: WorldState, q: QueryInternal): number[] {\n ensureQueryRegistered(state, q)\n if (state.queryArchetypeCache[q.id] && state.queryArchetypeStamp[q.id] === state.queryVersion) {\n return state.queryArchetypeCache[q.id]!\n }\n const bundle = state.queryMasks.get(q.id)!\n const words = state.options.maskWordCount\n const list: number[] = []\n for (let i = 0; i < state.archetypes.length; i++) {\n const arch = state.archetypes[i]!\n if (\n matches(arch.mask, bundle.withMask, bundle.anyMask, bundle.noneMask, bundle.anyHasBits, words)\n ) {\n list.push(i)\n }\n }\n state.queryArchetypeCache[q.id] = list\n state.queryArchetypeStamp[q.id] = state.queryVersion\n return list\n}\n\nexport function queryArchetypes(world: World, query: Query): readonly Archetype[] {\n const state = getWorldState(world)\n const q = query as QueryInternal\n const ids = getQueryArchetypes(state, q)\n const out: Archetype[] = []\n for (const id of ids) {\n const arch = state.archetypes[id]!\n out.push({ id: arch.id, mask: Array.from(arch.mask), size: arch.size })\n }\n return out\n}\n\nexport function runQuery(world: World, query: Query): readonly EntityId[] {\n const state = getWorldState(world)\n const q = query as QueryInternal\n const out: EntityId[] = []\n if (q.reactiveKind === 'enter') {\n const buf = state.reactiveBuffers.get(q.id)\n if (buf) {\n for (const e of buf.entered) out.push(e as EntityId)\n buf.entered.length = 0\n }\n return out\n }\n if (q.reactiveKind === 'exit') {\n const buf = state.reactiveBuffers.get(q.id)\n if (buf) {\n for (const e of buf.exited) out.push(e as EntityId)\n buf.exited.length = 0\n }\n return out\n }\n const archIds = getQueryArchetypes(state, q)\n for (const id of archIds) {\n const arch = state.archetypes[id]!\n for (let r = 0; r < arch.size; r++) {\n out.push(arch.entities[r] as EntityId)\n }\n }\n return out\n}\n\nexport function* iterQuery(world: World, query: Query): IterableIterator<EntityId> {\n const state = getWorldState(world)\n const q = query as QueryInternal\n if (q.reactiveKind !== 'normal') {\n const buf = state.reactiveBuffers.get(q.id)\n if (!buf) return\n const src = q.reactiveKind === 'enter' ? buf.entered : buf.exited\n for (const e of src) yield e as EntityId\n src.length = 0\n return\n }\n const archIds = getQueryArchetypes(state, q)\n for (const id of archIds) {\n const arch = state.archetypes[id]!\n for (let r = 0; r < arch.size; r++) {\n yield arch.entities[r] as EntityId\n }\n }\n}\n\nexport function forEachEntity(\n world: World,\n query: Query,\n fn: (eid: EntityId, ...cols: any[]) => void,\n): void {\n const state = getWorldState(world)\n const q = query as QueryInternal\n\n if (q.reactiveKind !== 'normal') {\n const buf = state.reactiveBuffers.get(q.id)\n if (!buf) return\n const src = q.reactiveKind === 'enter' ? buf.entered : buf.exited\n if (src.length === 0) return\n const cols = buildColumnViews(state, q)\n for (let i = 0; i < src.length; i++) {\n const e = src[i] as EntityId\n callWithCols(fn, e, cols)\n }\n src.length = 0\n return\n }\n\n ensureQueryRegistered(state, q)\n const archIds = getQueryArchetypes(state, q)\n const cols = buildColumnViews(state, q)\n for (const id of archIds) {\n const arch = state.archetypes[id]!\n const ents = arch.entities\n const n = arch.size\n for (let r = 0; r < n; r++) {\n callWithCols(fn, ents[r] as EntityId, cols)\n }\n }\n}\n\nfunction callWithCols(\n fn: (eid: EntityId, ...cols: any[]) => void,\n eid: EntityId,\n cols: any[],\n): void {\n // Specialise for low arities to avoid spread allocation.\n switch (cols.length) {\n case 0:\n fn(eid)\n break\n case 1:\n fn(eid, cols[0])\n break\n case 2:\n fn(eid, cols[0], cols[1])\n break\n case 3:\n fn(eid, cols[0], cols[1], cols[2])\n break\n case 4:\n fn(eid, cols[0], cols[1], cols[2], cols[3])\n break\n case 5:\n fn(eid, cols[0], cols[1], cols[2], cols[3], cols[4])\n break\n default:\n fn(eid, ...cols)\n }\n}\n\nfunction buildColumnViews(state: WorldState, q: QueryInternal): any[] {\n const out: any[] = []\n for (const comp of q.columnViewCache) {\n const info = getComponentInfo(comp)\n const bit = state.componentBitFor.get(info.id)\n if (bit === undefined) {\n out.push(undefined)\n continue\n }\n const storage = state.componentStorageByBit[bit]\n if (!storage) {\n out.push(undefined)\n continue\n }\n if (storage.kind === 'soa') out.push(storage.soa)\n else if (storage.kind === 'aos') out.push(storage.aos)\n else out.push(true) // tag\n }\n return out\n}\n\n// --- Reactive query updates (called from migration code) ---\n\nexport function recordEntityMaskChange(\n state: WorldState,\n eid: EntityId,\n changedBit: number,\n prevMask: Uint32Array,\n nextMask: Uint32Array,\n): void {\n // Lazy-register any reactive queries (and their sources) so we can correctly\n // determine match transitions even if the user only ever called enterQuery/exitQuery.\n for (const q of moduleQueryCache.values()) {\n if (q.reactiveKind === 'normal') continue\n if (q.sourceQuery && state.queries[q.sourceQuery.id] !== q.sourceQuery) {\n ensureQueryRegistered(state, q.sourceQuery)\n }\n }\n\n const involved = state.bitToQueries.get(changedBit)\n if (!involved) return\n const words = state.options.maskWordCount\n for (const qid of involved) {\n const q = state.queries[qid]\n if (!q) continue\n if (q.reactiveKind !== 'normal') continue\n const bundle = state.queryMasks.get(qid)\n if (!bundle) continue\n const wasMatch = matches(\n prevMask,\n bundle.withMask,\n bundle.anyMask,\n bundle.noneMask,\n bundle.anyHasBits,\n words,\n )\n const isMatch = matches(\n nextMask,\n bundle.withMask,\n bundle.anyMask,\n bundle.noneMask,\n bundle.anyHasBits,\n words,\n )\n if (!wasMatch && isMatch) {\n pushReactive(state, qid, 'enter', eid)\n } else if (wasMatch && !isMatch) {\n pushReactive(state, qid, 'exit', eid)\n }\n }\n}\n\nfunction pushReactive(\n state: WorldState,\n queryId: number,\n kind: 'enter' | 'exit',\n eid: EntityId,\n): void {\n // Walk the module cache (not just state.queries) so reactive variants that\n // haven't been registered with this world yet still receive events.\n for (const r of moduleQueryCache.values()) {\n if (r.sourceQueryId !== queryId) continue\n if (r.reactiveKind !== kind) continue\n // Lazily register the reactive query in this world so subsequent reads can find it\n ensureQueryRegistered(state, r)\n const buf = ensureReactiveBuffer(state, r.id)\n if (kind === 'enter') buf.entered.push(eid as number)\n else buf.exited.push(eid as number)\n }\n}\n\nfunction ensureReactiveBuffer(state: WorldState, qid: number): ReactiveBuffer {\n let buf = state.reactiveBuffers.get(qid)\n if (!buf) {\n buf = { entered: [], exited: [] }\n state.reactiveBuffers.set(qid, buf)\n }\n return buf\n}\n\n// --- Helpers ---\n\nfunction getComponentInfoById(componentId: number): ComponentInfo {\n // We need access to the component registry's lookup by id. Re-import dynamically to avoid cycles.\n const info = lazyGetComponentInfo(componentId)\n if (!info) throw new Error(`aiecsjs: component id ${componentId} not registered`)\n return info\n}\n\nlet _getComponentInfoFn: ((id: number) => ComponentInfo | undefined) | null = null\nexport function registerComponentLookup(fn: (id: number) => ComponentInfo | undefined): void {\n _getComponentInfoFn = fn\n}\nfunction lazyGetComponentInfo(id: number): ComponentInfo | undefined {\n return _getComponentInfoFn ? _getComponentInfoFn(id) : undefined\n}\n\nexport function _resetQueryRegistry_FOR_TESTS_ONLY(): void {\n moduleQueryCache.clear()\n nextQueryId = 1\n}\n"]}
|