aiecsjs 0.5.7 → 0.5.8

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 (39) hide show
  1. package/README.md +50 -935
  2. package/README_ZHTW.md +51 -945
  3. package/dist/{chunk-P5GW7GKY.cjs → chunk-3FV6UMUS.cjs} +2 -2
  4. package/dist/chunk-3FV6UMUS.cjs.map +1 -0
  5. package/dist/{chunk-SRX2MZPX.cjs → chunk-3ZLNR4JX.cjs} +2 -2
  6. package/dist/{chunk-SRX2MZPX.cjs.map → chunk-3ZLNR4JX.cjs.map} +1 -1
  7. package/dist/{chunk-22ICWJ5O.js → chunk-N5OETPTK.js} +2 -2
  8. package/dist/{chunk-22ICWJ5O.js.map → chunk-N5OETPTK.js.map} +1 -1
  9. package/dist/{chunk-AGWUE6JB.js → chunk-TNZMD7E5.js} +2 -2
  10. package/dist/chunk-TNZMD7E5.js.map +1 -0
  11. package/dist/{chunk-2KCN5RVK.js → chunk-YFTCD2UG.js} +2 -2
  12. package/dist/chunk-YFTCD2UG.js.map +1 -0
  13. package/dist/{chunk-5QWEV4VJ.cjs → chunk-ZIZSWIHW.cjs} +2 -2
  14. package/dist/chunk-ZIZSWIHW.cjs.map +1 -0
  15. package/dist/commands.cjs +1 -1
  16. package/dist/commands.js +1 -1
  17. package/dist/index.cjs +1 -1
  18. package/dist/index.cjs.map +1 -1
  19. package/dist/index.d.cts +1 -1
  20. package/dist/index.d.ts +1 -1
  21. package/dist/index.js +1 -1
  22. package/dist/index.js.map +1 -1
  23. package/dist/observers.cjs +1 -1
  24. package/dist/observers.js +1 -1
  25. package/dist/relations.cjs +1 -1
  26. package/dist/relations.cjs.map +1 -1
  27. package/dist/relations.js +1 -1
  28. package/dist/relations.js.map +1 -1
  29. package/dist/serialize.cjs +1 -1
  30. package/dist/serialize.js +1 -1
  31. package/dist/worker.cjs +1 -1
  32. package/dist/worker.js +1 -1
  33. package/llms-full.txt +109 -1538
  34. package/llms.txt +8 -29
  35. package/package.json +1 -1
  36. package/dist/chunk-2KCN5RVK.js.map +0 -1
  37. package/dist/chunk-5QWEV4VJ.cjs.map +0 -1
  38. package/dist/chunk-AGWUE6JB.js.map +0 -1
  39. package/dist/chunk-P5GW7GKY.cjs.map +0 -1
package/dist/index.cjs CHANGED
@@ -1,2 +1,2 @@
1
- 'use strict';var chunkSRX2MZPX_cjs=require('./chunk-SRX2MZPX.cjs'),chunk5QWEV4VJ_cjs=require('./chunk-5QWEV4VJ.cjs');var J={i8:"i8",u8:"u8",i16:"i16",u16:"u16",i32:"i32",u32:"u32",f32:"f32",f64:"f64",eid:"eid",bool:"bool"};function X(...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 Y(n,e){let r=chunk5QWEV4VJ_cjs.j(n);if(!chunk5QWEV4VJ_cjs.z(r,e))throw new i(e);return Object.freeze({id:e,worldId:r.id})}function b(n,e){let r=chunk5QWEV4VJ_cjs.j(n);if(e.worldId!==r.id)return null;let t=e.id,o=chunk5QWEV4VJ_cjs.t(t,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=chunk5QWEV4VJ_cjs.u(t,r.options);return C!==A?null:e.id}function Z(n,e){return b(n,e)!==null}chunkSRX2MZPX_cjs.j(n=>chunk5QWEV4VJ_cjs.R(n));chunk5QWEV4VJ_cjs.P((n,e,r,t,o)=>{chunkSRX2MZPX_cjs.i(n,e,r,t,o);});var pe=typeof SharedArrayBuffer<"u";Object.defineProperty(exports,"defineQuery",{enumerable:true,get:function(){return chunkSRX2MZPX_cjs.a}});Object.defineProperty(exports,"enterQuery",{enumerable:true,get:function(){return chunkSRX2MZPX_cjs.b}});Object.defineProperty(exports,"exitQuery",{enumerable:true,get:function(){return chunkSRX2MZPX_cjs.c}});Object.defineProperty(exports,"forEachEntity",{enumerable:true,get:function(){return chunkSRX2MZPX_cjs.g}});Object.defineProperty(exports,"forEachEntityIndexed",{enumerable:true,get:function(){return chunkSRX2MZPX_cjs.h}});Object.defineProperty(exports,"iterQuery",{enumerable:true,get:function(){return chunkSRX2MZPX_cjs.f}});Object.defineProperty(exports,"queryArchetypes",{enumerable:true,get:function(){return chunkSRX2MZPX_cjs.d}});Object.defineProperty(exports,"runQuery",{enumerable:true,get:function(){return chunkSRX2MZPX_cjs.e}});Object.defineProperty(exports,"EcsError",{enumerable:true,get:function(){return chunk5QWEV4VJ_cjs.i}});Object.defineProperty(exports,"VERSION",{enumerable:true,get:function(){return chunk5QWEV4VJ_cjs.a}});Object.defineProperty(exports,"addComponent",{enumerable:true,get:function(){return chunk5QWEV4VJ_cjs.K}});Object.defineProperty(exports,"createEntity",{enumerable:true,get:function(){return chunk5QWEV4VJ_cjs.v}});Object.defineProperty(exports,"createWorld",{enumerable:true,get:function(){return chunk5QWEV4VJ_cjs.l}});Object.defineProperty(exports,"defineComponent",{enumerable:true,get:function(){return chunk5QWEV4VJ_cjs.G}});Object.defineProperty(exports,"defineObjectComponent",{enumerable:true,get:function(){return chunk5QWEV4VJ_cjs.I}});Object.defineProperty(exports,"defineTag",{enumerable:true,get:function(){return chunk5QWEV4VJ_cjs.H}});Object.defineProperty(exports,"destroyEntity",{enumerable:true,get:function(){return chunk5QWEV4VJ_cjs.x}});Object.defineProperty(exports,"destroyWorld",{enumerable:true,get:function(){return chunk5QWEV4VJ_cjs.m}});Object.defineProperty(exports,"disposeWorld",{enumerable:true,get:function(){return chunk5QWEV4VJ_cjs.m}});Object.defineProperty(exports,"entityExists",{enumerable:true,get:function(){return chunk5QWEV4VJ_cjs.y}});Object.defineProperty(exports,"getComponent",{enumerable:true,get:function(){return chunk5QWEV4VJ_cjs.N}});Object.defineProperty(exports,"getEntityGeneration",{enumerable:true,get:function(){return chunk5QWEV4VJ_cjs.B}});Object.defineProperty(exports,"getEntityIndex",{enumerable:true,get:function(){return chunk5QWEV4VJ_cjs.A}});Object.defineProperty(exports,"getWorldCapacity",{enumerable:true,get:function(){return chunk5QWEV4VJ_cjs.p}});Object.defineProperty(exports,"getWorldSize",{enumerable:true,get:function(){return chunk5QWEV4VJ_cjs.o}});Object.defineProperty(exports,"hasComponent",{enumerable:true,get:function(){return chunk5QWEV4VJ_cjs.M}});Object.defineProperty(exports,"isEntity",{enumerable:true,get:function(){return chunk5QWEV4VJ_cjs.D}});Object.defineProperty(exports,"isWorld",{enumerable:true,get:function(){return chunk5QWEV4VJ_cjs.r}});Object.defineProperty(exports,"packEntity",{enumerable:true,get:function(){return chunk5QWEV4VJ_cjs.C}});Object.defineProperty(exports,"removeComponent",{enumerable:true,get:function(){return chunk5QWEV4VJ_cjs.L}});Object.defineProperty(exports,"resetWorld",{enumerable:true,get:function(){return chunk5QWEV4VJ_cjs.n}});Object.defineProperty(exports,"setComponent",{enumerable:true,get:function(){return chunk5QWEV4VJ_cjs.O}});exports.EntityNotAliveError=i;exports.IS_SAB_SUPPORTED=pe;exports.Types=J;exports.aliveRef=Z;exports.deref=b;exports.pipe=X;exports.refOf=Y;//# sourceMappingURL=index.cjs.map
1
+ 'use strict';var chunk3ZLNR4JX_cjs=require('./chunk-3ZLNR4JX.cjs'),chunkZIZSWIHW_cjs=require('./chunk-ZIZSWIHW.cjs');var J={i8:"i8",u8:"u8",i16:"i16",u16:"u16",i32:"i32",u32:"u32",f32:"f32",f64:"f64",eid:"eid",bool:"bool"};function X(...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 Y(n,e){let r=chunkZIZSWIHW_cjs.j(n);if(!chunkZIZSWIHW_cjs.z(r,e))throw new i(e);return Object.freeze({id:e,worldId:r.id})}function b(n,e){let r=chunkZIZSWIHW_cjs.j(n);if(e.worldId!==r.id)return null;let t=e.id,o=chunkZIZSWIHW_cjs.t(t,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=chunkZIZSWIHW_cjs.u(t,r.options);return C!==A?null:e.id}function Z(n,e){return b(n,e)!==null}chunk3ZLNR4JX_cjs.j(n=>chunkZIZSWIHW_cjs.R(n));chunkZIZSWIHW_cjs.P((n,e,r,t,o)=>{chunk3ZLNR4JX_cjs.i(n,e,r,t,o);});var me=typeof SharedArrayBuffer<"u";Object.defineProperty(exports,"defineQuery",{enumerable:true,get:function(){return chunk3ZLNR4JX_cjs.a}});Object.defineProperty(exports,"enterQuery",{enumerable:true,get:function(){return chunk3ZLNR4JX_cjs.b}});Object.defineProperty(exports,"exitQuery",{enumerable:true,get:function(){return chunk3ZLNR4JX_cjs.c}});Object.defineProperty(exports,"forEachEntity",{enumerable:true,get:function(){return chunk3ZLNR4JX_cjs.g}});Object.defineProperty(exports,"forEachEntityIndexed",{enumerable:true,get:function(){return chunk3ZLNR4JX_cjs.h}});Object.defineProperty(exports,"iterQuery",{enumerable:true,get:function(){return chunk3ZLNR4JX_cjs.f}});Object.defineProperty(exports,"queryArchetypes",{enumerable:true,get:function(){return chunk3ZLNR4JX_cjs.d}});Object.defineProperty(exports,"runQuery",{enumerable:true,get:function(){return chunk3ZLNR4JX_cjs.e}});Object.defineProperty(exports,"EcsError",{enumerable:true,get:function(){return chunkZIZSWIHW_cjs.i}});Object.defineProperty(exports,"VERSION",{enumerable:true,get:function(){return chunkZIZSWIHW_cjs.a}});Object.defineProperty(exports,"addComponent",{enumerable:true,get:function(){return chunkZIZSWIHW_cjs.K}});Object.defineProperty(exports,"createEntity",{enumerable:true,get:function(){return chunkZIZSWIHW_cjs.v}});Object.defineProperty(exports,"createWorld",{enumerable:true,get:function(){return chunkZIZSWIHW_cjs.l}});Object.defineProperty(exports,"defineComponent",{enumerable:true,get:function(){return chunkZIZSWIHW_cjs.G}});Object.defineProperty(exports,"defineObjectComponent",{enumerable:true,get:function(){return chunkZIZSWIHW_cjs.I}});Object.defineProperty(exports,"defineTag",{enumerable:true,get:function(){return chunkZIZSWIHW_cjs.H}});Object.defineProperty(exports,"destroyEntity",{enumerable:true,get:function(){return chunkZIZSWIHW_cjs.x}});Object.defineProperty(exports,"destroyWorld",{enumerable:true,get:function(){return chunkZIZSWIHW_cjs.m}});Object.defineProperty(exports,"disposeWorld",{enumerable:true,get:function(){return chunkZIZSWIHW_cjs.m}});Object.defineProperty(exports,"entityExists",{enumerable:true,get:function(){return chunkZIZSWIHW_cjs.y}});Object.defineProperty(exports,"getComponent",{enumerable:true,get:function(){return chunkZIZSWIHW_cjs.N}});Object.defineProperty(exports,"getEntityGeneration",{enumerable:true,get:function(){return chunkZIZSWIHW_cjs.B}});Object.defineProperty(exports,"getEntityIndex",{enumerable:true,get:function(){return chunkZIZSWIHW_cjs.A}});Object.defineProperty(exports,"getWorldCapacity",{enumerable:true,get:function(){return chunkZIZSWIHW_cjs.p}});Object.defineProperty(exports,"getWorldSize",{enumerable:true,get:function(){return chunkZIZSWIHW_cjs.o}});Object.defineProperty(exports,"hasComponent",{enumerable:true,get:function(){return chunkZIZSWIHW_cjs.M}});Object.defineProperty(exports,"isEntity",{enumerable:true,get:function(){return chunkZIZSWIHW_cjs.D}});Object.defineProperty(exports,"isWorld",{enumerable:true,get:function(){return chunkZIZSWIHW_cjs.r}});Object.defineProperty(exports,"packEntity",{enumerable:true,get:function(){return chunkZIZSWIHW_cjs.C}});Object.defineProperty(exports,"removeComponent",{enumerable:true,get:function(){return chunkZIZSWIHW_cjs.L}});Object.defineProperty(exports,"resetWorld",{enumerable:true,get:function(){return chunkZIZSWIHW_cjs.n}});Object.defineProperty(exports,"setComponent",{enumerable:true,get:function(){return chunkZIZSWIHW_cjs.O}});exports.EntityNotAliveError=i;exports.IS_SAB_SUPPORTED=me;exports.Types=J;exports.aliveRef=Z;exports.deref=b;exports.pipe=X;exports.refOf=Y;//# sourceMappingURL=index.cjs.map
2
2
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/internal/types.ts","../src/internal/pipe.ts","../src/ref.ts","../src/index.ts"],"names":["Types","pipe","systems","w","world","ctx","s","EntityNotAliveError","eid","refOf","entity","state","getWorldState","isAliveInternal","deref","ref","idx","unpackIdx","archId","arch","storedGen","refGen","unpackGen","aliveRef","registerComponentLookup","id","getComponentByInternalId","registerMaskChangeDispatch","bit","prev","next","recordEntityMaskChange","IS_SAB_SUPPORTED"],"mappings":"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","file":"index.cjs","sourcesContent":["// Shared type definitions for aiecsjs internals.\n// Public-facing types are re-exported from src/index.ts.\n\nexport type EntityId = number & { readonly __brand: 'EntityId' }\n\nexport const Types = {\n i8: 'i8',\n u8: 'u8',\n i16: 'i16',\n u16: 'u16',\n i32: 'i32',\n u32: 'u32',\n f32: 'f32',\n f64: 'f64',\n eid: 'eid',\n bool: 'bool',\n} as const\n\nexport type SoAFieldType =\n | 'i8'\n | 'u8'\n | 'i16'\n | 'u16'\n | 'i32'\n | 'u32'\n | 'f32'\n | 'f64'\n | 'eid'\n | 'bool'\n\nexport type SoAFieldDecl = SoAFieldType | readonly [SoAFieldType, number]\nexport type SoASchema = Readonly<Record<string, SoAFieldDecl>>\n\nexport interface SoAComponent<S extends SoASchema = SoASchema> {\n readonly __kind: 'soa'\n readonly __id: number\n readonly __schema: S\n}\n\nexport interface AoSComponent<T = unknown> {\n readonly __kind: 'aos'\n readonly __id: number\n readonly __factory: () => T\n}\n\nexport interface TagComponent {\n readonly __kind: 'tag'\n readonly __id: number\n}\n\nexport type ComponentLike = SoAComponent<any> | AoSComponent<any> | TagComponent\n\nexport type ColumnArray =\n | Int8Array\n | Uint8Array\n | Int16Array\n | Uint16Array\n | Int32Array\n | Uint32Array\n | Float32Array\n | Float64Array\n\nexport interface SoAColumns {\n [field: string]: ColumnArray\n}\n\nexport type ComponentView<C extends ComponentLike> = C extends SoAComponent<infer _S>\n ? SoAColumns\n : C extends AoSComponent<infer T>\n ? T | undefined\n : C extends TagComponent\n ? boolean\n : never\n\nexport type ComponentInit<C extends ComponentLike> = C extends SoAComponent<infer _S>\n ? Record<string, unknown>\n : C extends AoSComponent<infer T>\n ? Partial<T>\n : undefined\n\nexport interface QueryDescriptor {\n all?: ComponentLike[]\n any?: ComponentLike[]\n none?: ComponentLike[]\n}\n\nexport interface Query {\n readonly id: number\n readonly mask: ReadonlyArray<number>\n}\n\nexport interface Archetype {\n readonly id: number\n readonly mask: ReadonlyArray<number>\n readonly size: number\n}\n\nexport interface WorldOptions {\n initialCapacity?: number\n maxEntities?: number\n indexBits?: number\n generationBits?: number\n /**\n * RESERVED / UNIMPLEMENTED (0.x). Setting this has **no effect** today: the\n * world always allocates its own column storage and never reads or writes a\n * caller-supplied SAB. The 0.x Worker handoff is a snapshot-copy transport —\n * post `transferableSnapshot(world)` and rebuild via `adoptSnapshot` from\n * `aiecsjs/worker`; do not pre-allocate a SAB here. The field is kept so the\n * shape is forward-compatible with the true shared-column backing targeted\n * for 0.3+ (see STABILITY.md, `aiecsjs/worker`).\n */\n buffer?: SharedArrayBuffer\n /** RESERVED / UNIMPLEMENTED (0.x). Paired with {@link WorldOptions.buffer}; no effect today. */\n bufferByteOffset?: number\n}\n\nexport interface World {\n readonly id: number\n readonly capacity: number\n readonly version: string\n}\n\nexport type System<W extends World = World, Ctx = unknown> = (world: W, ctx: Ctx) => W\n\n// --- Internal-only types ---\n\nexport interface ResolvedWorldOptions {\n initialCapacity: number\n maxEntities: number\n indexBits: number\n generationBits: number\n indexMask: number\n generationMask: number\n maxComponents: number\n maskWordCount: number\n buffer: SharedArrayBuffer | null\n bufferByteOffset: number\n}\n\nexport interface FieldInfo {\n name: string\n type: SoAFieldType\n vectorLen: number // 1 for scalar\n ctor: TypedArrayConstructor\n bytesPerElement: number\n}\n\nexport type TypedArrayConstructor =\n | Int8ArrayConstructor\n | Uint8ArrayConstructor\n | Int16ArrayConstructor\n | Uint16ArrayConstructor\n | Int32ArrayConstructor\n | Uint32ArrayConstructor\n | Float32ArrayConstructor\n | Float64ArrayConstructor\n\nexport interface ComponentInfo {\n id: number\n kind: 'soa' | 'aos' | 'tag'\n schema: SoASchema | null\n fields: FieldInfo[]\n factory: (() => unknown) | null\n}\n\nexport interface WorldComponentStorage {\n // For each component bit in this world: storage backed by a column map (SoA), object array (AoS), or null (tag)\n kind: 'soa' | 'aos' | 'tag'\n componentId: number\n bit: number\n // SoA: field name → TypedArray of size `worldCapacity`\n soa?: SoAColumns\n // AoS: per-entity object array, sparse, indexed by eid; undefined for unowned\n aos?: (unknown | undefined)[]\n}\n\nexport interface ArchetypeState {\n id: number\n mask: Uint32Array\n size: number\n capacity: number\n entities: Uint32Array // packed eids in row order\n entityRow: Map<number, number> // eid → row (small archetype-local lookup)\n componentBits: number[] // sorted\n edgeAdd: Int32Array // [bit] → archetype id; -1 unknown\n edgeRemove: Int32Array // [bit] → archetype id; -1 unknown\n}\n\nexport interface QueryInternal extends Query {\n id: number\n all: number[]\n any: number[]\n none: number[]\n // Cache of column references per matched archetype (per query-call)\n columnViewCache: ComponentLike[] // the requested components in their declared order\n reactiveKind: 'normal' | 'enter' | 'exit'\n sourceQueryId: number // -1 for normal\n sourceQuery: QueryInternal | null // back-ref for reactive registration\n}\n\n// Per-world resolved bitmasks for a query. Component bits are assigned per\n// world, so the masks must live in WorldState — keeping them on the shared\n// QueryInternal would silently cross-contaminate worlds whose component\n// registration order differs.\nexport interface QueryMaskBundle {\n withMask: Uint32Array\n anyMask: Uint32Array\n noneMask: Uint32Array\n anyHasBits: boolean\n}\n\nexport interface ReactiveBuffer {\n entered: number[]\n exited: number[]\n}\n\nexport type ObserverEvent = 'add' | 'remove' | 'set'\n\nexport interface ObserverEntry {\n event: ObserverEvent\n componentBit: number // -1 means \"any component\" (used by query observe)\n queryId: number // -1 means component-only\n handler: (eid: EntityId, value?: unknown) => void\n}\n\nexport interface CommandBufferState {\n worldId: number\n ops: CommandOp[]\n nextPlaceholder: number\n flushing: boolean\n}\n\nexport type CommandOp =\n | { kind: 'create'; placeholder: number }\n | { kind: 'add'; eid: EntityId; component: ComponentLike; initial?: unknown }\n | { kind: 'remove'; eid: EntityId; component: ComponentLike }\n | { kind: 'destroy'; eid: EntityId }\n\nexport interface CommandBuffer {\n add<C extends ComponentLike>(eid: EntityId, component: C, initial?: ComponentInit<C>): void\n remove<C extends ComponentLike>(eid: EntityId, component: C): void\n destroy(eid: EntityId): void\n create(): EntityId\n}\n\nexport interface WorldMeta {\n magic: number\n formatVersion: number\n aiecsjsVersion: string\n indexBits: number\n generationBits: number\n maxComponents: number\n maskWordCount: number\n capacity: number\n componentSchemas: Array<{ id: number; kind: 'soa' | 'aos' | 'tag'; schema: SoASchema | null }>\n}\n\nexport interface TransferableSnapshot {\n // Union, not bare SharedArrayBuffer: in a non-cross-origin-isolated environment\n // `SharedArrayBuffer` is undefined, so transferableSnapshot falls back to a\n // plain ArrayBuffer (worker.ts). Declaring the honest union lets consumers\n // branch on `instanceof SharedArrayBuffer` instead of trusting a type that\n // would otherwise lie. Experimental subpath — the union is allowed to land in 0.x.\n buffer: SharedArrayBuffer | ArrayBuffer\n meta: WorldMeta\n}\n\nexport interface SerializeOptions {\n components?: ComponentLike[]\n}\n\nexport interface DeserializeOptions {\n components?: ComponentLike[]\n onUnknownVersion?: 'throw' | 'best-effort'\n}\n\nexport interface WorldSnapshot {\n version: string\n capacity: number\n entities: Array<{\n eid: number\n components: Array<{\n kind: 'soa' | 'aos' | 'tag'\n id: number\n data: unknown\n }>\n }>\n}\n\nexport interface DeltaSerializer {\n capture(): Uint8Array\n apply(world: World, delta: Uint8Array): void\n reset(): void\n}\n\nexport interface Relation<T = void> {\n readonly __kind: 'relation'\n readonly __id: number\n readonly __exclusive: boolean\n readonly __hasData: boolean\n}\n\nexport interface RelationStorage {\n rel: Relation<any>\n exclusive: Int32Array | null // [srcEid] → tgtEid; -1 means none\n outgoing: Map<number, number[]> // srcEid → tgtEid[]\n data: Map<number, Map<number, unknown>> // srcEid → (tgtEid → data); nested to stay correct across capacity growth\n}\n\nexport interface WorldState {\n // --- public view ---\n readonly id: number\n capacity: number\n readonly version: string\n\n // --- options ---\n options: ResolvedWorldOptions\n\n // --- entity allocation ---\n size: number // alive count\n nextFreshIndex: number // never-used index\n freeList: number[] // recycled indices\n generations: Uint8Array | Uint16Array // [eid] → version\n destroyed: boolean\n\n // --- component registry (per-world) ---\n componentBitFor: Map<number, number> // global component id → bit pos in this world\n componentInfoByBit: (ComponentInfo | null)[] // bit pos → ComponentInfo\n componentStorageByBit: (WorldComponentStorage | null)[]\n nextComponentBit: number\n\n // --- sparse entity state ---\n entityArchetype: Uint32Array // [eid] → archetype id (0 = unattached)\n // entityMask flattened: word w of mask of eid is at entityMask[eid * maskWordCount + w]\n entityMask: Uint32Array\n\n // --- archetype registry ---\n archetypes: ArchetypeState[]\n archetypeByMaskHash: Map<string, number>\n queryVersion: number\n\n // --- query cache ---\n queries: QueryInternal[] // by id\n queryMasks: Map<number, QueryMaskBundle> // queryId → per-world resolved bitmasks\n queryArchetypeCache: (number[] | null)[]\n queryArchetypeStamp: number[]\n bitToQueries: Map<number, Set<number>> // bit → queryIds that mention this bit\n\n // --- reactive query state ---\n reactiveBuffers: Map<number, ReactiveBuffer> // queryId → { entered, exited }\n\n // --- observers ---\n observers: ObserverEntry[]\n\n // --- relations ---\n relationStorage: Map<number, RelationStorage> // relation id → storage\n\n // --- worker / SAB ---\n sab: SharedArrayBuffer | null\n readOnly: boolean\n}\n","import type { System, World } from './types.js'\n\nexport function pipe<W extends World = World, Ctx = unknown>(\n ...systems: System<W, Ctx>[]\n): System<W, Ctx> {\n if (systems.length === 0) return (w: W) => w\n if (systems.length === 1) return systems[0]!\n return (world: W, ctx: Ctx) => {\n let w = world\n for (const s of systems) w = s(w, ctx)\n return w\n }\n}\n","// aiecsjs/ref — ABA-safe entity references.\n//\n// EntityRef<T> wraps a packed EntityId with a world identity check so that\n// stale references (pointing at a recycled slot with a different generation)\n// safely return null rather than silently accessing the wrong entity.\n//\n// EntityRef is in-memory only. Do not pass across worker boundaries or persist\n// to disk. The packed id field has meaning only within the world that issued it.\n// Generation counters reset on resetWorld / deserializeWorld / adoptSnapshot.\n\nimport { isAliveInternal, unpackGen, unpackIdx } from './internal/entity.js'\nimport type { EntityId, World } from './internal/types.js'\nimport { getWorldState } from './internal/world.js'\n\n/**\n * ABA-safe entity reference.\n *\n * Holds the packed EntityId and the id of the world that issued it.\n * Use `deref(world, ref)` to validate and retrieve the live entity id.\n *\n * The phantom type `T` allows callers to distinguish reference kinds at the\n * type system level — e.g. `EntityRef<'bullet'>` vs `EntityRef<'player'>` —\n * without any runtime overhead.\n *\n * @example\n * const ref = refOf<'bullet'>(world, e)\n * // later, in a system:\n * const live = deref(world, ref)\n * if (live !== null) {\n * // entity is still alive with the same generation\n * }\n */\nexport interface EntityRef<T = unknown> {\n readonly id: EntityId // packed value (index + generation)\n readonly worldId: number // world.id — guards against cross-world deref\n readonly __phantom?: T // phantom type tag; zero runtime size\n}\n\n/**\n * Thrown by `refOf` when the entity is not alive.\n * `deref` and `aliveRef` never throw — they return null/false.\n */\nexport class EntityNotAliveError extends Error {\n readonly eid: number\n constructor(eid: number) {\n super(`aiecsjs: entity ${eid} is not alive`)\n this.name = 'EntityNotAliveError'\n this.eid = eid\n }\n}\n\n/**\n * Create an ABA-safe reference to a live entity.\n *\n * @throws {EntityNotAliveError} if the entity is not alive (dead, never created, or eid === 0).\n *\n * The returned object is frozen. `id` is the packed EntityId; `worldId` is\n * the world's numeric id used by `deref` to reject cross-world lookups.\n *\n * The function may be called on the hot path — each call returns a new object\n * but performs no allocation beyond that.\n */\nexport function refOf<T = unknown>(world: World, entity: EntityId): EntityRef<T> {\n const state = getWorldState(world)\n if (!isAliveInternal(state, entity as number)) {\n throw new EntityNotAliveError(entity as number)\n }\n return Object.freeze({ id: entity, worldId: state.id }) as EntityRef<T>\n}\n\n/**\n * Resolve a ref to its live EntityId, or return null if stale.\n *\n * Returns null when any of the following hold:\n * 1. `ref.worldId !== state.id` — cross-world ref\n * 2. `getEntityIndex(ref.id) >= state.capacity` — index out-of-bounds\n * 3. `getEntityIndex(ref.id) === 0` — sentinel slot\n * 4. entity is not in any archetype's entityRow (slot released)\n * 5. generation mismatch → ABA: old ref points at recycled slot\n *\n * Never throws.\n */\nexport function deref<T = unknown>(world: World, ref: EntityRef<T>): EntityId | null {\n const state = getWorldState(world)\n\n // Cross-world guard\n if (ref.worldId !== state.id) return null\n\n const eid = ref.id as number\n const idx = unpackIdx(eid, state.options)\n\n // Sentinel and OOB guards\n if (idx <= 0) return null\n if (idx >= state.capacity) return null\n\n // Archetype membership check\n const archId = state.entityArchetype[idx] ?? 0\n const arch = state.archetypes[archId]\n if (!arch) return null\n if (!arch.entityRow.has(ref.id)) return null\n\n // Generation match — the core ABA protection\n const storedGen = state.generations[idx] ?? 0\n const refGen = unpackGen(eid, state.options)\n if (storedGen !== refGen) return null\n\n return ref.id\n}\n\n/**\n * Boolean guard form of `deref`. Equivalent to `deref(world, ref) !== null`.\n * Use in guard clauses when you do not need the resolved EntityId.\n *\n * Never throws.\n */\nexport function aliveRef<T = unknown>(world: World, ref: EntityRef<T>): boolean {\n return deref(world, ref) !== null\n}\n","// aiecsjs — public root entry.\n// Re-exports the core API and wires up the lazy cross-module references.\n\nexport { VERSION } from './version.js'\n\nimport {\n getComponentByInternalId,\n registerMaskChangeDispatch,\n registerObserverDispatch,\n} from './internal/component.js'\nimport { registerObserversAPI, registerRelationsCleanup } from './internal/entity.js'\nimport { recordEntityMaskChange, registerComponentLookup } from './internal/query.js'\nimport type { EntityId, WorldState } from './internal/types.js'\n\n// Wire query → component lookup\nregisterComponentLookup((id) => getComponentByInternalId(id))\n\n// Wire component mask-change → query reactive update\nregisterMaskChangeDispatch((state, eid, bit, prev, next) => {\n recordEntityMaskChange(state, eid, bit, prev, next)\n})\n\n// Observer dispatch is registered lazily by observers.ts on first import.\n// Relations cleanup is registered lazily by relations.ts on first import.\n\n// --- Type re-exports ---\nexport type {\n EntityId,\n World,\n WorldOptions,\n System,\n SoAComponent,\n AoSComponent,\n TagComponent,\n ComponentLike,\n ComponentInit,\n ComponentView,\n SoAColumns,\n SoASchema,\n SoAFieldType,\n SoAFieldDecl,\n Query,\n QueryDescriptor,\n Archetype,\n} from './internal/types.js'\n\n// --- World ---\nexport {\n createWorld,\n destroyWorld,\n resetWorld,\n getWorldSize,\n getWorldCapacity,\n isWorld,\n} from './internal/world.js'\n\n// `disposeWorld` is the new preferred name (added 0.2.0). It is the same\n// function as `destroyWorld`, exported under a name that aligns with the ai*js\n// ecosystem `dispose()` convention (aifsmjs Runtime, aibridgejs Bridge).\n//\n// `destroyWorld` is the original 0.1.x export. It is now deprecated as of 0.2.0\n// and is scheduled for removal in 1.0. New code should import `disposeWorld`.\nexport { destroyWorld as disposeWorld } from './internal/world.js'\n\n// --- Entity ---\nexport {\n createEntity,\n destroyEntity,\n entityExists,\n getEntityIndex,\n getEntityGeneration,\n packEntity,\n isEntity,\n} from './internal/entity.js'\n\n// --- Component ---\nexport {\n defineComponent,\n defineTag,\n defineObjectComponent,\n addComponent,\n removeComponent,\n hasComponent,\n getComponent,\n setComponent,\n} from './internal/component.js'\n\nexport { Types } from './internal/types.js'\n\n// --- Query ---\nexport {\n defineQuery,\n runQuery,\n iterQuery,\n forEachEntity,\n forEachEntityIndexed,\n enterQuery,\n exitQuery,\n queryArchetypes,\n} from './internal/query.js'\n\n// --- System ---\nexport { pipe } from './internal/pipe.js'\n\n// --- Utility ---\nexport const IS_SAB_SUPPORTED: boolean = typeof SharedArrayBuffer !== 'undefined'\n\n// --- Errors ---\nexport { EcsError } from './internal/errors.js'\n\n// --- EntityRef (ABA-safe references) ---\nexport type { EntityRef } from './ref.js'\nexport { refOf, deref, aliveRef, EntityNotAliveError } from './ref.js'\n"]}
1
+ {"version":3,"sources":["../src/internal/types.ts","../src/internal/pipe.ts","../src/ref.ts","../src/index.ts"],"names":["Types","pipe","systems","w","world","ctx","s","EntityNotAliveError","eid","refOf","entity","state","getWorldState","isAliveInternal","deref","ref","idx","unpackIdx","archId","arch","storedGen","refGen","unpackGen","aliveRef","registerComponentLookup","id","getComponentByInternalId","registerMaskChangeDispatch","bit","prev","next","recordEntityMaskChange","IS_SAB_SUPPORTED"],"mappings":"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","file":"index.cjs","sourcesContent":["// Shared type definitions for aiecsjs internals.\n// Public-facing types are re-exported from src/index.ts.\n\nexport type EntityId = number & { readonly __brand: 'EntityId' }\n\nexport const Types = {\n i8: 'i8',\n u8: 'u8',\n i16: 'i16',\n u16: 'u16',\n i32: 'i32',\n u32: 'u32',\n f32: 'f32',\n f64: 'f64',\n eid: 'eid',\n bool: 'bool',\n} as const\n\nexport type SoAFieldType =\n | 'i8'\n | 'u8'\n | 'i16'\n | 'u16'\n | 'i32'\n | 'u32'\n | 'f32'\n | 'f64'\n | 'eid'\n | 'bool'\n\nexport type SoAFieldDecl = SoAFieldType | readonly [SoAFieldType, number]\nexport type SoASchema = Readonly<Record<string, SoAFieldDecl>>\n\nexport interface SoAComponent<S extends SoASchema = SoASchema> {\n readonly __kind: 'soa'\n readonly __id: number\n readonly __schema: S\n}\n\nexport interface AoSComponent<T = unknown> {\n readonly __kind: 'aos'\n readonly __id: number\n readonly __factory: () => T\n}\n\nexport interface TagComponent {\n readonly __kind: 'tag'\n readonly __id: number\n}\n\nexport type ComponentLike = SoAComponent<any> | AoSComponent<any> | TagComponent\n\nexport type ColumnArray =\n | Int8Array\n | Uint8Array\n | Int16Array\n | Uint16Array\n | Int32Array\n | Uint32Array\n | Float32Array\n | Float64Array\n\nexport interface SoAColumns {\n [field: string]: ColumnArray\n}\n\nexport type ComponentView<C extends ComponentLike> = C extends SoAComponent<infer _S>\n ? SoAColumns\n : C extends AoSComponent<infer T>\n ? T | undefined\n : C extends TagComponent\n ? boolean\n : never\n\nexport type ComponentInit<C extends ComponentLike> = C extends SoAComponent<infer _S>\n ? Record<string, unknown>\n : C extends AoSComponent<infer T>\n ? Partial<T>\n : undefined\n\nexport interface QueryDescriptor {\n all?: ComponentLike[]\n any?: ComponentLike[]\n none?: ComponentLike[]\n}\n\nexport interface Query {\n readonly id: number\n readonly mask: ReadonlyArray<number>\n}\n\nexport interface Archetype {\n readonly id: number\n readonly mask: ReadonlyArray<number>\n readonly size: number\n}\n\nexport interface WorldOptions {\n initialCapacity?: number\n maxEntities?: number\n indexBits?: number\n generationBits?: number\n /**\n * RESERVED / UNIMPLEMENTED (0.x). Setting this has **no effect** today: the\n * world always allocates its own column storage and never reads or writes a\n * caller-supplied SAB. The 0.x Worker handoff is a snapshot-copy transport —\n * post `transferableSnapshot(world)` and rebuild via `adoptSnapshot` from\n * `aiecsjs/worker`; do not pre-allocate a SAB here. The field is kept so the\n * shape is forward-compatible with the true shared-column backing targeted\n * for 0.3+ (see STABILITY.md, `aiecsjs/worker`).\n */\n buffer?: SharedArrayBuffer\n /** RESERVED / UNIMPLEMENTED (0.x). Paired with {@link WorldOptions.buffer}; no effect today. */\n bufferByteOffset?: number\n}\n\nexport interface World {\n readonly id: number\n readonly capacity: number\n readonly version: string\n}\n\nexport type System<W extends World = World, Ctx = unknown> = (world: W, ctx: Ctx) => W\n\n// --- Internal-only types ---\n\nexport interface ResolvedWorldOptions {\n initialCapacity: number\n maxEntities: number\n indexBits: number\n generationBits: number\n indexMask: number\n generationMask: number\n maxComponents: number\n maskWordCount: number\n buffer: SharedArrayBuffer | null\n bufferByteOffset: number\n}\n\nexport interface FieldInfo {\n name: string\n type: SoAFieldType\n vectorLen: number // 1 for scalar\n ctor: TypedArrayConstructor\n bytesPerElement: number\n}\n\nexport type TypedArrayConstructor =\n | Int8ArrayConstructor\n | Uint8ArrayConstructor\n | Int16ArrayConstructor\n | Uint16ArrayConstructor\n | Int32ArrayConstructor\n | Uint32ArrayConstructor\n | Float32ArrayConstructor\n | Float64ArrayConstructor\n\nexport interface ComponentInfo {\n id: number\n kind: 'soa' | 'aos' | 'tag'\n schema: SoASchema | null\n fields: FieldInfo[]\n factory: (() => unknown) | null\n}\n\nexport interface WorldComponentStorage {\n // For each component bit in this world: storage backed by a column map (SoA), object array (AoS), or null (tag)\n kind: 'soa' | 'aos' | 'tag'\n componentId: number\n bit: number\n // SoA: field name → TypedArray of size `worldCapacity`\n soa?: SoAColumns\n // AoS: per-entity object array, sparse, indexed by eid; undefined for unowned\n aos?: (unknown | undefined)[]\n}\n\nexport interface ArchetypeState {\n id: number\n mask: Uint32Array\n size: number\n capacity: number\n entities: Uint32Array // packed eids in row order\n entityRow: Map<number, number> // eid → row (small archetype-local lookup)\n componentBits: number[] // sorted\n edgeAdd: Int32Array // [bit] → archetype id; -1 unknown\n edgeRemove: Int32Array // [bit] → archetype id; -1 unknown\n}\n\nexport interface QueryInternal extends Query {\n id: number\n all: number[]\n any: number[]\n none: number[]\n // Cache of column references per matched archetype (per query-call)\n columnViewCache: ComponentLike[] // the requested components in their declared order\n reactiveKind: 'normal' | 'enter' | 'exit'\n sourceQueryId: number // -1 for normal\n sourceQuery: QueryInternal | null // back-ref for reactive registration\n}\n\n// Per-world resolved bitmasks for a query. Component bits are assigned per\n// world, so the masks must live in WorldState — keeping them on the shared\n// QueryInternal would silently cross-contaminate worlds whose component\n// registration order differs.\nexport interface QueryMaskBundle {\n withMask: Uint32Array\n anyMask: Uint32Array\n noneMask: Uint32Array\n anyHasBits: boolean\n}\n\nexport interface ReactiveBuffer {\n entered: number[]\n exited: number[]\n}\n\nexport type ObserverEvent = 'add' | 'remove' | 'set'\n\nexport interface ObserverEntry {\n event: ObserverEvent\n componentBit: number // -1 means \"any component\" (used by query observe)\n queryId: number // -1 means component-only\n handler: (eid: EntityId, value?: unknown) => void\n}\n\nexport interface CommandBufferState {\n worldId: number\n ops: CommandOp[]\n nextPlaceholder: number\n flushing: boolean\n}\n\nexport type CommandOp =\n | { kind: 'create'; placeholder: number }\n | { kind: 'add'; eid: EntityId; component: ComponentLike; initial?: unknown }\n | { kind: 'remove'; eid: EntityId; component: ComponentLike }\n | { kind: 'destroy'; eid: EntityId }\n\nexport interface CommandBuffer {\n add<C extends ComponentLike>(eid: EntityId, component: C, initial?: ComponentInit<C>): void\n remove<C extends ComponentLike>(eid: EntityId, component: C): void\n destroy(eid: EntityId): void\n create(): EntityId\n}\n\nexport interface WorldMeta {\n magic: number\n formatVersion: number\n aiecsjsVersion: string\n indexBits: number\n generationBits: number\n maxComponents: number\n maskWordCount: number\n capacity: number\n componentSchemas: Array<{ id: number; kind: 'soa' | 'aos' | 'tag'; schema: SoASchema | null }>\n}\n\nexport interface TransferableSnapshot {\n // Union, not bare SharedArrayBuffer: in a non-cross-origin-isolated environment\n // `SharedArrayBuffer` is undefined, so transferableSnapshot falls back to a\n // plain ArrayBuffer (worker.ts). Declaring the honest union lets consumers\n // branch on `instanceof SharedArrayBuffer` instead of trusting a type that\n // would otherwise lie. Experimental subpath — the union is allowed to land in 0.x.\n buffer: SharedArrayBuffer | ArrayBuffer\n meta: WorldMeta\n}\n\nexport interface SerializeOptions {\n components?: ComponentLike[]\n}\n\nexport interface DeserializeOptions {\n components?: ComponentLike[]\n onUnknownVersion?: 'throw' | 'best-effort'\n}\n\nexport interface WorldSnapshot {\n version: string\n capacity: number\n entities: Array<{\n eid: number\n components: Array<{\n kind: 'soa' | 'aos' | 'tag'\n id: number\n data: unknown\n }>\n }>\n}\n\nexport interface DeltaSerializer {\n capture(): Uint8Array\n apply(world: World, delta: Uint8Array): void\n reset(): void\n}\n\nexport interface Relation<T = void> {\n readonly __kind: 'relation'\n readonly __id: number\n readonly __exclusive: boolean\n readonly __hasData: boolean\n}\n\nexport interface RelationStorage {\n rel: Relation<unknown>\n exclusive: Int32Array | null // [srcEid] → tgtEid; -1 means none\n // Reverse index for exclusive relations: tgtEid → set of srcEids currently\n // pointing at it. Lets destroy cleanup clear incoming edges in O(incoming)\n // instead of scanning the whole `exclusive` capacity. null for non-exclusive\n // storage. Kept in lockstep with every write to `exclusive`.\n incoming: Map<number, Set<number>> | null\n outgoing: Map<number, number[]> // srcEid → tgtEid[]\n data: Map<number, Map<number, unknown>> // srcEid → (tgtEid → data); nested to stay correct across capacity growth\n}\n\nexport interface WorldState {\n // --- public view ---\n readonly id: number\n capacity: number\n readonly version: string\n\n // --- options ---\n options: ResolvedWorldOptions\n\n // --- entity allocation ---\n size: number // alive count\n nextFreshIndex: number // never-used index\n freeList: number[] // recycled indices\n generations: Uint8Array | Uint16Array // [eid] → version\n destroyed: boolean\n\n // --- component registry (per-world) ---\n componentBitFor: Map<number, number> // global component id → bit pos in this world\n componentInfoByBit: (ComponentInfo | null)[] // bit pos → ComponentInfo\n componentStorageByBit: (WorldComponentStorage | null)[]\n nextComponentBit: number\n\n // --- sparse entity state ---\n entityArchetype: Uint32Array // [eid] → archetype id (0 = unattached)\n // entityMask flattened: word w of mask of eid is at entityMask[eid * maskWordCount + w]\n entityMask: Uint32Array\n\n // --- archetype registry ---\n archetypes: ArchetypeState[]\n archetypeByMaskHash: Map<string, number>\n queryVersion: number\n\n // --- query cache ---\n queries: QueryInternal[] // by id\n queryMasks: Map<number, QueryMaskBundle> // queryId → per-world resolved bitmasks\n queryArchetypeCache: (number[] | null)[]\n queryArchetypeStamp: number[]\n bitToQueries: Map<number, Set<number>> // bit → queryIds that mention this bit\n\n // --- reactive query state ---\n reactiveBuffers: Map<number, ReactiveBuffer> // queryId → { entered, exited }\n\n // --- observers ---\n observers: ObserverEntry[]\n\n // --- relations ---\n relationStorage: Map<number, RelationStorage> // relation id → storage\n\n // --- worker / SAB ---\n sab: SharedArrayBuffer | null\n readOnly: boolean\n}\n","import type { System, World } from './types.js'\n\nexport function pipe<W extends World = World, Ctx = unknown>(\n ...systems: System<W, Ctx>[]\n): System<W, Ctx> {\n if (systems.length === 0) return (w: W) => w\n if (systems.length === 1) return systems[0]!\n return (world: W, ctx: Ctx) => {\n let w = world\n for (const s of systems) w = s(w, ctx)\n return w\n }\n}\n","// aiecsjs/ref — ABA-safe entity references.\n//\n// EntityRef<T> wraps a packed EntityId with a world identity check so that\n// stale references (pointing at a recycled slot with a different generation)\n// safely return null rather than silently accessing the wrong entity.\n//\n// EntityRef is in-memory only. Do not pass across worker boundaries or persist\n// to disk. The packed id field has meaning only within the world that issued it.\n// Generation counters reset on resetWorld / deserializeWorld / adoptSnapshot.\n\nimport { isAliveInternal, unpackGen, unpackIdx } from './internal/entity.js'\nimport type { EntityId, World } from './internal/types.js'\nimport { getWorldState } from './internal/world.js'\n\n/**\n * ABA-safe entity reference.\n *\n * Holds the packed EntityId and the id of the world that issued it.\n * Use `deref(world, ref)` to validate and retrieve the live entity id.\n *\n * The phantom type `T` allows callers to distinguish reference kinds at the\n * type system level — e.g. `EntityRef<'bullet'>` vs `EntityRef<'player'>` —\n * without any runtime overhead.\n *\n * @example\n * const ref = refOf<'bullet'>(world, e)\n * // later, in a system:\n * const live = deref(world, ref)\n * if (live !== null) {\n * // entity is still alive with the same generation\n * }\n */\nexport interface EntityRef<T = unknown> {\n readonly id: EntityId // packed value (index + generation)\n readonly worldId: number // world.id — guards against cross-world deref\n readonly __phantom?: T // phantom type tag; zero runtime size\n}\n\n/**\n * Thrown by `refOf` when the entity is not alive.\n * `deref` and `aliveRef` never throw — they return null/false.\n */\nexport class EntityNotAliveError extends Error {\n readonly eid: number\n constructor(eid: number) {\n super(`aiecsjs: entity ${eid} is not alive`)\n this.name = 'EntityNotAliveError'\n this.eid = eid\n }\n}\n\n/**\n * Create an ABA-safe reference to a live entity.\n *\n * @throws {EntityNotAliveError} if the entity is not alive (dead, never created, or eid === 0).\n *\n * The returned object is frozen. `id` is the packed EntityId; `worldId` is\n * the world's numeric id used by `deref` to reject cross-world lookups.\n *\n * The function may be called on the hot path — each call returns a new object\n * but performs no allocation beyond that.\n */\nexport function refOf<T = unknown>(world: World, entity: EntityId): EntityRef<T> {\n const state = getWorldState(world)\n if (!isAliveInternal(state, entity as number)) {\n throw new EntityNotAliveError(entity as number)\n }\n return Object.freeze({ id: entity, worldId: state.id }) as EntityRef<T>\n}\n\n/**\n * Resolve a ref to its live EntityId, or return null if stale.\n *\n * Returns null when any of the following hold:\n * 1. `ref.worldId !== state.id` — cross-world ref\n * 2. `getEntityIndex(ref.id) >= state.capacity` — index out-of-bounds\n * 3. `getEntityIndex(ref.id) === 0` — sentinel slot\n * 4. entity is not in any archetype's entityRow (slot released)\n * 5. generation mismatch → ABA: old ref points at recycled slot\n *\n * Never throws.\n */\nexport function deref<T = unknown>(world: World, ref: EntityRef<T>): EntityId | null {\n const state = getWorldState(world)\n\n // Cross-world guard\n if (ref.worldId !== state.id) return null\n\n const eid = ref.id as number\n const idx = unpackIdx(eid, state.options)\n\n // Sentinel and OOB guards\n if (idx <= 0) return null\n if (idx >= state.capacity) return null\n\n // Archetype membership check\n const archId = state.entityArchetype[idx] ?? 0\n const arch = state.archetypes[archId]\n if (!arch) return null\n if (!arch.entityRow.has(ref.id)) return null\n\n // Generation match — the core ABA protection\n const storedGen = state.generations[idx] ?? 0\n const refGen = unpackGen(eid, state.options)\n if (storedGen !== refGen) return null\n\n return ref.id\n}\n\n/**\n * Boolean guard form of `deref`. Equivalent to `deref(world, ref) !== null`.\n * Use in guard clauses when you do not need the resolved EntityId.\n *\n * Never throws.\n */\nexport function aliveRef<T = unknown>(world: World, ref: EntityRef<T>): boolean {\n return deref(world, ref) !== null\n}\n","// aiecsjs — public root entry.\n// Re-exports the core API and wires up the lazy cross-module references.\n\nexport { VERSION } from './version.js'\n\nimport {\n getComponentByInternalId,\n registerMaskChangeDispatch,\n registerObserverDispatch,\n} from './internal/component.js'\nimport { registerObserversAPI, registerRelationsCleanup } from './internal/entity.js'\nimport { recordEntityMaskChange, registerComponentLookup } from './internal/query.js'\nimport type { EntityId, WorldState } from './internal/types.js'\n\n// Wire query → component lookup\nregisterComponentLookup((id) => getComponentByInternalId(id))\n\n// Wire component mask-change → query reactive update\nregisterMaskChangeDispatch((state, eid, bit, prev, next) => {\n recordEntityMaskChange(state, eid, bit, prev, next)\n})\n\n// Observer dispatch is registered lazily by observers.ts on first import.\n// Relations cleanup is registered lazily by relations.ts on first import.\n\n// --- Type re-exports ---\nexport type {\n EntityId,\n World,\n WorldOptions,\n System,\n SoAComponent,\n AoSComponent,\n TagComponent,\n ComponentLike,\n ComponentInit,\n ComponentView,\n SoAColumns,\n SoASchema,\n SoAFieldType,\n SoAFieldDecl,\n Query,\n QueryDescriptor,\n Archetype,\n} from './internal/types.js'\n\n// --- World ---\nexport {\n createWorld,\n destroyWorld,\n resetWorld,\n getWorldSize,\n getWorldCapacity,\n isWorld,\n} from './internal/world.js'\n\n// `disposeWorld` is the new preferred name (added 0.2.0). It is the same\n// function as `destroyWorld`, exported under a name that aligns with the ai*js\n// ecosystem `dispose()` convention (aifsmjs Runtime, aibridgejs Bridge).\n//\n// `destroyWorld` is the original 0.1.x export. It is now deprecated as of 0.2.0\n// and is scheduled for removal in 1.0. New code should import `disposeWorld`.\nexport { destroyWorld as disposeWorld } from './internal/world.js'\n\n// --- Entity ---\nexport {\n createEntity,\n destroyEntity,\n entityExists,\n getEntityIndex,\n getEntityGeneration,\n packEntity,\n isEntity,\n} from './internal/entity.js'\n\n// --- Component ---\nexport {\n defineComponent,\n defineTag,\n defineObjectComponent,\n addComponent,\n removeComponent,\n hasComponent,\n getComponent,\n setComponent,\n} from './internal/component.js'\n\nexport { Types } from './internal/types.js'\n\n// --- Query ---\nexport {\n defineQuery,\n runQuery,\n iterQuery,\n forEachEntity,\n forEachEntityIndexed,\n enterQuery,\n exitQuery,\n queryArchetypes,\n} from './internal/query.js'\n\n// --- System ---\nexport { pipe } from './internal/pipe.js'\n\n// --- Utility ---\nexport const IS_SAB_SUPPORTED: boolean = typeof SharedArrayBuffer !== 'undefined'\n\n// --- Errors ---\nexport { EcsError } from './internal/errors.js'\n\n// --- EntityRef (ABA-safe references) ---\nexport type { EntityRef } from './ref.js'\nexport { refOf, deref, aliveRef, EntityNotAliveError } from './ref.js'\n"]}
package/dist/index.d.cts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { o as WorldOptions, W as World, E as EntityId, c as ComponentLike, b as ComponentInit, k as SoASchema, h as SoAComponent, A as AoSComponent, T as TagComponent, f as QueryDescriptor, Q as Query, a as Archetype, l as System } from './types-BeVLA7xG.cjs';
2
2
  export { d as ComponentView, g as SoAColumns, i as SoAFieldDecl, j as SoAFieldType, n as Types } from './types-BeVLA7xG.cjs';
3
3
 
4
- declare const VERSION = "0.5.7";
4
+ declare const VERSION = "0.5.8";
5
5
 
6
6
  declare function createWorld(options?: WorldOptions): World;
7
7
  declare function destroyWorld(world: World): void;
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { o as WorldOptions, W as World, E as EntityId, c as ComponentLike, b as ComponentInit, k as SoASchema, h as SoAComponent, A as AoSComponent, T as TagComponent, f as QueryDescriptor, Q as Query, a as Archetype, l as System } from './types-BeVLA7xG.js';
2
2
  export { d as ComponentView, g as SoAColumns, i as SoAFieldDecl, j as SoAFieldType, n as Types } from './types-BeVLA7xG.js';
3
3
 
4
- declare const VERSION = "0.5.7";
4
+ declare const VERSION = "0.5.8";
5
5
 
6
6
  declare function createWorld(options?: WorldOptions): World;
7
7
  declare function destroyWorld(world: World): void;
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
- import {j,i as i$1}from'./chunk-22ICWJ5O.js';export{a as defineQuery,b as enterQuery,c as exitQuery,g as forEachEntity,h as forEachEntityIndexed,f as iterQuery,d as queryArchetypes,e as runQuery}from'./chunk-22ICWJ5O.js';import {R,P,j as j$1,z,t,u}from'./chunk-2KCN5RVK.js';export{i as EcsError,a as VERSION,K as addComponent,v as createEntity,l as createWorld,G as defineComponent,I as defineObjectComponent,H as defineTag,x as destroyEntity,m as destroyWorld,m as disposeWorld,y as entityExists,N as getComponent,B as getEntityGeneration,A as getEntityIndex,p as getWorldCapacity,o as getWorldSize,M as hasComponent,D as isEntity,r as isWorld,C as packEntity,L as removeComponent,n as resetWorld,O as setComponent}from'./chunk-2KCN5RVK.js';var J={i8:"i8",u8:"u8",i16:"i16",u16:"u16",i32:"i32",u32:"u32",f32:"f32",f64:"f64",eid:"eid",bool:"bool"};function X(...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 Y(n,e){let r=j$1(n);if(!z(r,e))throw new i(e);return Object.freeze({id:e,worldId:r.id})}function b(n,e){let r=j$1(n);if(e.worldId!==r.id)return null;let t$1=e.id,o=t(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=u(t$1,r.options);return C!==A?null:e.id}function Z(n,e){return b(n,e)!==null}j(n=>R(n));P((n,e,r,t,o)=>{i$1(n,e,r,t,o);});var pe=typeof SharedArrayBuffer<"u";export{i as EntityNotAliveError,pe as IS_SAB_SUPPORTED,J as Types,Z as aliveRef,b as deref,X as pipe,Y as refOf};//# sourceMappingURL=index.js.map
1
+ import {j,i as i$1}from'./chunk-N5OETPTK.js';export{a as defineQuery,b as enterQuery,c as exitQuery,g as forEachEntity,h as forEachEntityIndexed,f as iterQuery,d as queryArchetypes,e as runQuery}from'./chunk-N5OETPTK.js';import {R,P,j as j$1,z,t,u}from'./chunk-YFTCD2UG.js';export{i as EcsError,a as VERSION,K as addComponent,v as createEntity,l as createWorld,G as defineComponent,I as defineObjectComponent,H as defineTag,x as destroyEntity,m as destroyWorld,m as disposeWorld,y as entityExists,N as getComponent,B as getEntityGeneration,A as getEntityIndex,p as getWorldCapacity,o as getWorldSize,M as hasComponent,D as isEntity,r as isWorld,C as packEntity,L as removeComponent,n as resetWorld,O as setComponent}from'./chunk-YFTCD2UG.js';var J={i8:"i8",u8:"u8",i16:"i16",u16:"u16",i32:"i32",u32:"u32",f32:"f32",f64:"f64",eid:"eid",bool:"bool"};function X(...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 Y(n,e){let r=j$1(n);if(!z(r,e))throw new i(e);return Object.freeze({id:e,worldId:r.id})}function b(n,e){let r=j$1(n);if(e.worldId!==r.id)return null;let t$1=e.id,o=t(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=u(t$1,r.options);return C!==A?null:e.id}function Z(n,e){return b(n,e)!==null}j(n=>R(n));P((n,e,r,t,o)=>{i$1(n,e,r,t,o);});var me=typeof SharedArrayBuffer<"u";export{i as EntityNotAliveError,me as IS_SAB_SUPPORTED,J as Types,Z as aliveRef,b as deref,X as pipe,Y as refOf};//# sourceMappingURL=index.js.map
2
2
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/internal/types.ts","../src/internal/pipe.ts","../src/ref.ts","../src/index.ts"],"names":["Types","pipe","systems","w","world","ctx","s","EntityNotAliveError","eid","refOf","entity","state","getWorldState","isAliveInternal","deref","ref","idx","unpackIdx","archId","arch","storedGen","refGen","unpackGen","aliveRef","registerComponentLookup","id","getComponentByInternalId","registerMaskChangeDispatch","bit","prev","next","recordEntityMaskChange","IS_SAB_SUPPORTED"],"mappings":"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","file":"index.js","sourcesContent":["// Shared type definitions for aiecsjs internals.\n// Public-facing types are re-exported from src/index.ts.\n\nexport type EntityId = number & { readonly __brand: 'EntityId' }\n\nexport const Types = {\n i8: 'i8',\n u8: 'u8',\n i16: 'i16',\n u16: 'u16',\n i32: 'i32',\n u32: 'u32',\n f32: 'f32',\n f64: 'f64',\n eid: 'eid',\n bool: 'bool',\n} as const\n\nexport type SoAFieldType =\n | 'i8'\n | 'u8'\n | 'i16'\n | 'u16'\n | 'i32'\n | 'u32'\n | 'f32'\n | 'f64'\n | 'eid'\n | 'bool'\n\nexport type SoAFieldDecl = SoAFieldType | readonly [SoAFieldType, number]\nexport type SoASchema = Readonly<Record<string, SoAFieldDecl>>\n\nexport interface SoAComponent<S extends SoASchema = SoASchema> {\n readonly __kind: 'soa'\n readonly __id: number\n readonly __schema: S\n}\n\nexport interface AoSComponent<T = unknown> {\n readonly __kind: 'aos'\n readonly __id: number\n readonly __factory: () => T\n}\n\nexport interface TagComponent {\n readonly __kind: 'tag'\n readonly __id: number\n}\n\nexport type ComponentLike = SoAComponent<any> | AoSComponent<any> | TagComponent\n\nexport type ColumnArray =\n | Int8Array\n | Uint8Array\n | Int16Array\n | Uint16Array\n | Int32Array\n | Uint32Array\n | Float32Array\n | Float64Array\n\nexport interface SoAColumns {\n [field: string]: ColumnArray\n}\n\nexport type ComponentView<C extends ComponentLike> = C extends SoAComponent<infer _S>\n ? SoAColumns\n : C extends AoSComponent<infer T>\n ? T | undefined\n : C extends TagComponent\n ? boolean\n : never\n\nexport type ComponentInit<C extends ComponentLike> = C extends SoAComponent<infer _S>\n ? Record<string, unknown>\n : C extends AoSComponent<infer T>\n ? Partial<T>\n : undefined\n\nexport interface QueryDescriptor {\n all?: ComponentLike[]\n any?: ComponentLike[]\n none?: ComponentLike[]\n}\n\nexport interface Query {\n readonly id: number\n readonly mask: ReadonlyArray<number>\n}\n\nexport interface Archetype {\n readonly id: number\n readonly mask: ReadonlyArray<number>\n readonly size: number\n}\n\nexport interface WorldOptions {\n initialCapacity?: number\n maxEntities?: number\n indexBits?: number\n generationBits?: number\n /**\n * RESERVED / UNIMPLEMENTED (0.x). Setting this has **no effect** today: the\n * world always allocates its own column storage and never reads or writes a\n * caller-supplied SAB. The 0.x Worker handoff is a snapshot-copy transport —\n * post `transferableSnapshot(world)` and rebuild via `adoptSnapshot` from\n * `aiecsjs/worker`; do not pre-allocate a SAB here. The field is kept so the\n * shape is forward-compatible with the true shared-column backing targeted\n * for 0.3+ (see STABILITY.md, `aiecsjs/worker`).\n */\n buffer?: SharedArrayBuffer\n /** RESERVED / UNIMPLEMENTED (0.x). Paired with {@link WorldOptions.buffer}; no effect today. */\n bufferByteOffset?: number\n}\n\nexport interface World {\n readonly id: number\n readonly capacity: number\n readonly version: string\n}\n\nexport type System<W extends World = World, Ctx = unknown> = (world: W, ctx: Ctx) => W\n\n// --- Internal-only types ---\n\nexport interface ResolvedWorldOptions {\n initialCapacity: number\n maxEntities: number\n indexBits: number\n generationBits: number\n indexMask: number\n generationMask: number\n maxComponents: number\n maskWordCount: number\n buffer: SharedArrayBuffer | null\n bufferByteOffset: number\n}\n\nexport interface FieldInfo {\n name: string\n type: SoAFieldType\n vectorLen: number // 1 for scalar\n ctor: TypedArrayConstructor\n bytesPerElement: number\n}\n\nexport type TypedArrayConstructor =\n | Int8ArrayConstructor\n | Uint8ArrayConstructor\n | Int16ArrayConstructor\n | Uint16ArrayConstructor\n | Int32ArrayConstructor\n | Uint32ArrayConstructor\n | Float32ArrayConstructor\n | Float64ArrayConstructor\n\nexport interface ComponentInfo {\n id: number\n kind: 'soa' | 'aos' | 'tag'\n schema: SoASchema | null\n fields: FieldInfo[]\n factory: (() => unknown) | null\n}\n\nexport interface WorldComponentStorage {\n // For each component bit in this world: storage backed by a column map (SoA), object array (AoS), or null (tag)\n kind: 'soa' | 'aos' | 'tag'\n componentId: number\n bit: number\n // SoA: field name → TypedArray of size `worldCapacity`\n soa?: SoAColumns\n // AoS: per-entity object array, sparse, indexed by eid; undefined for unowned\n aos?: (unknown | undefined)[]\n}\n\nexport interface ArchetypeState {\n id: number\n mask: Uint32Array\n size: number\n capacity: number\n entities: Uint32Array // packed eids in row order\n entityRow: Map<number, number> // eid → row (small archetype-local lookup)\n componentBits: number[] // sorted\n edgeAdd: Int32Array // [bit] → archetype id; -1 unknown\n edgeRemove: Int32Array // [bit] → archetype id; -1 unknown\n}\n\nexport interface QueryInternal extends Query {\n id: number\n all: number[]\n any: number[]\n none: number[]\n // Cache of column references per matched archetype (per query-call)\n columnViewCache: ComponentLike[] // the requested components in their declared order\n reactiveKind: 'normal' | 'enter' | 'exit'\n sourceQueryId: number // -1 for normal\n sourceQuery: QueryInternal | null // back-ref for reactive registration\n}\n\n// Per-world resolved bitmasks for a query. Component bits are assigned per\n// world, so the masks must live in WorldState — keeping them on the shared\n// QueryInternal would silently cross-contaminate worlds whose component\n// registration order differs.\nexport interface QueryMaskBundle {\n withMask: Uint32Array\n anyMask: Uint32Array\n noneMask: Uint32Array\n anyHasBits: boolean\n}\n\nexport interface ReactiveBuffer {\n entered: number[]\n exited: number[]\n}\n\nexport type ObserverEvent = 'add' | 'remove' | 'set'\n\nexport interface ObserverEntry {\n event: ObserverEvent\n componentBit: number // -1 means \"any component\" (used by query observe)\n queryId: number // -1 means component-only\n handler: (eid: EntityId, value?: unknown) => void\n}\n\nexport interface CommandBufferState {\n worldId: number\n ops: CommandOp[]\n nextPlaceholder: number\n flushing: boolean\n}\n\nexport type CommandOp =\n | { kind: 'create'; placeholder: number }\n | { kind: 'add'; eid: EntityId; component: ComponentLike; initial?: unknown }\n | { kind: 'remove'; eid: EntityId; component: ComponentLike }\n | { kind: 'destroy'; eid: EntityId }\n\nexport interface CommandBuffer {\n add<C extends ComponentLike>(eid: EntityId, component: C, initial?: ComponentInit<C>): void\n remove<C extends ComponentLike>(eid: EntityId, component: C): void\n destroy(eid: EntityId): void\n create(): EntityId\n}\n\nexport interface WorldMeta {\n magic: number\n formatVersion: number\n aiecsjsVersion: string\n indexBits: number\n generationBits: number\n maxComponents: number\n maskWordCount: number\n capacity: number\n componentSchemas: Array<{ id: number; kind: 'soa' | 'aos' | 'tag'; schema: SoASchema | null }>\n}\n\nexport interface TransferableSnapshot {\n // Union, not bare SharedArrayBuffer: in a non-cross-origin-isolated environment\n // `SharedArrayBuffer` is undefined, so transferableSnapshot falls back to a\n // plain ArrayBuffer (worker.ts). Declaring the honest union lets consumers\n // branch on `instanceof SharedArrayBuffer` instead of trusting a type that\n // would otherwise lie. Experimental subpath — the union is allowed to land in 0.x.\n buffer: SharedArrayBuffer | ArrayBuffer\n meta: WorldMeta\n}\n\nexport interface SerializeOptions {\n components?: ComponentLike[]\n}\n\nexport interface DeserializeOptions {\n components?: ComponentLike[]\n onUnknownVersion?: 'throw' | 'best-effort'\n}\n\nexport interface WorldSnapshot {\n version: string\n capacity: number\n entities: Array<{\n eid: number\n components: Array<{\n kind: 'soa' | 'aos' | 'tag'\n id: number\n data: unknown\n }>\n }>\n}\n\nexport interface DeltaSerializer {\n capture(): Uint8Array\n apply(world: World, delta: Uint8Array): void\n reset(): void\n}\n\nexport interface Relation<T = void> {\n readonly __kind: 'relation'\n readonly __id: number\n readonly __exclusive: boolean\n readonly __hasData: boolean\n}\n\nexport interface RelationStorage {\n rel: Relation<any>\n exclusive: Int32Array | null // [srcEid] → tgtEid; -1 means none\n outgoing: Map<number, number[]> // srcEid → tgtEid[]\n data: Map<number, Map<number, unknown>> // srcEid → (tgtEid → data); nested to stay correct across capacity growth\n}\n\nexport interface WorldState {\n // --- public view ---\n readonly id: number\n capacity: number\n readonly version: string\n\n // --- options ---\n options: ResolvedWorldOptions\n\n // --- entity allocation ---\n size: number // alive count\n nextFreshIndex: number // never-used index\n freeList: number[] // recycled indices\n generations: Uint8Array | Uint16Array // [eid] → version\n destroyed: boolean\n\n // --- component registry (per-world) ---\n componentBitFor: Map<number, number> // global component id → bit pos in this world\n componentInfoByBit: (ComponentInfo | null)[] // bit pos → ComponentInfo\n componentStorageByBit: (WorldComponentStorage | null)[]\n nextComponentBit: number\n\n // --- sparse entity state ---\n entityArchetype: Uint32Array // [eid] → archetype id (0 = unattached)\n // entityMask flattened: word w of mask of eid is at entityMask[eid * maskWordCount + w]\n entityMask: Uint32Array\n\n // --- archetype registry ---\n archetypes: ArchetypeState[]\n archetypeByMaskHash: Map<string, number>\n queryVersion: number\n\n // --- query cache ---\n queries: QueryInternal[] // by id\n queryMasks: Map<number, QueryMaskBundle> // queryId → per-world resolved bitmasks\n queryArchetypeCache: (number[] | null)[]\n queryArchetypeStamp: number[]\n bitToQueries: Map<number, Set<number>> // bit → queryIds that mention this bit\n\n // --- reactive query state ---\n reactiveBuffers: Map<number, ReactiveBuffer> // queryId → { entered, exited }\n\n // --- observers ---\n observers: ObserverEntry[]\n\n // --- relations ---\n relationStorage: Map<number, RelationStorage> // relation id → storage\n\n // --- worker / SAB ---\n sab: SharedArrayBuffer | null\n readOnly: boolean\n}\n","import type { System, World } from './types.js'\n\nexport function pipe<W extends World = World, Ctx = unknown>(\n ...systems: System<W, Ctx>[]\n): System<W, Ctx> {\n if (systems.length === 0) return (w: W) => w\n if (systems.length === 1) return systems[0]!\n return (world: W, ctx: Ctx) => {\n let w = world\n for (const s of systems) w = s(w, ctx)\n return w\n }\n}\n","// aiecsjs/ref — ABA-safe entity references.\n//\n// EntityRef<T> wraps a packed EntityId with a world identity check so that\n// stale references (pointing at a recycled slot with a different generation)\n// safely return null rather than silently accessing the wrong entity.\n//\n// EntityRef is in-memory only. Do not pass across worker boundaries or persist\n// to disk. The packed id field has meaning only within the world that issued it.\n// Generation counters reset on resetWorld / deserializeWorld / adoptSnapshot.\n\nimport { isAliveInternal, unpackGen, unpackIdx } from './internal/entity.js'\nimport type { EntityId, World } from './internal/types.js'\nimport { getWorldState } from './internal/world.js'\n\n/**\n * ABA-safe entity reference.\n *\n * Holds the packed EntityId and the id of the world that issued it.\n * Use `deref(world, ref)` to validate and retrieve the live entity id.\n *\n * The phantom type `T` allows callers to distinguish reference kinds at the\n * type system level — e.g. `EntityRef<'bullet'>` vs `EntityRef<'player'>` —\n * without any runtime overhead.\n *\n * @example\n * const ref = refOf<'bullet'>(world, e)\n * // later, in a system:\n * const live = deref(world, ref)\n * if (live !== null) {\n * // entity is still alive with the same generation\n * }\n */\nexport interface EntityRef<T = unknown> {\n readonly id: EntityId // packed value (index + generation)\n readonly worldId: number // world.id — guards against cross-world deref\n readonly __phantom?: T // phantom type tag; zero runtime size\n}\n\n/**\n * Thrown by `refOf` when the entity is not alive.\n * `deref` and `aliveRef` never throw — they return null/false.\n */\nexport class EntityNotAliveError extends Error {\n readonly eid: number\n constructor(eid: number) {\n super(`aiecsjs: entity ${eid} is not alive`)\n this.name = 'EntityNotAliveError'\n this.eid = eid\n }\n}\n\n/**\n * Create an ABA-safe reference to a live entity.\n *\n * @throws {EntityNotAliveError} if the entity is not alive (dead, never created, or eid === 0).\n *\n * The returned object is frozen. `id` is the packed EntityId; `worldId` is\n * the world's numeric id used by `deref` to reject cross-world lookups.\n *\n * The function may be called on the hot path — each call returns a new object\n * but performs no allocation beyond that.\n */\nexport function refOf<T = unknown>(world: World, entity: EntityId): EntityRef<T> {\n const state = getWorldState(world)\n if (!isAliveInternal(state, entity as number)) {\n throw new EntityNotAliveError(entity as number)\n }\n return Object.freeze({ id: entity, worldId: state.id }) as EntityRef<T>\n}\n\n/**\n * Resolve a ref to its live EntityId, or return null if stale.\n *\n * Returns null when any of the following hold:\n * 1. `ref.worldId !== state.id` — cross-world ref\n * 2. `getEntityIndex(ref.id) >= state.capacity` — index out-of-bounds\n * 3. `getEntityIndex(ref.id) === 0` — sentinel slot\n * 4. entity is not in any archetype's entityRow (slot released)\n * 5. generation mismatch → ABA: old ref points at recycled slot\n *\n * Never throws.\n */\nexport function deref<T = unknown>(world: World, ref: EntityRef<T>): EntityId | null {\n const state = getWorldState(world)\n\n // Cross-world guard\n if (ref.worldId !== state.id) return null\n\n const eid = ref.id as number\n const idx = unpackIdx(eid, state.options)\n\n // Sentinel and OOB guards\n if (idx <= 0) return null\n if (idx >= state.capacity) return null\n\n // Archetype membership check\n const archId = state.entityArchetype[idx] ?? 0\n const arch = state.archetypes[archId]\n if (!arch) return null\n if (!arch.entityRow.has(ref.id)) return null\n\n // Generation match — the core ABA protection\n const storedGen = state.generations[idx] ?? 0\n const refGen = unpackGen(eid, state.options)\n if (storedGen !== refGen) return null\n\n return ref.id\n}\n\n/**\n * Boolean guard form of `deref`. Equivalent to `deref(world, ref) !== null`.\n * Use in guard clauses when you do not need the resolved EntityId.\n *\n * Never throws.\n */\nexport function aliveRef<T = unknown>(world: World, ref: EntityRef<T>): boolean {\n return deref(world, ref) !== null\n}\n","// aiecsjs — public root entry.\n// Re-exports the core API and wires up the lazy cross-module references.\n\nexport { VERSION } from './version.js'\n\nimport {\n getComponentByInternalId,\n registerMaskChangeDispatch,\n registerObserverDispatch,\n} from './internal/component.js'\nimport { registerObserversAPI, registerRelationsCleanup } from './internal/entity.js'\nimport { recordEntityMaskChange, registerComponentLookup } from './internal/query.js'\nimport type { EntityId, WorldState } from './internal/types.js'\n\n// Wire query → component lookup\nregisterComponentLookup((id) => getComponentByInternalId(id))\n\n// Wire component mask-change → query reactive update\nregisterMaskChangeDispatch((state, eid, bit, prev, next) => {\n recordEntityMaskChange(state, eid, bit, prev, next)\n})\n\n// Observer dispatch is registered lazily by observers.ts on first import.\n// Relations cleanup is registered lazily by relations.ts on first import.\n\n// --- Type re-exports ---\nexport type {\n EntityId,\n World,\n WorldOptions,\n System,\n SoAComponent,\n AoSComponent,\n TagComponent,\n ComponentLike,\n ComponentInit,\n ComponentView,\n SoAColumns,\n SoASchema,\n SoAFieldType,\n SoAFieldDecl,\n Query,\n QueryDescriptor,\n Archetype,\n} from './internal/types.js'\n\n// --- World ---\nexport {\n createWorld,\n destroyWorld,\n resetWorld,\n getWorldSize,\n getWorldCapacity,\n isWorld,\n} from './internal/world.js'\n\n// `disposeWorld` is the new preferred name (added 0.2.0). It is the same\n// function as `destroyWorld`, exported under a name that aligns with the ai*js\n// ecosystem `dispose()` convention (aifsmjs Runtime, aibridgejs Bridge).\n//\n// `destroyWorld` is the original 0.1.x export. It is now deprecated as of 0.2.0\n// and is scheduled for removal in 1.0. New code should import `disposeWorld`.\nexport { destroyWorld as disposeWorld } from './internal/world.js'\n\n// --- Entity ---\nexport {\n createEntity,\n destroyEntity,\n entityExists,\n getEntityIndex,\n getEntityGeneration,\n packEntity,\n isEntity,\n} from './internal/entity.js'\n\n// --- Component ---\nexport {\n defineComponent,\n defineTag,\n defineObjectComponent,\n addComponent,\n removeComponent,\n hasComponent,\n getComponent,\n setComponent,\n} from './internal/component.js'\n\nexport { Types } from './internal/types.js'\n\n// --- Query ---\nexport {\n defineQuery,\n runQuery,\n iterQuery,\n forEachEntity,\n forEachEntityIndexed,\n enterQuery,\n exitQuery,\n queryArchetypes,\n} from './internal/query.js'\n\n// --- System ---\nexport { pipe } from './internal/pipe.js'\n\n// --- Utility ---\nexport const IS_SAB_SUPPORTED: boolean = typeof SharedArrayBuffer !== 'undefined'\n\n// --- Errors ---\nexport { EcsError } from './internal/errors.js'\n\n// --- EntityRef (ABA-safe references) ---\nexport type { EntityRef } from './ref.js'\nexport { refOf, deref, aliveRef, EntityNotAliveError } from './ref.js'\n"]}
1
+ {"version":3,"sources":["../src/internal/types.ts","../src/internal/pipe.ts","../src/ref.ts","../src/index.ts"],"names":["Types","pipe","systems","w","world","ctx","s","EntityNotAliveError","eid","refOf","entity","state","getWorldState","isAliveInternal","deref","ref","idx","unpackIdx","archId","arch","storedGen","refGen","unpackGen","aliveRef","registerComponentLookup","id","getComponentByInternalId","registerMaskChangeDispatch","bit","prev","next","recordEntityMaskChange","IS_SAB_SUPPORTED"],"mappings":"suBAKO,IAAMA,CAAAA,CAAQ,CACnB,EAAA,CAAI,IAAA,CACJ,EAAA,CAAI,KACJ,GAAA,CAAK,KAAA,CACL,GAAA,CAAK,KAAA,CACL,GAAA,CAAK,KAAA,CACL,IAAK,KAAA,CACL,GAAA,CAAK,KAAA,CACL,GAAA,CAAK,KAAA,CACL,GAAA,CAAK,KAAA,CACL,IAAA,CAAM,MACR,ECdO,SAASC,CAAAA,CAAAA,GACXC,CAAAA,CACa,CAChB,OAAIA,CAAAA,CAAQ,MAAA,GAAW,CAAA,CAAWC,CAAAA,EAASA,CAAAA,CACvCD,CAAAA,CAAQ,SAAW,CAAA,CAAUA,CAAAA,CAAQ,CAAC,CAAA,CACnC,CAACE,CAAAA,CAAUC,IAAa,CAC7B,IAAIF,CAAAA,CAAIC,CAAAA,CACR,IAAA,IAAWE,CAAAA,IAAKJ,CAAAA,CAASC,CAAAA,CAAIG,CAAAA,CAAEH,CAAAA,CAAGE,CAAG,CAAA,CACrC,OAAOF,CACT,CACF,CC8BO,IAAMI,CAAAA,CAAN,cAAkC,KAAM,CACpC,IACT,WAAA,CAAYC,CAAAA,CAAa,CACvB,KAAA,CAAM,CAAA,gBAAA,EAAmBA,CAAG,eAAe,CAAA,CAC3C,IAAA,CAAK,IAAA,CAAO,qBAAA,CACZ,IAAA,CAAK,GAAA,CAAMA,EACb,CACF,EAaO,SAASC,CAAAA,CAAmBL,CAAAA,CAAcM,CAAAA,CAAgC,CAC/E,IAAMC,CAAAA,CAAQC,GAAAA,CAAcR,CAAK,CAAA,CACjC,GAAI,CAACS,CAAAA,CAAgBF,CAAAA,CAAOD,CAAgB,CAAA,CAC1C,MAAM,IAAIH,CAAAA,CAAoBG,CAAgB,EAEhD,OAAO,MAAA,CAAO,MAAA,CAAO,CAAE,EAAA,CAAIA,CAAAA,CAAQ,OAAA,CAASC,CAAAA,CAAM,EAAG,CAAC,CACxD,CAcO,SAASG,CAAAA,CAAmBV,EAAcW,CAAAA,CAAoC,CACnF,IAAMJ,CAAAA,CAAQC,GAAAA,CAAcR,CAAK,EAGjC,GAAIW,CAAAA,CAAI,OAAA,GAAYJ,CAAAA,CAAM,EAAA,CAAI,OAAO,KAErC,IAAMH,GAAAA,CAAMO,CAAAA,CAAI,EAAA,CACVC,CAAAA,CAAMC,CAAAA,CAAUT,GAAAA,CAAKG,CAAAA,CAAM,OAAO,CAAA,CAIxC,GADIK,CAAAA,EAAO,CAAA,EACPA,CAAAA,EAAOL,EAAM,QAAA,CAAU,OAAO,IAAA,CAGlC,IAAMO,CAAAA,CAASP,CAAAA,CAAM,gBAAgBK,CAAG,CAAA,EAAK,CAAA,CACvCG,CAAAA,CAAOR,CAAAA,CAAM,UAAA,CAAWO,CAAM,CAAA,CAEpC,GADI,CAACC,CAAAA,EACD,CAACA,CAAAA,CAAK,SAAA,CAAU,GAAA,CAAIJ,CAAAA,CAAI,EAAE,CAAA,CAAG,OAAO,IAAA,CAGxC,IAAMK,EAAYT,CAAAA,CAAM,WAAA,CAAYK,CAAG,CAAA,EAAK,CAAA,CACtCK,CAAAA,CAASC,EAAUd,GAAAA,CAAKG,CAAAA,CAAM,OAAO,CAAA,CAC3C,OAAIS,CAAAA,GAAcC,EAAe,IAAA,CAE1BN,CAAAA,CAAI,EACb,CAQO,SAASQ,CAAAA,CAAsBnB,CAAAA,CAAcW,CAAAA,CAA4B,CAC9E,OAAOD,CAAAA,CAAMV,CAAAA,CAAOW,CAAG,CAAA,GAAM,IAC/B,CCtGAS,CAAAA,CAAyBC,CAAAA,EAAOC,CAAAA,CAAyBD,CAAE,CAAC,CAAA,CAG5DE,CAAAA,CAA2B,CAAChB,CAAAA,CAAOH,CAAAA,CAAKoB,CAAAA,CAAKC,CAAAA,CAAMC,CAAAA,GAAS,CAC1DC,GAAAA,CAAuBpB,CAAAA,CAAOH,CAAAA,CAAKoB,CAAAA,CAAKC,CAAAA,CAAMC,CAAI,EACpD,CAAC,CAAA,CAqFM,IAAME,EAAAA,CAA4B,OAAO,iBAAA,CAAsB","file":"index.js","sourcesContent":["// Shared type definitions for aiecsjs internals.\n// Public-facing types are re-exported from src/index.ts.\n\nexport type EntityId = number & { readonly __brand: 'EntityId' }\n\nexport const Types = {\n i8: 'i8',\n u8: 'u8',\n i16: 'i16',\n u16: 'u16',\n i32: 'i32',\n u32: 'u32',\n f32: 'f32',\n f64: 'f64',\n eid: 'eid',\n bool: 'bool',\n} as const\n\nexport type SoAFieldType =\n | 'i8'\n | 'u8'\n | 'i16'\n | 'u16'\n | 'i32'\n | 'u32'\n | 'f32'\n | 'f64'\n | 'eid'\n | 'bool'\n\nexport type SoAFieldDecl = SoAFieldType | readonly [SoAFieldType, number]\nexport type SoASchema = Readonly<Record<string, SoAFieldDecl>>\n\nexport interface SoAComponent<S extends SoASchema = SoASchema> {\n readonly __kind: 'soa'\n readonly __id: number\n readonly __schema: S\n}\n\nexport interface AoSComponent<T = unknown> {\n readonly __kind: 'aos'\n readonly __id: number\n readonly __factory: () => T\n}\n\nexport interface TagComponent {\n readonly __kind: 'tag'\n readonly __id: number\n}\n\nexport type ComponentLike = SoAComponent<any> | AoSComponent<any> | TagComponent\n\nexport type ColumnArray =\n | Int8Array\n | Uint8Array\n | Int16Array\n | Uint16Array\n | Int32Array\n | Uint32Array\n | Float32Array\n | Float64Array\n\nexport interface SoAColumns {\n [field: string]: ColumnArray\n}\n\nexport type ComponentView<C extends ComponentLike> = C extends SoAComponent<infer _S>\n ? SoAColumns\n : C extends AoSComponent<infer T>\n ? T | undefined\n : C extends TagComponent\n ? boolean\n : never\n\nexport type ComponentInit<C extends ComponentLike> = C extends SoAComponent<infer _S>\n ? Record<string, unknown>\n : C extends AoSComponent<infer T>\n ? Partial<T>\n : undefined\n\nexport interface QueryDescriptor {\n all?: ComponentLike[]\n any?: ComponentLike[]\n none?: ComponentLike[]\n}\n\nexport interface Query {\n readonly id: number\n readonly mask: ReadonlyArray<number>\n}\n\nexport interface Archetype {\n readonly id: number\n readonly mask: ReadonlyArray<number>\n readonly size: number\n}\n\nexport interface WorldOptions {\n initialCapacity?: number\n maxEntities?: number\n indexBits?: number\n generationBits?: number\n /**\n * RESERVED / UNIMPLEMENTED (0.x). Setting this has **no effect** today: the\n * world always allocates its own column storage and never reads or writes a\n * caller-supplied SAB. The 0.x Worker handoff is a snapshot-copy transport —\n * post `transferableSnapshot(world)` and rebuild via `adoptSnapshot` from\n * `aiecsjs/worker`; do not pre-allocate a SAB here. The field is kept so the\n * shape is forward-compatible with the true shared-column backing targeted\n * for 0.3+ (see STABILITY.md, `aiecsjs/worker`).\n */\n buffer?: SharedArrayBuffer\n /** RESERVED / UNIMPLEMENTED (0.x). Paired with {@link WorldOptions.buffer}; no effect today. */\n bufferByteOffset?: number\n}\n\nexport interface World {\n readonly id: number\n readonly capacity: number\n readonly version: string\n}\n\nexport type System<W extends World = World, Ctx = unknown> = (world: W, ctx: Ctx) => W\n\n// --- Internal-only types ---\n\nexport interface ResolvedWorldOptions {\n initialCapacity: number\n maxEntities: number\n indexBits: number\n generationBits: number\n indexMask: number\n generationMask: number\n maxComponents: number\n maskWordCount: number\n buffer: SharedArrayBuffer | null\n bufferByteOffset: number\n}\n\nexport interface FieldInfo {\n name: string\n type: SoAFieldType\n vectorLen: number // 1 for scalar\n ctor: TypedArrayConstructor\n bytesPerElement: number\n}\n\nexport type TypedArrayConstructor =\n | Int8ArrayConstructor\n | Uint8ArrayConstructor\n | Int16ArrayConstructor\n | Uint16ArrayConstructor\n | Int32ArrayConstructor\n | Uint32ArrayConstructor\n | Float32ArrayConstructor\n | Float64ArrayConstructor\n\nexport interface ComponentInfo {\n id: number\n kind: 'soa' | 'aos' | 'tag'\n schema: SoASchema | null\n fields: FieldInfo[]\n factory: (() => unknown) | null\n}\n\nexport interface WorldComponentStorage {\n // For each component bit in this world: storage backed by a column map (SoA), object array (AoS), or null (tag)\n kind: 'soa' | 'aos' | 'tag'\n componentId: number\n bit: number\n // SoA: field name → TypedArray of size `worldCapacity`\n soa?: SoAColumns\n // AoS: per-entity object array, sparse, indexed by eid; undefined for unowned\n aos?: (unknown | undefined)[]\n}\n\nexport interface ArchetypeState {\n id: number\n mask: Uint32Array\n size: number\n capacity: number\n entities: Uint32Array // packed eids in row order\n entityRow: Map<number, number> // eid → row (small archetype-local lookup)\n componentBits: number[] // sorted\n edgeAdd: Int32Array // [bit] → archetype id; -1 unknown\n edgeRemove: Int32Array // [bit] → archetype id; -1 unknown\n}\n\nexport interface QueryInternal extends Query {\n id: number\n all: number[]\n any: number[]\n none: number[]\n // Cache of column references per matched archetype (per query-call)\n columnViewCache: ComponentLike[] // the requested components in their declared order\n reactiveKind: 'normal' | 'enter' | 'exit'\n sourceQueryId: number // -1 for normal\n sourceQuery: QueryInternal | null // back-ref for reactive registration\n}\n\n// Per-world resolved bitmasks for a query. Component bits are assigned per\n// world, so the masks must live in WorldState — keeping them on the shared\n// QueryInternal would silently cross-contaminate worlds whose component\n// registration order differs.\nexport interface QueryMaskBundle {\n withMask: Uint32Array\n anyMask: Uint32Array\n noneMask: Uint32Array\n anyHasBits: boolean\n}\n\nexport interface ReactiveBuffer {\n entered: number[]\n exited: number[]\n}\n\nexport type ObserverEvent = 'add' | 'remove' | 'set'\n\nexport interface ObserverEntry {\n event: ObserverEvent\n componentBit: number // -1 means \"any component\" (used by query observe)\n queryId: number // -1 means component-only\n handler: (eid: EntityId, value?: unknown) => void\n}\n\nexport interface CommandBufferState {\n worldId: number\n ops: CommandOp[]\n nextPlaceholder: number\n flushing: boolean\n}\n\nexport type CommandOp =\n | { kind: 'create'; placeholder: number }\n | { kind: 'add'; eid: EntityId; component: ComponentLike; initial?: unknown }\n | { kind: 'remove'; eid: EntityId; component: ComponentLike }\n | { kind: 'destroy'; eid: EntityId }\n\nexport interface CommandBuffer {\n add<C extends ComponentLike>(eid: EntityId, component: C, initial?: ComponentInit<C>): void\n remove<C extends ComponentLike>(eid: EntityId, component: C): void\n destroy(eid: EntityId): void\n create(): EntityId\n}\n\nexport interface WorldMeta {\n magic: number\n formatVersion: number\n aiecsjsVersion: string\n indexBits: number\n generationBits: number\n maxComponents: number\n maskWordCount: number\n capacity: number\n componentSchemas: Array<{ id: number; kind: 'soa' | 'aos' | 'tag'; schema: SoASchema | null }>\n}\n\nexport interface TransferableSnapshot {\n // Union, not bare SharedArrayBuffer: in a non-cross-origin-isolated environment\n // `SharedArrayBuffer` is undefined, so transferableSnapshot falls back to a\n // plain ArrayBuffer (worker.ts). Declaring the honest union lets consumers\n // branch on `instanceof SharedArrayBuffer` instead of trusting a type that\n // would otherwise lie. Experimental subpath — the union is allowed to land in 0.x.\n buffer: SharedArrayBuffer | ArrayBuffer\n meta: WorldMeta\n}\n\nexport interface SerializeOptions {\n components?: ComponentLike[]\n}\n\nexport interface DeserializeOptions {\n components?: ComponentLike[]\n onUnknownVersion?: 'throw' | 'best-effort'\n}\n\nexport interface WorldSnapshot {\n version: string\n capacity: number\n entities: Array<{\n eid: number\n components: Array<{\n kind: 'soa' | 'aos' | 'tag'\n id: number\n data: unknown\n }>\n }>\n}\n\nexport interface DeltaSerializer {\n capture(): Uint8Array\n apply(world: World, delta: Uint8Array): void\n reset(): void\n}\n\nexport interface Relation<T = void> {\n readonly __kind: 'relation'\n readonly __id: number\n readonly __exclusive: boolean\n readonly __hasData: boolean\n}\n\nexport interface RelationStorage {\n rel: Relation<unknown>\n exclusive: Int32Array | null // [srcEid] → tgtEid; -1 means none\n // Reverse index for exclusive relations: tgtEid → set of srcEids currently\n // pointing at it. Lets destroy cleanup clear incoming edges in O(incoming)\n // instead of scanning the whole `exclusive` capacity. null for non-exclusive\n // storage. Kept in lockstep with every write to `exclusive`.\n incoming: Map<number, Set<number>> | null\n outgoing: Map<number, number[]> // srcEid → tgtEid[]\n data: Map<number, Map<number, unknown>> // srcEid → (tgtEid → data); nested to stay correct across capacity growth\n}\n\nexport interface WorldState {\n // --- public view ---\n readonly id: number\n capacity: number\n readonly version: string\n\n // --- options ---\n options: ResolvedWorldOptions\n\n // --- entity allocation ---\n size: number // alive count\n nextFreshIndex: number // never-used index\n freeList: number[] // recycled indices\n generations: Uint8Array | Uint16Array // [eid] → version\n destroyed: boolean\n\n // --- component registry (per-world) ---\n componentBitFor: Map<number, number> // global component id → bit pos in this world\n componentInfoByBit: (ComponentInfo | null)[] // bit pos → ComponentInfo\n componentStorageByBit: (WorldComponentStorage | null)[]\n nextComponentBit: number\n\n // --- sparse entity state ---\n entityArchetype: Uint32Array // [eid] → archetype id (0 = unattached)\n // entityMask flattened: word w of mask of eid is at entityMask[eid * maskWordCount + w]\n entityMask: Uint32Array\n\n // --- archetype registry ---\n archetypes: ArchetypeState[]\n archetypeByMaskHash: Map<string, number>\n queryVersion: number\n\n // --- query cache ---\n queries: QueryInternal[] // by id\n queryMasks: Map<number, QueryMaskBundle> // queryId → per-world resolved bitmasks\n queryArchetypeCache: (number[] | null)[]\n queryArchetypeStamp: number[]\n bitToQueries: Map<number, Set<number>> // bit → queryIds that mention this bit\n\n // --- reactive query state ---\n reactiveBuffers: Map<number, ReactiveBuffer> // queryId → { entered, exited }\n\n // --- observers ---\n observers: ObserverEntry[]\n\n // --- relations ---\n relationStorage: Map<number, RelationStorage> // relation id → storage\n\n // --- worker / SAB ---\n sab: SharedArrayBuffer | null\n readOnly: boolean\n}\n","import type { System, World } from './types.js'\n\nexport function pipe<W extends World = World, Ctx = unknown>(\n ...systems: System<W, Ctx>[]\n): System<W, Ctx> {\n if (systems.length === 0) return (w: W) => w\n if (systems.length === 1) return systems[0]!\n return (world: W, ctx: Ctx) => {\n let w = world\n for (const s of systems) w = s(w, ctx)\n return w\n }\n}\n","// aiecsjs/ref — ABA-safe entity references.\n//\n// EntityRef<T> wraps a packed EntityId with a world identity check so that\n// stale references (pointing at a recycled slot with a different generation)\n// safely return null rather than silently accessing the wrong entity.\n//\n// EntityRef is in-memory only. Do not pass across worker boundaries or persist\n// to disk. The packed id field has meaning only within the world that issued it.\n// Generation counters reset on resetWorld / deserializeWorld / adoptSnapshot.\n\nimport { isAliveInternal, unpackGen, unpackIdx } from './internal/entity.js'\nimport type { EntityId, World } from './internal/types.js'\nimport { getWorldState } from './internal/world.js'\n\n/**\n * ABA-safe entity reference.\n *\n * Holds the packed EntityId and the id of the world that issued it.\n * Use `deref(world, ref)` to validate and retrieve the live entity id.\n *\n * The phantom type `T` allows callers to distinguish reference kinds at the\n * type system level — e.g. `EntityRef<'bullet'>` vs `EntityRef<'player'>` —\n * without any runtime overhead.\n *\n * @example\n * const ref = refOf<'bullet'>(world, e)\n * // later, in a system:\n * const live = deref(world, ref)\n * if (live !== null) {\n * // entity is still alive with the same generation\n * }\n */\nexport interface EntityRef<T = unknown> {\n readonly id: EntityId // packed value (index + generation)\n readonly worldId: number // world.id — guards against cross-world deref\n readonly __phantom?: T // phantom type tag; zero runtime size\n}\n\n/**\n * Thrown by `refOf` when the entity is not alive.\n * `deref` and `aliveRef` never throw — they return null/false.\n */\nexport class EntityNotAliveError extends Error {\n readonly eid: number\n constructor(eid: number) {\n super(`aiecsjs: entity ${eid} is not alive`)\n this.name = 'EntityNotAliveError'\n this.eid = eid\n }\n}\n\n/**\n * Create an ABA-safe reference to a live entity.\n *\n * @throws {EntityNotAliveError} if the entity is not alive (dead, never created, or eid === 0).\n *\n * The returned object is frozen. `id` is the packed EntityId; `worldId` is\n * the world's numeric id used by `deref` to reject cross-world lookups.\n *\n * The function may be called on the hot path — each call returns a new object\n * but performs no allocation beyond that.\n */\nexport function refOf<T = unknown>(world: World, entity: EntityId): EntityRef<T> {\n const state = getWorldState(world)\n if (!isAliveInternal(state, entity as number)) {\n throw new EntityNotAliveError(entity as number)\n }\n return Object.freeze({ id: entity, worldId: state.id }) as EntityRef<T>\n}\n\n/**\n * Resolve a ref to its live EntityId, or return null if stale.\n *\n * Returns null when any of the following hold:\n * 1. `ref.worldId !== state.id` — cross-world ref\n * 2. `getEntityIndex(ref.id) >= state.capacity` — index out-of-bounds\n * 3. `getEntityIndex(ref.id) === 0` — sentinel slot\n * 4. entity is not in any archetype's entityRow (slot released)\n * 5. generation mismatch → ABA: old ref points at recycled slot\n *\n * Never throws.\n */\nexport function deref<T = unknown>(world: World, ref: EntityRef<T>): EntityId | null {\n const state = getWorldState(world)\n\n // Cross-world guard\n if (ref.worldId !== state.id) return null\n\n const eid = ref.id as number\n const idx = unpackIdx(eid, state.options)\n\n // Sentinel and OOB guards\n if (idx <= 0) return null\n if (idx >= state.capacity) return null\n\n // Archetype membership check\n const archId = state.entityArchetype[idx] ?? 0\n const arch = state.archetypes[archId]\n if (!arch) return null\n if (!arch.entityRow.has(ref.id)) return null\n\n // Generation match — the core ABA protection\n const storedGen = state.generations[idx] ?? 0\n const refGen = unpackGen(eid, state.options)\n if (storedGen !== refGen) return null\n\n return ref.id\n}\n\n/**\n * Boolean guard form of `deref`. Equivalent to `deref(world, ref) !== null`.\n * Use in guard clauses when you do not need the resolved EntityId.\n *\n * Never throws.\n */\nexport function aliveRef<T = unknown>(world: World, ref: EntityRef<T>): boolean {\n return deref(world, ref) !== null\n}\n","// aiecsjs — public root entry.\n// Re-exports the core API and wires up the lazy cross-module references.\n\nexport { VERSION } from './version.js'\n\nimport {\n getComponentByInternalId,\n registerMaskChangeDispatch,\n registerObserverDispatch,\n} from './internal/component.js'\nimport { registerObserversAPI, registerRelationsCleanup } from './internal/entity.js'\nimport { recordEntityMaskChange, registerComponentLookup } from './internal/query.js'\nimport type { EntityId, WorldState } from './internal/types.js'\n\n// Wire query → component lookup\nregisterComponentLookup((id) => getComponentByInternalId(id))\n\n// Wire component mask-change → query reactive update\nregisterMaskChangeDispatch((state, eid, bit, prev, next) => {\n recordEntityMaskChange(state, eid, bit, prev, next)\n})\n\n// Observer dispatch is registered lazily by observers.ts on first import.\n// Relations cleanup is registered lazily by relations.ts on first import.\n\n// --- Type re-exports ---\nexport type {\n EntityId,\n World,\n WorldOptions,\n System,\n SoAComponent,\n AoSComponent,\n TagComponent,\n ComponentLike,\n ComponentInit,\n ComponentView,\n SoAColumns,\n SoASchema,\n SoAFieldType,\n SoAFieldDecl,\n Query,\n QueryDescriptor,\n Archetype,\n} from './internal/types.js'\n\n// --- World ---\nexport {\n createWorld,\n destroyWorld,\n resetWorld,\n getWorldSize,\n getWorldCapacity,\n isWorld,\n} from './internal/world.js'\n\n// `disposeWorld` is the new preferred name (added 0.2.0). It is the same\n// function as `destroyWorld`, exported under a name that aligns with the ai*js\n// ecosystem `dispose()` convention (aifsmjs Runtime, aibridgejs Bridge).\n//\n// `destroyWorld` is the original 0.1.x export. It is now deprecated as of 0.2.0\n// and is scheduled for removal in 1.0. New code should import `disposeWorld`.\nexport { destroyWorld as disposeWorld } from './internal/world.js'\n\n// --- Entity ---\nexport {\n createEntity,\n destroyEntity,\n entityExists,\n getEntityIndex,\n getEntityGeneration,\n packEntity,\n isEntity,\n} from './internal/entity.js'\n\n// --- Component ---\nexport {\n defineComponent,\n defineTag,\n defineObjectComponent,\n addComponent,\n removeComponent,\n hasComponent,\n getComponent,\n setComponent,\n} from './internal/component.js'\n\nexport { Types } from './internal/types.js'\n\n// --- Query ---\nexport {\n defineQuery,\n runQuery,\n iterQuery,\n forEachEntity,\n forEachEntityIndexed,\n enterQuery,\n exitQuery,\n queryArchetypes,\n} from './internal/query.js'\n\n// --- System ---\nexport { pipe } from './internal/pipe.js'\n\n// --- Utility ---\nexport const IS_SAB_SUPPORTED: boolean = typeof SharedArrayBuffer !== 'undefined'\n\n// --- Errors ---\nexport { EcsError } from './internal/errors.js'\n\n// --- EntityRef (ABA-safe references) ---\nexport type { EntityRef } from './ref.js'\nexport { refOf, deref, aliveRef, EntityNotAliveError } from './ref.js'\n"]}
@@ -1,2 +1,2 @@
1
- 'use strict';var chunkSRX2MZPX_cjs=require('./chunk-SRX2MZPX.cjs'),chunk5QWEV4VJ_cjs=require('./chunk-5QWEV4VJ.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=chunk5QWEV4VJ_cjs.j(n);chunkSRX2MZPX_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=chunk5QWEV4VJ_cjs.j(n),s=chunk5QWEV4VJ_cjs.J(e),i=r.componentBitFor.get(s.id);i===void 0&&(i=chunk5QWEV4VJ_cjs.q(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=chunk5QWEV4VJ_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);}}chunk5QWEV4VJ_cjs.Q({fireAdd:E,fireRemove:M,fireSet:g});chunk5QWEV4VJ_cjs.E({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;chunk5QWEV4VJ_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;chunk5QWEV4VJ_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
1
+ 'use strict';var chunk3ZLNR4JX_cjs=require('./chunk-3ZLNR4JX.cjs'),chunkZIZSWIHW_cjs=require('./chunk-ZIZSWIHW.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=chunkZIZSWIHW_cjs.j(n);chunk3ZLNR4JX_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=chunkZIZSWIHW_cjs.j(n),s=chunkZIZSWIHW_cjs.J(e),i=r.componentBitFor.get(s.id);i===void 0&&(i=chunkZIZSWIHW_cjs.q(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=chunkZIZSWIHW_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);}}chunkZIZSWIHW_cjs.Q({fireAdd:E,fireRemove:M,fireSet:g});chunkZIZSWIHW_cjs.E({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;chunkZIZSWIHW_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;chunkZIZSWIHW_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-22ICWJ5O.js';import {Q,E as E$1,g as g$1,h,j,J,q}from'./chunk-2KCN5RVK.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=j(n);e(n,e$1);let i={event:o,componentBit:-1,queryId:e$1.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=j(n),s=J(e),i=r.componentBitFor.get(s.id);i===void 0&&(i=q(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=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);}}Q({fireAdd:E,fireRemove:M,fireSet:g});E$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
1
+ import {e}from'./chunk-N5OETPTK.js';import {Q,E as E$1,g as g$1,h,j,J,q}from'./chunk-YFTCD2UG.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=j(n);e(n,e$1);let i={event:o,componentBit:-1,queryId:e$1.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=j(n),s=J(e),i=r.componentBitFor.get(s.id);i===void 0&&(i=q(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=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);}}Q({fireAdd:E,fireRemove:M,fireSet:g});E$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
@@ -1,2 +1,2 @@
1
- 'use strict';var chunk5QWEV4VJ_cjs=require('./chunk-5QWEV4VJ.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=chunk5QWEV4VJ_cjs.j(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=chunk5QWEV4VJ_cjs.j(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=chunk5QWEV4VJ_cjs.j(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 [chunk5QWEV4VJ_cjs.s(o,d,i.options)]}}return []}let e=n.outgoing.get(t);return e?e.map(o=>{let d=i.generations[o]??0;return chunk5QWEV4VJ_cjs.s(o,d,i.options)}):[]}function w(r,l,s,i){let n=chunk5QWEV4VJ_cjs.j(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)}chunk5QWEV4VJ_cjs.F((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
1
+ 'use strict';var chunkZIZSWIHW_cjs=require('./chunk-ZIZSWIHW.cjs');var m=1;function v(a){return {__kind:"relation",__id:m++,__exclusive:a?.exclusive??false,__hasData:false}}var y=v({exclusive:true});function p(a,l){let o=a.relationStorage.get(l.__id);return o||(o={rel:l,exclusive:l.__exclusive?new Int32Array(a.capacity).fill(-1):null,incoming:l.__exclusive?new Map:null,outgoing:new Map,data:new Map},a.relationStorage.set(l.__id,o),o)}function _(a,l,o){let e=a.incoming;if(!e)return;let n=e.get(o);n||(n=new Set,e.set(o,n)),n.add(l);}function f(a,l,o){let e=a.incoming;if(!e)return;let n=e.get(o);n&&(n.delete(l),n.size===0&&e.delete(o));}function S(a,l,o,e,n){let t=chunkZIZSWIHW_cjs.j(a);if(t.readOnly)throw new Error("aiecsjs: cannot mutate a read-only world");let i=p(t,o),s=l&t.options.indexMask,c=e&t.options.indexMask;if(i.exclusive){if(s>=i.exclusive.length){let u=new Int32Array(Math.max(s+1,i.exclusive.length*2)).fill(-1);u.set(i.exclusive),i.exclusive=u;}let r=i.exclusive[s]??-1;if(r>=0&&r!==c){let u=i.data.get(s);u?.delete(r),u&&u.size===0&&i.data.delete(s),f(i,s,r);}i.exclusive[s]=c,r!==c&&_(i,s,c);}else {let r=i.outgoing.get(s);r||(r=[],i.outgoing.set(s,r)),r.includes(c)||r.push(c);}if(n!==void 0){let r=i.data.get(s);r||(r=new Map,i.data.set(s,r)),r.set(c,n);}}function w(a,l,o,e){let n=chunkZIZSWIHW_cjs.j(a);if(n.readOnly)throw new Error("aiecsjs: cannot mutate a read-only world");let t=n.relationStorage.get(o.__id);if(!t)return;let i=l&n.options.indexMask,s=e&n.options.indexMask;if(t.exclusive)i<t.exclusive.length&&t.exclusive[i]===s&&(t.exclusive[i]=-1,f(t,i,s));else {let r=t.outgoing.get(i);if(r){let u=r.indexOf(s);u>=0&&r.splice(u,1),r.length===0&&t.outgoing.delete(i);}}let c=t.data.get(i);c&&(c.delete(s),c.size===0&&t.data.delete(i));}function k(a,l,o){let e=chunkZIZSWIHW_cjs.j(a),n=e.relationStorage.get(o.__id);if(!n)return [];let t=l&e.options.indexMask;if(n.exclusive){if(t<n.exclusive.length){let s=n.exclusive[t]??-1;if(s>=0){let c=e.generations[s]??0;return [chunkZIZSWIHW_cjs.s(s,c,e.options)]}}return []}let i=n.outgoing.get(t);return i?i.map(s=>{let c=e.generations[s]??0;return chunkZIZSWIHW_cjs.s(s,c,e.options)}):[]}function I(a,l,o,e){let n=chunkZIZSWIHW_cjs.j(a),t=n.relationStorage.get(o.__id);if(!t)return;let i=l&n.options.indexMask,s=e&n.options.indexMask;return t.data.get(i)?.get(s)}chunkZIZSWIHW_cjs.F((a,l)=>{let o=l&a.options.indexMask;for(let e of a.relationStorage.values()){if(e.exclusive){if(o<e.exclusive.length){let t=e.exclusive[o]??-1;t>=0&&f(e,o,t),e.exclusive[o]=-1;}let n=e.incoming?.get(o);if(n){for(let t of n)t<e.exclusive.length&&(e.exclusive[t]=-1);e.incoming?.delete(o);}}e.outgoing.delete(o);for(let[n,t]of e.outgoing){let i=t.indexOf(o);i>=0&&(t.splice(i,1),t.length===0&&e.outgoing.delete(n));}e.data.delete(o);for(let[n,t]of e.data)t.delete(o),t.size===0&&e.data.delete(n);}});exports.ChildOf=y;exports.addRelation=S;exports.defineRelation=v;exports.getRelationData=I;exports.getRelationTargets=k;exports.removeRelation=w;//# sourceMappingURL=relations.cjs.map
2
2
  //# sourceMappingURL=relations.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/relations.ts"],"names":["nextRelationId","defineRelation","options","ChildOf","getOrCreateStorage","state","rel","storage","addRelation","world","source","target","data","getWorldState","src","tgt","next","prevTgt","prevInner","list","inner","removeRelation","idx","getRelationTargets","gen","packEid","tIdx","getRelationData","registerRelationsCleanup","eid","e","i"],"mappings":"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","file":"relations.cjs","sourcesContent":["// aiecsjs/relations — entity-to-entity relations (experimental in 0.1).\n\nimport { packEid, registerRelationsCleanup } from './internal/entity.js'\nimport type { EntityId, Relation, RelationStorage, World, WorldState } from './internal/types.js'\nimport { getWorldState } from './internal/world.js'\n\nlet nextRelationId = 1\n\nexport function defineRelation<T = void>(options?: { exclusive?: boolean }): Relation<T> {\n const id = nextRelationId++\n return {\n __kind: 'relation',\n __id: id,\n __exclusive: options?.exclusive ?? false,\n __hasData: false,\n } as Relation<T>\n}\n\nexport const ChildOf: Relation = defineRelation({ exclusive: true })\n\nfunction getOrCreateStorage(state: WorldState, rel: Relation<any>): RelationStorage {\n let storage = state.relationStorage.get(rel.__id)\n if (storage) return storage\n storage = {\n rel,\n exclusive: rel.__exclusive ? new Int32Array(state.capacity).fill(-1) : null,\n outgoing: new Map<number, number[]>(),\n data: new Map<number, Map<number, unknown>>(),\n }\n state.relationStorage.set(rel.__id, storage)\n return storage\n}\n\nexport function addRelation<T>(\n world: World,\n source: EntityId,\n rel: Relation<T>,\n target: EntityId,\n data?: T,\n): void {\n const state = getWorldState(world)\n if (state.readOnly) throw new Error('aiecsjs: cannot mutate a read-only world')\n const storage = getOrCreateStorage(state, rel as Relation<any>)\n // Use raw idx as keys in relation storage so slot reuse invalidation is consistent\n const src = (source as number) & state.options.indexMask\n const tgt = (target as number) & state.options.indexMask\n\n if (storage.exclusive) {\n if (src >= storage.exclusive.length) {\n const next = new Int32Array(Math.max(src + 1, storage.exclusive.length * 2)).fill(-1)\n next.set(storage.exclusive)\n storage.exclusive = next\n }\n // Exclusive redirect: if this source already pointed at a different target,\n // drop that previous target's data entry so getRelationData stays consistent\n // with getRelationTargets, which reports only the current exclusive target.\n const prevTgt = storage.exclusive[src] ?? -1\n if (prevTgt >= 0 && prevTgt !== tgt) {\n const prevInner = storage.data.get(src)\n prevInner?.delete(prevTgt)\n if (prevInner && prevInner.size === 0) storage.data.delete(src)\n }\n storage.exclusive[src] = tgt\n } else {\n let list = storage.outgoing.get(src)\n if (!list) {\n list = []\n storage.outgoing.set(src, list)\n }\n if (!list.includes(tgt)) list.push(tgt)\n }\n\n if (data !== undefined) {\n let inner = storage.data.get(src)\n if (!inner) {\n inner = new Map<number, unknown>()\n storage.data.set(src, inner)\n }\n inner.set(tgt, data)\n }\n}\n\nexport function removeRelation(\n world: World,\n source: EntityId,\n rel: Relation,\n target: EntityId,\n): void {\n const state = getWorldState(world)\n if (state.readOnly) throw new Error('aiecsjs: cannot mutate a read-only world')\n const storage = state.relationStorage.get(rel.__id)\n if (!storage) return\n const src = (source as number) & state.options.indexMask\n const tgt = (target as number) & state.options.indexMask\n if (storage.exclusive) {\n if (src < storage.exclusive.length && storage.exclusive[src] === tgt) {\n storage.exclusive[src] = -1\n }\n } else {\n const list = storage.outgoing.get(src)\n if (list) {\n const idx = list.indexOf(tgt)\n if (idx >= 0) list.splice(idx, 1)\n if (list.length === 0) storage.outgoing.delete(src)\n }\n }\n const inner = storage.data.get(src)\n if (inner) {\n inner.delete(tgt)\n if (inner.size === 0) storage.data.delete(src)\n }\n}\n\nexport function getRelationTargets(\n world: World,\n source: EntityId,\n rel: Relation,\n): readonly EntityId[] {\n const state = getWorldState(world)\n const storage = state.relationStorage.get(rel.__id)\n if (!storage) return []\n const src = (source as number) & state.options.indexMask\n if (storage.exclusive) {\n if (src < storage.exclusive.length) {\n const tgt = storage.exclusive[src] ?? -1\n if (tgt >= 0) {\n const gen = state.generations[tgt] ?? 0\n return [packEid(tgt, gen, state.options)]\n }\n }\n return []\n }\n const list = storage.outgoing.get(src)\n if (!list) return []\n return list.map((tIdx) => {\n const gen = state.generations[tIdx] ?? 0\n return packEid(tIdx, gen, state.options)\n })\n}\n\n/**\n * Read the data payload attached to a relation edge.\n *\n * Returns the `data` value that was supplied to {@link addRelation} for the\n * `(source, rel, target)` triple, or `undefined` when:\n * - the relation has never been stored for this world,\n * - no edge from `source` to `target` via `rel` exists, or\n * - the edge was added without a data argument.\n *\n * **Slot-keying semantic (ABA caveat):** relation storage keys edges by raw\n * entity slot index (`entityId & indexMask`), not by the full packed EntityId\n * that includes the generation counter. This means that if entity A is\n * destroyed and a *different* entity B is later created in the same slot, B\n * will inherit any edges that A had — unless the destroy cleanup hook ran\n * (which it does when `destroyEntity` is called). Callers that cache a\n * source/target EntityId should validate liveness with `entityExists` before\n * calling `getRelationData` if slot recycling is a concern.\n */\nexport function getRelationData<T>(\n world: World,\n source: EntityId,\n rel: Relation<T>,\n target: EntityId,\n): T | undefined {\n const state = getWorldState(world)\n const storage = state.relationStorage.get(rel.__id)\n if (!storage) return undefined\n const src = (source as number) & state.options.indexMask\n const tgt = (target as number) & state.options.indexMask\n return storage.data.get(src)?.get(tgt) as T | undefined\n}\n\n// Cleanup hook: when an entity is destroyed, remove all relations involving it.\nregisterRelationsCleanup((state: WorldState, eid: EntityId) => {\n const e = (eid as number) & state.options.indexMask\n for (const storage of state.relationStorage.values()) {\n if (storage.exclusive) {\n if (e < storage.exclusive.length) storage.exclusive[e] = -1\n // Also clear any references TO this entity as target\n for (let i = 0; i < storage.exclusive.length; i++) {\n if (storage.exclusive[i] === e) storage.exclusive[i] = -1\n }\n }\n storage.outgoing.delete(e)\n for (const [src, list] of storage.outgoing) {\n const idx = list.indexOf(e)\n if (idx >= 0) {\n list.splice(idx, 1)\n if (list.length === 0) storage.outgoing.delete(src)\n }\n }\n // Clear data entries involving this entity. The outer key is the source\n // eid; entries whose source IS this entity drop entirely. Entries that\n // reference this entity as a target are pruned from each inner map.\n storage.data.delete(e)\n for (const [src, inner] of storage.data) {\n inner.delete(e)\n if (inner.size === 0) storage.data.delete(src)\n }\n }\n})\n"]}
1
+ {"version":3,"sources":["../src/relations.ts"],"names":["nextRelationId","defineRelation","options","ChildOf","getOrCreateStorage","state","rel","storage","linkIncoming","src","tgt","incoming","set","unlinkIncoming","addRelation","world","source","target","data","getWorldState","next","prevTgt","prevInner","list","inner","removeRelation","idx","getRelationTargets","gen","packEid","tIdx","getRelationData","registerRelationsCleanup","eid","e","cur","incomingSet"],"mappings":"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","file":"relations.cjs","sourcesContent":["// aiecsjs/relations — entity-to-entity relations (experimental in 0.1).\n\nimport { packEid, registerRelationsCleanup } from './internal/entity.js'\nimport type { EntityId, Relation, RelationStorage, World, WorldState } from './internal/types.js'\nimport { getWorldState } from './internal/world.js'\n\nlet nextRelationId = 1\n\nexport function defineRelation<T = void>(options?: { exclusive?: boolean }): Relation<T> {\n const id = nextRelationId++\n return {\n __kind: 'relation',\n __id: id,\n __exclusive: options?.exclusive ?? false,\n __hasData: false,\n } as Relation<T>\n}\n\nexport const ChildOf: Relation = defineRelation({ exclusive: true })\n\nfunction getOrCreateStorage(state: WorldState, rel: Relation<unknown>): RelationStorage {\n let storage = state.relationStorage.get(rel.__id)\n if (storage) return storage\n storage = {\n rel,\n exclusive: rel.__exclusive ? new Int32Array(state.capacity).fill(-1) : null,\n incoming: rel.__exclusive ? new Map<number, Set<number>>() : null,\n outgoing: new Map<number, number[]>(),\n data: new Map<number, Map<number, unknown>>(),\n }\n state.relationStorage.set(rel.__id, storage)\n return storage\n}\n\n// --- Exclusive reverse-index (incoming) helpers ---\n// `incoming` mirrors the `exclusive` array: incoming.get(tgt) is the set of\n// source slots whose exclusive[src] === tgt. Every place that writes a non-`-1`\n// value into `exclusive` must call `linkIncoming`, and every place that clears a\n// slot (sets it back to -1) must call `unlinkIncoming`, so the reverse index\n// never drifts from the forward array.\n\nfunction linkIncoming(storage: RelationStorage, src: number, tgt: number): void {\n const incoming = storage.incoming\n if (!incoming) return\n let set = incoming.get(tgt)\n if (!set) {\n set = new Set<number>()\n incoming.set(tgt, set)\n }\n set.add(src)\n}\n\nfunction unlinkIncoming(storage: RelationStorage, src: number, tgt: number): void {\n const incoming = storage.incoming\n if (!incoming) return\n const set = incoming.get(tgt)\n if (!set) return\n set.delete(src)\n if (set.size === 0) incoming.delete(tgt)\n}\n\nexport function addRelation<T>(\n world: World,\n source: EntityId,\n rel: Relation<T>,\n target: EntityId,\n data?: T,\n): void {\n const state = getWorldState(world)\n if (state.readOnly) throw new Error('aiecsjs: cannot mutate a read-only world')\n const storage = getOrCreateStorage(state, rel as Relation<unknown>)\n // Use raw idx as keys in relation storage so slot reuse invalidation is consistent\n const src = (source as number) & state.options.indexMask\n const tgt = (target as number) & state.options.indexMask\n\n if (storage.exclusive) {\n if (src >= storage.exclusive.length) {\n const next = new Int32Array(Math.max(src + 1, storage.exclusive.length * 2)).fill(-1)\n next.set(storage.exclusive)\n storage.exclusive = next\n }\n // Exclusive redirect: if this source already pointed at a different target,\n // drop that previous target's data entry so getRelationData stays consistent\n // with getRelationTargets, which reports only the current exclusive target.\n const prevTgt = storage.exclusive[src] ?? -1\n if (prevTgt >= 0 && prevTgt !== tgt) {\n const prevInner = storage.data.get(src)\n prevInner?.delete(prevTgt)\n if (prevInner && prevInner.size === 0) storage.data.delete(src)\n unlinkIncoming(storage, src, prevTgt)\n }\n storage.exclusive[src] = tgt\n if (prevTgt !== tgt) linkIncoming(storage, src, tgt)\n } else {\n let list = storage.outgoing.get(src)\n if (!list) {\n list = []\n storage.outgoing.set(src, list)\n }\n if (!list.includes(tgt)) list.push(tgt)\n }\n\n if (data !== undefined) {\n let inner = storage.data.get(src)\n if (!inner) {\n inner = new Map<number, unknown>()\n storage.data.set(src, inner)\n }\n inner.set(tgt, data)\n }\n}\n\nexport function removeRelation(\n world: World,\n source: EntityId,\n rel: Relation,\n target: EntityId,\n): void {\n const state = getWorldState(world)\n if (state.readOnly) throw new Error('aiecsjs: cannot mutate a read-only world')\n const storage = state.relationStorage.get(rel.__id)\n if (!storage) return\n const src = (source as number) & state.options.indexMask\n const tgt = (target as number) & state.options.indexMask\n if (storage.exclusive) {\n if (src < storage.exclusive.length && storage.exclusive[src] === tgt) {\n storage.exclusive[src] = -1\n unlinkIncoming(storage, src, tgt)\n }\n } else {\n const list = storage.outgoing.get(src)\n if (list) {\n const idx = list.indexOf(tgt)\n if (idx >= 0) list.splice(idx, 1)\n if (list.length === 0) storage.outgoing.delete(src)\n }\n }\n const inner = storage.data.get(src)\n if (inner) {\n inner.delete(tgt)\n if (inner.size === 0) storage.data.delete(src)\n }\n}\n\nexport function getRelationTargets(\n world: World,\n source: EntityId,\n rel: Relation,\n): readonly EntityId[] {\n const state = getWorldState(world)\n const storage = state.relationStorage.get(rel.__id)\n if (!storage) return []\n const src = (source as number) & state.options.indexMask\n if (storage.exclusive) {\n if (src < storage.exclusive.length) {\n const tgt = storage.exclusive[src] ?? -1\n if (tgt >= 0) {\n const gen = state.generations[tgt] ?? 0\n return [packEid(tgt, gen, state.options)]\n }\n }\n return []\n }\n const list = storage.outgoing.get(src)\n if (!list) return []\n return list.map((tIdx) => {\n const gen = state.generations[tIdx] ?? 0\n return packEid(tIdx, gen, state.options)\n })\n}\n\n/**\n * Read the data payload attached to a relation edge.\n *\n * Returns the `data` value that was supplied to {@link addRelation} for the\n * `(source, rel, target)` triple, or `undefined` when:\n * - the relation has never been stored for this world,\n * - no edge from `source` to `target` via `rel` exists, or\n * - the edge was added without a data argument.\n *\n * **Slot-keying semantic (ABA caveat):** relation storage keys edges by raw\n * entity slot index (`entityId & indexMask`), not by the full packed EntityId\n * that includes the generation counter. This means that if entity A is\n * destroyed and a *different* entity B is later created in the same slot, B\n * will inherit any edges that A had — unless the destroy cleanup hook ran\n * (which it does when `destroyEntity` is called). Callers that cache a\n * source/target EntityId should validate liveness with `entityExists` before\n * calling `getRelationData` if slot recycling is a concern.\n */\nexport function getRelationData<T>(\n world: World,\n source: EntityId,\n rel: Relation<T>,\n target: EntityId,\n): T | undefined {\n const state = getWorldState(world)\n const storage = state.relationStorage.get(rel.__id)\n if (!storage) return undefined\n const src = (source as number) & state.options.indexMask\n const tgt = (target as number) & state.options.indexMask\n return storage.data.get(src)?.get(tgt) as T | undefined\n}\n\n// Cleanup hook: when an entity is destroyed, remove all relations involving it.\nregisterRelationsCleanup((state: WorldState, eid: EntityId) => {\n const e = (eid as number) & state.options.indexMask\n for (const storage of state.relationStorage.values()) {\n if (storage.exclusive) {\n // `e` as a SOURCE: clear its own exclusive slot and unlink it from whatever\n // target it currently points at, so the reverse index stays consistent.\n if (e < storage.exclusive.length) {\n const cur = storage.exclusive[e] ?? -1\n if (cur >= 0) unlinkIncoming(storage, e, cur)\n storage.exclusive[e] = -1\n }\n // `e` as a TARGET: clear every source that points at it. Use the reverse\n // index to touch only the actual incoming sources (O(incoming)) instead of\n // scanning the entire `exclusive` capacity (O(capacity)) — the bounded-cost\n // path for large sparse relation tables. Behaviour is identical to the old\n // full scan: every src with exclusive[src] === e is reset to -1.\n const incomingSet = storage.incoming?.get(e)\n if (incomingSet) {\n for (const src of incomingSet) {\n if (src < storage.exclusive.length) storage.exclusive[src] = -1\n }\n storage.incoming?.delete(e)\n }\n }\n storage.outgoing.delete(e)\n for (const [src, list] of storage.outgoing) {\n const idx = list.indexOf(e)\n if (idx >= 0) {\n list.splice(idx, 1)\n if (list.length === 0) storage.outgoing.delete(src)\n }\n }\n // Clear data entries involving this entity. The outer key is the source\n // eid; entries whose source IS this entity drop entirely. Entries that\n // reference this entity as a target are pruned from each inner map.\n storage.data.delete(e)\n for (const [src, inner] of storage.data) {\n inner.delete(e)\n if (inner.size === 0) storage.data.delete(src)\n }\n }\n})\n"]}
package/dist/relations.js CHANGED
@@ -1,2 +1,2 @@
1
- import {F,j,s}from'./chunk-2KCN5RVK.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,n){let t=j(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=j(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$1){let i=j(r),n=i.relationStorage.get(s$1.__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 [s(o,d,i.options)]}}return []}let e=n.outgoing.get(t);return e?e.map(o=>{let d=i.generations[o]??0;return s(o,d,i.options)}):[]}function w(r,l,s,i){let n=j(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)}F((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
1
+ import {F,j,s}from'./chunk-YFTCD2UG.js';var m=1;function v(a){return {__kind:"relation",__id:m++,__exclusive:a?.exclusive??false,__hasData:false}}var y=v({exclusive:true});function p(a,l){let o=a.relationStorage.get(l.__id);return o||(o={rel:l,exclusive:l.__exclusive?new Int32Array(a.capacity).fill(-1):null,incoming:l.__exclusive?new Map:null,outgoing:new Map,data:new Map},a.relationStorage.set(l.__id,o),o)}function _(a,l,o){let e=a.incoming;if(!e)return;let n=e.get(o);n||(n=new Set,e.set(o,n)),n.add(l);}function f(a,l,o){let e=a.incoming;if(!e)return;let n=e.get(o);n&&(n.delete(l),n.size===0&&e.delete(o));}function S(a,l,o,e,n){let t=j(a);if(t.readOnly)throw new Error("aiecsjs: cannot mutate a read-only world");let i=p(t,o),s=l&t.options.indexMask,c=e&t.options.indexMask;if(i.exclusive){if(s>=i.exclusive.length){let u=new Int32Array(Math.max(s+1,i.exclusive.length*2)).fill(-1);u.set(i.exclusive),i.exclusive=u;}let r=i.exclusive[s]??-1;if(r>=0&&r!==c){let u=i.data.get(s);u?.delete(r),u&&u.size===0&&i.data.delete(s),f(i,s,r);}i.exclusive[s]=c,r!==c&&_(i,s,c);}else {let r=i.outgoing.get(s);r||(r=[],i.outgoing.set(s,r)),r.includes(c)||r.push(c);}if(n!==void 0){let r=i.data.get(s);r||(r=new Map,i.data.set(s,r)),r.set(c,n);}}function w(a,l,o,e){let n=j(a);if(n.readOnly)throw new Error("aiecsjs: cannot mutate a read-only world");let t=n.relationStorage.get(o.__id);if(!t)return;let i=l&n.options.indexMask,s=e&n.options.indexMask;if(t.exclusive)i<t.exclusive.length&&t.exclusive[i]===s&&(t.exclusive[i]=-1,f(t,i,s));else {let r=t.outgoing.get(i);if(r){let u=r.indexOf(s);u>=0&&r.splice(u,1),r.length===0&&t.outgoing.delete(i);}}let c=t.data.get(i);c&&(c.delete(s),c.size===0&&t.data.delete(i));}function k(a,l,o){let e=j(a),n=e.relationStorage.get(o.__id);if(!n)return [];let t=l&e.options.indexMask;if(n.exclusive){if(t<n.exclusive.length){let s$1=n.exclusive[t]??-1;if(s$1>=0){let c=e.generations[s$1]??0;return [s(s$1,c,e.options)]}}return []}let i=n.outgoing.get(t);return i?i.map(s$1=>{let c=e.generations[s$1]??0;return s(s$1,c,e.options)}):[]}function I(a,l,o,e){let n=j(a),t=n.relationStorage.get(o.__id);if(!t)return;let i=l&n.options.indexMask,s=e&n.options.indexMask;return t.data.get(i)?.get(s)}F((a,l)=>{let o=l&a.options.indexMask;for(let e of a.relationStorage.values()){if(e.exclusive){if(o<e.exclusive.length){let t=e.exclusive[o]??-1;t>=0&&f(e,o,t),e.exclusive[o]=-1;}let n=e.incoming?.get(o);if(n){for(let t of n)t<e.exclusive.length&&(e.exclusive[t]=-1);e.incoming?.delete(o);}}e.outgoing.delete(o);for(let[n,t]of e.outgoing){let i=t.indexOf(o);i>=0&&(t.splice(i,1),t.length===0&&e.outgoing.delete(n));}e.data.delete(o);for(let[n,t]of e.data)t.delete(o),t.size===0&&e.data.delete(n);}});export{y as ChildOf,S as addRelation,v as defineRelation,I as getRelationData,k as getRelationTargets,w as removeRelation};//# sourceMappingURL=relations.js.map
2
2
  //# sourceMappingURL=relations.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/relations.ts"],"names":["nextRelationId","defineRelation","options","ChildOf","getOrCreateStorage","state","rel","storage","addRelation","world","source","target","data","getWorldState","src","tgt","next","prevTgt","prevInner","list","inner","removeRelation","idx","getRelationTargets","gen","packEid","tIdx","getRelationData","registerRelationsCleanup","eid","e","i"],"mappings":"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","file":"relations.js","sourcesContent":["// aiecsjs/relations — entity-to-entity relations (experimental in 0.1).\n\nimport { packEid, registerRelationsCleanup } from './internal/entity.js'\nimport type { EntityId, Relation, RelationStorage, World, WorldState } from './internal/types.js'\nimport { getWorldState } from './internal/world.js'\n\nlet nextRelationId = 1\n\nexport function defineRelation<T = void>(options?: { exclusive?: boolean }): Relation<T> {\n const id = nextRelationId++\n return {\n __kind: 'relation',\n __id: id,\n __exclusive: options?.exclusive ?? false,\n __hasData: false,\n } as Relation<T>\n}\n\nexport const ChildOf: Relation = defineRelation({ exclusive: true })\n\nfunction getOrCreateStorage(state: WorldState, rel: Relation<any>): RelationStorage {\n let storage = state.relationStorage.get(rel.__id)\n if (storage) return storage\n storage = {\n rel,\n exclusive: rel.__exclusive ? new Int32Array(state.capacity).fill(-1) : null,\n outgoing: new Map<number, number[]>(),\n data: new Map<number, Map<number, unknown>>(),\n }\n state.relationStorage.set(rel.__id, storage)\n return storage\n}\n\nexport function addRelation<T>(\n world: World,\n source: EntityId,\n rel: Relation<T>,\n target: EntityId,\n data?: T,\n): void {\n const state = getWorldState(world)\n if (state.readOnly) throw new Error('aiecsjs: cannot mutate a read-only world')\n const storage = getOrCreateStorage(state, rel as Relation<any>)\n // Use raw idx as keys in relation storage so slot reuse invalidation is consistent\n const src = (source as number) & state.options.indexMask\n const tgt = (target as number) & state.options.indexMask\n\n if (storage.exclusive) {\n if (src >= storage.exclusive.length) {\n const next = new Int32Array(Math.max(src + 1, storage.exclusive.length * 2)).fill(-1)\n next.set(storage.exclusive)\n storage.exclusive = next\n }\n // Exclusive redirect: if this source already pointed at a different target,\n // drop that previous target's data entry so getRelationData stays consistent\n // with getRelationTargets, which reports only the current exclusive target.\n const prevTgt = storage.exclusive[src] ?? -1\n if (prevTgt >= 0 && prevTgt !== tgt) {\n const prevInner = storage.data.get(src)\n prevInner?.delete(prevTgt)\n if (prevInner && prevInner.size === 0) storage.data.delete(src)\n }\n storage.exclusive[src] = tgt\n } else {\n let list = storage.outgoing.get(src)\n if (!list) {\n list = []\n storage.outgoing.set(src, list)\n }\n if (!list.includes(tgt)) list.push(tgt)\n }\n\n if (data !== undefined) {\n let inner = storage.data.get(src)\n if (!inner) {\n inner = new Map<number, unknown>()\n storage.data.set(src, inner)\n }\n inner.set(tgt, data)\n }\n}\n\nexport function removeRelation(\n world: World,\n source: EntityId,\n rel: Relation,\n target: EntityId,\n): void {\n const state = getWorldState(world)\n if (state.readOnly) throw new Error('aiecsjs: cannot mutate a read-only world')\n const storage = state.relationStorage.get(rel.__id)\n if (!storage) return\n const src = (source as number) & state.options.indexMask\n const tgt = (target as number) & state.options.indexMask\n if (storage.exclusive) {\n if (src < storage.exclusive.length && storage.exclusive[src] === tgt) {\n storage.exclusive[src] = -1\n }\n } else {\n const list = storage.outgoing.get(src)\n if (list) {\n const idx = list.indexOf(tgt)\n if (idx >= 0) list.splice(idx, 1)\n if (list.length === 0) storage.outgoing.delete(src)\n }\n }\n const inner = storage.data.get(src)\n if (inner) {\n inner.delete(tgt)\n if (inner.size === 0) storage.data.delete(src)\n }\n}\n\nexport function getRelationTargets(\n world: World,\n source: EntityId,\n rel: Relation,\n): readonly EntityId[] {\n const state = getWorldState(world)\n const storage = state.relationStorage.get(rel.__id)\n if (!storage) return []\n const src = (source as number) & state.options.indexMask\n if (storage.exclusive) {\n if (src < storage.exclusive.length) {\n const tgt = storage.exclusive[src] ?? -1\n if (tgt >= 0) {\n const gen = state.generations[tgt] ?? 0\n return [packEid(tgt, gen, state.options)]\n }\n }\n return []\n }\n const list = storage.outgoing.get(src)\n if (!list) return []\n return list.map((tIdx) => {\n const gen = state.generations[tIdx] ?? 0\n return packEid(tIdx, gen, state.options)\n })\n}\n\n/**\n * Read the data payload attached to a relation edge.\n *\n * Returns the `data` value that was supplied to {@link addRelation} for the\n * `(source, rel, target)` triple, or `undefined` when:\n * - the relation has never been stored for this world,\n * - no edge from `source` to `target` via `rel` exists, or\n * - the edge was added without a data argument.\n *\n * **Slot-keying semantic (ABA caveat):** relation storage keys edges by raw\n * entity slot index (`entityId & indexMask`), not by the full packed EntityId\n * that includes the generation counter. This means that if entity A is\n * destroyed and a *different* entity B is later created in the same slot, B\n * will inherit any edges that A had — unless the destroy cleanup hook ran\n * (which it does when `destroyEntity` is called). Callers that cache a\n * source/target EntityId should validate liveness with `entityExists` before\n * calling `getRelationData` if slot recycling is a concern.\n */\nexport function getRelationData<T>(\n world: World,\n source: EntityId,\n rel: Relation<T>,\n target: EntityId,\n): T | undefined {\n const state = getWorldState(world)\n const storage = state.relationStorage.get(rel.__id)\n if (!storage) return undefined\n const src = (source as number) & state.options.indexMask\n const tgt = (target as number) & state.options.indexMask\n return storage.data.get(src)?.get(tgt) as T | undefined\n}\n\n// Cleanup hook: when an entity is destroyed, remove all relations involving it.\nregisterRelationsCleanup((state: WorldState, eid: EntityId) => {\n const e = (eid as number) & state.options.indexMask\n for (const storage of state.relationStorage.values()) {\n if (storage.exclusive) {\n if (e < storage.exclusive.length) storage.exclusive[e] = -1\n // Also clear any references TO this entity as target\n for (let i = 0; i < storage.exclusive.length; i++) {\n if (storage.exclusive[i] === e) storage.exclusive[i] = -1\n }\n }\n storage.outgoing.delete(e)\n for (const [src, list] of storage.outgoing) {\n const idx = list.indexOf(e)\n if (idx >= 0) {\n list.splice(idx, 1)\n if (list.length === 0) storage.outgoing.delete(src)\n }\n }\n // Clear data entries involving this entity. The outer key is the source\n // eid; entries whose source IS this entity drop entirely. Entries that\n // reference this entity as a target are pruned from each inner map.\n storage.data.delete(e)\n for (const [src, inner] of storage.data) {\n inner.delete(e)\n if (inner.size === 0) storage.data.delete(src)\n }\n }\n})\n"]}
1
+ {"version":3,"sources":["../src/relations.ts"],"names":["nextRelationId","defineRelation","options","ChildOf","getOrCreateStorage","state","rel","storage","linkIncoming","src","tgt","incoming","set","unlinkIncoming","addRelation","world","source","target","data","getWorldState","next","prevTgt","prevInner","list","inner","removeRelation","idx","getRelationTargets","gen","packEid","tIdx","getRelationData","registerRelationsCleanup","eid","e","cur","incomingSet"],"mappings":"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","file":"relations.js","sourcesContent":["// aiecsjs/relations — entity-to-entity relations (experimental in 0.1).\n\nimport { packEid, registerRelationsCleanup } from './internal/entity.js'\nimport type { EntityId, Relation, RelationStorage, World, WorldState } from './internal/types.js'\nimport { getWorldState } from './internal/world.js'\n\nlet nextRelationId = 1\n\nexport function defineRelation<T = void>(options?: { exclusive?: boolean }): Relation<T> {\n const id = nextRelationId++\n return {\n __kind: 'relation',\n __id: id,\n __exclusive: options?.exclusive ?? false,\n __hasData: false,\n } as Relation<T>\n}\n\nexport const ChildOf: Relation = defineRelation({ exclusive: true })\n\nfunction getOrCreateStorage(state: WorldState, rel: Relation<unknown>): RelationStorage {\n let storage = state.relationStorage.get(rel.__id)\n if (storage) return storage\n storage = {\n rel,\n exclusive: rel.__exclusive ? new Int32Array(state.capacity).fill(-1) : null,\n incoming: rel.__exclusive ? new Map<number, Set<number>>() : null,\n outgoing: new Map<number, number[]>(),\n data: new Map<number, Map<number, unknown>>(),\n }\n state.relationStorage.set(rel.__id, storage)\n return storage\n}\n\n// --- Exclusive reverse-index (incoming) helpers ---\n// `incoming` mirrors the `exclusive` array: incoming.get(tgt) is the set of\n// source slots whose exclusive[src] === tgt. Every place that writes a non-`-1`\n// value into `exclusive` must call `linkIncoming`, and every place that clears a\n// slot (sets it back to -1) must call `unlinkIncoming`, so the reverse index\n// never drifts from the forward array.\n\nfunction linkIncoming(storage: RelationStorage, src: number, tgt: number): void {\n const incoming = storage.incoming\n if (!incoming) return\n let set = incoming.get(tgt)\n if (!set) {\n set = new Set<number>()\n incoming.set(tgt, set)\n }\n set.add(src)\n}\n\nfunction unlinkIncoming(storage: RelationStorage, src: number, tgt: number): void {\n const incoming = storage.incoming\n if (!incoming) return\n const set = incoming.get(tgt)\n if (!set) return\n set.delete(src)\n if (set.size === 0) incoming.delete(tgt)\n}\n\nexport function addRelation<T>(\n world: World,\n source: EntityId,\n rel: Relation<T>,\n target: EntityId,\n data?: T,\n): void {\n const state = getWorldState(world)\n if (state.readOnly) throw new Error('aiecsjs: cannot mutate a read-only world')\n const storage = getOrCreateStorage(state, rel as Relation<unknown>)\n // Use raw idx as keys in relation storage so slot reuse invalidation is consistent\n const src = (source as number) & state.options.indexMask\n const tgt = (target as number) & state.options.indexMask\n\n if (storage.exclusive) {\n if (src >= storage.exclusive.length) {\n const next = new Int32Array(Math.max(src + 1, storage.exclusive.length * 2)).fill(-1)\n next.set(storage.exclusive)\n storage.exclusive = next\n }\n // Exclusive redirect: if this source already pointed at a different target,\n // drop that previous target's data entry so getRelationData stays consistent\n // with getRelationTargets, which reports only the current exclusive target.\n const prevTgt = storage.exclusive[src] ?? -1\n if (prevTgt >= 0 && prevTgt !== tgt) {\n const prevInner = storage.data.get(src)\n prevInner?.delete(prevTgt)\n if (prevInner && prevInner.size === 0) storage.data.delete(src)\n unlinkIncoming(storage, src, prevTgt)\n }\n storage.exclusive[src] = tgt\n if (prevTgt !== tgt) linkIncoming(storage, src, tgt)\n } else {\n let list = storage.outgoing.get(src)\n if (!list) {\n list = []\n storage.outgoing.set(src, list)\n }\n if (!list.includes(tgt)) list.push(tgt)\n }\n\n if (data !== undefined) {\n let inner = storage.data.get(src)\n if (!inner) {\n inner = new Map<number, unknown>()\n storage.data.set(src, inner)\n }\n inner.set(tgt, data)\n }\n}\n\nexport function removeRelation(\n world: World,\n source: EntityId,\n rel: Relation,\n target: EntityId,\n): void {\n const state = getWorldState(world)\n if (state.readOnly) throw new Error('aiecsjs: cannot mutate a read-only world')\n const storage = state.relationStorage.get(rel.__id)\n if (!storage) return\n const src = (source as number) & state.options.indexMask\n const tgt = (target as number) & state.options.indexMask\n if (storage.exclusive) {\n if (src < storage.exclusive.length && storage.exclusive[src] === tgt) {\n storage.exclusive[src] = -1\n unlinkIncoming(storage, src, tgt)\n }\n } else {\n const list = storage.outgoing.get(src)\n if (list) {\n const idx = list.indexOf(tgt)\n if (idx >= 0) list.splice(idx, 1)\n if (list.length === 0) storage.outgoing.delete(src)\n }\n }\n const inner = storage.data.get(src)\n if (inner) {\n inner.delete(tgt)\n if (inner.size === 0) storage.data.delete(src)\n }\n}\n\nexport function getRelationTargets(\n world: World,\n source: EntityId,\n rel: Relation,\n): readonly EntityId[] {\n const state = getWorldState(world)\n const storage = state.relationStorage.get(rel.__id)\n if (!storage) return []\n const src = (source as number) & state.options.indexMask\n if (storage.exclusive) {\n if (src < storage.exclusive.length) {\n const tgt = storage.exclusive[src] ?? -1\n if (tgt >= 0) {\n const gen = state.generations[tgt] ?? 0\n return [packEid(tgt, gen, state.options)]\n }\n }\n return []\n }\n const list = storage.outgoing.get(src)\n if (!list) return []\n return list.map((tIdx) => {\n const gen = state.generations[tIdx] ?? 0\n return packEid(tIdx, gen, state.options)\n })\n}\n\n/**\n * Read the data payload attached to a relation edge.\n *\n * Returns the `data` value that was supplied to {@link addRelation} for the\n * `(source, rel, target)` triple, or `undefined` when:\n * - the relation has never been stored for this world,\n * - no edge from `source` to `target` via `rel` exists, or\n * - the edge was added without a data argument.\n *\n * **Slot-keying semantic (ABA caveat):** relation storage keys edges by raw\n * entity slot index (`entityId & indexMask`), not by the full packed EntityId\n * that includes the generation counter. This means that if entity A is\n * destroyed and a *different* entity B is later created in the same slot, B\n * will inherit any edges that A had — unless the destroy cleanup hook ran\n * (which it does when `destroyEntity` is called). Callers that cache a\n * source/target EntityId should validate liveness with `entityExists` before\n * calling `getRelationData` if slot recycling is a concern.\n */\nexport function getRelationData<T>(\n world: World,\n source: EntityId,\n rel: Relation<T>,\n target: EntityId,\n): T | undefined {\n const state = getWorldState(world)\n const storage = state.relationStorage.get(rel.__id)\n if (!storage) return undefined\n const src = (source as number) & state.options.indexMask\n const tgt = (target as number) & state.options.indexMask\n return storage.data.get(src)?.get(tgt) as T | undefined\n}\n\n// Cleanup hook: when an entity is destroyed, remove all relations involving it.\nregisterRelationsCleanup((state: WorldState, eid: EntityId) => {\n const e = (eid as number) & state.options.indexMask\n for (const storage of state.relationStorage.values()) {\n if (storage.exclusive) {\n // `e` as a SOURCE: clear its own exclusive slot and unlink it from whatever\n // target it currently points at, so the reverse index stays consistent.\n if (e < storage.exclusive.length) {\n const cur = storage.exclusive[e] ?? -1\n if (cur >= 0) unlinkIncoming(storage, e, cur)\n storage.exclusive[e] = -1\n }\n // `e` as a TARGET: clear every source that points at it. Use the reverse\n // index to touch only the actual incoming sources (O(incoming)) instead of\n // scanning the entire `exclusive` capacity (O(capacity)) — the bounded-cost\n // path for large sparse relation tables. Behaviour is identical to the old\n // full scan: every src with exclusive[src] === e is reset to -1.\n const incomingSet = storage.incoming?.get(e)\n if (incomingSet) {\n for (const src of incomingSet) {\n if (src < storage.exclusive.length) storage.exclusive[src] = -1\n }\n storage.incoming?.delete(e)\n }\n }\n storage.outgoing.delete(e)\n for (const [src, list] of storage.outgoing) {\n const idx = list.indexOf(e)\n if (idx >= 0) {\n list.splice(idx, 1)\n if (list.length === 0) storage.outgoing.delete(src)\n }\n }\n // Clear data entries involving this entity. The outer key is the source\n // eid; entries whose source IS this entity drop entirely. Entries that\n // reference this entity as a target are pruned from each inner map.\n storage.data.delete(e)\n for (const [src, inner] of storage.data) {\n inner.delete(e)\n if (inner.size === 0) storage.data.delete(src)\n }\n }\n})\n"]}
@@ -1,2 +1,2 @@
1
- 'use strict';var chunkP5GW7GKY_cjs=require('./chunk-P5GW7GKY.cjs');require('./chunk-5QWEV4VJ.cjs');Object.defineProperty(exports,"createDeltaSerializer",{enumerable:true,get:function(){return chunkP5GW7GKY_cjs.e}});Object.defineProperty(exports,"deserializeWorld",{enumerable:true,get:function(){return chunkP5GW7GKY_cjs.b}});Object.defineProperty(exports,"fromJSON",{enumerable:true,get:function(){return chunkP5GW7GKY_cjs.d}});Object.defineProperty(exports,"serializeWorld",{enumerable:true,get:function(){return chunkP5GW7GKY_cjs.a}});Object.defineProperty(exports,"toJSON",{enumerable:true,get:function(){return chunkP5GW7GKY_cjs.c}});//# sourceMappingURL=serialize.cjs.map
1
+ 'use strict';var chunk3FV6UMUS_cjs=require('./chunk-3FV6UMUS.cjs');require('./chunk-ZIZSWIHW.cjs');Object.defineProperty(exports,"createDeltaSerializer",{enumerable:true,get:function(){return chunk3FV6UMUS_cjs.e}});Object.defineProperty(exports,"deserializeWorld",{enumerable:true,get:function(){return chunk3FV6UMUS_cjs.b}});Object.defineProperty(exports,"fromJSON",{enumerable:true,get:function(){return chunk3FV6UMUS_cjs.d}});Object.defineProperty(exports,"serializeWorld",{enumerable:true,get:function(){return chunk3FV6UMUS_cjs.a}});Object.defineProperty(exports,"toJSON",{enumerable:true,get:function(){return chunk3FV6UMUS_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-AGWUE6JB.js';import'./chunk-2KCN5RVK.js';//# sourceMappingURL=serialize.js.map
1
+ export{e as createDeltaSerializer,b as deserializeWorld,d as fromJSON,a as serializeWorld,c as toJSON}from'./chunk-TNZMD7E5.js';import'./chunk-YFTCD2UG.js';//# sourceMappingURL=serialize.js.map
2
2
  //# sourceMappingURL=serialize.js.map
package/dist/worker.cjs CHANGED
@@ -1,2 +1,2 @@
1
- 'use strict';var chunkP5GW7GKY_cjs=require('./chunk-P5GW7GKY.cjs'),chunk5QWEV4VJ_cjs=require('./chunk-5QWEV4VJ.cjs');var l=1095320899;function W(r){let e=chunk5QWEV4VJ_cjs.j(r),o=chunkP5GW7GKY_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 chunkP5GW7GKY_cjs.b(e)}function S(r,e){let o=new Uint8Array(r),t=chunkP5GW7GKY_cjs.b(o);if(e?.readOnly){let n=chunk5QWEV4VJ_cjs.j(t);n.readOnly=true;}return t}function B(r){chunk5QWEV4VJ_cjs.k(r.id)&&chunk5QWEV4VJ_cjs.m(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:chunk5QWEV4VJ_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=B;exports.transferableSnapshot=W;//# sourceMappingURL=worker.cjs.map
1
+ 'use strict';var chunk3FV6UMUS_cjs=require('./chunk-3FV6UMUS.cjs'),chunkZIZSWIHW_cjs=require('./chunk-ZIZSWIHW.cjs');var l=1095320899;function W(r){let e=chunkZIZSWIHW_cjs.j(r),o=chunk3FV6UMUS_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 chunk3FV6UMUS_cjs.b(e)}function S(r,e){let o=new Uint8Array(r),t=chunk3FV6UMUS_cjs.b(o);if(e?.readOnly){let n=chunkZIZSWIHW_cjs.j(t);n.readOnly=true;}return t}function B(r){chunkZIZSWIHW_cjs.k(r.id)&&chunkZIZSWIHW_cjs.m(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:chunkZIZSWIHW_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=B;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-AGWUE6JB.js';import {j,k,m as m$1,a as a$1}from'./chunk-2KCN5RVK.js';var l=1095320899;function W(r){let e=j(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=j(t);n.readOnly=true;}return t}function B(r){k(r.id)&&m$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,B as detachWorld,W as transferableSnapshot};//# sourceMappingURL=worker.js.map
1
+ import {a,b as b$1}from'./chunk-TNZMD7E5.js';import {j,k,m as m$1,a as a$1}from'./chunk-YFTCD2UG.js';var l=1095320899;function W(r){let e=j(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=j(t);n.readOnly=true;}return t}function B(r){k(r.id)&&m$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,B as detachWorld,W as transferableSnapshot};//# sourceMappingURL=worker.js.map
2
2
  //# sourceMappingURL=worker.js.map