@@ -120,6 +120,18 @@ export class World {
120120 return componentTypes . join ( "," ) ;
121121 }
122122
123+ /**
124+ * Creates a new entity.
125+ * The entity is created with an empty component set and can be configured using `set()`.
126+ *
127+ * @template T - The initial component type (defaults to void if not specified)
128+ * @returns A unique identifier for the new entity
129+ *
130+ * @example
131+ * const entity = world.new<MyComponent>();
132+ * world.set(entity, MyComponent, { value: 42 });
133+ * world.sync();
134+ */
123135 new < T = void > ( ) : EntityId < T > {
124136 const entityId = this . entityIdManager . allocate ( ) ;
125137 let emptyArchetype = this . ensureArchetype ( [ ] ) ;
@@ -167,10 +179,40 @@ export class World {
167179 }
168180 }
169181
182+ /**
183+ * Checks if an entity exists in the world.
184+ *
185+ * @param entityId - The entity identifier to check
186+ * @returns `true` if the entity exists, `false` otherwise
187+ *
188+ * @example
189+ * if (world.exists(entityId)) {
190+ * console.log("Entity exists");
191+ * }
192+ */
170193 exists ( entityId : EntityId ) : boolean {
171194 return this . entityToArchetype . has ( entityId ) ;
172195 }
173196
197+ /**
198+ * Adds or updates a component on an entity (or marks void component as present).
199+ * The change is buffered and takes effect after calling `world.sync()`.
200+ * If the entity does not exist, throws an error.
201+ *
202+ * @overload set(entityId: EntityId, componentType: EntityId<void>): void
203+ * Marks a void component as present on the entity
204+ *
205+ * @overload set<T>(entityId: EntityId, componentType: EntityId<T>, component: NoInfer<T>): void
206+ * Adds or updates a component with data on the entity
207+ *
208+ * @throws {Error } If the entity does not exist
209+ * @throws {Error } If the component type is invalid or is a wildcard relation
210+ *
211+ * @example
212+ * world.set(entity, Position, { x: 10, y: 20 });
213+ * world.set(entity, Marker); // void component
214+ * world.sync(); // Apply changes
215+ */
174216 set ( entityId : EntityId , componentType : EntityId < void > ) : void ;
175217 set < T > ( entityId : EntityId , componentType : EntityId < T > , component : NoInfer < T > ) : void ;
176218 set ( entityId : EntityId , componentType : EntityId , component ?: any ) : void {
@@ -189,6 +231,22 @@ export class World {
189231 this . commandBuffer . set ( entityId , componentType , component ) ;
190232 }
191233
234+ /**
235+ * Removes a component from an entity.
236+ * The change is buffered and takes effect after calling `world.sync()`.
237+ * If the entity does not exist, throws an error.
238+ *
239+ * @template T - The component data type
240+ * @param entityId - The entity identifier
241+ * @param componentType - The component type to remove
242+ *
243+ * @throws {Error } If the entity does not exist
244+ * @throws {Error } If the component type is invalid
245+ *
246+ * @example
247+ * world.remove(entity, Position);
248+ * world.sync(); // Apply changes
249+ */
192250 remove < T > ( entityId : EntityId , componentType : EntityId < T > ) : void {
193251 if ( ! this . exists ( entityId ) ) {
194252 throw new Error ( `Entity ${ entityId } does not exist` ) ;
@@ -202,10 +260,35 @@ export class World {
202260 this . commandBuffer . remove ( entityId , componentType ) ;
203261 }
204262
263+ /**
264+ * Deletes an entity and all its components from the world.
265+ * The change is buffered and takes effect after calling `world.sync()`.
266+ * Related entities may trigger cascade delete hooks if configured.
267+ *
268+ * @param entityId - The entity identifier to delete
269+ *
270+ * @example
271+ * world.delete(entity);
272+ * world.sync(); // Apply changes
273+ */
205274 delete ( entityId : EntityId ) : void {
206275 this . commandBuffer . delete ( entityId ) ;
207276 }
208277
278+ /**
279+ * Checks if an entity has a specific component.
280+ * Immediately reflects the current state without waiting for `sync()`.
281+ *
282+ * @template T - The component data type
283+ * @param entityId - The entity identifier
284+ * @param componentType - The component type to check
285+ * @returns `true` if the entity has the component, `false` otherwise
286+ *
287+ * @example
288+ * if (world.has(entity, Position)) {
289+ * const pos = world.get(entity, Position);
290+ * }
291+ */
209292 has < T > ( entityId : EntityId , componentType : EntityId < T > ) : boolean {
210293 const archetype = this . entityToArchetype . get ( entityId ) ;
211294 if ( ! archetype ) return false ;
@@ -219,6 +302,27 @@ export class World {
219302 return false ;
220303 }
221304
305+ /**
306+ * Retrieves a component from an entity.
307+ * For wildcard relations, returns all relations of that type.
308+ * Throws an error if the component does not exist; use `has()` to check first or use `getOptional()`.
309+ *
310+ * @overload get<T>(entityId: EntityId<T>): T
311+ * When called with only an entity ID, retrieves the entity's primary component.
312+ *
313+ * @overload get<T>(entityId: EntityId, componentType: WildcardRelationId<T>): [EntityId<unknown>, T][]
314+ * For wildcard relations, returns an array of [target entity, component value] pairs.
315+ *
316+ * @overload get<T>(entityId: EntityId, componentType: EntityId<T>): T
317+ * Retrieves a specific component from the entity.
318+ *
319+ * @throws {Error } If the entity does not exist
320+ * @throws {Error } If the component does not exist on the entity
321+ *
322+ * @example
323+ * const position = world.get(entity, Position); // Throws if no Position
324+ * const relations = world.get(entity, relation(Parent, "*")); // Wildcard relation
325+ */
222326 get < T > ( entityId : EntityId < T > ) : T ;
223327 get < T > ( entityId : EntityId , componentType : WildcardRelationId < T > ) : [ EntityId < unknown > , T ] [ ] ;
224328 get < T > ( entityId : EntityId , componentType : EntityId < T > ) : T ;
@@ -247,6 +351,26 @@ export class World {
247351 return archetype . get ( entityId , componentType ) ;
248352 }
249353
354+ /**
355+ * Safely retrieves a component from an entity without throwing an error.
356+ * Returns `undefined` if the component does not exist.
357+ * For wildcard relations, returns `undefined` if there are no relations.
358+ *
359+ * @template T - The component data type
360+ * @overload getOptional<T>(entityId: EntityId<T>): { value: T } | undefined
361+ * Retrieves the entity's primary component safely.
362+ *
363+ * @overload getOptional<T>(entityId: EntityId, componentType: EntityId<T>): { value: T } | undefined
364+ * Retrieves a specific component safely.
365+ *
366+ * @throws {Error } If the entity does not exist
367+ *
368+ * @example
369+ * const position = world.getOptional(entity, Position);
370+ * if (position) {
371+ * console.log(position.value.x);
372+ * }
373+ */
250374 getOptional < T > ( entityId : EntityId < T > ) : { value : T } | undefined ;
251375 getOptional < T > ( entityId : EntityId , componentType : EntityId < T > ) : { value : T } | undefined ;
252376 getOptional < T > ( entityId : EntityId , componentType : EntityId < T > = entityId as EntityId < T > ) : { value : T } | undefined {
@@ -268,7 +392,40 @@ export class World {
268392 }
269393
270394 /**
271- * @deprecated use array overload with LifecycleCallback
395+ * Registers a lifecycle hook that responds to component changes.
396+ * The hook callback is invoked when components matching the specified types are added, updated, or removed.
397+ *
398+ * @deprecated For single components, use the array overload with LifecycleCallback for better multi-component support
399+ *
400+ * @overload hook<T>(componentType: EntityId<T>, hook: LegacyLifecycleHook<T> | LegacyLifecycleCallback<T>): () => void
401+ * Registers a hook for a single component type (legacy API).
402+ *
403+ * @overload hook<const T extends readonly ComponentType<any>[]>(
404+ * componentTypes: T,
405+ * hook: LifecycleHook<T> | LifecycleCallback<T>,
406+ * ): () => void
407+ * Registers a hook for multiple component types.
408+ * The hook is triggered when all required components change together.
409+ *
410+ * @param componentTypesOrSingle - A single component type or an array of component types
411+ * @param hook - Either a hook object with on_init/on_set/on_remove handlers, or a callback function
412+ * @returns A function that unsubscribes the hook when called
413+ *
414+ * @throws {Error } If no required components are specified in array overload
415+ *
416+ * @example
417+ * // Array overload (recommended)
418+ * const unsubscribe = world.hook([Position, Velocity], {
419+ * on_init: (entityId, position, velocity) => console.log("Initialized"),
420+ * on_set: (entityId, position, velocity) => console.log("Updated"),
421+ * on_remove: (entityId, position, velocity) => console.log("Removed"),
422+ * });
423+ * unsubscribe(); // Remove hook
424+ *
425+ * // Callback style
426+ * const unsubscribe = world.hook([Position], (event, entityId, position) => {
427+ * if (event === "init") console.log("Initialized");
428+ * });
272429 */
273430 hook < T > ( componentType : EntityId < T > , hook : LegacyLifecycleHook < T > | LegacyLifecycleCallback < T > ) : ( ) => void ;
274431 hook < const T extends readonly ComponentType < any > [ ] > (
@@ -411,10 +568,48 @@ export class World {
411568 }
412569 }
413570
571+ /**
572+ * Synchronizes all buffered commands (set/remove/delete) to the world.
573+ * This method must be called after making changes via `set()`, `remove()`, or `delete()` for them to take effect.
574+ * Typically called once per frame at the end of your game loop.
575+ *
576+ * @example
577+ * world.set(entity, Position, { x: 10, y: 20 });
578+ * world.remove(entity, OldComponent);
579+ * world.sync(); // Apply all buffered changes
580+ */
414581 sync ( ) : void {
415582 this . commandBuffer . execute ( ) ;
416583 }
417584
585+ /**
586+ * Creates a cached query for efficiently iterating entities with specific components.
587+ * The query is cached internally and reused across calls with the same component types and filter.
588+ *
589+ * **Important:** Store the query reference and reuse it across frames for optimal performance.
590+ * Creating a new query each frame defeats the caching mechanism.
591+ *
592+ * @param componentTypes - Array of component types to match
593+ * @param filter - Optional filter for additional constraints (e.g., without specific components)
594+ * @returns A Query instance that can be used to iterate matching entities
595+ *
596+ * @example
597+ * // Create once, reuse many times
598+ * const movementQuery = world.createQuery([Position, Velocity]);
599+ *
600+ * // In game loop
601+ * movementQuery.forEach((entity) => {
602+ * const pos = world.get(entity, Position);
603+ * const vel = world.get(entity, Velocity);
604+ * pos.x += vel.x;
605+ * pos.y += vel.y;
606+ * });
607+ *
608+ * // With filter
609+ * const activeQuery = world.createQuery([Position], {
610+ * without: [Disabled]
611+ * });
612+ */
418613 createQuery ( componentTypes : EntityId < any > [ ] , filter : QueryFilter = { } ) : Query {
419614 const sortedTypes = [ ...componentTypes ] . sort ( ( a , b ) => a - b ) ;
420615 const filterKey = serializeQueryFilter ( filter ) ;
@@ -431,10 +626,39 @@ export class World {
431626 return query ;
432627 }
433628
629+ /**
630+ * Creates a new entity builder for fluent entity configuration.
631+ * Useful for building entities with multiple components in a single expression.
632+ *
633+ * @returns An EntityBuilder instance
634+ *
635+ * @example
636+ * const entity = world.spawn()
637+ * .with(Position, { x: 0, y: 0 })
638+ * .with(Velocity, { x: 1, y: 1 })
639+ * .build();
640+ * world.sync(); // Apply changes
641+ */
434642 spawn ( ) : EntityBuilder {
435643 return new EntityBuilder ( this ) ;
436644 }
437645
646+ /**
647+ * Spawns multiple entities with a configuration callback.
648+ * More efficient than calling `spawn()` multiple times when creating many entities.
649+ *
650+ * @param count - Number of entities to spawn
651+ * @param configure - Callback that receives an EntityBuilder and index; must return the configured builder
652+ * @returns Array of created entity IDs
653+ *
654+ * @example
655+ * const entities = world.spawnMany(100, (builder, index) => {
656+ * return builder
657+ * .with(Position, { x: index * 10, y: 0 })
658+ * .with(Velocity, { x: 0, y: 1 });
659+ * });
660+ * world.sync();
661+ */
438662 spawnMany ( count : number , configure : ( builder : EntityBuilder , index : number ) => EntityBuilder ) : EntityId [ ] {
439663 const entities : EntityId [ ] = [ ] ;
440664 for ( let i = 0 ; i < count ; i ++ ) {
@@ -455,6 +679,17 @@ export class World {
455679 }
456680 }
457681
682+ /**
683+ * Releases a cached query and frees its resources if no longer needed.
684+ * Call this when you're done using a query to allow the world to clean up its cache entry.
685+ *
686+ * @param query - The query to release
687+ *
688+ * @example
689+ * const query = world.createQuery([Position]);
690+ * // ... use query ...
691+ * world.releaseQuery(query); // Optional cleanup
692+ */
458693 releaseQuery ( query : Query ) : void {
459694 for ( const [ k , v ] of this . queryCache . entries ( ) ) {
460695 if ( v . query === query ) {
@@ -469,6 +704,14 @@ export class World {
469704 }
470705 }
471706
707+ /**
708+ * Returns all archetypes that contain entities with the specified components.
709+ * Used internally for query optimization but can be useful for debugging.
710+ *
711+ * @param componentTypes - Array of component types to match
712+ * @returns Array of Archetype objects containing matching components
713+ * @internal
714+ */
472715 getMatchingArchetypes ( componentTypes : EntityId < any > [ ] ) : Archetype [ ] {
473716 if ( componentTypes . length === 0 ) {
474717 return [ ...this . archetypes ] ;
@@ -512,6 +755,33 @@ export class World {
512755 return firstList . filter ( ( archetype ) => archetypeLists . slice ( 1 ) . every ( ( list ) => list . includes ( archetype ) ) ) ;
513756 }
514757
758+ /**
759+ * Queries entities with specific components.
760+ * For simpler use cases, prefer using `createQuery()` with `forEach()` which is cached and more efficient.
761+ *
762+ * @overload query(componentTypes: EntityId<any>[]): EntityId[]
763+ * Returns an array of entity IDs that have all specified components.
764+ *
765+ * @overload query<const T extends readonly EntityId<any>[]>(
766+ * componentTypes: T,
767+ * includeComponents: true,
768+ * ): Array<{ entity: EntityId; components: ComponentTuple<T> }>
769+ * Returns entities along with their component data.
770+ *
771+ * @param componentTypes - Array of component types to query
772+ * @param includeComponents - If true, includes component data in results
773+ * @returns Array of entity IDs or objects with entities and components
774+ *
775+ * @example
776+ * // Just entity IDs
777+ * const entities = world.query([Position, Velocity]);
778+ *
779+ * // With components
780+ * const results = world.query([Position, Velocity], true);
781+ * results.forEach(({ entity, components: [pos, vel] }) => {
782+ * pos.x += vel.x;
783+ * });
784+ */
515785 query ( componentTypes : EntityId < any > [ ] ) : EntityId [ ] ;
516786 query < const T extends readonly EntityId < any > [ ] > (
517787 componentTypes : T ,
@@ -726,6 +996,27 @@ export class World {
726996 }
727997 }
728998
999+ /**
1000+ * Serializes the entire world state to a plain JavaScript object.
1001+ * This creates a "memory snapshot" that can be stored or transmitted.
1002+ * The snapshot can be restored using `new World(snapshot)`.
1003+ *
1004+ * **Note:** This is NOT automatically persistent storage. To persist data,
1005+ * you must serialize the returned object to JSON or another format yourself.
1006+ *
1007+ * @returns A serializable object representing the world state
1008+ *
1009+ * @example
1010+ * // Create snapshot
1011+ * const snapshot = world.serialize();
1012+ *
1013+ * // Save to storage (example)
1014+ * localStorage.setItem('save', JSON.stringify(snapshot));
1015+ *
1016+ * // Later, restore from snapshot
1017+ * const savedData = JSON.parse(localStorage.getItem('save'));
1018+ * const newWorld = new World(savedData);
1019+ */
7291020 serialize ( ) : SerializedWorld {
7301021 const entities : SerializedEntity [ ] = [ ] ;
7311022
0 commit comments