@codehz/ecs 0.6.5 → 0.6.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/builder.d.mts CHANGED
@@ -436,21 +436,182 @@ declare class World {
436
436
  constructor(snapshot?: SerializedWorld);
437
437
  private deserializeSnapshot;
438
438
  private createArchetypeSignature;
439
+ /**
440
+ * Creates a new entity.
441
+ * The entity is created with an empty component set and can be configured using `set()`.
442
+ *
443
+ * @template T - The initial component type (defaults to void if not specified)
444
+ * @returns A unique identifier for the new entity
445
+ *
446
+ * @example
447
+ * const entity = world.new<MyComponent>();
448
+ * world.set(entity, MyComponent, { value: 42 });
449
+ * world.sync();
450
+ */
439
451
  new<T = void>(): EntityId<T>;
440
452
  private destroyEntityImmediate;
453
+ /**
454
+ * Checks if an entity exists in the world.
455
+ *
456
+ * @param entityId - The entity identifier to check
457
+ * @returns `true` if the entity exists, `false` otherwise
458
+ *
459
+ * @example
460
+ * if (world.exists(entityId)) {
461
+ * console.log("Entity exists");
462
+ * }
463
+ */
441
464
  exists(entityId: EntityId): boolean;
465
+ /**
466
+ * Adds or updates a component on an entity (or marks void component as present).
467
+ * The change is buffered and takes effect after calling `world.sync()`.
468
+ * If the entity does not exist, throws an error.
469
+ *
470
+ * @overload set(entityId: EntityId, componentType: EntityId<void>): void
471
+ * Marks a void component as present on the entity
472
+ *
473
+ * @overload set<T>(entityId: EntityId, componentType: EntityId<T>, component: NoInfer<T>): void
474
+ * Adds or updates a component with data on the entity
475
+ *
476
+ * @throws {Error} If the entity does not exist
477
+ * @throws {Error} If the component type is invalid or is a wildcard relation
478
+ *
479
+ * @example
480
+ * world.set(entity, Position, { x: 10, y: 20 });
481
+ * world.set(entity, Marker); // void component
482
+ * world.sync(); // Apply changes
483
+ */
442
484
  set(entityId: EntityId, componentType: EntityId<void>): void;
443
485
  set<T>(entityId: EntityId, componentType: EntityId<T>, component: NoInfer<T>): void;
486
+ /**
487
+ * Removes a component from an entity.
488
+ * The change is buffered and takes effect after calling `world.sync()`.
489
+ * If the entity does not exist, throws an error.
490
+ *
491
+ * @template T - The component data type
492
+ * @param entityId - The entity identifier
493
+ * @param componentType - The component type to remove
494
+ *
495
+ * @throws {Error} If the entity does not exist
496
+ * @throws {Error} If the component type is invalid
497
+ *
498
+ * @example
499
+ * world.remove(entity, Position);
500
+ * world.sync(); // Apply changes
501
+ */
444
502
  remove<T>(entityId: EntityId, componentType: EntityId<T>): void;
503
+ /**
504
+ * Deletes an entity and all its components from the world.
505
+ * The change is buffered and takes effect after calling `world.sync()`.
506
+ * Related entities may trigger cascade delete hooks if configured.
507
+ *
508
+ * @param entityId - The entity identifier to delete
509
+ *
510
+ * @example
511
+ * world.delete(entity);
512
+ * world.sync(); // Apply changes
513
+ */
445
514
  delete(entityId: EntityId): void;
515
+ /**
516
+ * Checks if an entity has a specific component.
517
+ * Immediately reflects the current state without waiting for `sync()`.
518
+ *
519
+ * @template T - The component data type
520
+ * @param entityId - The entity identifier
521
+ * @param componentType - The component type to check
522
+ * @returns `true` if the entity has the component, `false` otherwise
523
+ *
524
+ * @example
525
+ * if (world.has(entity, Position)) {
526
+ * const pos = world.get(entity, Position);
527
+ * }
528
+ */
446
529
  has<T>(entityId: EntityId, componentType: EntityId<T>): boolean;
530
+ /**
531
+ * Retrieves a component from an entity.
532
+ * For wildcard relations, returns all relations of that type.
533
+ * Throws an error if the component does not exist; use `has()` to check first or use `getOptional()`.
534
+ *
535
+ * @overload get<T>(entityId: EntityId<T>): T
536
+ * When called with only an entity ID, retrieves the entity's primary component.
537
+ *
538
+ * @overload get<T>(entityId: EntityId, componentType: WildcardRelationId<T>): [EntityId<unknown>, T][]
539
+ * For wildcard relations, returns an array of [target entity, component value] pairs.
540
+ *
541
+ * @overload get<T>(entityId: EntityId, componentType: EntityId<T>): T
542
+ * Retrieves a specific component from the entity.
543
+ *
544
+ * @throws {Error} If the entity does not exist
545
+ * @throws {Error} If the component does not exist on the entity
546
+ *
547
+ * @example
548
+ * const position = world.get(entity, Position); // Throws if no Position
549
+ * const relations = world.get(entity, relation(Parent, "*")); // Wildcard relation
550
+ */
551
+ get<T>(entityId: EntityId<T>): T;
447
552
  get<T>(entityId: EntityId, componentType: WildcardRelationId<T>): [EntityId<unknown>, T][];
448
553
  get<T>(entityId: EntityId, componentType: EntityId<T>): T;
554
+ /**
555
+ * Safely retrieves a component from an entity without throwing an error.
556
+ * Returns `undefined` if the component does not exist.
557
+ * For wildcard relations, returns `undefined` if there are no relations.
558
+ *
559
+ * @template T - The component data type
560
+ * @overload getOptional<T>(entityId: EntityId<T>): { value: T } | undefined
561
+ * Retrieves the entity's primary component safely.
562
+ *
563
+ * @overload getOptional<T>(entityId: EntityId, componentType: EntityId<T>): { value: T } | undefined
564
+ * Retrieves a specific component safely.
565
+ *
566
+ * @throws {Error} If the entity does not exist
567
+ *
568
+ * @example
569
+ * const position = world.getOptional(entity, Position);
570
+ * if (position) {
571
+ * console.log(position.value.x);
572
+ * }
573
+ */
574
+ getOptional<T>(entityId: EntityId<T>): {
575
+ value: T;
576
+ } | undefined;
449
577
  getOptional<T>(entityId: EntityId, componentType: EntityId<T>): {
450
578
  value: T;
451
579
  } | undefined;
452
580
  /**
453
- * @deprecated use array overload with LifecycleCallback
581
+ * Registers a lifecycle hook that responds to component changes.
582
+ * The hook callback is invoked when components matching the specified types are added, updated, or removed.
583
+ *
584
+ * @deprecated For single components, use the array overload with LifecycleCallback for better multi-component support
585
+ *
586
+ * @overload hook<T>(componentType: EntityId<T>, hook: LegacyLifecycleHook<T> | LegacyLifecycleCallback<T>): () => void
587
+ * Registers a hook for a single component type (legacy API).
588
+ *
589
+ * @overload hook<const T extends readonly ComponentType<any>[]>(
590
+ * componentTypes: T,
591
+ * hook: LifecycleHook<T> | LifecycleCallback<T>,
592
+ * ): () => void
593
+ * Registers a hook for multiple component types.
594
+ * The hook is triggered when all required components change together.
595
+ *
596
+ * @param componentTypesOrSingle - A single component type or an array of component types
597
+ * @param hook - Either a hook object with on_init/on_set/on_remove handlers, or a callback function
598
+ * @returns A function that unsubscribes the hook when called
599
+ *
600
+ * @throws {Error} If no required components are specified in array overload
601
+ *
602
+ * @example
603
+ * // Array overload (recommended)
604
+ * const unsubscribe = world.hook([Position, Velocity], {
605
+ * on_init: (entityId, position, velocity) => console.log("Initialized"),
606
+ * on_set: (entityId, position, velocity) => console.log("Updated"),
607
+ * on_remove: (entityId, position, velocity) => console.log("Removed"),
608
+ * });
609
+ * unsubscribe(); // Remove hook
610
+ *
611
+ * // Callback style
612
+ * const unsubscribe = world.hook([Position], (event, entityId, position) => {
613
+ * if (event === "init") console.log("Initialized");
614
+ * });
454
615
  */
455
616
  hook<T>(componentType: EntityId<T>, hook: LegacyLifecycleHook<T> | LegacyLifecycleCallback<T>): () => void;
456
617
  hook<const T extends readonly ComponentType<any>[]>(componentTypes: T, hook: LifecycleHook<T> | LifecycleCallback<T>): () => void;
@@ -458,15 +619,128 @@ declare class World {
458
619
  unhook<T>(componentType: EntityId<T>, hook: LegacyLifecycleHook<T>): void;
459
620
  /** @deprecated use the unsubscribe function returned by hook() instead */
460
621
  unhook<const T extends readonly ComponentType<any>[]>(componentTypes: T, hook: LifecycleHook<T>): void;
622
+ /**
623
+ * Synchronizes all buffered commands (set/remove/delete) to the world.
624
+ * This method must be called after making changes via `set()`, `remove()`, or `delete()` for them to take effect.
625
+ * Typically called once per frame at the end of your game loop.
626
+ *
627
+ * @example
628
+ * world.set(entity, Position, { x: 10, y: 20 });
629
+ * world.remove(entity, OldComponent);
630
+ * world.sync(); // Apply all buffered changes
631
+ */
461
632
  sync(): void;
633
+ /**
634
+ * Creates a cached query for efficiently iterating entities with specific components.
635
+ * The query is cached internally and reused across calls with the same component types and filter.
636
+ *
637
+ * **Important:** Store the query reference and reuse it across frames for optimal performance.
638
+ * Creating a new query each frame defeats the caching mechanism.
639
+ *
640
+ * @param componentTypes - Array of component types to match
641
+ * @param filter - Optional filter for additional constraints (e.g., without specific components)
642
+ * @returns A Query instance that can be used to iterate matching entities
643
+ *
644
+ * @example
645
+ * // Create once, reuse many times
646
+ * const movementQuery = world.createQuery([Position, Velocity]);
647
+ *
648
+ * // In game loop
649
+ * movementQuery.forEach((entity) => {
650
+ * const pos = world.get(entity, Position);
651
+ * const vel = world.get(entity, Velocity);
652
+ * pos.x += vel.x;
653
+ * pos.y += vel.y;
654
+ * });
655
+ *
656
+ * // With filter
657
+ * const activeQuery = world.createQuery([Position], {
658
+ * without: [Disabled]
659
+ * });
660
+ */
462
661
  createQuery(componentTypes: EntityId<any>[], filter?: QueryFilter): Query;
662
+ /**
663
+ * Creates a new entity builder for fluent entity configuration.
664
+ * Useful for building entities with multiple components in a single expression.
665
+ *
666
+ * @returns An EntityBuilder instance
667
+ *
668
+ * @example
669
+ * const entity = world.spawn()
670
+ * .with(Position, { x: 0, y: 0 })
671
+ * .with(Velocity, { x: 1, y: 1 })
672
+ * .build();
673
+ * world.sync(); // Apply changes
674
+ */
463
675
  spawn(): EntityBuilder;
676
+ /**
677
+ * Spawns multiple entities with a configuration callback.
678
+ * More efficient than calling `spawn()` multiple times when creating many entities.
679
+ *
680
+ * @param count - Number of entities to spawn
681
+ * @param configure - Callback that receives an EntityBuilder and index; must return the configured builder
682
+ * @returns Array of created entity IDs
683
+ *
684
+ * @example
685
+ * const entities = world.spawnMany(100, (builder, index) => {
686
+ * return builder
687
+ * .with(Position, { x: index * 10, y: 0 })
688
+ * .with(Velocity, { x: 0, y: 1 });
689
+ * });
690
+ * world.sync();
691
+ */
464
692
  spawnMany(count: number, configure: (builder: EntityBuilder, index: number) => EntityBuilder): EntityId[];
465
693
  _registerQuery(query: Query): void;
466
694
  _unregisterQuery(query: Query): void;
695
+ /**
696
+ * Releases a cached query and frees its resources if no longer needed.
697
+ * Call this when you're done using a query to allow the world to clean up its cache entry.
698
+ *
699
+ * @param query - The query to release
700
+ *
701
+ * @example
702
+ * const query = world.createQuery([Position]);
703
+ * // ... use query ...
704
+ * world.releaseQuery(query); // Optional cleanup
705
+ */
467
706
  releaseQuery(query: Query): void;
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
+ */
468
715
  getMatchingArchetypes(componentTypes: EntityId<any>[]): Archetype[];
469
716
  private getArchetypesWithComponents;
717
+ /**
718
+ * Queries entities with specific components.
719
+ * For simpler use cases, prefer using `createQuery()` with `forEach()` which is cached and more efficient.
720
+ *
721
+ * @overload query(componentTypes: EntityId<any>[]): EntityId[]
722
+ * Returns an array of entity IDs that have all specified components.
723
+ *
724
+ * @overload query<const T extends readonly EntityId<any>[]>(
725
+ * componentTypes: T,
726
+ * includeComponents: true,
727
+ * ): Array<{ entity: EntityId; components: ComponentTuple<T> }>
728
+ * Returns entities along with their component data.
729
+ *
730
+ * @param componentTypes - Array of component types to query
731
+ * @param includeComponents - If true, includes component data in results
732
+ * @returns Array of entity IDs or objects with entities and components
733
+ *
734
+ * @example
735
+ * // Just entity IDs
736
+ * const entities = world.query([Position, Velocity]);
737
+ *
738
+ * // With components
739
+ * const results = world.query([Position, Velocity], true);
740
+ * results.forEach(({ entity, components: [pos, vel] }) => {
741
+ * pos.x += vel.x;
742
+ * });
743
+ */
470
744
  query(componentTypes: EntityId<any>[]): EntityId[];
471
745
  query<const T extends readonly EntityId<any>[]>(componentTypes: T, includeComponents: true): Array<{
472
746
  entity: EntityId;
@@ -483,6 +757,27 @@ declare class World {
483
757
  private archetypeReferencesEntity;
484
758
  private cleanupArchetypesReferencingEntity;
485
759
  private removeArchetype;
760
+ /**
761
+ * Serializes the entire world state to a plain JavaScript object.
762
+ * This creates a "memory snapshot" that can be stored or transmitted.
763
+ * The snapshot can be restored using `new World(snapshot)`.
764
+ *
765
+ * **Note:** This is NOT automatically persistent storage. To persist data,
766
+ * you must serialize the returned object to JSON or another format yourself.
767
+ *
768
+ * @returns A serializable object representing the world state
769
+ *
770
+ * @example
771
+ * // Create snapshot
772
+ * const snapshot = world.serialize();
773
+ *
774
+ * // Save to storage (example)
775
+ * localStorage.setItem('save', JSON.stringify(snapshot));
776
+ *
777
+ * // Later, restore from snapshot
778
+ * const savedData = JSON.parse(localStorage.getItem('save'));
779
+ * const newWorld = new World(savedData);
780
+ */
486
781
  serialize(): SerializedWorld;
487
782
  }
488
783
  //#endregion
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codehz/ecs",
3
- "version": "0.6.5",
3
+ "version": "0.6.6",
4
4
  "repository": {
5
5
  "url": "https://github.com/codehz/ecs"
6
6
  },
package/world.mjs CHANGED
@@ -1799,6 +1799,18 @@ var World = class {
1799
1799
  createArchetypeSignature(componentTypes) {
1800
1800
  return componentTypes.join(",");
1801
1801
  }
1802
+ /**
1803
+ * Creates a new entity.
1804
+ * The entity is created with an empty component set and can be configured using `set()`.
1805
+ *
1806
+ * @template T - The initial component type (defaults to void if not specified)
1807
+ * @returns A unique identifier for the new entity
1808
+ *
1809
+ * @example
1810
+ * const entity = world.new<MyComponent>();
1811
+ * world.set(entity, MyComponent, { value: 42 });
1812
+ * world.sync();
1813
+ */
1802
1814
  new() {
1803
1815
  const entityId = this.entityIdManager.allocate();
1804
1816
  let emptyArchetype = this.ensureArchetype([]);
@@ -1829,6 +1841,17 @@ var World = class {
1829
1841
  this.entityIdManager.deallocate(cur);
1830
1842
  }
1831
1843
  }
1844
+ /**
1845
+ * Checks if an entity exists in the world.
1846
+ *
1847
+ * @param entityId - The entity identifier to check
1848
+ * @returns `true` if the entity exists, `false` otherwise
1849
+ *
1850
+ * @example
1851
+ * if (world.exists(entityId)) {
1852
+ * console.log("Entity exists");
1853
+ * }
1854
+ */
1832
1855
  exists(entityId) {
1833
1856
  return this.entityToArchetype.has(entityId);
1834
1857
  }
@@ -1839,14 +1862,55 @@ var World = class {
1839
1862
  if (detailedType.type === "wildcard-relation") throw new Error(`Cannot directly add wildcard relation components: ${componentType}`);
1840
1863
  this.commandBuffer.set(entityId, componentType, component$1);
1841
1864
  }
1865
+ /**
1866
+ * Removes a component from an entity.
1867
+ * The change is buffered and takes effect after calling `world.sync()`.
1868
+ * If the entity does not exist, throws an error.
1869
+ *
1870
+ * @template T - The component data type
1871
+ * @param entityId - The entity identifier
1872
+ * @param componentType - The component type to remove
1873
+ *
1874
+ * @throws {Error} If the entity does not exist
1875
+ * @throws {Error} If the component type is invalid
1876
+ *
1877
+ * @example
1878
+ * world.remove(entity, Position);
1879
+ * world.sync(); // Apply changes
1880
+ */
1842
1881
  remove(entityId, componentType) {
1843
1882
  if (!this.exists(entityId)) throw new Error(`Entity ${entityId} does not exist`);
1844
1883
  if (getDetailedIdType(componentType).type === "invalid") throw new Error(`Invalid component type: ${componentType}`);
1845
1884
  this.commandBuffer.remove(entityId, componentType);
1846
1885
  }
1886
+ /**
1887
+ * Deletes an entity and all its components from the world.
1888
+ * The change is buffered and takes effect after calling `world.sync()`.
1889
+ * Related entities may trigger cascade delete hooks if configured.
1890
+ *
1891
+ * @param entityId - The entity identifier to delete
1892
+ *
1893
+ * @example
1894
+ * world.delete(entity);
1895
+ * world.sync(); // Apply changes
1896
+ */
1847
1897
  delete(entityId) {
1848
1898
  this.commandBuffer.delete(entityId);
1849
1899
  }
1900
+ /**
1901
+ * Checks if an entity has a specific component.
1902
+ * Immediately reflects the current state without waiting for `sync()`.
1903
+ *
1904
+ * @template T - The component data type
1905
+ * @param entityId - The entity identifier
1906
+ * @param componentType - The component type to check
1907
+ * @returns `true` if the entity has the component, `false` otherwise
1908
+ *
1909
+ * @example
1910
+ * if (world.has(entity, Position)) {
1911
+ * const pos = world.get(entity, Position);
1912
+ * }
1913
+ */
1850
1914
  has(entityId, componentType) {
1851
1915
  const archetype = this.entityToArchetype.get(entityId);
1852
1916
  if (!archetype) return false;
@@ -1854,7 +1918,7 @@ var World = class {
1854
1918
  if (isDontFragmentRelation(componentType)) return this.dontFragmentRelations.get(entityId)?.has(componentType) ?? false;
1855
1919
  return false;
1856
1920
  }
1857
- get(entityId, componentType) {
1921
+ get(entityId, componentType = entityId) {
1858
1922
  const archetype = this.entityToArchetype.get(entityId);
1859
1923
  if (!archetype) throw new Error(`Entity ${entityId} does not exist`);
1860
1924
  if (componentType >= 0 || componentType % 2 ** 42 !== 0) {
@@ -1864,7 +1928,7 @@ var World = class {
1864
1928
  }
1865
1929
  return archetype.get(entityId, componentType);
1866
1930
  }
1867
- getOptional(entityId, componentType) {
1931
+ getOptional(entityId, componentType = entityId) {
1868
1932
  const archetype = this.entityToArchetype.get(entityId);
1869
1933
  if (!archetype) throw new Error(`Entity ${entityId} does not exist`);
1870
1934
  if (isWildcardRelationId(componentType)) {
@@ -1957,9 +2021,47 @@ var World = class {
1957
2021
  }
1958
2022
  }
1959
2023
  }
2024
+ /**
2025
+ * Synchronizes all buffered commands (set/remove/delete) to the world.
2026
+ * This method must be called after making changes via `set()`, `remove()`, or `delete()` for them to take effect.
2027
+ * Typically called once per frame at the end of your game loop.
2028
+ *
2029
+ * @example
2030
+ * world.set(entity, Position, { x: 10, y: 20 });
2031
+ * world.remove(entity, OldComponent);
2032
+ * world.sync(); // Apply all buffered changes
2033
+ */
1960
2034
  sync() {
1961
2035
  this.commandBuffer.execute();
1962
2036
  }
2037
+ /**
2038
+ * Creates a cached query for efficiently iterating entities with specific components.
2039
+ * The query is cached internally and reused across calls with the same component types and filter.
2040
+ *
2041
+ * **Important:** Store the query reference and reuse it across frames for optimal performance.
2042
+ * Creating a new query each frame defeats the caching mechanism.
2043
+ *
2044
+ * @param componentTypes - Array of component types to match
2045
+ * @param filter - Optional filter for additional constraints (e.g., without specific components)
2046
+ * @returns A Query instance that can be used to iterate matching entities
2047
+ *
2048
+ * @example
2049
+ * // Create once, reuse many times
2050
+ * const movementQuery = world.createQuery([Position, Velocity]);
2051
+ *
2052
+ * // In game loop
2053
+ * movementQuery.forEach((entity) => {
2054
+ * const pos = world.get(entity, Position);
2055
+ * const vel = world.get(entity, Velocity);
2056
+ * pos.x += vel.x;
2057
+ * pos.y += vel.y;
2058
+ * });
2059
+ *
2060
+ * // With filter
2061
+ * const activeQuery = world.createQuery([Position], {
2062
+ * without: [Disabled]
2063
+ * });
2064
+ */
1963
2065
  createQuery(componentTypes, filter = {}) {
1964
2066
  const sortedTypes = [...componentTypes].sort((a, b) => a - b);
1965
2067
  const filterKey = serializeQueryFilter(filter);
@@ -1976,9 +2078,38 @@ var World = class {
1976
2078
  });
1977
2079
  return query;
1978
2080
  }
2081
+ /**
2082
+ * Creates a new entity builder for fluent entity configuration.
2083
+ * Useful for building entities with multiple components in a single expression.
2084
+ *
2085
+ * @returns An EntityBuilder instance
2086
+ *
2087
+ * @example
2088
+ * const entity = world.spawn()
2089
+ * .with(Position, { x: 0, y: 0 })
2090
+ * .with(Velocity, { x: 1, y: 1 })
2091
+ * .build();
2092
+ * world.sync(); // Apply changes
2093
+ */
1979
2094
  spawn() {
1980
2095
  return new EntityBuilder(this);
1981
2096
  }
2097
+ /**
2098
+ * Spawns multiple entities with a configuration callback.
2099
+ * More efficient than calling `spawn()` multiple times when creating many entities.
2100
+ *
2101
+ * @param count - Number of entities to spawn
2102
+ * @param configure - Callback that receives an EntityBuilder and index; must return the configured builder
2103
+ * @returns Array of created entity IDs
2104
+ *
2105
+ * @example
2106
+ * const entities = world.spawnMany(100, (builder, index) => {
2107
+ * return builder
2108
+ * .with(Position, { x: index * 10, y: 0 })
2109
+ * .with(Velocity, { x: 0, y: 1 });
2110
+ * });
2111
+ * world.sync();
2112
+ */
1982
2113
  spawnMany(count, configure) {
1983
2114
  const entities = [];
1984
2115
  for (let i = 0; i < count; i++) {
@@ -1994,6 +2125,17 @@ var World = class {
1994
2125
  const index = this.queries.indexOf(query);
1995
2126
  if (index !== -1) this.queries.splice(index, 1);
1996
2127
  }
2128
+ /**
2129
+ * Releases a cached query and frees its resources if no longer needed.
2130
+ * Call this when you're done using a query to allow the world to clean up its cache entry.
2131
+ *
2132
+ * @param query - The query to release
2133
+ *
2134
+ * @example
2135
+ * const query = world.createQuery([Position]);
2136
+ * // ... use query ...
2137
+ * world.releaseQuery(query); // Optional cleanup
2138
+ */
1997
2139
  releaseQuery(query) {
1998
2140
  for (const [k, v] of this.queryCache.entries()) if (v.query === query) {
1999
2141
  v.refCount--;
@@ -2005,6 +2147,14 @@ var World = class {
2005
2147
  return;
2006
2148
  }
2007
2149
  }
2150
+ /**
2151
+ * Returns all archetypes that contain entities with the specified components.
2152
+ * Used internally for query optimization but can be useful for debugging.
2153
+ *
2154
+ * @param componentTypes - Array of component types to match
2155
+ * @returns Array of Archetype objects containing matching components
2156
+ * @internal
2157
+ */
2008
2158
  getMatchingArchetypes(componentTypes) {
2009
2159
  if (componentTypes.length === 0) return [...this.archetypes];
2010
2160
  const regularComponents = [];
@@ -2148,6 +2298,27 @@ var World = class {
2148
2298
  }
2149
2299
  for (const query of this.queries) query.removeArchetype(archetype);
2150
2300
  }
2301
+ /**
2302
+ * Serializes the entire world state to a plain JavaScript object.
2303
+ * This creates a "memory snapshot" that can be stored or transmitted.
2304
+ * The snapshot can be restored using `new World(snapshot)`.
2305
+ *
2306
+ * **Note:** This is NOT automatically persistent storage. To persist data,
2307
+ * you must serialize the returned object to JSON or another format yourself.
2308
+ *
2309
+ * @returns A serializable object representing the world state
2310
+ *
2311
+ * @example
2312
+ * // Create snapshot
2313
+ * const snapshot = world.serialize();
2314
+ *
2315
+ * // Save to storage (example)
2316
+ * localStorage.setItem('save', JSON.stringify(snapshot));
2317
+ *
2318
+ * // Later, restore from snapshot
2319
+ * const savedData = JSON.parse(localStorage.getItem('save'));
2320
+ * const newWorld = new World(savedData);
2321
+ */
2151
2322
  serialize() {
2152
2323
  const entities = [];
2153
2324
  for (const archetype of this.archetypes) {