aiecsjs 0.2.1 → 0.3.0

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/CHANGELOG.md CHANGED
@@ -8,12 +8,74 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
8
8
 
9
9
  ## [Unreleased]
10
10
 
11
- ### Planned for 0.3+
11
+ ### Planned for 0.4+
12
12
 
13
- - Implement ABA-safe `EntityRef` and graduate `getEntityGeneration` / `packEntity` from experimental → stable.
14
13
  - Add `pipeAsync` for async system composition.
15
14
  - Doc-test harness so README code blocks are mechanically verified.
16
15
  - Promote `aiecsjs/relations` and `aiecsjs/worker` (true SAB-shared columns) to `stable`.
16
+ - Document the 8-bit generation wrap caveat in [STABILITY.md](./STABILITY.md): with the
17
+ default `generationBits=8`, a single slot recycled 256 times wraps back to its
18
+ starting generation, briefly re-opening the ABA window. Safe for v0.5 shmup
19
+ workloads (~5000 frame to wrap a single slot at 60 fps × ~1k destroys); high-churn
20
+ pools should set `createWorld({ generationBits: 16 })`. See test
21
+ [tests/ref.test.ts](./tests/ref.test.ts) `generation wrap` describe block.
22
+
23
+ ## [0.3.0] - 2026-05-29
24
+
25
+ ### Added (API)
26
+
27
+ - **`EntityRef<T>`** — ABA-safe entity reference. `refOf(world, eid)` builds one;
28
+ `deref(world, ref)` returns the entity id when still alive (generation match)
29
+ or `null` otherwise; `aliveRef(world, ref)` is the boolean guard form. Phantom
30
+ type `T` lets callers distinguish ref kinds (e.g. `EntityRef<'bullet'>`) without
31
+ runtime cost. Refs are in-memory only — not serializable across worker / disk.
32
+ - **`EntityNotAliveError`** — thrown by `refOf` when the entity is dead or invalid.
33
+ `deref` / `aliveRef` never throw.
34
+
35
+ ### Changed
36
+
37
+ - **`EntityId` now packs index + generation** into a single 32-bit number
38
+ `(generation << indexBits) | index` (default `indexBits=24, generationBits=8`).
39
+ `EntityId` remains opaque per STABILITY contract; the layout is implementation
40
+ detail. **Migration note**: do not compare `EntityId` numbers directly
41
+ (`eid === 42` will break across slot recycles); use
42
+ `getEntityIndex(eid)` for index comparison or `refOf(world, eid).id` for
43
+ identity matching that survives slot reuse.
44
+ - **`getEntityGeneration` / `packEntity` graduate to `stable`** (were `experimental`
45
+ since 0.2.0). Both now return real values. These functions use default 24/8 bit
46
+ layout; for non-default `createWorld({ indexBits, generationBits })`, use
47
+ `EntityRef` and `deref` instead of manual unpacking.
48
+
49
+ ### Fixed
50
+
51
+ - **ABA bug on entity slot recycle**: previously `entityExists` and `isAliveInternal`
52
+ only checked archetype membership; a stale `EntityId` pointing at a recycled slot
53
+ would silently report alive. With packed generation + `deref` generation match,
54
+ stale refs now correctly invalidate.
55
+ - **`destroyEntity` generation wrap mask aligned with `options.generationBits`**
56
+ (was hard-coded `& 0xffff`). The mask now correctly uses
57
+ `state.options.generationMask`, fixing inconsistency for non-default
58
+ `generationBits` values.
59
+
60
+ ### Documentation
61
+
62
+ - `onSet` JSDoc clarifies that `addComponent` does NOT trigger `onSet`, and
63
+ direct writes to column views returned by `getComponent` (e.g. `col.x[idx] = 5`)
64
+ also do NOT trigger `onSet`. Only `setComponent` on an already-present
65
+ component fires the callback. Anti-pattern example included.
66
+
67
+ ### Compatibility
68
+
69
+ - `EntityId` layout change is **not** breaking at the type system level (opaque
70
+ branded number), but consumers who relied on `eid === N` direct comparison
71
+ will need to migrate (see Migration note above).
72
+ - All existing `stable` exports unchanged.
73
+ - `aiecsjs/worker` snapshot wire format unchanged (still uses raw indices).
74
+ - `aiecsjs/serialize` wire format unchanged.
75
+
76
+ ### Build & tooling
77
+
78
+ - `VERSION` constant bumped to `0.3.0`.
17
79
 
18
80
  ## [0.2.1] - 2026-05-28
19
81
 
package/STABILITY.md CHANGED
@@ -34,8 +34,13 @@ The **root** entry (`aiecsjs`) is the stable core: world, entity, component, que
34
34
  | `destroyEntity` | stable | 0.1.0 | |
35
35
  | `entityExists` | stable | 0.1.0 | |
36
36
  | `getEntityIndex` | stable | 0.1.0 | |
37
- | `getEntityGeneration` | **experimental** | 0.1.0 | Returns 0 in 0.x (generation tracked internally but not encoded in EntityId). Real values arrive with ABA-safe `EntityRef` in **0.3+**. |
38
- | `packEntity` | **experimental** | 0.1.0 | Identity helper in 0.x. Returns the index unchanged. Real packing arrives with `EntityRef` in **0.3+**. |
37
+ | `getEntityGeneration` | stable | 0.3.0 | Returns real generation value packed into EntityId (default 24-bit index, 8-bit generation). For non-default `createWorld({ indexBits, generationBits })`, use `EntityRef` + `deref` instead. |
38
+ | `packEntity` | stable | 0.3.0 | Packs index + generation into an EntityId using default 24/8 bit layout. For non-default bit sizes, use `EntityRef` + `deref` instead. |
39
+ | `refOf` | stable | 0.3.0 | Throws `EntityNotAliveError` for dead entity. |
40
+ | `deref` | stable | 0.3.0 | Returns null for stale / cross-world refs; never throws. |
41
+ | `aliveRef` | stable | 0.3.0 | Boolean guard form of `deref`; never throws. |
42
+ | `EntityRef` (type) | stable | 0.3.0 | In-memory only; not serializable. |
43
+ | `EntityNotAliveError` | stable | 0.3.0 | Thrown only by `refOf`. |
39
44
  | `defineComponent` | stable | 0.1.0 | |
40
45
  | `defineTag` | stable | 0.1.0 | |
41
46
  | `defineObjectComponent` | stable | 0.1.0 | AoS components are main-thread only; not SAB-shareable. |
@@ -130,8 +135,9 @@ Everything under this prefix is **internal**. It exists for the implementation's
130
135
  |---|---|---|
131
136
  | 0.1.x | Core surface (world, entity, component, query, system, loop, commands, observers, serialize) | Initial publish; all marked experimental at the package level but per-export stable where listed. |
132
137
  | 0.2.0 | Safety + alignment | Prototype-pollution hardening, observer `{ signal? }`, `disposeWorld` alias, `getEntityGeneration` / `packEntity` re-labelled experimental, `verify:llms` gate. See [CHANGELOG.md](./CHANGELOG.md#020---2026-05-28). |
133
- | 0.3+ | Relations stabilisation + EntityRef + SAB | `aiecsjs/relations` graduates to stable; ABA-safe `EntityRef` lands and `getEntityGeneration` / `packEntity` start returning real values; `aiecsjs/worker` adopts true shared-memory column aliasing. |
134
- | 0.3.x | Hardening, relations stabilization, multi-threading polish | `aiecsjs/relations` and `aiecsjs/worker` → stable. |
138
+ | 0.3.x | EntityRef + generation packing | ABA-safe; `getEntityGeneration` / `packEntity` → stable. |
139
+ | 0.4+ | Relations stabilisation + true SAB worker | `aiecsjs/relations` and `aiecsjs/worker` → stable. |
140
+ | 0.6+ | Multi-World snapshot diff transport (placeholder) | experimental — design TBD. |
135
141
  | 1.0.0 | API freeze | All `stable` exports frozen for 1.x. |
136
142
 
137
143
  ## How to check stability at runtime
package/STABILITY_ZHTW.md CHANGED
@@ -33,8 +33,13 @@ aiecsjs 遵循 [semver](https://semver.org/)。在 **0.x** 系列內:
33
33
  | `destroyEntity` | stable | 0.1.0 | |
34
34
  | `entityExists` | stable | 0.1.0 | |
35
35
  | `getEntityIndex` | stable | 0.1.0 | |
36
- | `getEntityGeneration` | stable | 0.1.0 | |
37
- | `packEntity` | stable | 0.1.0 | |
36
+ | `getEntityGeneration` | stable | 0.3.0 | 回傳 EntityId 中實際打包的 generation 值(預設 24-bit index、8-bit generation)。若使用非預設 `createWorld({ indexBits, generationBits })`,請改用 `EntityRef` + `deref`。 |
37
+ | `packEntity` | stable | 0.3.0 | 使用預設 24/8 bit 佈局將 index + generation 打包為 EntityId。若使用非預設 bit 大小,請改用 `EntityRef` + `deref`。 |
38
+ | `refOf` | stable | 0.3.0 | entity 不存活時拋出 `EntityNotAliveError`。 |
39
+ | `deref` | stable | 0.3.0 | 對過期或跨 world 的 ref 回傳 null;絕不拋出錯誤。 |
40
+ | `aliveRef` | stable | 0.3.0 | `deref` 的布林 guard 形式;絕不拋出錯誤。 |
41
+ | `EntityRef`(type) | stable | 0.3.0 | 僅限記憶體內使用;不可序列化。 |
42
+ | `EntityNotAliveError` | stable | 0.3.0 | 僅由 `refOf` 拋出。 |
38
43
  | `defineComponent` | stable | 0.1.0 | |
39
44
  | `defineTag` | stable | 0.1.0 | |
40
45
  | `defineObjectComponent` | stable | 0.1.0 | AoS 元件僅限主執行緒;不可跨 SAB 共享。 |
@@ -129,7 +134,9 @@ aiecsjs 遵循 [semver](https://semver.org/)。在 **0.x** 系列內:
129
134
  |---|---|---|
130
135
  | 0.1.x | 核心表面(world、entity、component、query、system、loop、commands、observers、serialize) | 初次發佈;package 整體標 experimental,但各 export 表中列為 stable 者皆穩定。 |
131
136
  | 0.2.0 | 安全與生態對齊 | 原型污染強化、observer `{ signal? }`、`disposeWorld` 別名、`getEntityGeneration` / `packEntity` 改 experimental、`verify:llms` gate。詳見 [CHANGELOG.md](./CHANGELOG.md#020---2026-05-28)。 |
132
- | 0.3+ | Relations 穩定化 + EntityRef + SAB | `aiecsjs/relations` 升為 stable;ABA-safe `EntityRef` 上線,`getEntityGeneration` / `packEntity` 開始回傳真值;`aiecsjs/worker` 採用真正 shared-memory column aliasing。 |
137
+ | 0.3.x | EntityRef + generation packing | ABA-safe;`getEntityGeneration` / `packEntity` → stable。 |
138
+ | 0.4+ | Relations 穩定化 + 真正 SAB worker | `aiecsjs/relations` 與 `aiecsjs/worker` → stable。 |
139
+ | 0.6+ | Multi-World snapshot diff transport(佔位) | experimental — 設計待定。 |
133
140
  | 1.0.0 | API 凍結 | 所有 `stable` 匯出於 1.x 系列凍結。 |
134
141
 
135
142
  ## 在執行時檢查穩定度