aiecsjs 0.5.2 → 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.
Files changed (37) hide show
  1. package/README.md +65 -28
  2. package/README_ZHTW.md +65 -28
  3. package/api.json +31 -2
  4. package/dist/chunk-COORLIYF.js +2 -0
  5. package/dist/chunk-COORLIYF.js.map +1 -0
  6. package/dist/{chunk-RNWXP3GU.cjs → chunk-D4VGTV76.cjs} +2 -2
  7. package/dist/{chunk-RNWXP3GU.cjs.map → chunk-D4VGTV76.cjs.map} +1 -1
  8. package/dist/{chunk-2E3GMRF5.js → chunk-FZWRVGAA.js} +2 -2
  9. package/dist/{chunk-2E3GMRF5.js.map → chunk-FZWRVGAA.js.map} +1 -1
  10. package/dist/chunk-NCYUF4YW.cjs +2 -0
  11. package/dist/chunk-NCYUF4YW.cjs.map +1 -0
  12. package/dist/{chunk-WZMRKSK5.js → chunk-UAJNL4VY.js} +2 -2
  13. package/dist/{chunk-WZMRKSK5.js.map → chunk-UAJNL4VY.js.map} +1 -1
  14. package/dist/{chunk-FEICW2CS.cjs → chunk-XBCASW7K.cjs} +2 -2
  15. package/dist/{chunk-FEICW2CS.cjs.map → chunk-XBCASW7K.cjs.map} +1 -1
  16. package/dist/commands.cjs +1 -1
  17. package/dist/commands.js +1 -1
  18. package/dist/index.cjs +1 -1
  19. package/dist/index.cjs.map +1 -1
  20. package/dist/index.d.cts +18 -2
  21. package/dist/index.d.ts +18 -2
  22. package/dist/index.js +1 -1
  23. package/dist/index.js.map +1 -1
  24. package/dist/observers.cjs +1 -1
  25. package/dist/observers.js +1 -1
  26. package/dist/relations.cjs +1 -1
  27. package/dist/relations.js +1 -1
  28. package/dist/serialize.cjs +1 -1
  29. package/dist/serialize.js +1 -1
  30. package/dist/worker.cjs +1 -1
  31. package/dist/worker.js +1 -1
  32. package/llms-full.txt +74 -31
  33. package/package.json +1 -1
  34. package/dist/chunk-P2KTJ2NQ.cjs +0 -2
  35. package/dist/chunk-P2KTJ2NQ.cjs.map +0 -1
  36. package/dist/chunk-Q55M3YEQ.js +0 -2
  37. package/dist/chunk-Q55M3YEQ.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, forEachEntity, Types } from 'aiecsjs'
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,11 +36,13 @@ 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) => { forEachEntity(w, movers, (e, pos, vel) => { pos.x[e] += vel.x[e] * dt; pos.y[e] += vel.y[e] * dt }); return w }
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
+ > **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
+
44
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
@@ -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, forEachEntity, pipe, Types,
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
- forEachEntity(w, movers, (e, pos, vel) => {
169
- pos.x[e] += vel.x[e] * dt
170
- pos.y[e] += vel.y[e] * dt
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
- forEachEntity(w, decaying, (e, life) => {
177
- life.remaining[e] -= dt
178
- if (life.remaining[e] <= 0) destroyEntity(w, e)
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
- forEachEntity(world, defineQuery([Position, Velocity]), (e, pos, vel) => {
247
- pos.x[e] += vel.x[e] * dt
248
- pos.y[e] += vel.y[e] * dt
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
- forEachEntity(world, dying, (e, health) => {
327
- if (health.hp[e] <= 0) cb.destroy(e)
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, forEachEntity, Types } from 'aiecsjs'
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
- forEachEntity(world, movers, (e, pos, vel) => {
554
- pos.x[e] += vel.x[e]; pos.y[e] += vel.y[e]
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, forEachEntity, Types } from 'aiecsjs'
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 = (e) => {
601
- const world = adoptSnapshot(e.data)
635
+ self.onmessage = (msg) => {
636
+ const world = adoptSnapshot(msg.data)
602
637
  const movers = defineQuery([Position, Velocity])
603
638
  setInterval(() => {
604
- forEachEntity(world, movers, (e, pos, vel) => {
605
- pos.x[e] += vel.x[e]
606
- pos.y[e] += vel.y[e]
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, forEachEntity, pipe, Types } from 'aiecsjs'
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
- forEachEntity(w, movers, (e, p, v) => { p.x[e] += v.x[e] * dt; p.y[e] += v.y[e] * dt })
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. Indexing by an alive `eid` is always safe up to `getWorldCapacity(world)`.
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,7 +918,9 @@ 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
- - **`forEachEntity` is the fast path.** `runQuery` allocates an array; `for...of iterQuery(...)` allocates an iterator. In hot loops, use `forEachEntity`.
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
 
@@ -954,11 +991,17 @@ 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
 
957
- ## [0.5.2] - 2026-06-05
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).
958
999
 
959
1000
  ### Docs
960
1001
 
961
- - Review-driven documentation fixes (`README.md`, `README_ZHTW.md`, `llms-full.txt`): clarity and accuracy from a cross-package code review.
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.
962
1005
 
963
1006
  ### Fixed
964
1007
 
@@ -1223,7 +1266,7 @@ The "documentation honesty + test backstop" release. No new public APIs; this is
1223
1266
 
1224
1267
  ### Implementation notes
1225
1268
 
1226
- - **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]` work directly without per-archetype indirection. Trade-off: iteration over archetypes reads columns at potentially non-contiguous offsets; for hot data this stays in L1.
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.
1227
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.
1228
1271
  - **Bitmask queries**: multi-word Uint32 masks, default 8 words (256 components). Per-world bit allocation, global component identity.
1229
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aiecsjs",
3
- "version": "0.5.2",
3
+ "version": "0.5.3",
4
4
  "description": "TypeScript-first archetype ECS with TypedArray SoA, SAB-ready snapshot transport, and AI-readable docs.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -1,2 +0,0 @@
1
- 'use strict';var chunkRNWXP3GU_cjs=require('./chunk-RNWXP3GU.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=chunkRNWXP3GU_cjs.b(t),r=chunkRNWXP3GU_cjs.b(t),o=chunkRNWXP3GU_cjs.b(t);for(let a of e.all){let u=b(a),d=chunkRNWXP3GU_cjs.p(n,u);chunkRNWXP3GU_cjs.c(i,d);}for(let a of e.any){let u=b(a),d=chunkRNWXP3GU_cjs.p(n,u);chunkRNWXP3GU_cjs.c(r,d);}for(let a of e.none){let u=b(a),d=chunkRNWXP3GU_cjs.p(n,u);chunkRNWXP3GU_cjs.c(o,d);}let c={withMask:i,anyMask:r,noneMask:o,anyHasBits:!chunkRNWXP3GU_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=[...chunkRNWXP3GU_cjs.f(i),...chunkRNWXP3GU_cjs.f(r),...chunkRNWXP3GU_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];chunkRNWXP3GU_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=chunkRNWXP3GU_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=chunkRNWXP3GU_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=chunkRNWXP3GU_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=chunkRNWXP3GU_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=chunkRNWXP3GU_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=chunkRNWXP3GU_cjs.e(i,u.withMask,u.anyMask,u.noneMask,u.anyHasBits,c),y=chunkRNWXP3GU_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-P2KTJ2NQ.cjs.map
2
- //# sourceMappingURL=chunk-P2KTJ2NQ.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-P2KTJ2NQ.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"]}
@@ -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-WZMRKSK5.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-Q55M3YEQ.js.map
2
- //# sourceMappingURL=chunk-Q55M3YEQ.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-Q55M3YEQ.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"]}