aiecsjs 0.5.2 → 0.5.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/README.md +65 -28
  2. package/README_ZHTW.md +65 -28
  3. package/api.json +31 -2
  4. package/dist/chunk-COORLIYF.js +2 -0
  5. package/dist/chunk-COORLIYF.js.map +1 -0
  6. package/dist/{chunk-RNWXP3GU.cjs → chunk-D4VGTV76.cjs} +2 -2
  7. package/dist/{chunk-RNWXP3GU.cjs.map → chunk-D4VGTV76.cjs.map} +1 -1
  8. package/dist/{chunk-2E3GMRF5.js → chunk-FZWRVGAA.js} +2 -2
  9. package/dist/{chunk-2E3GMRF5.js.map → chunk-FZWRVGAA.js.map} +1 -1
  10. package/dist/chunk-NCYUF4YW.cjs +2 -0
  11. package/dist/chunk-NCYUF4YW.cjs.map +1 -0
  12. package/dist/{chunk-WZMRKSK5.js → chunk-UAJNL4VY.js} +2 -2
  13. package/dist/{chunk-WZMRKSK5.js.map → chunk-UAJNL4VY.js.map} +1 -1
  14. package/dist/{chunk-FEICW2CS.cjs → chunk-XBCASW7K.cjs} +2 -2
  15. package/dist/{chunk-FEICW2CS.cjs.map → chunk-XBCASW7K.cjs.map} +1 -1
  16. package/dist/commands.cjs +1 -1
  17. package/dist/commands.js +1 -1
  18. package/dist/index.cjs +1 -1
  19. package/dist/index.cjs.map +1 -1
  20. package/dist/index.d.cts +18 -2
  21. package/dist/index.d.ts +18 -2
  22. package/dist/index.js +1 -1
  23. package/dist/index.js.map +1 -1
  24. package/dist/observers.cjs +1 -1
  25. package/dist/observers.js +1 -1
  26. package/dist/relations.cjs +1 -1
  27. package/dist/relations.js +1 -1
  28. package/dist/serialize.cjs +1 -1
  29. package/dist/serialize.js +1 -1
  30. package/dist/worker.cjs +1 -1
  31. package/dist/worker.js +1 -1
  32. package/llms-full.txt +74 -31
  33. package/package.json +1 -1
  34. package/dist/chunk-P2KTJ2NQ.cjs +0 -2
  35. package/dist/chunk-P2KTJ2NQ.cjs.map +0 -1
  36. package/dist/chunk-Q55M3YEQ.js +0 -2
  37. package/dist/chunk-Q55M3YEQ.js.map +0 -1
@@ -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 buffer?: SharedArrayBuffer\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 buffer: SharedArrayBuffer\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 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// --- 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 buffer?: SharedArrayBuffer\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 buffer: SharedArrayBuffer\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// --- 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-BGEeHad-.cjs';
2
2
  export { d as ComponentView, g as SoAColumns, i as SoAFieldDecl, j as SoAFieldType, n as Types } from './types-BGEeHad-.cjs';
3
3
 
4
- declare const VERSION = "0.5.2";
4
+ declare const VERSION = "0.5.3";
5
5
 
6
6
  declare function createWorld(options?: WorldOptions): World;
7
7
  declare function destroyWorld(world: World): void;
@@ -53,6 +53,22 @@ declare function queryArchetypes(world: World, query: Query): readonly Archetype
53
53
  declare function runQuery(world: World, query: Query): readonly EntityId[];
54
54
  declare function iterQuery(world: World, query: Query): IterableIterator<EntityId>;
55
55
  declare function forEachEntity(world: World, query: Query, fn: (eid: EntityId, ...cols: any[]) => void): void;
56
+ /**
57
+ * Like {@link forEachEntity}, but yields the masked column index `i` alongside
58
+ * the packed `EntityId`. The callback signature is `(e, i, ...cols)`:
59
+ *
60
+ * - `e` — the packed EntityId (carries the generation in its high bits). Use
61
+ * it for in-loop `destroyEntity` / `hasComponent` / `refOf`, exactly as with
62
+ * `forEachEntity`.
63
+ * - `i` — `e & indexMask`, the raw slot index. This is the **correct subscript**
64
+ * for SoA column views (`pos.x[i]`), and stays correct after a slot is
65
+ * recycled — where indexing with the packed `e` would read out of bounds.
66
+ * - `...cols` — the same column views `forEachEntity` passes.
67
+ *
68
+ * This closes the packed-EntityId footgun (A1): callers no longer need to call
69
+ * `getEntityIndex(e)` (or hand-mask) themselves to index columns safely.
70
+ */
71
+ declare function forEachEntityIndexed(world: World, query: Query, fn: (e: EntityId, i: number, ...cols: any[]) => void): void;
56
72
 
57
73
  declare function pipe<W extends World = World, Ctx = unknown>(...systems: System<W, Ctx>[]): System<W, Ctx>;
58
74
 
@@ -122,4 +138,4 @@ declare function aliveRef<T = unknown>(world: World, ref: EntityRef<T>): boolean
122
138
 
123
139
  declare const IS_SAB_SUPPORTED: boolean;
124
140
 
125
- export { AoSComponent, Archetype, ComponentInit, ComponentLike, EntityId, EntityNotAliveError, type EntityRef, IS_SAB_SUPPORTED, Query, QueryDescriptor, SoAComponent, SoASchema, System, TagComponent, VERSION, World, WorldOptions, addComponent, aliveRef, createEntity, createWorld, defineComponent, defineObjectComponent, defineQuery, defineTag, deref, destroyEntity, destroyWorld, destroyWorld as disposeWorld, enterQuery, entityExists, exitQuery, forEachEntity, getComponent, getEntityGeneration, getEntityIndex, getWorldCapacity, getWorldSize, hasComponent, isEntity, isWorld, iterQuery, packEntity, pipe, queryArchetypes, refOf, removeComponent, resetWorld, runQuery, setComponent };
141
+ export { AoSComponent, Archetype, ComponentInit, ComponentLike, EntityId, EntityNotAliveError, type EntityRef, IS_SAB_SUPPORTED, Query, QueryDescriptor, SoAComponent, SoASchema, System, TagComponent, VERSION, World, WorldOptions, addComponent, aliveRef, createEntity, createWorld, defineComponent, defineObjectComponent, defineQuery, defineTag, deref, destroyEntity, destroyWorld, destroyWorld as disposeWorld, enterQuery, entityExists, exitQuery, forEachEntity, forEachEntityIndexed, getComponent, getEntityGeneration, getEntityIndex, getWorldCapacity, getWorldSize, hasComponent, isEntity, isWorld, iterQuery, packEntity, pipe, queryArchetypes, refOf, removeComponent, resetWorld, runQuery, setComponent };
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-BGEeHad-.js';
2
2
  export { d as ComponentView, g as SoAColumns, i as SoAFieldDecl, j as SoAFieldType, n as Types } from './types-BGEeHad-.js';
3
3
 
4
- declare const VERSION = "0.5.2";
4
+ declare const VERSION = "0.5.3";
5
5
 
6
6
  declare function createWorld(options?: WorldOptions): World;
7
7
  declare function destroyWorld(world: World): void;
@@ -53,6 +53,22 @@ declare function queryArchetypes(world: World, query: Query): readonly Archetype
53
53
  declare function runQuery(world: World, query: Query): readonly EntityId[];
54
54
  declare function iterQuery(world: World, query: Query): IterableIterator<EntityId>;
55
55
  declare function forEachEntity(world: World, query: Query, fn: (eid: EntityId, ...cols: any[]) => void): void;
56
+ /**
57
+ * Like {@link forEachEntity}, but yields the masked column index `i` alongside
58
+ * the packed `EntityId`. The callback signature is `(e, i, ...cols)`:
59
+ *
60
+ * - `e` — the packed EntityId (carries the generation in its high bits). Use
61
+ * it for in-loop `destroyEntity` / `hasComponent` / `refOf`, exactly as with
62
+ * `forEachEntity`.
63
+ * - `i` — `e & indexMask`, the raw slot index. This is the **correct subscript**
64
+ * for SoA column views (`pos.x[i]`), and stays correct after a slot is
65
+ * recycled — where indexing with the packed `e` would read out of bounds.
66
+ * - `...cols` — the same column views `forEachEntity` passes.
67
+ *
68
+ * This closes the packed-EntityId footgun (A1): callers no longer need to call
69
+ * `getEntityIndex(e)` (or hand-mask) themselves to index columns safely.
70
+ */
71
+ declare function forEachEntityIndexed(world: World, query: Query, fn: (e: EntityId, i: number, ...cols: any[]) => void): void;
56
72
 
57
73
  declare function pipe<W extends World = World, Ctx = unknown>(...systems: System<W, Ctx>[]): System<W, Ctx>;
58
74
 
@@ -122,4 +138,4 @@ declare function aliveRef<T = unknown>(world: World, ref: EntityRef<T>): boolean
122
138
 
123
139
  declare const IS_SAB_SUPPORTED: boolean;
124
140
 
125
- export { AoSComponent, Archetype, ComponentInit, ComponentLike, EntityId, EntityNotAliveError, type EntityRef, IS_SAB_SUPPORTED, Query, QueryDescriptor, SoAComponent, SoASchema, System, TagComponent, VERSION, World, WorldOptions, addComponent, aliveRef, createEntity, createWorld, defineComponent, defineObjectComponent, defineQuery, defineTag, deref, destroyEntity, destroyWorld, destroyWorld as disposeWorld, enterQuery, entityExists, exitQuery, forEachEntity, getComponent, getEntityGeneration, getEntityIndex, getWorldCapacity, getWorldSize, hasComponent, isEntity, isWorld, iterQuery, packEntity, pipe, queryArchetypes, refOf, removeComponent, resetWorld, runQuery, setComponent };
141
+ export { AoSComponent, Archetype, ComponentInit, ComponentLike, EntityId, EntityNotAliveError, type EntityRef, IS_SAB_SUPPORTED, Query, QueryDescriptor, SoAComponent, SoASchema, System, TagComponent, VERSION, World, WorldOptions, addComponent, aliveRef, createEntity, createWorld, defineComponent, defineObjectComponent, defineQuery, defineTag, deref, destroyEntity, destroyWorld, destroyWorld as disposeWorld, enterQuery, entityExists, exitQuery, forEachEntity, forEachEntityIndexed, getComponent, getEntityGeneration, getEntityIndex, getWorldCapacity, getWorldSize, hasComponent, isEntity, isWorld, iterQuery, packEntity, pipe, queryArchetypes, refOf, removeComponent, resetWorld, runQuery, setComponent };
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
- import {i as i$1,h}from'./chunk-Q55M3YEQ.js';export{a as defineQuery,b as enterQuery,c as exitQuery,g as forEachEntity,f as iterQuery,d as queryArchetypes,e as runQuery}from'./chunk-Q55M3YEQ.js';import {Q,O,i as i$2,y,s,t}from'./chunk-WZMRKSK5.js';export{a as VERSION,J as addComponent,u as createEntity,k as createWorld,F as defineComponent,H as defineObjectComponent,G as defineTag,w as destroyEntity,l as destroyWorld,l as disposeWorld,x as entityExists,M as getComponent,A as getEntityGeneration,z as getEntityIndex,o as getWorldCapacity,n as getWorldSize,L as hasComponent,C as isEntity,q as isWorld,B as packEntity,K as removeComponent,m as resetWorld,N as setComponent}from'./chunk-WZMRKSK5.js';var K={i8:"i8",u8:"u8",i16:"i16",u16:"u16",i32:"i32",u32:"u32",f32:"f32",f64:"f64",eid:"eid",bool:"bool"};function $(...n){return n.length===0?e=>e:n.length===1?n[0]:(e,r)=>{let t=e;for(let o of n)t=o(t,r);return t}}var i=class extends Error{eid;constructor(e){super(`aiecsjs: entity ${e} is not alive`),this.name="EntityNotAliveError",this.eid=e;}};function J(n,e){let r=i$2(n);if(!y(r,e))throw new i(e);return Object.freeze({id:e,worldId:r.id})}function b(n,e){let r=i$2(n);if(e.worldId!==r.id)return null;let t$1=e.id,o=s(t$1,r.options);if(o<=0||o>=r.capacity)return null;let x=r.entityArchetype[o]??0,d=r.archetypes[x];if(!d||!d.entityRow.has(e.id))return null;let C=r.generations[o]??0,A=t(t$1,r.options);return C!==A?null:e.id}function X(n,e){return b(n,e)!==null}i$1(n=>Q(n));O((n,e,r,t,o)=>{h(n,e,r,t,o);});var ae=typeof SharedArrayBuffer<"u";export{i as EntityNotAliveError,ae as IS_SAB_SUPPORTED,K as Types,X as aliveRef,b as deref,$ as pipe,J as refOf};//# sourceMappingURL=index.js.map
1
+ import {j,i as i$1}from'./chunk-COORLIYF.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-COORLIYF.js';import {Q,O,i as i$2,y,s,t}from'./chunk-UAJNL4VY.js';export{a as VERSION,J as addComponent,u as createEntity,k as createWorld,F as defineComponent,H as defineObjectComponent,G as defineTag,w as destroyEntity,l as destroyWorld,l as disposeWorld,x as entityExists,M as getComponent,A as getEntityGeneration,z as getEntityIndex,o as getWorldCapacity,n as getWorldSize,L as hasComponent,C as isEntity,q as isWorld,B as packEntity,K as removeComponent,m as resetWorld,N as setComponent}from'./chunk-UAJNL4VY.js';var $={i8:"i8",u8:"u8",i16:"i16",u16:"u16",i32:"i32",u32:"u32",f32:"f32",f64:"f64",eid:"eid",bool:"bool"};function J(...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 X(n,e){let r=i$2(n);if(!y(r,e))throw new i(e);return Object.freeze({id:e,worldId:r.id})}function b(n,e){let r=i$2(n);if(e.worldId!==r.id)return null;let t$1=e.id,o=s(t$1,r.options);if(o<=0||o>=r.capacity)return null;let x=r.entityArchetype[o]??0,d=r.archetypes[x];if(!d||!d.entityRow.has(e.id))return null;let C=r.generations[o]??0,A=t(t$1,r.options);return C!==A?null:e.id}function Y(n,e){return b(n,e)!==null}j(n=>Q(n));O((n,e,r,t,o)=>{i$1(n,e,r,t,o);});var de=typeof SharedArrayBuffer<"u";export{i as EntityNotAliveError,de as IS_SAB_SUPPORTED,$ as Types,Y as aliveRef,b as deref,J as pipe,X 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 buffer?: SharedArrayBuffer\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 buffer: SharedArrayBuffer\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 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// --- 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.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 buffer?: SharedArrayBuffer\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 buffer: SharedArrayBuffer\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// --- 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 chunkP2KTJ2NQ_cjs=require('./chunk-P2KTJ2NQ.cjs'),chunkRNWXP3GU_cjs=require('./chunk-RNWXP3GU.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=chunkRNWXP3GU_cjs.i(n);chunkP2KTJ2NQ_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=chunkRNWXP3GU_cjs.i(n),s=chunkRNWXP3GU_cjs.I(e),i=r.componentBitFor.get(s.id);i===void 0&&(i=chunkRNWXP3GU_cjs.p(r,s));let u={event:o,componentBit:i,queryId:-1,handler:t};return r.observers.push(u),()=>{let d=r.observers.indexOf(u);d>=0&&r.observers.splice(d,1);}}function E(n,e,o){let t=Array.from(n.observers);for(let r of t)r.event==="add"&&r.componentBit===o&&n.observers.includes(r)&&r.handler(e);O(n,e,"add");}function M(n,e,o){let t=Array.from(n.observers);for(let r of t)r.event==="remove"&&r.componentBit===o&&n.observers.includes(r)&&r.handler(e);O(n,e,"remove");}function g(n,e,o,t){let r=Array.from(n.observers);for(let s of r)s.event==="set"&&n.observers.includes(s)&&(s.componentBit===o||s.queryId!==-1&&n.queries[s.queryId]?.all.includes(n.componentInfoByBit[o]?.id??-1))&&s.handler(e,t);}function O(n,e,o,t,r){let s=n.options.maskWordCount,i=(e&n.options.indexMask)*s,u=Array.from(n.observers);for(let d of u){if(d.event!==o||d.queryId===-1||!n.observers.includes(d))continue;let c=n.queryMasks.get(d.queryId);if(!c)continue;let l=chunkRNWXP3GU_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);}}chunkRNWXP3GU_cjs.P({fireAdd:E,fireRemove:M,fireSet:g});chunkRNWXP3GU_cjs.D({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;chunkRNWXP3GU_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;chunkRNWXP3GU_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 chunkNCYUF4YW_cjs=require('./chunk-NCYUF4YW.cjs'),chunkD4VGTV76_cjs=require('./chunk-D4VGTV76.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=chunkD4VGTV76_cjs.i(n);chunkNCYUF4YW_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=chunkD4VGTV76_cjs.i(n),s=chunkD4VGTV76_cjs.I(e),i=r.componentBitFor.get(s.id);i===void 0&&(i=chunkD4VGTV76_cjs.p(r,s));let u={event:o,componentBit:i,queryId:-1,handler:t};return r.observers.push(u),()=>{let d=r.observers.indexOf(u);d>=0&&r.observers.splice(d,1);}}function E(n,e,o){let t=Array.from(n.observers);for(let r of t)r.event==="add"&&r.componentBit===o&&n.observers.includes(r)&&r.handler(e);O(n,e,"add");}function M(n,e,o){let t=Array.from(n.observers);for(let r of t)r.event==="remove"&&r.componentBit===o&&n.observers.includes(r)&&r.handler(e);O(n,e,"remove");}function g(n,e,o,t){let r=Array.from(n.observers);for(let s of r)s.event==="set"&&n.observers.includes(s)&&(s.componentBit===o||s.queryId!==-1&&n.queries[s.queryId]?.all.includes(n.componentInfoByBit[o]?.id??-1))&&s.handler(e,t);}function O(n,e,o,t,r){let s=n.options.maskWordCount,i=(e&n.options.indexMask)*s,u=Array.from(n.observers);for(let d of u){if(d.event!==o||d.queryId===-1||!n.observers.includes(d))continue;let c=n.queryMasks.get(d.queryId);if(!c)continue;let l=chunkD4VGTV76_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);}}chunkD4VGTV76_cjs.P({fireAdd:E,fireRemove:M,fireSet:g});chunkD4VGTV76_cjs.D({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;chunkD4VGTV76_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;chunkD4VGTV76_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-Q55M3YEQ.js';import {P,D,g as g$1,h,i,I,p}from'./chunk-WZMRKSK5.js';function f(n,e){if(!e)return n;if(e.aborted)return n(),()=>{};let o=()=>{n();};e.addEventListener("abort",o,{once:true});let t=false;return ()=>{t||(t=true,e.removeEventListener("abort",o),n());}}function w(n,e,o,t){return f(b(n,e,"add",o),t?.signal)}function x(n,e,o,t){return f(b(n,e,"remove",o),t?.signal)}function C(n,e,o,t){return f(b(n,e,"set",o),t?.signal)}function L(n,e$1,o,t,r){let s=i(n);e(n,e$1);let i$1={event:o,componentBit:-1,queryId:e$1.id,handler:t};return s.observers.push(i$1),f(()=>{let d=s.observers.indexOf(i$1);d>=0&&s.observers.splice(d,1);},r?.signal)}function b(n,e,o,t){let r=i(n),s=I(e),i$1=r.componentBitFor.get(s.id);i$1===void 0&&(i$1=p(r,s));let u={event:o,componentBit:i$1,queryId:-1,handler:t};return r.observers.push(u),()=>{let d=r.observers.indexOf(u);d>=0&&r.observers.splice(d,1);}}function E(n,e,o){let t=Array.from(n.observers);for(let r of t)r.event==="add"&&r.componentBit===o&&n.observers.includes(r)&&r.handler(e);O(n,e,"add");}function M(n,e,o){let t=Array.from(n.observers);for(let r of t)r.event==="remove"&&r.componentBit===o&&n.observers.includes(r)&&r.handler(e);O(n,e,"remove");}function g(n,e,o,t){let r=Array.from(n.observers);for(let s of r)s.event==="set"&&n.observers.includes(s)&&(s.componentBit===o||s.queryId!==-1&&n.queries[s.queryId]?.all.includes(n.componentInfoByBit[o]?.id??-1))&&s.handler(e,t);}function O(n,e,o,t,r){let s=n.options.maskWordCount,i=(e&n.options.indexMask)*s,u=Array.from(n.observers);for(let d of u){if(d.event!==o||d.queryId===-1||!n.observers.includes(d))continue;let c=n.queryMasks.get(d.queryId);if(!c)continue;let l=h(n.entityMask,i,s,c.withMask,c.anyMask,c.noneMask,c.anyHasBits);o==="add"&&l&&d.handler(e),o==="remove"&&!l&&d.handler(e);}}P({fireAdd:E,fireRemove:M,fireSet:g});D({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-COORLIYF.js';import {P,D,g as g$1,h,i,I,p}from'./chunk-UAJNL4VY.js';function f(n,e){if(!e)return n;if(e.aborted)return n(),()=>{};let o=()=>{n();};e.addEventListener("abort",o,{once:true});let t=false;return ()=>{t||(t=true,e.removeEventListener("abort",o),n());}}function w(n,e,o,t){return f(b(n,e,"add",o),t?.signal)}function x(n,e,o,t){return f(b(n,e,"remove",o),t?.signal)}function C(n,e,o,t){return f(b(n,e,"set",o),t?.signal)}function L(n,e$1,o,t,r){let s=i(n);e(n,e$1);let i$1={event:o,componentBit:-1,queryId:e$1.id,handler:t};return s.observers.push(i$1),f(()=>{let d=s.observers.indexOf(i$1);d>=0&&s.observers.splice(d,1);},r?.signal)}function b(n,e,o,t){let r=i(n),s=I(e),i$1=r.componentBitFor.get(s.id);i$1===void 0&&(i$1=p(r,s));let u={event:o,componentBit:i$1,queryId:-1,handler:t};return r.observers.push(u),()=>{let d=r.observers.indexOf(u);d>=0&&r.observers.splice(d,1);}}function E(n,e,o){let t=Array.from(n.observers);for(let r of t)r.event==="add"&&r.componentBit===o&&n.observers.includes(r)&&r.handler(e);O(n,e,"add");}function M(n,e,o){let t=Array.from(n.observers);for(let r of t)r.event==="remove"&&r.componentBit===o&&n.observers.includes(r)&&r.handler(e);O(n,e,"remove");}function g(n,e,o,t){let r=Array.from(n.observers);for(let s of r)s.event==="set"&&n.observers.includes(s)&&(s.componentBit===o||s.queryId!==-1&&n.queries[s.queryId]?.all.includes(n.componentInfoByBit[o]?.id??-1))&&s.handler(e,t);}function O(n,e,o,t,r){let s=n.options.maskWordCount,i=(e&n.options.indexMask)*s,u=Array.from(n.observers);for(let d of u){if(d.event!==o||d.queryId===-1||!n.observers.includes(d))continue;let c=n.queryMasks.get(d.queryId);if(!c)continue;let l=h(n.entityMask,i,s,c.withMask,c.anyMask,c.noneMask,c.anyHasBits);o==="add"&&l&&d.handler(e),o==="remove"&&!l&&d.handler(e);}}P({fireAdd:E,fireRemove:M,fireSet:g});D({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 chunkRNWXP3GU_cjs=require('./chunk-RNWXP3GU.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=chunkRNWXP3GU_cjs.i(r);if(t.readOnly)throw new Error("aiecsjs: cannot mutate a read-only world");let e=v(t,s),o=l&t.options.indexMask,d=i&t.options.indexMask;if(e.exclusive){if(o>=e.exclusive.length){let c=new Int32Array(Math.max(o+1,e.exclusive.length*2)).fill(-1);c.set(e.exclusive),e.exclusive=c;}let a=e.exclusive[o]??-1;if(a>=0&&a!==d){let c=e.data.get(o);c?.delete(a),c&&c.size===0&&e.data.delete(o);}e.exclusive[o]=d;}else {let a=e.outgoing.get(o);a||(a=[],e.outgoing.set(o,a)),a.includes(d)||a.push(d);}if(n!==void 0){let a=e.data.get(o);a||(a=new Map,e.data.set(o,a)),a.set(d,n);}}function b(r,l,s,i){let n=chunkRNWXP3GU_cjs.i(r);if(n.readOnly)throw new Error("aiecsjs: cannot mutate a read-only world");let t=n.relationStorage.get(s.__id);if(!t)return;let e=l&n.options.indexMask,o=i&n.options.indexMask;if(t.exclusive)e<t.exclusive.length&&t.exclusive[e]===o&&(t.exclusive[e]=-1);else {let a=t.outgoing.get(e);if(a){let c=a.indexOf(o);c>=0&&a.splice(c,1),a.length===0&&t.outgoing.delete(e);}}let d=t.data.get(e);d&&(d.delete(o),d.size===0&&t.data.delete(e));}function h(r,l,s){let i=chunkRNWXP3GU_cjs.i(r),n=i.relationStorage.get(s.__id);if(!n)return [];let t=l&i.options.indexMask;if(n.exclusive){if(t<n.exclusive.length){let o=n.exclusive[t]??-1;if(o>=0){let d=i.generations[o]??0;return [chunkRNWXP3GU_cjs.r(o,d,i.options)]}}return []}let e=n.outgoing.get(t);return e?e.map(o=>{let d=i.generations[o]??0;return chunkRNWXP3GU_cjs.r(o,d,i.options)}):[]}function w(r,l,s,i){let n=chunkRNWXP3GU_cjs.i(r),t=n.relationStorage.get(s.__id);if(!t)return;let e=l&n.options.indexMask,o=i&n.options.indexMask;return t.data.get(e)?.get(o)}chunkRNWXP3GU_cjs.E((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 chunkD4VGTV76_cjs=require('./chunk-D4VGTV76.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=chunkD4VGTV76_cjs.i(r);if(t.readOnly)throw new Error("aiecsjs: cannot mutate a read-only world");let e=v(t,s),o=l&t.options.indexMask,d=i&t.options.indexMask;if(e.exclusive){if(o>=e.exclusive.length){let c=new Int32Array(Math.max(o+1,e.exclusive.length*2)).fill(-1);c.set(e.exclusive),e.exclusive=c;}let a=e.exclusive[o]??-1;if(a>=0&&a!==d){let c=e.data.get(o);c?.delete(a),c&&c.size===0&&e.data.delete(o);}e.exclusive[o]=d;}else {let a=e.outgoing.get(o);a||(a=[],e.outgoing.set(o,a)),a.includes(d)||a.push(d);}if(n!==void 0){let a=e.data.get(o);a||(a=new Map,e.data.set(o,a)),a.set(d,n);}}function b(r,l,s,i){let n=chunkD4VGTV76_cjs.i(r);if(n.readOnly)throw new Error("aiecsjs: cannot mutate a read-only world");let t=n.relationStorage.get(s.__id);if(!t)return;let e=l&n.options.indexMask,o=i&n.options.indexMask;if(t.exclusive)e<t.exclusive.length&&t.exclusive[e]===o&&(t.exclusive[e]=-1);else {let a=t.outgoing.get(e);if(a){let c=a.indexOf(o);c>=0&&a.splice(c,1),a.length===0&&t.outgoing.delete(e);}}let d=t.data.get(e);d&&(d.delete(o),d.size===0&&t.data.delete(e));}function h(r,l,s){let i=chunkD4VGTV76_cjs.i(r),n=i.relationStorage.get(s.__id);if(!n)return [];let t=l&i.options.indexMask;if(n.exclusive){if(t<n.exclusive.length){let o=n.exclusive[t]??-1;if(o>=0){let d=i.generations[o]??0;return [chunkD4VGTV76_cjs.r(o,d,i.options)]}}return []}let e=n.outgoing.get(t);return e?e.map(o=>{let d=i.generations[o]??0;return chunkD4VGTV76_cjs.r(o,d,i.options)}):[]}function w(r,l,s,i){let n=chunkD4VGTV76_cjs.i(r),t=n.relationStorage.get(s.__id);if(!t)return;let e=l&n.options.indexMask,o=i&n.options.indexMask;return t.data.get(e)?.get(o)}chunkD4VGTV76_cjs.E((r,l)=>{let s=l&r.options.indexMask;for(let i of r.relationStorage.values()){if(i.exclusive){s<i.exclusive.length&&(i.exclusive[s]=-1);for(let n=0;n<i.exclusive.length;n++)i.exclusive[n]===s&&(i.exclusive[n]=-1);}i.outgoing.delete(s);for(let[n,t]of i.outgoing){let e=t.indexOf(s);e>=0&&(t.splice(e,1),t.length===0&&i.outgoing.delete(n));}i.data.delete(s);for(let[n,t]of i.data)t.delete(s),t.size===0&&i.data.delete(n);}});exports.ChildOf=_;exports.addRelation=R;exports.defineRelation=p;exports.getRelationData=w;exports.getRelationTargets=h;exports.removeRelation=b;//# sourceMappingURL=relations.cjs.map
2
2
  //# sourceMappingURL=relations.cjs.map
package/dist/relations.js CHANGED
@@ -1,2 +1,2 @@
1
- import {E,i,r}from'./chunk-WZMRKSK5.js';var x=1;function p(r){return {__kind:"relation",__id:x++,__exclusive:r?.exclusive??false,__hasData:false}}var _=p({exclusive:true});function v(r,l){let s=r.relationStorage.get(l.__id);return s||(s={rel:l,exclusive:l.__exclusive?new Int32Array(r.capacity).fill(-1):null,outgoing:new Map,data:new Map},r.relationStorage.set(l.__id,s),s)}function R(r,l,s,i$1,n){let t=i(r);if(t.readOnly)throw new Error("aiecsjs: cannot mutate a read-only world");let e=v(t,s),o=l&t.options.indexMask,d=i$1&t.options.indexMask;if(e.exclusive){if(o>=e.exclusive.length){let c=new Int32Array(Math.max(o+1,e.exclusive.length*2)).fill(-1);c.set(e.exclusive),e.exclusive=c;}let a=e.exclusive[o]??-1;if(a>=0&&a!==d){let c=e.data.get(o);c?.delete(a),c&&c.size===0&&e.data.delete(o);}e.exclusive[o]=d;}else {let a=e.outgoing.get(o);a||(a=[],e.outgoing.set(o,a)),a.includes(d)||a.push(d);}if(n!==void 0){let a=e.data.get(o);a||(a=new Map,e.data.set(o,a)),a.set(d,n);}}function b(r,l,s,i$1){let n=i(r);if(n.readOnly)throw new Error("aiecsjs: cannot mutate a read-only world");let t=n.relationStorage.get(s.__id);if(!t)return;let e=l&n.options.indexMask,o=i$1&n.options.indexMask;if(t.exclusive)e<t.exclusive.length&&t.exclusive[e]===o&&(t.exclusive[e]=-1);else {let a=t.outgoing.get(e);if(a){let c=a.indexOf(o);c>=0&&a.splice(c,1),a.length===0&&t.outgoing.delete(e);}}let d=t.data.get(e);d&&(d.delete(o),d.size===0&&t.data.delete(e));}function h(r$1,l,s){let i$1=i(r$1),n=i$1.relationStorage.get(s.__id);if(!n)return [];let t=l&i$1.options.indexMask;if(n.exclusive){if(t<n.exclusive.length){let o=n.exclusive[t]??-1;if(o>=0){let d=i$1.generations[o]??0;return [r(o,d,i$1.options)]}}return []}let e=n.outgoing.get(t);return e?e.map(o=>{let d=i$1.generations[o]??0;return r(o,d,i$1.options)}):[]}function w(r,l,s,i$1){let n=i(r),t=n.relationStorage.get(s.__id);if(!t)return;let e=l&n.options.indexMask,o=i$1&n.options.indexMask;return t.data.get(e)?.get(o)}E((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 {E,i,r}from'./chunk-UAJNL4VY.js';var x=1;function p(r){return {__kind:"relation",__id:x++,__exclusive:r?.exclusive??false,__hasData:false}}var _=p({exclusive:true});function v(r,l){let s=r.relationStorage.get(l.__id);return s||(s={rel:l,exclusive:l.__exclusive?new Int32Array(r.capacity).fill(-1):null,outgoing:new Map,data:new Map},r.relationStorage.set(l.__id,s),s)}function R(r,l,s,i$1,n){let t=i(r);if(t.readOnly)throw new Error("aiecsjs: cannot mutate a read-only world");let e=v(t,s),o=l&t.options.indexMask,d=i$1&t.options.indexMask;if(e.exclusive){if(o>=e.exclusive.length){let c=new Int32Array(Math.max(o+1,e.exclusive.length*2)).fill(-1);c.set(e.exclusive),e.exclusive=c;}let a=e.exclusive[o]??-1;if(a>=0&&a!==d){let c=e.data.get(o);c?.delete(a),c&&c.size===0&&e.data.delete(o);}e.exclusive[o]=d;}else {let a=e.outgoing.get(o);a||(a=[],e.outgoing.set(o,a)),a.includes(d)||a.push(d);}if(n!==void 0){let a=e.data.get(o);a||(a=new Map,e.data.set(o,a)),a.set(d,n);}}function b(r,l,s,i$1){let n=i(r);if(n.readOnly)throw new Error("aiecsjs: cannot mutate a read-only world");let t=n.relationStorage.get(s.__id);if(!t)return;let e=l&n.options.indexMask,o=i$1&n.options.indexMask;if(t.exclusive)e<t.exclusive.length&&t.exclusive[e]===o&&(t.exclusive[e]=-1);else {let a=t.outgoing.get(e);if(a){let c=a.indexOf(o);c>=0&&a.splice(c,1),a.length===0&&t.outgoing.delete(e);}}let d=t.data.get(e);d&&(d.delete(o),d.size===0&&t.data.delete(e));}function h(r$1,l,s){let i$1=i(r$1),n=i$1.relationStorage.get(s.__id);if(!n)return [];let t=l&i$1.options.indexMask;if(n.exclusive){if(t<n.exclusive.length){let o=n.exclusive[t]??-1;if(o>=0){let d=i$1.generations[o]??0;return [r(o,d,i$1.options)]}}return []}let e=n.outgoing.get(t);return e?e.map(o=>{let d=i$1.generations[o]??0;return r(o,d,i$1.options)}):[]}function w(r,l,s,i$1){let n=i(r),t=n.relationStorage.get(s.__id);if(!t)return;let e=l&n.options.indexMask,o=i$1&n.options.indexMask;return t.data.get(e)?.get(o)}E((r,l)=>{let s=l&r.options.indexMask;for(let i of r.relationStorage.values()){if(i.exclusive){s<i.exclusive.length&&(i.exclusive[s]=-1);for(let n=0;n<i.exclusive.length;n++)i.exclusive[n]===s&&(i.exclusive[n]=-1);}i.outgoing.delete(s);for(let[n,t]of i.outgoing){let e=t.indexOf(s);e>=0&&(t.splice(e,1),t.length===0&&i.outgoing.delete(n));}i.data.delete(s);for(let[n,t]of i.data)t.delete(s),t.size===0&&i.data.delete(n);}});export{_ as ChildOf,R as addRelation,p as defineRelation,w as getRelationData,h as getRelationTargets,b as removeRelation};//# sourceMappingURL=relations.js.map
2
2
  //# sourceMappingURL=relations.js.map
@@ -1,2 +1,2 @@
1
- 'use strict';var chunkFEICW2CS_cjs=require('./chunk-FEICW2CS.cjs');require('./chunk-RNWXP3GU.cjs');Object.defineProperty(exports,"createDeltaSerializer",{enumerable:true,get:function(){return chunkFEICW2CS_cjs.e}});Object.defineProperty(exports,"deserializeWorld",{enumerable:true,get:function(){return chunkFEICW2CS_cjs.b}});Object.defineProperty(exports,"fromJSON",{enumerable:true,get:function(){return chunkFEICW2CS_cjs.d}});Object.defineProperty(exports,"serializeWorld",{enumerable:true,get:function(){return chunkFEICW2CS_cjs.a}});Object.defineProperty(exports,"toJSON",{enumerable:true,get:function(){return chunkFEICW2CS_cjs.c}});//# sourceMappingURL=serialize.cjs.map
1
+ 'use strict';var chunkXBCASW7K_cjs=require('./chunk-XBCASW7K.cjs');require('./chunk-D4VGTV76.cjs');Object.defineProperty(exports,"createDeltaSerializer",{enumerable:true,get:function(){return chunkXBCASW7K_cjs.e}});Object.defineProperty(exports,"deserializeWorld",{enumerable:true,get:function(){return chunkXBCASW7K_cjs.b}});Object.defineProperty(exports,"fromJSON",{enumerable:true,get:function(){return chunkXBCASW7K_cjs.d}});Object.defineProperty(exports,"serializeWorld",{enumerable:true,get:function(){return chunkXBCASW7K_cjs.a}});Object.defineProperty(exports,"toJSON",{enumerable:true,get:function(){return chunkXBCASW7K_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-2E3GMRF5.js';import'./chunk-WZMRKSK5.js';//# sourceMappingURL=serialize.js.map
1
+ export{e as createDeltaSerializer,b as deserializeWorld,d as fromJSON,a as serializeWorld,c as toJSON}from'./chunk-FZWRVGAA.js';import'./chunk-UAJNL4VY.js';//# sourceMappingURL=serialize.js.map
2
2
  //# sourceMappingURL=serialize.js.map
package/dist/worker.cjs CHANGED
@@ -1,2 +1,2 @@
1
- 'use strict';var chunkFEICW2CS_cjs=require('./chunk-FEICW2CS.cjs'),chunkRNWXP3GU_cjs=require('./chunk-RNWXP3GU.cjs');var l=1095320899;function W(r){let e=chunkRNWXP3GU_cjs.i(r),o=chunkFEICW2CS_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 chunkFEICW2CS_cjs.b(e)}function S(r,e){let o=new Uint8Array(r),t=chunkFEICW2CS_cjs.b(o);if(e?.readOnly){let n=chunkRNWXP3GU_cjs.i(t);n.readOnly=true;}return t}function w(r){chunkRNWXP3GU_cjs.j(r.id)&&chunkRNWXP3GU_cjs.l(r);}function p(r){let e=[];for(let o of r.componentInfoByBit)o&&e.push({id:o.id,kind:o.kind,schema:o.schema});return {magic:l,formatVersion:1,aiecsjsVersion:chunkRNWXP3GU_cjs.a,indexBits:r.options.indexBits,generationBits:r.options.generationBits,maxComponents:r.options.maxComponents,maskWordCount:r.options.maskWordCount,capacity:r.capacity,componentSchemas:e}}function m(r){if(r.magic!==l)throw new Error("aiecsjs: invalid snapshot meta (wrong magic)");if(r.formatVersion!==1)throw new Error(`aiecsjs: unsupported snapshot format version ${r.formatVersion}`)}exports.adoptSnapshot=b;exports.attachWorld=S;exports.detachWorld=w;exports.transferableSnapshot=W;//# sourceMappingURL=worker.cjs.map
1
+ 'use strict';var chunkXBCASW7K_cjs=require('./chunk-XBCASW7K.cjs'),chunkD4VGTV76_cjs=require('./chunk-D4VGTV76.cjs');var l=1095320899;function W(r){let e=chunkD4VGTV76_cjs.i(r),o=chunkXBCASW7K_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 chunkXBCASW7K_cjs.b(e)}function S(r,e){let o=new Uint8Array(r),t=chunkXBCASW7K_cjs.b(o);if(e?.readOnly){let n=chunkD4VGTV76_cjs.i(t);n.readOnly=true;}return t}function w(r){chunkD4VGTV76_cjs.j(r.id)&&chunkD4VGTV76_cjs.l(r);}function p(r){let e=[];for(let o of r.componentInfoByBit)o&&e.push({id:o.id,kind:o.kind,schema:o.schema});return {magic:l,formatVersion:1,aiecsjsVersion:chunkD4VGTV76_cjs.a,indexBits:r.options.indexBits,generationBits:r.options.generationBits,maxComponents:r.options.maxComponents,maskWordCount:r.options.maskWordCount,capacity:r.capacity,componentSchemas:e}}function m(r){if(r.magic!==l)throw new Error("aiecsjs: invalid snapshot meta (wrong magic)");if(r.formatVersion!==1)throw new Error(`aiecsjs: unsupported snapshot format version ${r.formatVersion}`)}exports.adoptSnapshot=b;exports.attachWorld=S;exports.detachWorld=w;exports.transferableSnapshot=W;//# sourceMappingURL=worker.cjs.map
2
2
  //# sourceMappingURL=worker.cjs.map
package/dist/worker.js CHANGED
@@ -1,2 +1,2 @@
1
- import {a,b as b$1}from'./chunk-2E3GMRF5.js';import {i,j,l as l$1,a as a$1}from'./chunk-WZMRKSK5.js';var l=1095320899;function W(r){let e=i(r),o=a(r);if(typeof SharedArrayBuffer>"u"){let n=new ArrayBuffer(o.byteLength);return new Uint8Array(n).set(o),{buffer:n,meta:p(e)}}let t=new SharedArrayBuffer(o.byteLength);return new Uint8Array(t).set(o),{buffer:t,meta:p(e)}}function b(r){m(r.meta);let e=new Uint8Array(r.buffer);return b$1(e)}function S(r,e){let o=new Uint8Array(r),t=b$1(o);if(e?.readOnly){let n=i(t);n.readOnly=true;}return t}function w(r){j(r.id)&&l$1(r);}function p(r){let e=[];for(let o of r.componentInfoByBit)o&&e.push({id:o.id,kind:o.kind,schema:o.schema});return {magic:l,formatVersion:1,aiecsjsVersion:a$1,indexBits:r.options.indexBits,generationBits:r.options.generationBits,maxComponents:r.options.maxComponents,maskWordCount:r.options.maskWordCount,capacity:r.capacity,componentSchemas:e}}function m(r){if(r.magic!==l)throw new Error("aiecsjs: invalid snapshot meta (wrong magic)");if(r.formatVersion!==1)throw new Error(`aiecsjs: unsupported snapshot format version ${r.formatVersion}`)}export{b as adoptSnapshot,S as attachWorld,w as detachWorld,W as transferableSnapshot};//# sourceMappingURL=worker.js.map
1
+ import {a,b as b$1}from'./chunk-FZWRVGAA.js';import {i,j,l as l$1,a as a$1}from'./chunk-UAJNL4VY.js';var l=1095320899;function W(r){let e=i(r),o=a(r);if(typeof SharedArrayBuffer>"u"){let n=new ArrayBuffer(o.byteLength);return new Uint8Array(n).set(o),{buffer:n,meta:p(e)}}let t=new SharedArrayBuffer(o.byteLength);return new Uint8Array(t).set(o),{buffer:t,meta:p(e)}}function b(r){m(r.meta);let e=new Uint8Array(r.buffer);return b$1(e)}function S(r,e){let o=new Uint8Array(r),t=b$1(o);if(e?.readOnly){let n=i(t);n.readOnly=true;}return t}function w(r){j(r.id)&&l$1(r);}function p(r){let e=[];for(let o of r.componentInfoByBit)o&&e.push({id:o.id,kind:o.kind,schema:o.schema});return {magic:l,formatVersion:1,aiecsjsVersion:a$1,indexBits:r.options.indexBits,generationBits:r.options.generationBits,maxComponents:r.options.maxComponents,maskWordCount:r.options.maskWordCount,capacity:r.capacity,componentSchemas:e}}function m(r){if(r.magic!==l)throw new Error("aiecsjs: invalid snapshot meta (wrong magic)");if(r.formatVersion!==1)throw new Error(`aiecsjs: unsupported snapshot format version ${r.formatVersion}`)}export{b as adoptSnapshot,S as attachWorld,w as detachWorld,W as transferableSnapshot};//# sourceMappingURL=worker.js.map
2
2
  //# sourceMappingURL=worker.js.map