aiecsjs 0.4.0 → 0.5.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/README.md +1 -1
- package/README_ZHTW.md +4 -4
- package/api.json +1 -1
- package/dist/{chunk-B777DQR7.cjs → chunk-4QBOZQZR.cjs} +2 -2
- package/dist/{chunk-B777DQR7.cjs.map → chunk-4QBOZQZR.cjs.map} +1 -1
- package/dist/{chunk-SQWZUC2Q.js → chunk-AROBEZEB.js} +2 -2
- package/dist/{chunk-SQWZUC2Q.js.map → chunk-AROBEZEB.js.map} +1 -1
- package/dist/{chunk-SJDWI3OZ.cjs → chunk-IJ4BTSN2.cjs} +2 -2
- package/dist/{chunk-SJDWI3OZ.cjs.map → chunk-IJ4BTSN2.cjs.map} +1 -1
- package/dist/{chunk-CTESP3XL.js → chunk-JLU2PG6H.js} +2 -2
- package/dist/{chunk-CTESP3XL.js.map → chunk-JLU2PG6H.js.map} +1 -1
- package/dist/{chunk-F7KNZ27O.js → chunk-XDLAI4YT.js} +2 -2
- package/dist/{chunk-F7KNZ27O.js.map → chunk-XDLAI4YT.js.map} +1 -1
- package/dist/{chunk-RHH5JA74.cjs → chunk-Z6ATHRHC.cjs} +2 -2
- package/dist/{chunk-RHH5JA74.cjs.map → chunk-Z6ATHRHC.cjs.map} +1 -1
- package/dist/commands.cjs +1 -1
- package/dist/commands.js +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/observers.cjs +1 -1
- package/dist/observers.js +1 -1
- package/dist/relations.cjs +1 -1
- package/dist/relations.js +1 -1
- package/dist/serialize.cjs +1 -1
- package/dist/serialize.js +1 -1
- package/dist/worker.cjs +1 -1
- package/dist/worker.js +1 -1
- package/llms-full.txt +23 -5
- package/package.json +5 -8
- package/CHANGELOG.md +0 -292
- package/STABILITY.md +0 -156
- package/STABILITY_ZHTW.md +0 -155
- package/docs/MIGRATION.md +0 -252
- package/docs/MIGRATION_ZHTW.md +0 -252
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import {i as i$1,h}from'./chunk-
|
|
1
|
+
import {i as i$1,h}from'./chunk-AROBEZEB.js';export{a as defineQuery,b as enterQuery,c as exitQuery,g as forEachEntity,f as iterQuery,d as queryArchetypes,e as runQuery}from'./chunk-AROBEZEB.js';import {P,N,i as i$2,x,s,t}from'./chunk-XDLAI4YT.js';export{a as VERSION,I as addComponent,u as createEntity,k as createWorld,E as defineComponent,G as defineObjectComponent,F as defineTag,v as destroyEntity,l as destroyWorld,l as disposeWorld,w as entityExists,L as getComponent,z as getEntityGeneration,y as getEntityIndex,o as getWorldCapacity,n as getWorldSize,K as hasComponent,B as isEntity,q as isWorld,A as packEntity,J as removeComponent,m as resetWorld,M as setComponent}from'./chunk-XDLAI4YT.js';var K={i8:"i8",u8:"u8",i16:"i16",u16:"u16",i32:"i32",u32:"u32",f32:"f32",f64:"f64",eid:"eid",bool:"bool"};function $(...n){return n.length===0?e=>e:n.length===1?n[0]:(e,r)=>{let t=e;for(let o of n)t=o(t,r);return t}}var i=class extends Error{eid;constructor(e){super(`aiecsjs: entity ${e} is not alive`),this.name="EntityNotAliveError",this.eid=e;}};function J(n,e){let r=i$2(n);if(!x(r,e))throw new i(e);return Object.freeze({id:e,worldId:r.id})}function b(n,e){let r=i$2(n);if(e.worldId!==r.id)return null;let t$1=e.id,o=s(t$1,r.options);if(o<=0||o>=r.capacity)return null;let x=r.entityArchetype[o]??0,d=r.archetypes[x];if(!d||!d.entityRow.has(e.id))return null;let C=r.generations[o]??0,A=t(t$1,r.options);return C!==A?null:e.id}function X(n,e){return b(n,e)!==null}i$1(n=>P(n));N((n,e,r,t,o)=>{h(n,e,r,t,o);});var ae=typeof SharedArrayBuffer<"u";export{i as EntityNotAliveError,ae as IS_SAB_SUPPORTED,K as Types,X as aliveRef,b as deref,$ as pipe,J as refOf};//# sourceMappingURL=index.js.map
|
|
2
2
|
//# sourceMappingURL=index.js.map
|
package/dist/observers.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
'use strict';var
|
|
1
|
+
'use strict';var chunkZ6ATHRHC_cjs=require('./chunk-Z6ATHRHC.cjs'),chunkIJ4BTSN2_cjs=require('./chunk-IJ4BTSN2.cjs');function f(n,e){if(!e)return n;if(e.aborted)return n(),()=>{};let o=()=>{n();};e.addEventListener("abort",o,{once:true});let t=false;return ()=>{t||(t=true,e.removeEventListener("abort",o),n());}}function w(n,e,o,t){return f(b(n,e,"add",o),t?.signal)}function x(n,e,o,t){return f(b(n,e,"remove",o),t?.signal)}function C(n,e,o,t){return f(b(n,e,"set",o),t?.signal)}function L(n,e,o,t,r){let s=chunkIJ4BTSN2_cjs.i(n);chunkZ6ATHRHC_cjs.e(n,e);let i={event:o,componentBit:-1,queryId:e.id,handler:t};return s.observers.push(i),f(()=>{let d=s.observers.indexOf(i);d>=0&&s.observers.splice(d,1);},r?.signal)}function b(n,e,o,t){let r=chunkIJ4BTSN2_cjs.i(n),s=chunkIJ4BTSN2_cjs.H(e),i=r.componentBitFor.get(s.id);i===void 0&&(i=chunkIJ4BTSN2_cjs.p(r,s));let u={event:o,componentBit:i,queryId:-1,handler:t};return r.observers.push(u),()=>{let d=r.observers.indexOf(u);d>=0&&r.observers.splice(d,1);}}function E(n,e,o){let t=Array.from(n.observers);for(let r of t)r.event==="add"&&r.componentBit===o&&n.observers.includes(r)&&r.handler(e);O(n,e,"add");}function M(n,e,o){let t=Array.from(n.observers);for(let r of t)r.event==="remove"&&r.componentBit===o&&n.observers.includes(r)&&r.handler(e);O(n,e,"remove");}function g(n,e,o,t){let r=Array.from(n.observers);for(let s of r)s.event==="set"&&n.observers.includes(s)&&(s.componentBit===o||s.queryId!==-1&&n.queries[s.queryId]?.all.includes(n.componentInfoByBit[o]?.id??-1))&&s.handler(e,t);}function O(n,e,o,t,r){let s=n.options.maskWordCount,i=(e&n.options.indexMask)*s,u=Array.from(n.observers);for(let d of u){if(d.event!==o||d.queryId===-1||!n.observers.includes(d))continue;let c=n.queryMasks.get(d.queryId);if(!c)continue;let l=chunkIJ4BTSN2_cjs.h(n.entityMask,i,s,c.withMask,c.anyMask,c.noneMask,c.anyHasBits);o==="add"&&l&&d.handler(e),o==="remove"&&!l&&d.handler(e);}}chunkIJ4BTSN2_cjs.O({fireAdd:E,fireRemove:M,fireSet:g});chunkIJ4BTSN2_cjs.C({dispatchDestroyObservers(n,e){let o=n.options.maskWordCount,t=(e&n.options.indexMask)*o,r=new Uint32Array(o);for(let i=0;i<o;i++)r[i]=n.entityMask[t+i]??0;chunkIJ4BTSN2_cjs.g(r,0,o,i=>{let u=Array.from(n.observers);for(let d of u)d.event==="remove"&&d.componentBit===i&&n.observers.includes(d)&&d.handler(e);});let s=Array.from(n.observers);for(let i of s){if(i.event!=="remove"||i.queryId===-1||!n.observers.includes(i))continue;let u=n.queryMasks.get(i.queryId);if(!u)continue;chunkIJ4BTSN2_cjs.h(r,0,o,u.withMask,u.anyMask,u.noneMask,u.anyHasBits)&&i.handler(e);}}});exports.observe=L;exports.onAdd=w;exports.onRemove=x;exports.onSet=C;//# sourceMappingURL=observers.cjs.map
|
|
2
2
|
//# sourceMappingURL=observers.cjs.map
|
package/dist/observers.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import {e}from'./chunk-
|
|
1
|
+
import {e}from'./chunk-AROBEZEB.js';import {O as O$1,C as C$1,g as g$1,h,i,H,p}from'./chunk-XDLAI4YT.js';function f(n,e){if(!e)return n;if(e.aborted)return n(),()=>{};let o=()=>{n();};e.addEventListener("abort",o,{once:true});let t=false;return ()=>{t||(t=true,e.removeEventListener("abort",o),n());}}function w(n,e,o,t){return f(b(n,e,"add",o),t?.signal)}function x(n,e,o,t){return f(b(n,e,"remove",o),t?.signal)}function C(n,e,o,t){return f(b(n,e,"set",o),t?.signal)}function L(n,e$1,o,t,r){let s=i(n);e(n,e$1);let i$1={event:o,componentBit:-1,queryId:e$1.id,handler:t};return s.observers.push(i$1),f(()=>{let d=s.observers.indexOf(i$1);d>=0&&s.observers.splice(d,1);},r?.signal)}function b(n,e,o,t){let r=i(n),s=H(e),i$1=r.componentBitFor.get(s.id);i$1===void 0&&(i$1=p(r,s));let u={event:o,componentBit:i$1,queryId:-1,handler:t};return r.observers.push(u),()=>{let d=r.observers.indexOf(u);d>=0&&r.observers.splice(d,1);}}function E(n,e,o){let t=Array.from(n.observers);for(let r of t)r.event==="add"&&r.componentBit===o&&n.observers.includes(r)&&r.handler(e);O(n,e,"add");}function M(n,e,o){let t=Array.from(n.observers);for(let r of t)r.event==="remove"&&r.componentBit===o&&n.observers.includes(r)&&r.handler(e);O(n,e,"remove");}function g(n,e,o,t){let r=Array.from(n.observers);for(let s of r)s.event==="set"&&n.observers.includes(s)&&(s.componentBit===o||s.queryId!==-1&&n.queries[s.queryId]?.all.includes(n.componentInfoByBit[o]?.id??-1))&&s.handler(e,t);}function O(n,e,o,t,r){let s=n.options.maskWordCount,i=(e&n.options.indexMask)*s,u=Array.from(n.observers);for(let d of u){if(d.event!==o||d.queryId===-1||!n.observers.includes(d))continue;let c=n.queryMasks.get(d.queryId);if(!c)continue;let l=h(n.entityMask,i,s,c.withMask,c.anyMask,c.noneMask,c.anyHasBits);o==="add"&&l&&d.handler(e),o==="remove"&&!l&&d.handler(e);}}O$1({fireAdd:E,fireRemove:M,fireSet:g});C$1({dispatchDestroyObservers(n,e){let o=n.options.maskWordCount,t=(e&n.options.indexMask)*o,r=new Uint32Array(o);for(let i=0;i<o;i++)r[i]=n.entityMask[t+i]??0;g$1(r,0,o,i=>{let u=Array.from(n.observers);for(let d of u)d.event==="remove"&&d.componentBit===i&&n.observers.includes(d)&&d.handler(e);});let s=Array.from(n.observers);for(let i of s){if(i.event!=="remove"||i.queryId===-1||!n.observers.includes(i))continue;let u=n.queryMasks.get(i.queryId);if(!u)continue;h(r,0,o,u.withMask,u.anyMask,u.noneMask,u.anyHasBits)&&i.handler(e);}}});export{L as observe,w as onAdd,x as onRemove,C as onSet};//# sourceMappingURL=observers.js.map
|
|
2
2
|
//# sourceMappingURL=observers.js.map
|
package/dist/relations.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
'use strict';var
|
|
1
|
+
'use strict';var chunkIJ4BTSN2_cjs=require('./chunk-IJ4BTSN2.cjs');var x=1;function p(r){return {__kind:"relation",__id:x++,__exclusive:r?.exclusive??false,__hasData:false}}var _=p({exclusive:true});function v(r,l){let s=r.relationStorage.get(l.__id);return s||(s={rel:l,exclusive:l.__exclusive?new Int32Array(r.capacity).fill(-1):null,outgoing:new Map,data:new Map},r.relationStorage.set(l.__id,s),s)}function R(r,l,s,i,n){let t=chunkIJ4BTSN2_cjs.i(r);if(t.readOnly)throw new Error("aiecsjs: cannot mutate a read-only world");let e=v(t,s),o=l&t.options.indexMask,d=i&t.options.indexMask;if(e.exclusive){if(o>=e.exclusive.length){let c=new Int32Array(Math.max(o+1,e.exclusive.length*2)).fill(-1);c.set(e.exclusive),e.exclusive=c;}let a=e.exclusive[o]??-1;if(a>=0&&a!==d){let c=e.data.get(o);c?.delete(a),c&&c.size===0&&e.data.delete(o);}e.exclusive[o]=d;}else {let a=e.outgoing.get(o);a||(a=[],e.outgoing.set(o,a)),a.includes(d)||a.push(d);}if(n!==void 0){let a=e.data.get(o);a||(a=new Map,e.data.set(o,a)),a.set(d,n);}}function b(r,l,s,i){let n=chunkIJ4BTSN2_cjs.i(r);if(n.readOnly)throw new Error("aiecsjs: cannot mutate a read-only world");let t=n.relationStorage.get(s.__id);if(!t)return;let e=l&n.options.indexMask,o=i&n.options.indexMask;if(t.exclusive)e<t.exclusive.length&&t.exclusive[e]===o&&(t.exclusive[e]=-1);else {let a=t.outgoing.get(e);if(a){let c=a.indexOf(o);c>=0&&a.splice(c,1),a.length===0&&t.outgoing.delete(e);}}let d=t.data.get(e);d&&(d.delete(o),d.size===0&&t.data.delete(e));}function h(r,l,s){let i=chunkIJ4BTSN2_cjs.i(r),n=i.relationStorage.get(s.__id);if(!n)return [];let t=l&i.options.indexMask;if(n.exclusive){if(t<n.exclusive.length){let o=n.exclusive[t]??-1;if(o>=0){let d=i.generations[o]??0;return [chunkIJ4BTSN2_cjs.r(o,d,i.options)]}}return []}let e=n.outgoing.get(t);return e?e.map(o=>{let d=i.generations[o]??0;return chunkIJ4BTSN2_cjs.r(o,d,i.options)}):[]}function w(r,l,s,i){let n=chunkIJ4BTSN2_cjs.i(r),t=n.relationStorage.get(s.__id);if(!t)return;let e=l&n.options.indexMask,o=i&n.options.indexMask;return t.data.get(e)?.get(o)}chunkIJ4BTSN2_cjs.D((r,l)=>{let s=l&r.options.indexMask;for(let i of r.relationStorage.values()){if(i.exclusive){s<i.exclusive.length&&(i.exclusive[s]=-1);for(let n=0;n<i.exclusive.length;n++)i.exclusive[n]===s&&(i.exclusive[n]=-1);}i.outgoing.delete(s);for(let[n,t]of i.outgoing){let e=t.indexOf(s);e>=0&&(t.splice(e,1),t.length===0&&i.outgoing.delete(n));}i.data.delete(s);for(let[n,t]of i.data)t.delete(s),t.size===0&&i.data.delete(n);}});exports.ChildOf=_;exports.addRelation=R;exports.defineRelation=p;exports.getRelationData=w;exports.getRelationTargets=h;exports.removeRelation=b;//# sourceMappingURL=relations.cjs.map
|
|
2
2
|
//# sourceMappingURL=relations.cjs.map
|
package/dist/relations.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import {D,i,r}from'./chunk-
|
|
1
|
+
import {D,i,r}from'./chunk-XDLAI4YT.js';var x=1;function p(r){return {__kind:"relation",__id:x++,__exclusive:r?.exclusive??false,__hasData:false}}var _=p({exclusive:true});function v(r,l){let s=r.relationStorage.get(l.__id);return s||(s={rel:l,exclusive:l.__exclusive?new Int32Array(r.capacity).fill(-1):null,outgoing:new Map,data:new Map},r.relationStorage.set(l.__id,s),s)}function R(r,l,s,i$1,n){let t=i(r);if(t.readOnly)throw new Error("aiecsjs: cannot mutate a read-only world");let e=v(t,s),o=l&t.options.indexMask,d=i$1&t.options.indexMask;if(e.exclusive){if(o>=e.exclusive.length){let c=new Int32Array(Math.max(o+1,e.exclusive.length*2)).fill(-1);c.set(e.exclusive),e.exclusive=c;}let a=e.exclusive[o]??-1;if(a>=0&&a!==d){let c=e.data.get(o);c?.delete(a),c&&c.size===0&&e.data.delete(o);}e.exclusive[o]=d;}else {let a=e.outgoing.get(o);a||(a=[],e.outgoing.set(o,a)),a.includes(d)||a.push(d);}if(n!==void 0){let a=e.data.get(o);a||(a=new Map,e.data.set(o,a)),a.set(d,n);}}function b(r,l,s,i$1){let n=i(r);if(n.readOnly)throw new Error("aiecsjs: cannot mutate a read-only world");let t=n.relationStorage.get(s.__id);if(!t)return;let e=l&n.options.indexMask,o=i$1&n.options.indexMask;if(t.exclusive)e<t.exclusive.length&&t.exclusive[e]===o&&(t.exclusive[e]=-1);else {let a=t.outgoing.get(e);if(a){let c=a.indexOf(o);c>=0&&a.splice(c,1),a.length===0&&t.outgoing.delete(e);}}let d=t.data.get(e);d&&(d.delete(o),d.size===0&&t.data.delete(e));}function h(r$1,l,s){let i$1=i(r$1),n=i$1.relationStorage.get(s.__id);if(!n)return [];let t=l&i$1.options.indexMask;if(n.exclusive){if(t<n.exclusive.length){let o=n.exclusive[t]??-1;if(o>=0){let d=i$1.generations[o]??0;return [r(o,d,i$1.options)]}}return []}let e=n.outgoing.get(t);return e?e.map(o=>{let d=i$1.generations[o]??0;return r(o,d,i$1.options)}):[]}function w(r,l,s,i$1){let n=i(r),t=n.relationStorage.get(s.__id);if(!t)return;let e=l&n.options.indexMask,o=i$1&n.options.indexMask;return t.data.get(e)?.get(o)}D((r,l)=>{let s=l&r.options.indexMask;for(let i of r.relationStorage.values()){if(i.exclusive){s<i.exclusive.length&&(i.exclusive[s]=-1);for(let n=0;n<i.exclusive.length;n++)i.exclusive[n]===s&&(i.exclusive[n]=-1);}i.outgoing.delete(s);for(let[n,t]of i.outgoing){let e=t.indexOf(s);e>=0&&(t.splice(e,1),t.length===0&&i.outgoing.delete(n));}i.data.delete(s);for(let[n,t]of i.data)t.delete(s),t.size===0&&i.data.delete(n);}});export{_ as ChildOf,R as addRelation,p as defineRelation,w as getRelationData,h as getRelationTargets,b as removeRelation};//# sourceMappingURL=relations.js.map
|
|
2
2
|
//# sourceMappingURL=relations.js.map
|
package/dist/serialize.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
'use strict';var
|
|
1
|
+
'use strict';var chunk4QBOZQZR_cjs=require('./chunk-4QBOZQZR.cjs');require('./chunk-IJ4BTSN2.cjs');Object.defineProperty(exports,"createDeltaSerializer",{enumerable:true,get:function(){return chunk4QBOZQZR_cjs.e}});Object.defineProperty(exports,"deserializeWorld",{enumerable:true,get:function(){return chunk4QBOZQZR_cjs.b}});Object.defineProperty(exports,"fromJSON",{enumerable:true,get:function(){return chunk4QBOZQZR_cjs.d}});Object.defineProperty(exports,"serializeWorld",{enumerable:true,get:function(){return chunk4QBOZQZR_cjs.a}});Object.defineProperty(exports,"toJSON",{enumerable:true,get:function(){return chunk4QBOZQZR_cjs.c}});//# sourceMappingURL=serialize.cjs.map
|
|
2
2
|
//# sourceMappingURL=serialize.cjs.map
|
package/dist/serialize.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export{e as createDeltaSerializer,b as deserializeWorld,d as fromJSON,a as serializeWorld,c as toJSON}from'./chunk-
|
|
1
|
+
export{e as createDeltaSerializer,b as deserializeWorld,d as fromJSON,a as serializeWorld,c as toJSON}from'./chunk-JLU2PG6H.js';import'./chunk-XDLAI4YT.js';//# sourceMappingURL=serialize.js.map
|
|
2
2
|
//# sourceMappingURL=serialize.js.map
|
package/dist/worker.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
'use strict';var
|
|
1
|
+
'use strict';var chunk4QBOZQZR_cjs=require('./chunk-4QBOZQZR.cjs'),chunkIJ4BTSN2_cjs=require('./chunk-IJ4BTSN2.cjs');var l=1095320899;function W(r){let e=chunkIJ4BTSN2_cjs.i(r),o=chunk4QBOZQZR_cjs.a(r);if(typeof SharedArrayBuffer>"u"){let n=new ArrayBuffer(o.byteLength);return new Uint8Array(n).set(o),{buffer:n,meta:p(e)}}let t=new SharedArrayBuffer(o.byteLength);return new Uint8Array(t).set(o),{buffer:t,meta:p(e)}}function b(r){m(r.meta);let e=new Uint8Array(r.buffer);return chunk4QBOZQZR_cjs.b(e)}function S(r,e){let o=new Uint8Array(r),t=chunk4QBOZQZR_cjs.b(o);if(e?.readOnly){let n=chunkIJ4BTSN2_cjs.i(t);n.readOnly=true;}return t}function w(r){chunkIJ4BTSN2_cjs.j(r.id)&&chunkIJ4BTSN2_cjs.l(r);}function p(r){let e=[];for(let o of r.componentInfoByBit)o&&e.push({id:o.id,kind:o.kind,schema:o.schema});return {magic:l,formatVersion:1,aiecsjsVersion:chunkIJ4BTSN2_cjs.a,indexBits:r.options.indexBits,generationBits:r.options.generationBits,maxComponents:r.options.maxComponents,maskWordCount:r.options.maskWordCount,capacity:r.capacity,componentSchemas:e}}function m(r){if(r.magic!==l)throw new Error("aiecsjs: invalid snapshot meta (wrong magic)");if(r.formatVersion!==1)throw new Error(`aiecsjs: unsupported snapshot format version ${r.formatVersion}`)}exports.adoptSnapshot=b;exports.attachWorld=S;exports.detachWorld=w;exports.transferableSnapshot=W;//# sourceMappingURL=worker.cjs.map
|
|
2
2
|
//# sourceMappingURL=worker.cjs.map
|
package/dist/worker.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import {a,b as b$1}from'./chunk-
|
|
1
|
+
import {a,b as b$1}from'./chunk-JLU2PG6H.js';import {i,j,l as l$1,a as a$1}from'./chunk-XDLAI4YT.js';var l=1095320899;function W(r){let e=i(r),o=a(r);if(typeof SharedArrayBuffer>"u"){let n=new ArrayBuffer(o.byteLength);return new Uint8Array(n).set(o),{buffer:n,meta:p(e)}}let t=new SharedArrayBuffer(o.byteLength);return new Uint8Array(t).set(o),{buffer:t,meta:p(e)}}function b(r){m(r.meta);let e=new Uint8Array(r.buffer);return b$1(e)}function S(r,e){let o=new Uint8Array(r),t=b$1(o);if(e?.readOnly){let n=i(t);n.readOnly=true;}return t}function w(r){j(r.id)&&l$1(r);}function p(r){let e=[];for(let o of r.componentInfoByBit)o&&e.push({id:o.id,kind:o.kind,schema:o.schema});return {magic:l,formatVersion:1,aiecsjsVersion:a$1,indexBits:r.options.indexBits,generationBits:r.options.generationBits,maxComponents:r.options.maxComponents,maskWordCount:r.options.maskWordCount,capacity:r.capacity,componentSchemas:e}}function m(r){if(r.magic!==l)throw new Error("aiecsjs: invalid snapshot meta (wrong magic)");if(r.formatVersion!==1)throw new Error(`aiecsjs: unsupported snapshot format version ${r.formatVersion}`)}export{b as adoptSnapshot,S as attachWorld,w as detachWorld,W as transferableSnapshot};//# sourceMappingURL=worker.js.map
|
|
2
2
|
//# sourceMappingURL=worker.js.map
|
package/llms-full.txt
CHANGED
|
@@ -690,7 +690,7 @@ ws.onmessage = (e) => remoteDelta.apply(remoteWorld, new Uint8Array(e.data))
|
|
|
690
690
|
|
|
691
691
|
## Migration Guides
|
|
692
692
|
|
|
693
|
-
Full tables in [`docs/MIGRATION.md`](
|
|
693
|
+
Full tables in [`docs/MIGRATION.md`](https://github.com/yshengliao/aiecsjs/blob/main/docs/MIGRATION.md).
|
|
694
694
|
|
|
695
695
|
### From bitECS 0.4
|
|
696
696
|
|
|
@@ -935,12 +935,32 @@ See [`CHANGELOG.md`](./CHANGELOG.md).
|
|
|
935
935
|
|
|
936
936
|
# Changelog
|
|
937
937
|
|
|
938
|
-
[English](CHANGELOG.md) | [繁體中文](CHANGELOG_ZHTW.md)
|
|
939
|
-
|
|
940
938
|
All notable changes to `aiecsjs` are recorded in this file.
|
|
941
939
|
|
|
942
940
|
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).
|
|
943
941
|
|
|
942
|
+
## [0.5.0] - 2026-05-30
|
|
943
|
+
|
|
944
|
+
ai\*js family version-unify milestone — the seven packages align on a common `0.5.0`. **No runtime API change; `dist/` is byte-identical to 0.4.1 apart from the bumped `VERSION` string.**
|
|
945
|
+
|
|
946
|
+
### Changed
|
|
947
|
+
|
|
948
|
+
- **Migration-guide links repointed to GitHub blob URLs.** `docs/` became repository-only in 0.4.1 (dropped from the npm `files[]`), so the relative `./docs/MIGRATION*.md` links in `README.md` / `README_ZHTW.md` no longer resolved from the npm package page or the installed tarball. They now point at `https://github.com/yshengliao/aiecsjs/blob/main/docs/…` so consumers can follow them.
|
|
949
|
+
- **Version aligned to the ai\*js family `0.5.0` unify milestone.** A coordinated family-wide minor bump; aiecsjs carries no source / public-API / relations change in this release.
|
|
950
|
+
|
|
951
|
+
## [0.4.1] - 2026-05-29
|
|
952
|
+
|
|
953
|
+
Consistency patch — packaging and documentation surface aligned to the ai*js family. **No runtime API change; `dist/` is byte-identical to 0.4.0 apart from the bumped `VERSION` string.**
|
|
954
|
+
|
|
955
|
+
### Changed
|
|
956
|
+
|
|
957
|
+
- **`package.json` packaging metadata aligned to family conventions**: `engines.node` `">=18"` → `">=18.0.0"`; `repository.url` gains the `git+` prefix (`git+https://github.com/yshengliao/aiecsjs.git`). Both are semantically equivalent — registry/tooling hygiene only.
|
|
958
|
+
- **`files[]` trimmed to the family-minimal set plus `api.json`**: the npm tarball now ships `dist`, `README.md`, `README_ZHTW.md`, `LICENSE`, `llms.txt`, `llms-full.txt`, and `api.json`. `LICENSE` is now listed explicitly (it was already published via npm's automatic root-LICENSE inclusion). `STABILITY.md`, `CHANGELOG.md`, and `docs/` are no longer bundled — they remain in the repository and stay reachable from the README/`llms.txt` links on GitHub. `api.json` is **deliberately retained**: it is the machine-readable export manifest (stability + `since` per entry) that this package's "AI-readable docs" contract advertises, so it remains the tarball's stability surface for tooling.
|
|
959
|
+
|
|
960
|
+
### Removed
|
|
961
|
+
|
|
962
|
+
- **Redundant Traditional-Chinese doc duplicates**: `STABILITY_ZHTW.md` and `CHANGELOG_ZHTW.md` removed. The family keeps `README_ZHTW.md` as the single Traditional-Chinese entry point; per-export stability and the changelog are English-canonical (with `api.json` carrying the machine-readable stability surface). The plain-pipe language-switcher line atop `STABILITY.md` / `CHANGELOG.md` and the now-dangling `_ZHTW` references inside `README_ZHTW.md` were removed accordingly.
|
|
963
|
+
|
|
944
964
|
## [0.4.0] - 2026-05-29
|
|
945
965
|
|
|
946
966
|
### Added
|
|
@@ -1232,8 +1252,6 @@ The "documentation honesty + test backstop" release. No new public APIs; this is
|
|
|
1232
1252
|
|
|
1233
1253
|
# Stability Contract
|
|
1234
1254
|
|
|
1235
|
-
[English](STABILITY.md) | [繁體中文](STABILITY_ZHTW.md)
|
|
1236
|
-
|
|
1237
1255
|
This document is the per-export stability promise for `aiecsjs`. It is the contract AI tools and human users can rely on when pinning versions and writing import paths.
|
|
1238
1256
|
|
|
1239
1257
|
## Policy
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "aiecsjs",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
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",
|
|
@@ -49,22 +49,19 @@
|
|
|
49
49
|
"dist",
|
|
50
50
|
"README.md",
|
|
51
51
|
"README_ZHTW.md",
|
|
52
|
-
"
|
|
53
|
-
"STABILITY_ZHTW.md",
|
|
54
|
-
"CHANGELOG.md",
|
|
52
|
+
"LICENSE",
|
|
55
53
|
"llms.txt",
|
|
56
54
|
"llms-full.txt",
|
|
57
|
-
"api.json"
|
|
58
|
-
"docs"
|
|
55
|
+
"api.json"
|
|
59
56
|
],
|
|
60
57
|
"engines": {
|
|
61
|
-
"node": ">=18"
|
|
58
|
+
"node": ">=18.0.0"
|
|
62
59
|
},
|
|
63
60
|
"license": "MIT",
|
|
64
61
|
"author": "yshengliao",
|
|
65
62
|
"repository": {
|
|
66
63
|
"type": "git",
|
|
67
|
-
"url": "https://github.com/yshengliao/aiecsjs.git"
|
|
64
|
+
"url": "git+https://github.com/yshengliao/aiecsjs.git"
|
|
68
65
|
},
|
|
69
66
|
"keywords": [
|
|
70
67
|
"ecs",
|
package/CHANGELOG.md
DELETED
|
@@ -1,292 +0,0 @@
|
|
|
1
|
-
# Changelog
|
|
2
|
-
|
|
3
|
-
[English](CHANGELOG.md) | [繁體中文](CHANGELOG_ZHTW.md)
|
|
4
|
-
|
|
5
|
-
All notable changes to `aiecsjs` are recorded in this file.
|
|
6
|
-
|
|
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
|
-
|
|
9
|
-
## [0.4.0] - 2026-05-29
|
|
10
|
-
|
|
11
|
-
### Added
|
|
12
|
-
|
|
13
|
-
- **`getRelationData(world, source, rel, target)`**: new stable export on `aiecsjs/relations`. Returns the `data` payload attached via `addRelation`, or `undefined` when no such edge exists or no data was stored. Closes the write-only-data asymmetry present since 0.1: `addRelation` accepted a data argument but there was no corresponding public read path.
|
|
14
|
-
|
|
15
|
-
### Changed
|
|
16
|
-
|
|
17
|
-
- **`aiecsjs/relations` graduated from experimental to stable.** The graph API (`defineRelation`, `addRelation`, `removeRelation`, `getRelationTargets`, `getRelationData`) and the built-in `ChildOf` relation are now frozen for the 1.x track. See [`STABILITY.md`](./STABILITY.md) for the full stability contract, including the raw slot-keying ABA semantic.
|
|
18
|
-
- **`aiecsjs/worker` remains experimental.** True SAB shared-memory column aliasing is deferred; the worker sub-path continues on snapshot-copy semantics.
|
|
19
|
-
|
|
20
|
-
### Build & Tooling
|
|
21
|
-
|
|
22
|
-
- **size-limit → `scripts/check-size.mjs`**: replaced the `size-limit` + `@size-limit/file` dev dependencies with a zero-dependency script that measures transitive chunk-closure gzip size per ESM entry. Required because `tsup splitting: true` (introduced in 0.3.1) makes each entry a thin re-export shell; the vanilla single-file measurement reported ~899 B for index when the true closure is ~7295 B. The new script resolves chunk imports recursively via BFS, sums per-file gzip, and enforces per-entry budgets.
|
|
23
|
-
- **npm → pnpm**: migrated from `package-lock.json` to `pnpm-lock.yaml`. Added `"packageManager": "pnpm@9.12.3"` and `"publishConfig": { "access": "public" }`. CI and publish workflows updated to use `pnpm/action-setup@v6` + `pnpm install --frozen-lockfile`. `npm publish --provenance --access public` in the publish workflow is intentionally preserved (OIDC trusted publishing requires npm CLI, not pnpm publish).
|
|
24
|
-
- **Coverage tests added + unreachable gaps documented**: new tests cover previously-unreachable paths in `serialize.ts`, `component.ts`, `query.ts`, and `loop.ts`. Thresholds updated to the honestly-achieved floor (statements 95 / branches 81 / functions 98 / lines 99). Unreachable-by-design gaps are now documented in `vitest.config.ts` with Chesterton rationale.
|
|
25
|
-
|
|
26
|
-
## [0.3.1] - 2026-05-29
|
|
27
|
-
|
|
28
|
-
### Fixed
|
|
29
|
-
|
|
30
|
-
- **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).
|
|
31
|
-
- **`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.
|
|
32
|
-
- **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.
|
|
33
|
-
- **`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.
|
|
34
|
-
- **`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).
|
|
35
|
-
|
|
36
|
-
### Known Limitations
|
|
37
|
-
|
|
38
|
-
- **`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.
|
|
39
|
-
|
|
40
|
-
### Documentation
|
|
41
|
-
|
|
42
|
-
- 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.
|
|
43
|
-
|
|
44
|
-
### Build & Tooling
|
|
45
|
-
|
|
46
|
-
- 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 */`.
|
|
47
|
-
- `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.
|
|
48
|
-
- Dispose three-cycle tests, error-path tests, and observer handler-throw behaviour documented in `tests/world.test.ts` / `tests/observers.test.ts`.
|
|
49
|
-
- `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.
|
|
50
|
-
|
|
51
|
-
## [Unreleased]
|
|
52
|
-
|
|
53
|
-
### Planned for 0.4+
|
|
54
|
-
|
|
55
|
-
- Add `pipeAsync` for async system composition.
|
|
56
|
-
- Doc-test harness so README code blocks are mechanically verified.
|
|
57
|
-
- Promote `aiecsjs/worker` to `stable` once true SAB shared-memory column aliasing is implemented.
|
|
58
|
-
- Document the 8-bit generation wrap caveat in [STABILITY.md](./STABILITY.md): with the
|
|
59
|
-
default `generationBits=8`, a single slot recycled 256 times wraps back to its
|
|
60
|
-
starting generation, briefly re-opening the ABA window. Safe for v0.5 shmup
|
|
61
|
-
workloads (~5000 frame to wrap a single slot at 60 fps × ~1k destroys); high-churn
|
|
62
|
-
pools should set `createWorld({ indexBits: 16, generationBits: 16 })` (16 + 16 = 32 bits;
|
|
63
|
-
65 536 entities × 65 536 generations). See test
|
|
64
|
-
[tests/ref.test.ts](./tests/ref.test.ts) `generation wrap` describe block.
|
|
65
|
-
|
|
66
|
-
## [0.3.0] - 2026-05-29
|
|
67
|
-
|
|
68
|
-
### Added (API)
|
|
69
|
-
|
|
70
|
-
- **`EntityRef<T>`** — ABA-safe entity reference. `refOf(world, eid)` builds one;
|
|
71
|
-
`deref(world, ref)` returns the entity id when still alive (generation match)
|
|
72
|
-
or `null` otherwise; `aliveRef(world, ref)` is the boolean guard form. Phantom
|
|
73
|
-
type `T` lets callers distinguish ref kinds (e.g. `EntityRef<'bullet'>`) without
|
|
74
|
-
runtime cost. Refs are in-memory only — not serializable across worker / disk.
|
|
75
|
-
- **`EntityNotAliveError`** — thrown by `refOf` when the entity is dead or invalid.
|
|
76
|
-
`deref` / `aliveRef` never throw.
|
|
77
|
-
|
|
78
|
-
### Changed
|
|
79
|
-
|
|
80
|
-
- **`EntityId` now packs index + generation** into a single 32-bit number
|
|
81
|
-
`(generation << indexBits) | index` (default `indexBits=24, generationBits=8`).
|
|
82
|
-
`EntityId` remains opaque per STABILITY contract; the layout is implementation
|
|
83
|
-
detail. **Migration note**: do not compare `EntityId` numbers directly
|
|
84
|
-
(`eid === 42` will break across slot recycles); use
|
|
85
|
-
`getEntityIndex(eid)` for index comparison or `refOf(world, eid).id` for
|
|
86
|
-
identity matching that survives slot reuse.
|
|
87
|
-
- **`getEntityGeneration` / `packEntity` graduate to `stable`** (were `experimental`
|
|
88
|
-
since 0.2.0). Both now return real values. These functions use default 24/8 bit
|
|
89
|
-
layout; for non-default `createWorld({ indexBits, generationBits })`, use
|
|
90
|
-
`EntityRef` and `deref` instead of manual unpacking.
|
|
91
|
-
|
|
92
|
-
### Fixed
|
|
93
|
-
|
|
94
|
-
- **ABA bug on entity slot recycle**: previously `entityExists` and `isAliveInternal`
|
|
95
|
-
only checked archetype membership; a stale `EntityId` pointing at a recycled slot
|
|
96
|
-
would silently report alive. With packed generation + `deref` generation match,
|
|
97
|
-
stale refs now correctly invalidate.
|
|
98
|
-
- **`destroyEntity` generation wrap mask aligned with `options.generationBits`**
|
|
99
|
-
(was hard-coded `& 0xffff`). The mask now correctly uses
|
|
100
|
-
`state.options.generationMask`, fixing inconsistency for non-default
|
|
101
|
-
`generationBits` values.
|
|
102
|
-
|
|
103
|
-
### Documentation
|
|
104
|
-
|
|
105
|
-
- `onSet` JSDoc clarifies that `addComponent` does NOT trigger `onSet`, and
|
|
106
|
-
direct writes to column views returned by `getComponent` (e.g. `col.x[idx] = 5`)
|
|
107
|
-
also do NOT trigger `onSet`. Only `setComponent` on an already-present
|
|
108
|
-
component fires the callback. Anti-pattern example included.
|
|
109
|
-
|
|
110
|
-
### Compatibility
|
|
111
|
-
|
|
112
|
-
- `EntityId` layout change is **not** breaking at the type system level (opaque
|
|
113
|
-
branded number), but consumers who relied on `eid === N` direct comparison
|
|
114
|
-
will need to migrate (see Migration note above).
|
|
115
|
-
- All existing `stable` exports unchanged.
|
|
116
|
-
- `aiecsjs/worker` snapshot wire format unchanged (still uses raw indices).
|
|
117
|
-
- `aiecsjs/serialize` wire format unchanged.
|
|
118
|
-
|
|
119
|
-
### Build & tooling
|
|
120
|
-
|
|
121
|
-
- `VERSION` constant bumped to `0.3.0`.
|
|
122
|
-
|
|
123
|
-
## [0.2.1] - 2026-05-28
|
|
124
|
-
|
|
125
|
-
### Security
|
|
126
|
-
|
|
127
|
-
- **Resolve two Dependabot moderate advisories** on the transitive dev-only graph by upgrading `vitest` 1.6.0 → 4.1.7. Adds `vite` 8.0.14 as a direct devDependency to satisfy vitest 4's peer range (`^6 || ^7 || ^8`). These are dev-only — runtime surface unchanged.
|
|
128
|
-
- [GHSA-67mh-4wv8-2f99](https://github.com/advisories/GHSA-67mh-4wv8-2f99) `esbuild <=0.24.2` CORS development server data leak (fixed in 0.25.0).
|
|
129
|
-
- [GHSA-4w7w-66w2-5vf9](https://github.com/advisories/GHSA-4w7w-66w2-5vf9) `vite <=6.4.1` path traversal in optimized deps `.map` handling (fixed in 6.4.2 / 7.3.2 / 8.0.5).
|
|
130
|
-
|
|
131
|
-
### Changed
|
|
132
|
-
|
|
133
|
-
- **README opening unified across the ai*js family**: five-badge shields row (npm + CI + License + AI Generated + 繁體中文/English), one-line tagline as blockquote, ecosystem footer linking to the other two packages. Replaces the previous mixed style (text language switcher + 5 ad-hoc badges).
|
|
134
|
-
- **`VERSION` constant bumped to 0.2.1** ([src/version.ts](src/version.ts)) so `world.version` and snapshot meta reflect this release.
|
|
135
|
-
|
|
136
|
-
Runtime surface unchanged. Production bundles are byte-identical to 0.2.0.
|
|
137
|
-
|
|
138
|
-
## [0.2.0] - 2026-05-28
|
|
139
|
-
|
|
140
|
-
### Fixed (correctness + security)
|
|
141
|
-
|
|
142
|
-
- **Prototype-pollution hardening in AoS `writeInitial`** ([src/internal/component.ts](src/internal/component.ts)): replaced `Object.assign(inst, initial)` with an explicit own-key copy that filters `__proto__` / `constructor` / `prototype`. Closes a path where a malicious `JSON.parse` payload reaching `addComponent` / `setComponent` / `fromJSON` / `deserializeWorld` could clobber the per-instance prototype.
|
|
143
|
-
- **Observer dispatch is now safe against unsubscribe-during-iteration** ([src/observers.ts](src/observers.ts)): every `fire*` walks a snapshot of `state.observers` (`Array.from(...)` + `includes` guard) so a handler that calls its own returned disposer no longer skips sibling observers in the same fire round.
|
|
144
|
-
- **`removeComponent` writes the new entity mask BEFORE firing observers** ([src/internal/component.ts](src/internal/component.ts)): query-targeted `remove` observers read `state.entityMask` to decide if the entity left the matching set; with the previous ordering the bit was still set during dispatch and the remove never fired. Brings `removeComponent` in line with `addComponent`'s "mutate then fire" order.
|
|
145
|
-
- **`destroyEntity` now emits query-targeted `remove` events** ([src/observers.ts](src/observers.ts) `dispatchDestroyObservers`): in addition to per-component `onRemove`, the destroy hook now walks query observers and fires `remove` for any query the entity was matching pre-destroy. `wasMatch` is computed against a **snapshot of the pre-destroy mask** (not live `state.entityMask`) so a Phase 1 reentrant handler that mutates the entity's mask cannot suppress query removes in Phase 2 (regression caught by the round-2 review).
|
|
146
|
-
- **`deserializeWorld` / `attachWorld` / `adoptSnapshot` binary length fields are bounds-checked** ([src/serialize.ts](src/serialize.ts)): `verLen` and `jsonLen` carry explicit `off + len <= bytes.length` assertions and a 64 MiB cap. `attachWorld` and `adoptSnapshot` ([src/worker.ts](src/worker.ts)) both carry SECURITY JSDocs that document the trust boundary expectation for SAB / TransferableSnapshot transports.
|
|
147
|
-
|
|
148
|
-
### Added (API)
|
|
149
|
-
|
|
150
|
-
- **`disposeWorld(world)`** — new export that aliases `destroyWorld`. Aligns with the ai*js ecosystem `dispose()` convention (`aifsmjs.Runtime.dispose`, `aibridgejs.Bridge.dispose`). Prefer this name in new code; `destroyWorld` is retained as a deprecated alias and is scheduled for removal in 1.0.
|
|
151
|
-
- **`{ signal?: AbortSignal }` on every observer**: `onAdd`, `onRemove`, `onSet`, and `observe` now accept an options object. When the signal aborts, the observer auto-unsubscribes. The returned unsubscribe function remains valid and idempotent. New exported type `ObserverOptions` documents the shape. This closes a long-running gap noted in the AI ecosystem audit — long-lived observers on user-controlled lifecycles (UI components, async pipelines) no longer require manual cleanup wiring.
|
|
152
|
-
|
|
153
|
-
### Changed (stability)
|
|
154
|
-
|
|
155
|
-
- `getEntityGeneration` and `packEntity` re-classified from `stable` → `experimental` in `STABILITY.md` and `api.json`. In 0.1 these returned `0` / identity and that has not changed — the relabel honestly admits the deferred encoding work. Real values arrive when ABA-safe `EntityRef` lands.
|
|
156
|
-
- `destroyWorld` re-classified from `stable` → `deprecated`. Behaviour unchanged; the deprecation is the API-naming alignment described above. Use `disposeWorld` instead.
|
|
157
|
-
|
|
158
|
-
### Documentation
|
|
159
|
-
|
|
160
|
-
- `onSet` now carries a JSDoc and README paragraph clarifying that it is a **low-level mutation hook**, not a reactive value-predicate query. `enterQuery` / `exitQuery` continue to be the structural-change surface; reactive value tracking remains an explicit non-goal of the core.
|
|
161
|
-
- README observer section gains an `AbortController`-based unsubscribe example.
|
|
162
|
-
|
|
163
|
-
### Build & tooling
|
|
164
|
-
|
|
165
|
-
- Added [Biome](https://biomejs.dev/) lint + format (`biome.json`, `npm run lint`, `npm run format`). Brings parity with `aifsmjs` and `aibridgejs` and surfaces `noExplicitAny` warnings in legacy `src/internal/*` for follow-up cleanup.
|
|
166
|
-
- Added `scripts/verify-exports.mjs` and the `npm run verify:exports` script; gates that every `package.json#exports` entry has a real file in `dist/`. Wired into `prepublishOnly`.
|
|
167
|
-
- New `CONTRIBUTING.md` with the same shape used by `aifsmjs` (quick start, scope policy, release flow).
|
|
168
|
-
|
|
169
|
-
### Compatibility
|
|
170
|
-
|
|
171
|
-
This release is **non-breaking at runtime**. All existing code that called `destroyWorld(world)`, registered observers without options, or read `getEntityGeneration` continues to work. The stability label change is documentation-only.
|
|
172
|
-
|
|
173
|
-
## [0.1.4] - 2026-05-28
|
|
174
|
-
|
|
175
|
-
Docs-only release. Adds a cross-package integration section pointing at the `aibridgejs` JSON envelope contract; no source code changes.
|
|
176
|
-
|
|
177
|
-
### Documentation
|
|
178
|
-
|
|
179
|
-
- README and README_ZHTW gained an "Integration with aibridgejs" section explaining that `bridge.call` / `bridge.emit` enforce JSON-safe payloads and silently drop `Date`, `Map`, `Set`, and class instances. The correct shape for streaming world state across the bridge is `toJSON(world)` (or `serializeWorld(world)` wrapped in a JSON envelope) before emitting, not `getComponent(...)` direct. See [aiecsjs README · Integration with aibridgejs](README.md#integration-with-aibridgejs).
|
|
180
|
-
- Verified via the `aijs-integration-smoke` companion project: every named export from `aifsmjs@0.1.2`, `aibridgejs@0.1.3`, and `aiecsjs@0.1.3` can coexist in a single TypeScript module with zero identifier collisions under `tsc --noEmit --strict`.
|
|
181
|
-
|
|
182
|
-
## [0.1.3] - 2026-05-28
|
|
183
|
-
|
|
184
|
-
A "no known silent bugs" release. Two correctness fixes, one hot-path allocation removal, and a small batch of style cleanups. No public API behaviour changes; `_getWorldState` is removed from the root export (was undocumented, unused by every sub-path, leading underscore signalled internal).
|
|
185
|
-
|
|
186
|
-
### Fixed
|
|
187
|
-
|
|
188
|
-
- `aiecsjs/relations` relation data store no longer keys edges by `srcEid * worldCapacity + tgtEid`. After the world grew, the same `(src, tgt)` pair computed a different key and earlier entries became orphaned. Storage is now a nested `Map<srcEid, Map<tgtEid, data>>`, independent of capacity. The cleanup hook on `destroyEntity` was updated to match. v0.1 has no public retrieve API so the bug was user-invisible, but it would have surfaced the moment a retrieve surface landed in 0.2.
|
|
189
|
-
- Per-world resolved query bitmasks no longer live on the module-global `QueryInternal`. When the same `defineQuery(...)` handle was used by two worlds whose component registration orders differed, the second world's per-world mask overwrote the first world's, and `runQuery` silently returned wrong rows in world A. Masks now live in `WorldState.queryMasks: Map<queryId, QueryMaskBundle>`, isolated per world. Regression test in `tests/multi-world.test.ts` exercises the cross-order scenario.
|
|
190
|
-
|
|
191
|
-
### Changed (internal)
|
|
192
|
-
|
|
193
|
-
- Observer dispatch (`dispatchQueryObservers`) no longer allocates a temporary `Uint32Array` on every mutation event. Added `matchesEntityMask` helper in `bitmask.ts` that reads directly from `state.entityMask` at a base offset.
|
|
194
|
-
- Shared bit-iteration extracted as `forEachSetBit(mask, base, words, fn)` in `bitmask.ts`. `clearAllEntityStorages` (`component.ts`) and `dispatchDestroyObservers` (`observers.ts`) now share that single implementation instead of inlining the same `word & -word` / `Math.clz32` pattern three times.
|
|
195
|
-
- `state.generations[idx]` is written without an `as any` cast. `Uint8Array | Uint16Array` already supports indexed read/write.
|
|
196
|
-
- Removed the `void oldCap` no-op from `growEntityArrays`.
|
|
197
|
-
- Removed `_getWorldState` from the root `aiecsjs` export. Sub-paths (`aiecsjs/serialize`, `aiecsjs/worker`) already import `getWorldState` directly from the internal module; the leading-underscore root re-export had no consumer.
|
|
198
|
-
|
|
199
|
-
### Planned for 0.3
|
|
200
|
-
|
|
201
|
-
- Promote `aiecsjs/relations` and `aiecsjs/worker` to `stable`.
|
|
202
|
-
- Stabilize the network delta wire format.
|
|
203
|
-
- Add automated benchmark suite committed to repo.
|
|
204
|
-
|
|
205
|
-
### Planned for 1.0
|
|
206
|
-
|
|
207
|
-
- API freeze for the 1.x line.
|
|
208
|
-
- Drop the experimental status label.
|
|
209
|
-
|
|
210
|
-
## [0.1.2] - 2026-05-28
|
|
211
|
-
|
|
212
|
-
CI/CD smoke-test release. No user-facing source or behavioural changes since 0.1.1; this bump exists solely to validate the tag-triggered publish workflow (see `.github/workflows/publish.yml`) end-to-end against the npm registry with provenance attestation.
|
|
213
|
-
|
|
214
|
-
### Build & tooling
|
|
215
|
-
|
|
216
|
-
- Confirmed that pushing a `v*.*.*` tag triggers `.github/workflows/publish.yml`, runs `prepublishOnly` (typecheck + tests + build + size budget), and publishes to npm with sigstore provenance.
|
|
217
|
-
|
|
218
|
-
## [0.1.1] - 2026-05-28
|
|
219
|
-
|
|
220
|
-
The "documentation honesty + test backstop" release. No new public APIs; this is the version of 0.1.0 that ships with the public surface, the documentation, and the test coverage in agreement.
|
|
221
|
-
|
|
222
|
-
### Fixed
|
|
223
|
-
|
|
224
|
-
- `destroyEntity` now clears the SoA columns and undefines the AoS slots that the destroyed entity owned. Previously only the entity mask was cleared, leaving stale data at the slot visible to debug snapshots and the serialisation path. Public `hasComponent` / query behaviour was already correct, so user-visible behaviour is unchanged; this closes the gap surfaced by the new `destroyEntity zeroes the destroyed entity’s SoA slot` test.
|
|
225
|
-
|
|
226
|
-
### Changed (docs hygiene)
|
|
227
|
-
|
|
228
|
-
- README and STABILITY now describe `aiecsjs/worker` honestly as a snapshot-copy transport for 0.1; true shared columns remain a 0.2 target. README description and `package.json` description updated accordingly.
|
|
229
|
-
- README clarifies that 0.1 `EntityId` is a bare slot index; internal generation is tracked for slot reuse but not encoded in the ID. ABA-safe `EntityRef` is on the 0.2 roadmap.
|
|
230
|
-
- Sub-paths (`loop` / `commands` / `observers` / `serialize` / `worker` / `relations`) re-positioned in STABILITY as utility / adapter sub-paths; the root `aiecsjs` is the stable core surface. Tree-shakers should drop any sub-path the app does not import.
|
|
231
|
-
- README adds a "What aiecsjs does NOT do" section listing explicit non-goals (system scheduler, render binding, physics, network replication, value-predicate reactive queries, prefab/inheritance).
|
|
232
|
-
- Language version filenames renamed from `*.zh-TW.md` to `*_ZHTW.md`. Cross-links, `llms.txt`, and `package.json` `files` updated. Future language variants follow the same uppercase ISO 639-1 pattern.
|
|
233
|
-
- Removed emoji from documentation prose (language switchers, status banners).
|
|
234
|
-
|
|
235
|
-
### Build & tooling
|
|
236
|
-
|
|
237
|
-
- tsup build now runs with `minify: true`.
|
|
238
|
-
- `size-limit` added as a dev dependency; per-export gzip budgets enforced via `npm run size`. Current measurements: core 5.49 kB, all sub-paths combined 12.6 kB gzip.
|
|
239
|
-
- GitHub Actions CI workflow added: typecheck → test → build → size check on push and PR to `main`.
|
|
240
|
-
- `prepublishOnly` now runs typecheck, tests, build, and the size budget gate before allowing publish.
|
|
241
|
-
|
|
242
|
-
### Tests
|
|
243
|
-
|
|
244
|
-
- Test count increased from 84 to 140. New file `tests/internal/bitmask.test.ts` covers the multi-word bitmask helpers in isolation (27 cases including `matches` truth table). New file `tests/multi-world.test.ts` covers per-world isolation when the same component is reused. Existing files gained: naive linear-filter cross-check against `runQuery` for all clause combinations, archetype migration boundary path, query mid-traversal stability and lazy cache behaviour, SoA field clear assertions on both `removeComponent` and `destroyEntity`, SoA vector-length round trip, `maxEntities` / `maxComponents` boundary throws, observer fan-out for destroy across multiple components, `onSet` value content, query observer ignores unrelated mutation, relation source-side destroy cleanup, exclusive relation storage resize, worker `readOnly` rejects add / remove / destroy, serialize `options.components` filter, `onUnknownVersion: throw | best-effort` paths, command buffer placeholder resolves into a queryable entity, slot-reuse limitation made explicit. Loop tests rewritten on top of `vi.useFakeTimers({ toFake: ['performance', ...] })` for deterministic dt validation.
|
|
245
|
-
|
|
246
|
-
## [0.1.0] - 2026-05-27
|
|
247
|
-
|
|
248
|
-
**Initial release.** All 50 documented exports across 7 modules are implemented and covered by 84 passing Vitest behaviour tests. Built with tsup to dual ESM + CJS, ships `.d.ts` declarations and source maps.
|
|
249
|
-
|
|
250
|
-
### Implementation notes
|
|
251
|
-
|
|
252
|
-
- **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.
|
|
253
|
-
- **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.
|
|
254
|
-
- **Bitmask queries**: multi-word Uint32 masks, default 8 words (256 components). Per-world bit allocation, global component identity.
|
|
255
|
-
- **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.
|
|
256
|
-
- **Binary serialization**: a JSON payload wrapped in a 4-byte magic + version header. Compact binary column encoding is planned for 0.2.
|
|
257
|
-
|
|
258
|
-
### Added
|
|
259
|
-
|
|
260
|
-
- `README.md` (English) and `README_ZHTW.md` (Traditional Chinese) with quick start, guide, API reference, performance notes, multi-threading guide, WebGPU interop section, serialization guide, migration guides, and "For AI Agents" section.
|
|
261
|
-
- `llms.txt` — Jeremy Howard format AI-discovery file.
|
|
262
|
-
- `llms-full.txt` — Single-file complete reference for LLM consumption.
|
|
263
|
-
- `api.json` — Machine-readable export manifest with stability and `since` fields on every entry.
|
|
264
|
-
- `STABILITY.md` and `STABILITY_ZHTW.md` — Per-export stability contract.
|
|
265
|
-
- `docs/MIGRATION.md` and `docs/MIGRATION_ZHTW.md` — Migration guides from bitECS 0.4, miniplex 2.0, and ECSY.
|
|
266
|
-
|
|
267
|
-
### API surface declared
|
|
268
|
-
|
|
269
|
-
- Core: `createWorld`, `destroyWorld`, `resetWorld`, `getWorldSize`, `getWorldCapacity`.
|
|
270
|
-
- Entity: `createEntity`, `destroyEntity`, `entityExists`, `getEntityIndex`, `getEntityGeneration`, `packEntity`.
|
|
271
|
-
- Component: `defineComponent`, `defineTag`, `defineObjectComponent`, `addComponent`, `removeComponent`, `hasComponent`, `getComponent`, `setComponent`, `Types`.
|
|
272
|
-
- Query: `defineQuery`, `runQuery`, `forEachEntity`, `iterQuery`, `enterQuery`, `exitQuery`, `queryArchetypes` (experimental).
|
|
273
|
-
- System: `pipe`.
|
|
274
|
-
- Subpath `aiecsjs/loop`: `createLoop`.
|
|
275
|
-
- Subpath `aiecsjs/commands`: `createCommandBuffer`, `flush`, `withCommandBuffer`.
|
|
276
|
-
- Subpath `aiecsjs/observers`: `observe`, `onAdd`, `onRemove`, `onSet`.
|
|
277
|
-
- Subpath `aiecsjs/serialize`: `serializeWorld`, `deserializeWorld`, `toJSON`, `fromJSON`, `createDeltaSerializer` (experimental).
|
|
278
|
-
- Subpath `aiecsjs/worker` (experimental): `transferableSnapshot`, `adoptSnapshot`, `attachWorld`, `detachWorld`.
|
|
279
|
-
- Subpath `aiecsjs/relations` (experimental, not implemented): `defineRelation`, `addRelation`, `removeRelation`, `getRelationTargets`, `ChildOf`.
|
|
280
|
-
- Utility: `VERSION`, `IS_SAB_SUPPORTED`, `isWorld`, `isEntity`.
|
|
281
|
-
|
|
282
|
-
### Known limitations in 0.1
|
|
283
|
-
|
|
284
|
-
- `aiecsjs/relations` and `aiecsjs/worker` are implemented but tagged experimental; API may shift.
|
|
285
|
-
- Network delta wire format is JSON-based; binary patch format is planned for 0.2.
|
|
286
|
-
- AoS components are main-thread only; cannot be shared via SharedArrayBuffer.
|
|
287
|
-
- No automatic system scheduler / parallel execution.
|
|
288
|
-
- Worker/SAB uses snapshot-copy in 0.1 rather than true shared-memory aliasing.
|
|
289
|
-
- EntityId is unversioned; ABA-safe references arrive with `EntityRef` in 0.2.
|
|
290
|
-
|
|
291
|
-
[Unreleased]: https://github.com/yshengliao/aiecsjs/compare/v0.1.0...HEAD
|
|
292
|
-
[0.1.0]: https://github.com/yshengliao/aiecsjs/releases/tag/v0.1.0
|