aiecsjs 0.2.1 → 0.3.1
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 +90 -2
- package/README.md +8 -3
- package/README_ZHTW.md +8 -3
- package/STABILITY.md +10 -4
- package/STABILITY_ZHTW.md +10 -3
- package/api.json +568 -100
- package/dist/chunk-37HMYMJG.cjs +2 -0
- package/dist/chunk-37HMYMJG.cjs.map +1 -0
- package/dist/chunk-4RPFOVQC.cjs +2 -0
- package/dist/chunk-4RPFOVQC.cjs.map +1 -0
- package/dist/chunk-AGHSWLNQ.cjs +2 -0
- package/dist/chunk-AGHSWLNQ.cjs.map +1 -0
- package/dist/chunk-FDXCXNZK.js +2 -0
- package/dist/chunk-FDXCXNZK.js.map +1 -0
- package/dist/chunk-L5CMKMIP.js +2 -0
- package/dist/chunk-L5CMKMIP.js.map +1 -0
- package/dist/chunk-QOKIEF7C.js +2 -0
- package/dist/chunk-QOKIEF7C.js.map +1 -0
- package/dist/commands.cjs +1 -1
- package/dist/commands.cjs.map +1 -1
- package/dist/commands.js +1 -1
- package/dist/commands.js.map +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +87 -4
- package/dist/index.d.ts +87 -4
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/observers.cjs +1 -1
- package/dist/observers.cjs.map +1 -1
- package/dist/observers.d.cts +16 -3
- package/dist/observers.d.ts +16 -3
- package/dist/observers.js +1 -1
- package/dist/observers.js.map +1 -1
- package/dist/relations.cjs +1 -1
- package/dist/relations.cjs.map +1 -1
- package/dist/relations.js +1 -1
- package/dist/relations.js.map +1 -1
- package/dist/serialize.cjs +1 -1
- package/dist/serialize.cjs.map +1 -1
- package/dist/serialize.d.cts +7 -0
- package/dist/serialize.d.ts +7 -0
- package/dist/serialize.js +1 -1
- package/dist/serialize.js.map +1 -1
- package/dist/worker.cjs +1 -1
- package/dist/worker.cjs.map +1 -1
- package/dist/worker.js +1 -1
- package/dist/worker.js.map +1 -1
- package/llms-full.txt +108 -9
- package/package.json +6 -2
package/CHANGELOG.md
CHANGED
|
@@ -6,14 +6,102 @@ All notable changes to `aiecsjs` are recorded in this file.
|
|
|
6
6
|
|
|
7
7
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
8
8
|
|
|
9
|
+
## [0.3.1] - 2026-05-29
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
|
|
13
|
+
- **Packed EntityId signed-overflow for generation ≥ 128**: `createEntity` returned a negative number diverging from the unsigned value stored in archetype row arrays (`Uint32Array`), so query iteration (`runQuery`/`iterQuery`/`forEachEntity`) yielded an eid that failed `entityRow` lookups; `refOf`/`entityExists`/`deref` on a query-iterated high-generation entity misbehaved (`refOf` threw on a live entity). `packEid`/`packEntity` now normalise with `>>> 0`. No public-bundle behaviour change beyond the corrected eid representation (EntityId is opaque + in-memory-only).
|
|
14
|
+
- **`toJSON` silently dropped high-generation entities** (gen ≥ 128 with default 8-bit generation): `toJSON` contained its own inline pack expression that produced a signed (negative) result, diverging from the unsigned key stored in `arch.entityRow`. The affected entity passed the archetype check but failed `entityRow.has()`, so it was omitted from every snapshot and `serializeWorld` call. Fixed by replacing the inline expression with the canonical `packEid` (which applies `>>> 0`). SPOT principle: one pack source of truth.
|
|
15
|
+
- **Cross-subpath registry isolation** (`tsup splitting: false` → `splitting: true`): each compiled entry point (`dist/index.js`, `dist/serialize.js`, etc.) previously bundled its own private copy of `internal/world.ts`, including the module-scope `worldRegistry`. A world created via the core subpath was invisible to `serializeWorld`/`getRelationTargets`/`transferableSnapshot` imported from their respective subpaths, causing `world N is destroyed or unknown` at runtime. With `splitting: true`, esbuild extracts a shared chunk used by all entries; ESM and CJS are both verified by the new `scripts/check-dist-subpaths.mjs` smoke script.
|
|
16
|
+
- **`getRelationTargets` returned raw index as `EntityId`** (gen always 0): `addRelation` stores the target as a raw slot index (`& indexMask`). The previous return path cast this raw index directly to `EntityId`, which is equivalent to a packed id with generation 0. For any target that had been recycled (gen > 0), callers received a stale id that failed `entityExists`, `entityRow` lookups, and component access. Fixed by re-packing each raw index against the current generation via `packEid` before returning.
|
|
17
|
+
- **`resolveOptions` did not validate `indexBits + generationBits ≤ 32`**: the individual range checks (`indexBits ∈ [1, 24]`, `generationBits ∈ [0, 16]`) allowed combinations such as `indexBits=24, generationBits=16` (40 bits), where `gen << 24` silently overflowed and high-generation bits were lost. A sum check is now enforced with a clear error message. The `[Unreleased]` example corrected accordingly (`indexBits: 16, generationBits: 16` = 32 bits).
|
|
18
|
+
|
|
19
|
+
### Known Limitations
|
|
20
|
+
|
|
21
|
+
- **`createDeltaSerializer.apply` with a recycled target world**: `apply` uses the raw entity index from the delta snapshot as the `EntityId` directly. When the target world has already recycled any of those slots (generation > 0), component operations silently act on the wrong packed id. This is a known limitation of the experimental delta API; the common usage (delta → a fresh gen-0 render-mirror world) is unaffected. A proper raw-index-to-packed-id mapping is planned for 0.4. Avoid `apply` against a world that has previously destroyed entities.
|
|
22
|
+
|
|
23
|
+
### Documentation
|
|
24
|
+
|
|
25
|
+
- README / README_ZHTW updated to reflect the shipped 0.3.0 `EntityRef` API: the previous README still described EntityRef as "targeted for 0.3+" and `getEntityGeneration`/`packEntity` as experimental. Both files now correctly state EntityId has been packed since 0.3, and `EntityRef` / `refOf` / `deref` / `aliveRef` / `EntityNotAliveError` are all stable since 0.3.0. API table entries for these symbols added.
|
|
26
|
+
|
|
27
|
+
### Build & Tooling
|
|
28
|
+
|
|
29
|
+
- Coverage gate: `@vitest/coverage-v8` installed and wired into `prepublishOnly` (replaces `npm run test`) and CI. Thresholds: statements 95 / branches 80 / functions 97 / lines 98 — the achievable bar on pristine source. The branch figure honours the `?? 0` / `noUncheckedIndexedAccess` idiom on TypedArray reads (nullish-fallback branches unreachable by design); thresholds are raised only by adding tests, never by stripping defensive guards or scattering `/* v8 ignore */`.
|
|
30
|
+
- `fast-check` property tests (`tests/properties.test.ts`): pack/unpack round-trip invariant (asserts `e >= 0` to guard the P0 regression) and ABA-deref always-null invariant.
|
|
31
|
+
- Dispose three-cycle tests, error-path tests, and observer handler-throw behaviour documented in `tests/world.test.ts` / `tests/observers.test.ts`.
|
|
32
|
+
- `scripts/check-dist-subpaths.mjs` (`npm run verify:dist`): post-build smoke test that imports `createWorld`+`createEntity` from the core subpath and calls `serializeWorld`, `addRelation`/`getRelationTargets`, and `transferableSnapshot` from their respective subpaths for both ESM (`dist/*.js`) and CJS (`dist/*.cjs`). Wired into `prepublishOnly` (after `build`) and CI.
|
|
33
|
+
|
|
9
34
|
## [Unreleased]
|
|
10
35
|
|
|
11
|
-
### Planned for 0.
|
|
36
|
+
### Planned for 0.4+
|
|
12
37
|
|
|
13
|
-
- Implement ABA-safe `EntityRef` and graduate `getEntityGeneration` / `packEntity` from experimental → stable.
|
|
14
38
|
- Add `pipeAsync` for async system composition.
|
|
15
39
|
- Doc-test harness so README code blocks are mechanically verified.
|
|
16
40
|
- Promote `aiecsjs/relations` and `aiecsjs/worker` (true SAB-shared columns) to `stable`.
|
|
41
|
+
- Document the 8-bit generation wrap caveat in [STABILITY.md](./STABILITY.md): with the
|
|
42
|
+
default `generationBits=8`, a single slot recycled 256 times wraps back to its
|
|
43
|
+
starting generation, briefly re-opening the ABA window. Safe for v0.5 shmup
|
|
44
|
+
workloads (~5000 frame to wrap a single slot at 60 fps × ~1k destroys); high-churn
|
|
45
|
+
pools should set `createWorld({ indexBits: 16, generationBits: 16 })` (16 + 16 = 32 bits;
|
|
46
|
+
65 536 entities × 65 536 generations). See test
|
|
47
|
+
[tests/ref.test.ts](./tests/ref.test.ts) `generation wrap` describe block.
|
|
48
|
+
|
|
49
|
+
## [0.3.0] - 2026-05-29
|
|
50
|
+
|
|
51
|
+
### Added (API)
|
|
52
|
+
|
|
53
|
+
- **`EntityRef<T>`** — ABA-safe entity reference. `refOf(world, eid)` builds one;
|
|
54
|
+
`deref(world, ref)` returns the entity id when still alive (generation match)
|
|
55
|
+
or `null` otherwise; `aliveRef(world, ref)` is the boolean guard form. Phantom
|
|
56
|
+
type `T` lets callers distinguish ref kinds (e.g. `EntityRef<'bullet'>`) without
|
|
57
|
+
runtime cost. Refs are in-memory only — not serializable across worker / disk.
|
|
58
|
+
- **`EntityNotAliveError`** — thrown by `refOf` when the entity is dead or invalid.
|
|
59
|
+
`deref` / `aliveRef` never throw.
|
|
60
|
+
|
|
61
|
+
### Changed
|
|
62
|
+
|
|
63
|
+
- **`EntityId` now packs index + generation** into a single 32-bit number
|
|
64
|
+
`(generation << indexBits) | index` (default `indexBits=24, generationBits=8`).
|
|
65
|
+
`EntityId` remains opaque per STABILITY contract; the layout is implementation
|
|
66
|
+
detail. **Migration note**: do not compare `EntityId` numbers directly
|
|
67
|
+
(`eid === 42` will break across slot recycles); use
|
|
68
|
+
`getEntityIndex(eid)` for index comparison or `refOf(world, eid).id` for
|
|
69
|
+
identity matching that survives slot reuse.
|
|
70
|
+
- **`getEntityGeneration` / `packEntity` graduate to `stable`** (were `experimental`
|
|
71
|
+
since 0.2.0). Both now return real values. These functions use default 24/8 bit
|
|
72
|
+
layout; for non-default `createWorld({ indexBits, generationBits })`, use
|
|
73
|
+
`EntityRef` and `deref` instead of manual unpacking.
|
|
74
|
+
|
|
75
|
+
### Fixed
|
|
76
|
+
|
|
77
|
+
- **ABA bug on entity slot recycle**: previously `entityExists` and `isAliveInternal`
|
|
78
|
+
only checked archetype membership; a stale `EntityId` pointing at a recycled slot
|
|
79
|
+
would silently report alive. With packed generation + `deref` generation match,
|
|
80
|
+
stale refs now correctly invalidate.
|
|
81
|
+
- **`destroyEntity` generation wrap mask aligned with `options.generationBits`**
|
|
82
|
+
(was hard-coded `& 0xffff`). The mask now correctly uses
|
|
83
|
+
`state.options.generationMask`, fixing inconsistency for non-default
|
|
84
|
+
`generationBits` values.
|
|
85
|
+
|
|
86
|
+
### Documentation
|
|
87
|
+
|
|
88
|
+
- `onSet` JSDoc clarifies that `addComponent` does NOT trigger `onSet`, and
|
|
89
|
+
direct writes to column views returned by `getComponent` (e.g. `col.x[idx] = 5`)
|
|
90
|
+
also do NOT trigger `onSet`. Only `setComponent` on an already-present
|
|
91
|
+
component fires the callback. Anti-pattern example included.
|
|
92
|
+
|
|
93
|
+
### Compatibility
|
|
94
|
+
|
|
95
|
+
- `EntityId` layout change is **not** breaking at the type system level (opaque
|
|
96
|
+
branded number), but consumers who relied on `eid === N` direct comparison
|
|
97
|
+
will need to migrate (see Migration note above).
|
|
98
|
+
- All existing `stable` exports unchanged.
|
|
99
|
+
- `aiecsjs/worker` snapshot wire format unchanged (still uses raw indices).
|
|
100
|
+
- `aiecsjs/serialize` wire format unchanged.
|
|
101
|
+
|
|
102
|
+
### Build & tooling
|
|
103
|
+
|
|
104
|
+
- `VERSION` constant bumped to `0.3.0`.
|
|
17
105
|
|
|
18
106
|
## [0.2.1] - 2026-05-28
|
|
19
107
|
|
package/README.md
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
|
|
11
11
|
Part of the [ai\*js micro-runtime ecosystem](https://github.com/yshengliao) — see also [aifsmjs](https://github.com/yshengliao/aifsmjs) (FSM) and [aibridgejs](https://github.com/yshengliao/aibridgejs) (cross-context RPC).
|
|
12
12
|
|
|
13
|
-
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.
|
|
13
|
+
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.
|
|
14
14
|
|
|
15
15
|
```ts
|
|
16
16
|
import { createWorld, createEntity, defineComponent, defineQuery, pipe, forEachEntity, Types } from 'aiecsjs'
|
|
@@ -380,8 +380,13 @@ type WorldOptions = {
|
|
|
380
380
|
| `destroyEntity` | `(world: World, eid: EntityId) => void` | stable |
|
|
381
381
|
| `entityExists` | `(world: World, eid: EntityId) => boolean` | stable |
|
|
382
382
|
| `getEntityIndex` | `(eid: EntityId) => number` | stable |
|
|
383
|
-
| `getEntityGeneration` | `(eid: EntityId) => number` |
|
|
384
|
-
| `packEntity` | `(index: number, generation: number) => EntityId` |
|
|
383
|
+
| `getEntityGeneration` | `(eid: EntityId) => number` | stable (since 0.3.0) — returns the 8-bit generation field; uses default 24/8 layout |
|
|
384
|
+
| `packEntity` | `(index: number, generation: number) => EntityId` | stable (since 0.3.0) — packs index + generation using default 24/8 layout |
|
|
385
|
+
| `refOf` | `<T>(world: World, eid: EntityId) => EntityRef<T>` | stable (since 0.3.0) — creates ABA-safe ref; throws `EntityNotAliveError` if entity is dead |
|
|
386
|
+
| `deref` | `<T>(world: World, ref: EntityRef<T>) => EntityId \| null` | stable (since 0.3.0) — returns live `EntityId` or `null` if stale/cross-world; never throws |
|
|
387
|
+
| `aliveRef` | `<T>(world: World, ref: EntityRef<T>) => boolean` | stable (since 0.3.0) — boolean guard form of `deref`; never throws |
|
|
388
|
+
| `EntityRef` | `interface EntityRef<T> { id: EntityId; worldId: number }` | stable (since 0.3.0) — opaque ABA-safe reference; in-memory only |
|
|
389
|
+
| `EntityNotAliveError` | `class EntityNotAliveError extends Error { eid: number }` | stable (since 0.3.0) — thrown by `refOf` when entity is not alive |
|
|
385
390
|
|
|
386
391
|
### Component — `aiecsjs`
|
|
387
392
|
|
package/README_ZHTW.md
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
|
|
11
11
|
隸屬 [ai\*js micro-runtime 生態系](https://github.com/yshengliao) ─ 另見 [aifsmjs](https://github.com/yshengliao/aifsmjs)(FSM)與 [aibridgejs](https://github.com/yshengliao/aibridgejs)(cross-context RPC)。
|
|
12
12
|
|
|
13
|
-
aiecsjs 採用 **原型表格搭配 TypedArray 欄位** 與 **位元遮罩查詢**,這正是 piecs 與 wolf-ecs 在公開效能評測中名列前茅所採用的架構。API 為 **函式式且可 tree-shake**,以 `pipe()` 組合。元件(Component)同時支援 SoA(結構陣列)與 AoS
|
|
13
|
+
aiecsjs 採用 **原型表格搭配 TypedArray 欄位** 與 **位元遮罩查詢**,這正是 piecs 與 wolf-ecs 在公開效能評測中名列前茅所採用的架構。API 為 **函式式且可 tree-shake**,以 `pipe()` 組合。元件(Component)同時支援 SoA(結構陣列)與 AoS(結構物件)兩種佈局。自 0.3 起,`EntityId` 將 index 與 generation 打包為單一 32-bit 數字;具 ABA 安全的 `EntityRef` API 已於 0.3.0 正式推出。
|
|
14
14
|
|
|
15
15
|
```ts
|
|
16
16
|
import { createWorld, createEntity, defineComponent, defineQuery, pipe, forEachEntity, Types } from 'aiecsjs'
|
|
@@ -387,8 +387,13 @@ type WorldOptions = {
|
|
|
387
387
|
| `destroyEntity` | `(world: World, eid: EntityId) => void` | stable |
|
|
388
388
|
| `entityExists` | `(world: World, eid: EntityId) => boolean` | stable |
|
|
389
389
|
| `getEntityIndex` | `(eid: EntityId) => number` | stable |
|
|
390
|
-
| `getEntityGeneration` | `(eid: EntityId) => number` | stable |
|
|
391
|
-
| `packEntity` | `(index: number, generation: number) => EntityId` | stable |
|
|
390
|
+
| `getEntityGeneration` | `(eid: EntityId) => number` | stable(自 0.3.0)— 回傳 8-bit generation 欄位;使用預設 24/8 佈局 |
|
|
391
|
+
| `packEntity` | `(index: number, generation: number) => EntityId` | stable(自 0.3.0)— 使用預設 24/8 佈局打包 index + generation |
|
|
392
|
+
| `refOf` | `<T>(world: World, eid: EntityId) => EntityRef<T>` | stable(自 0.3.0)— 建立 ABA-safe ref;entity 已死亡時拋出 `EntityNotAliveError` |
|
|
393
|
+
| `deref` | `<T>(world: World, ref: EntityRef<T>) => EntityId \| null` | stable(自 0.3.0)— 回傳存活的 `EntityId`,否則回傳 `null`;絕不拋出 |
|
|
394
|
+
| `aliveRef` | `<T>(world: World, ref: EntityRef<T>) => boolean` | stable(自 0.3.0)— `deref` 的布林 guard 形式;絕不拋出 |
|
|
395
|
+
| `EntityRef` | `interface EntityRef<T> { id: EntityId; worldId: number }` | stable(自 0.3.0)— 不透明的 ABA-safe 參照;僅限記憶體內使用 |
|
|
396
|
+
| `EntityNotAliveError` | `class EntityNotAliveError extends Error { eid: number }` | stable(自 0.3.0)— `refOf` 在 entity 不存活時拋出 |
|
|
392
397
|
|
|
393
398
|
### Component — `aiecsjs`
|
|
394
399
|
|
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` |
|
|
38
|
-
| `packEntity` |
|
|
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
|
|
134
|
-
| 0.
|
|
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.
|
|
37
|
-
| `packEntity` | stable | 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
|
|
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
|
## 在執行時檢查穩定度
|