@codehz/ecs 0.10.1 → 0.10.2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codehz/ecs",
3
- "version": "0.10.1",
3
+ "version": "0.10.2",
4
4
  "license": "MIT",
5
5
  "module": "src/index.ts",
6
6
  "type": "module",
@@ -273,34 +273,41 @@ export class World {
273
273
  }
274
274
 
275
275
  /**
276
- * Adds or updates a component on an entity (or marks void component as present).
276
+ * Marks a void component as present on an entity.
277
277
  * The change is buffered and takes effect after calling `world.sync()`.
278
- * If the entity does not exist, throws an error.
279
278
  *
280
- * @overload set(entityId: EntityId, componentType: EntityId<void>): void
281
- * Marks a void component as present on the entity
279
+ * @throws {Error} If the entity does not exist
280
+ * @throws {Error} If the component type is invalid or is a wildcard relation
282
281
  *
283
- * @overload set<T>(componentId: ComponentId<T>, component: Exclude<NoInfer<T>, number>): void
282
+ * @example
283
+ * world.set(entity, Marker);
284
+ * world.sync();
285
+ */
286
+ set(entityId: EntityId, componentType: EntityId<void>): void;
287
+ /**
284
288
  * @deprecated Use `world.singleton(componentId).set(value)` or `world.set(componentId, componentId, value)` instead.
285
- * Compatibility shorthand for singleton component data when the second argument is not a number
289
+ * Compatibility shorthand for singleton component data when the second argument is not a number.
286
290
  *
287
- * @overload set<T>(entityId: EntityId, componentType: EntityId<T>, component: NoInfer<T>): void
288
- * Adds or updates a component with data on the entity
291
+ * @throws {Error} If the component entity does not exist
292
+ *
293
+ * @example
294
+ * world.set(GlobalConfig, { debug: true });
295
+ * world.sync();
296
+ */
297
+ set<T>(componentId: ComponentId<T>, component: Exclude<NoInfer<T>, number>): void;
298
+ /**
299
+ * Adds or updates component data on an entity.
300
+ * The change is buffered and takes effect after calling `world.sync()`.
289
301
  *
290
302
  * @throws {Error} If the entity does not exist
291
303
  * @throws {Error} If the component type is invalid or is a wildcard relation
292
304
  *
293
305
  * @example
294
306
  * world.set(entity, Position, { x: 10, y: 20 });
295
- * world.set(entity, Marker); // void component
296
- * world.singleton(GlobalConfig).set({ debug: true }); // singleton component
297
- * world.set(GlobalConfig, { debug: true }); // deprecated singleton compatibility shorthand
298
- * world.sync(); // Apply changes
307
+ * world.sync();
299
308
  */
300
- set(entityId: EntityId, componentType: EntityId<void>): void;
301
- /** @deprecated Use `world.singleton(componentId).set(value)` or `world.set(componentId, componentId, value)` instead. */
302
- set<T>(componentId: ComponentId<T>, component: Exclude<NoInfer<T>, number>): void;
303
309
  set<T>(entityId: EntityId, componentType: EntityId<T>, component: NoInfer<T>): void;
310
+ /** Internal implementation for `set()` overloads. */
304
311
  set(entityId: EntityId | ComponentId, componentTypeOrComponent?: EntityId | any, maybeComponent?: any): void {
305
312
  const {
306
313
  entityId: targetEntityId,
@@ -319,13 +326,6 @@ export class World {
319
326
  /**
320
327
  * Removes a component from an entity.
321
328
  * The change is buffered and takes effect after calling `world.sync()`.
322
- * If the entity does not exist, throws an error.
323
- *
324
- * @overload remove<T>(entityId: EntityId, componentType: EntityId<T>): void
325
- * Removes a component from an entity.
326
- *
327
- * @overload remove<T>(componentId: ComponentId<T>): void
328
- * Removes a singleton component (shorthand for remove(componentId, componentId)).
329
329
  *
330
330
  * @template T - The component data type
331
331
  * @param entityId - The entity identifier
@@ -336,11 +336,22 @@ export class World {
336
336
  *
337
337
  * @example
338
338
  * world.remove(entity, Position);
339
+ * world.sync(); // Apply changes
340
+ */
341
+ remove<T>(entityId: EntityId, componentType: EntityId<T>): void;
342
+ /**
343
+ * Removes a singleton component (shorthand for remove(componentId, componentId)).
344
+ * The change is buffered and takes effect after calling `world.sync()`.
345
+ *
346
+ * @template T - The component data type
347
+ *
348
+ * @throws {Error} If the component entity does not exist
349
+ *
350
+ * @example
339
351
  * world.remove(GlobalConfig); // Remove singleton component
340
352
  * world.sync(); // Apply changes
341
353
  */
342
354
  remove<T>(componentId: ComponentId<T>): void;
343
- remove<T>(entityId: EntityId, componentType: EntityId<T>): void;
344
355
  remove<T>(entityId: EntityId | ComponentId, componentType?: EntityId<T>): void {
345
356
  const { entityId: targetEntityId, componentType: targetComponentType } = resolveRemoveOperation(
346
357
  entityId,
@@ -400,15 +411,8 @@ export class World {
400
411
  *
401
412
  * Immediately reflects the current state without waiting for `sync()`.
402
413
  *
403
- * @overload has<T>(entityId: EntityId, componentType: EntityId<T>): boolean
404
- * Checks if a specific component type is present on the entity.
405
- *
406
- * @overload has<T>(componentId: ComponentId<T>): boolean
407
- * Shorthand for checking a **singleton component** — a component that is its own
408
- * entity (component-as-entity pattern). Equivalent to `has(componentId, componentId)`.
409
- *
410
414
  * @template T - The component data type
411
- * @param entityId - The entity identifier, or a singleton component ID
415
+ * @param entityId - The entity identifier
412
416
  * @param componentType - The component type to check
413
417
  * @returns `true` if the entity has the component, `false` otherwise
414
418
  *
@@ -417,17 +421,24 @@ export class World {
417
421
  * if (world.has(entity, Position)) {
418
422
  * const pos = world.get(entity, Position);
419
423
  * }
424
+ */
425
+ has<T>(entityId: EntityId, componentType: EntityId<T>): boolean;
426
+ /**
427
+ * Checks if a **singleton component** (component-as-entity) is present.
428
+ * Equivalent to `has(componentId, componentId)`.
429
+ *
430
+ * Immediately reflects the current state without waiting for `sync()`.
420
431
  *
421
- * // Check a singleton component (component-as-entity)
432
+ * @template T - The component data type
433
+ * @param componentId - The singleton component ID
434
+ * @returns `true` if the singleton component exists, `false` otherwise
435
+ *
436
+ * @example
422
437
  * if (world.has(GlobalConfig)) {
423
438
  * const config = world.get(GlobalConfig);
424
439
  * }
425
- *
426
- * // Use exists() for entity liveness checks
427
- * if (world.exists(entity)) { ... }
428
440
  */
429
441
  has<T>(componentId: ComponentId<T>): boolean;
430
- has<T>(entityId: EntityId, componentType: EntityId<T>): boolean;
431
442
  has<T>(entityId: EntityId | ComponentId, componentType?: EntityId<T>): boolean {
432
443
  // Handle singleton component overload: has(componentId)
433
444
  if (componentType === undefined) {
@@ -461,28 +472,45 @@ export class World {
461
472
 
462
473
  /**
463
474
  * Retrieves a component from an entity.
464
- * For wildcard relations, returns all relations of that type.
465
475
  * Throws an error if the component does not exist; use `has()` to check first or use `getOptional()`.
466
476
  *
467
- * @overload get<T>(entityId: EntityId<T>): T
468
- * When called with only an entity ID, retrieves the entity's primary component.
477
+ * @template T - The component data type
478
+ * @param entityId - The entity identifier
479
+ * @param componentType - The component type to retrieve
469
480
  *
470
- * @overload get<T>(entityId: EntityId, componentType: WildcardRelationId<T>): [EntityId<unknown>, T][]
471
- * For wildcard relations, returns an array of [target entity, component value] pairs.
481
+ * @throws {Error} If the entity does not exist
482
+ * @throws {Error} If the component does not exist on the entity
472
483
  *
473
- * @overload get<T>(entityId: EntityId, componentType: EntityId<T>): T
474
- * Retrieves a specific component from the entity.
484
+ * @example
485
+ * const position = world.get(entity, Position);
486
+ */
487
+ get<T>(entityId: EntityId, componentType: EntityId<T>): T;
488
+ /**
489
+ * Retrieves all relations of a given wildcard type for an entity.
490
+ * Returns an array of [target entity, component value] pairs.
491
+ *
492
+ * @template T - The component data type
493
+ * @param entityId - The entity identifier
494
+ * @param componentType - The wildcard relation type
495
+ * @returns Array of [target entity, component value] pairs
475
496
  *
476
497
  * @throws {Error} If the entity does not exist
477
- * @throws {Error} If the component does not exist on the entity
478
498
  *
479
499
  * @example
480
- * const position = world.get(entity, Position); // Throws if no Position
481
- * const relations = world.get(entity, relation(Parent, "*")); // Wildcard relation
500
+ * const relations = world.get(entity, relation(Parent, "*"));
482
501
  */
483
- get<T>(entityId: EntityId<T>): T;
484
502
  get<T>(entityId: EntityId, componentType: WildcardRelationId<T>): [EntityId<unknown>, T][];
485
- get<T>(entityId: EntityId, componentType: EntityId<T>): T;
503
+ /**
504
+ * Retrieves the entity's primary component when called with only an entity ID.
505
+ *
506
+ * @template T - The component data type
507
+ * @param entityId - The entity identifier
508
+ * @returns The component value
509
+ *
510
+ * @throws {Error} If the entity does not exist
511
+ * @throws {Error} If the component does not exist on the entity
512
+ */
513
+ get<T>(entityId: EntityId<T>): T;
486
514
  get<T>(
487
515
  entityId: EntityId,
488
516
  componentType: EntityId<T> | WildcardRelationId<T> = entityId as EntityId<T>,
@@ -521,17 +549,11 @@ export class World {
521
549
  /**
522
550
  * Safely retrieves a component from an entity without throwing an error.
523
551
  * Returns `undefined` if the component does not exist.
524
- * For wildcard relations, returns `undefined` if there are no relations.
525
552
  *
526
553
  * @template T - The component data type
527
- * @overload getOptional<T>(entityId: EntityId<T>): { value: T } | undefined
528
- * Retrieves the entity's primary component safely.
529
- *
530
- * @overload getOptional<T>(entityId: EntityId, componentType: WildcardRelationId<T>): { value: [EntityId<unknown>, T][] } | undefined
531
- * Retrieves all matching relation values safely.
532
- *
533
- * @overload getOptional<T>(entityId: EntityId, componentType: EntityId<T>): { value: T } | undefined
534
- * Retrieves a specific component safely.
554
+ * @param entityId - The entity identifier
555
+ * @param componentType - The component type to retrieve
556
+ * @returns The component value wrapped in `{ value }`, or `undefined` if absent
535
557
  *
536
558
  * @throws {Error} If the entity does not exist
537
559
  *
@@ -541,12 +563,33 @@ export class World {
541
563
  * console.log(position.value.x);
542
564
  * }
543
565
  */
544
- getOptional<T>(entityId: EntityId<T>): { value: T } | undefined;
566
+ getOptional<T>(entityId: EntityId, componentType: EntityId<T>): { value: T } | undefined;
567
+ /**
568
+ * Safely retrieves all matching relation values for a wildcard relation type.
569
+ * Returns `undefined` if there are no relations.
570
+ *
571
+ * @template T - The component data type
572
+ * @param entityId - The entity identifier
573
+ * @param componentType - The wildcard relation type
574
+ * @returns Array of [target, value] pairs wrapped in `{ value }`, or `undefined` if none
575
+ *
576
+ * @throws {Error} If the entity does not exist
577
+ */
545
578
  getOptional<T>(
546
579
  entityId: EntityId,
547
580
  componentType: WildcardRelationId<T>,
548
581
  ): { value: [EntityId<unknown>, T][] } | undefined;
549
- getOptional<T>(entityId: EntityId, componentType: EntityId<T>): { value: T } | undefined;
582
+ /**
583
+ * Safely retrieves the entity's primary component without throwing an error.
584
+ * Returns `undefined` if the component does not exist.
585
+ *
586
+ * @template T - The component data type
587
+ * @param entityId - The entity identifier
588
+ * @returns The component value wrapped in `{ value }`, or `undefined` if absent
589
+ *
590
+ * @throws {Error} If the entity does not exist
591
+ */
592
+ getOptional<T>(entityId: EntityId<T>): { value: T } | undefined;
550
593
  getOptional<T>(
551
594
  entityId: EntityId,
552
595
  componentType: EntityId<T> | WildcardRelationId<T> = entityId as EntityId<T>,
@@ -786,21 +829,15 @@ export class World {
786
829
 
787
830
  /**
788
831
  * Registers a lifecycle hook that responds to component changes.
789
- * The hook callback is invoked when components matching the specified types are added, updated, or removed.
790
- * @overload hook<const T extends readonly ComponentType<any>[]>(
791
- * componentTypes: T,
792
- * hook: LifecycleHook<T> | LifecycleCallback<T>,
793
- * filter?: QueryFilter,
794
- * ): () => void
795
- * Registers a hook for multiple component types.
796
- * The hook is triggered when entities enter/exit the matching set.
832
+ * The hook callback is invoked when components matching the specified types
833
+ * are added, updated, or removed.
797
834
  *
798
835
  * @param componentTypes - Component types that define the matching entity set
799
836
  * @param hook - Either a hook object with on_init/on_set/on_remove handlers, or a callback function
800
837
  * @param filter - Optional query-style filter applied to the hook match set
801
838
  * @returns A function that unsubscribes the hook when called
802
839
  *
803
- * @throws {Error} If no required components are specified in array overload
840
+ * @throws {Error} If no required components are specified
804
841
  *
805
842
  * @example
806
843
  * const unsubscribe = world.hook([Position, Velocity], {
@@ -1085,32 +1122,30 @@ export class World {
1085
1122
 
1086
1123
  /**
1087
1124
  * Queries entities with specific components.
1088
- * For simpler use cases, prefer using `createQuery()` with `forEach()` which is cached and more efficient.
1089
- *
1090
- * @overload query(componentTypes: EntityId<any>[]): EntityId[]
1091
1125
  * Returns an array of entity IDs that have all specified components.
1092
- *
1093
- * @overload query<const T extends readonly EntityId<any>[]>(
1094
- * componentTypes: T,
1095
- * includeComponents: true,
1096
- * ): Array<{ entity: EntityId; components: ComponentTuple<T> }>
1097
- * Returns entities along with their component data.
1126
+ * For simpler use cases, prefer using `createQuery()` with `forEach()` which is cached and more efficient.
1098
1127
  *
1099
1128
  * @param componentTypes - Array of component types to query
1100
- * @param includeComponents - If true, includes component data in results
1101
- * @returns Array of entity IDs or objects with entities and components
1129
+ * @returns Array of entity IDs matching the query
1102
1130
  *
1103
1131
  * @example
1104
- * // Just entity IDs
1105
1132
  * const entities = world.query([Position, Velocity]);
1133
+ */
1134
+ query(componentTypes: EntityId<any>[]): EntityId[];
1135
+ /**
1136
+ * Queries entities with specific components and returns their component data.
1137
+ *
1138
+ * @template T - The tuple of component types
1139
+ * @param componentTypes - Array of component types to query
1140
+ * @param includeComponents - Must be `true` to include component data
1141
+ * @returns Array of objects with entity and component data
1106
1142
  *
1107
- * // With components
1143
+ * @example
1108
1144
  * const results = world.query([Position, Velocity], true);
1109
1145
  * results.forEach(({ entity, components: [pos, vel] }) => {
1110
1146
  * pos.x += vel.x;
1111
1147
  * });
1112
1148
  */
1113
- query(componentTypes: EntityId<any>[]): EntityId[];
1114
1149
  query<const T extends readonly EntityId<any>[]>(
1115
1150
  componentTypes: T,
1116
1151
  includeComponents: true,