@codehz/ecs 0.10.0 → 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.
@@ -1022,8 +1022,9 @@ interface SingletonHandleOps<T> {
1022
1022
  /**
1023
1023
  * Explicit handle for a singleton component (component-as-entity).
1024
1024
  *
1025
- * This provides an explicit and concise API for singleton components without
1026
- * overloading `world.set()` semantics.
1025
+ * This is the preferred API for singleton components.
1026
+ * `world.set(componentId, value)` remains available only as a deprecated
1027
+ * compatibility shorthand.
1027
1028
  *
1028
1029
  * @example
1029
1030
  * const config = world.singleton(Config);
@@ -1050,6 +1051,7 @@ declare class SingletonHandle<T = void> {
1050
1051
  * Manages entities and components
1051
1052
  */
1052
1053
  declare class World {
1054
+ private static readonly DEPRECATED_SINGLETON_SET_SHORTHAND_WARNING;
1053
1055
  private entityIdManager;
1054
1056
  private entityReferences;
1055
1057
  /** Sparse relation storage (for components created with `sparse: true`), shared with all Archetype instances */
@@ -1112,37 +1114,43 @@ declare class World {
1112
1114
  */
1113
1115
  exists(entityId: EntityId): boolean;
1114
1116
  /**
1115
- * Adds or updates a component on an entity (or marks void component as present).
1117
+ * Marks a void component as present on an entity.
1116
1118
  * The change is buffered and takes effect after calling `world.sync()`.
1117
- * If the entity does not exist, throws an error.
1118
1119
  *
1119
- * @overload set(entityId: EntityId, componentType: EntityId<void>): void
1120
- * Marks a void component as present on the entity
1120
+ * @throws {Error} If the entity does not exist
1121
+ * @throws {Error} If the component type is invalid or is a wildcard relation
1121
1122
  *
1122
- * @overload set<T>(entityId: EntityId, componentType: EntityId<T>, component: NoInfer<T>): void
1123
- * Adds or updates a component with data on the entity
1123
+ * @example
1124
+ * world.set(entity, Marker);
1125
+ * world.sync();
1126
+ */
1127
+ set(entityId: EntityId, componentType: EntityId<void>): void;
1128
+ /**
1129
+ * @deprecated Use `world.singleton(componentId).set(value)` or `world.set(componentId, componentId, value)` instead.
1130
+ * Compatibility shorthand for singleton component data when the second argument is not a number.
1131
+ *
1132
+ * @throws {Error} If the component entity does not exist
1133
+ *
1134
+ * @example
1135
+ * world.set(GlobalConfig, { debug: true });
1136
+ * world.sync();
1137
+ */
1138
+ set<T>(componentId: ComponentId<T>, component: Exclude<NoInfer<T>, number>): void;
1139
+ /**
1140
+ * Adds or updates component data on an entity.
1141
+ * The change is buffered and takes effect after calling `world.sync()`.
1124
1142
  *
1125
1143
  * @throws {Error} If the entity does not exist
1126
1144
  * @throws {Error} If the component type is invalid or is a wildcard relation
1127
1145
  *
1128
1146
  * @example
1129
1147
  * world.set(entity, Position, { x: 10, y: 20 });
1130
- * world.set(entity, Marker); // void component
1131
- * world.singleton(GlobalConfig).set({ debug: true }); // singleton component
1132
- * world.sync(); // Apply changes
1148
+ * world.sync();
1133
1149
  */
1134
- set(entityId: EntityId, componentType: EntityId<void>): void;
1135
1150
  set<T>(entityId: EntityId, componentType: EntityId<T>, component: NoInfer<T>): void;
1136
1151
  /**
1137
1152
  * Removes a component from an entity.
1138
1153
  * The change is buffered and takes effect after calling `world.sync()`.
1139
- * If the entity does not exist, throws an error.
1140
- *
1141
- * @overload remove<T>(entityId: EntityId, componentType: EntityId<T>): void
1142
- * Removes a component from an entity.
1143
- *
1144
- * @overload remove<T>(componentId: ComponentId<T>): void
1145
- * Removes a singleton component (shorthand for remove(componentId, componentId)).
1146
1154
  *
1147
1155
  * @template T - The component data type
1148
1156
  * @param entityId - The entity identifier
@@ -1153,11 +1161,22 @@ declare class World {
1153
1161
  *
1154
1162
  * @example
1155
1163
  * world.remove(entity, Position);
1164
+ * world.sync(); // Apply changes
1165
+ */
1166
+ remove<T>(entityId: EntityId, componentType: EntityId<T>): void;
1167
+ /**
1168
+ * Removes a singleton component (shorthand for remove(componentId, componentId)).
1169
+ * The change is buffered and takes effect after calling `world.sync()`.
1170
+ *
1171
+ * @template T - The component data type
1172
+ *
1173
+ * @throws {Error} If the component entity does not exist
1174
+ *
1175
+ * @example
1156
1176
  * world.remove(GlobalConfig); // Remove singleton component
1157
1177
  * world.sync(); // Apply changes
1158
1178
  */
1159
1179
  remove<T>(componentId: ComponentId<T>): void;
1160
- remove<T>(entityId: EntityId, componentType: EntityId<T>): void;
1161
1180
  /**
1162
1181
  * Deletes an entity and all its components from the world.
1163
1182
  * The change is buffered and takes effect after calling `world.sync()`.
@@ -1191,15 +1210,8 @@ declare class World {
1191
1210
  *
1192
1211
  * Immediately reflects the current state without waiting for `sync()`.
1193
1212
  *
1194
- * @overload has<T>(entityId: EntityId, componentType: EntityId<T>): boolean
1195
- * Checks if a specific component type is present on the entity.
1196
- *
1197
- * @overload has<T>(componentId: ComponentId<T>): boolean
1198
- * Shorthand for checking a **singleton component** — a component that is its own
1199
- * entity (component-as-entity pattern). Equivalent to `has(componentId, componentId)`.
1200
- *
1201
1213
  * @template T - The component data type
1202
- * @param entityId - The entity identifier, or a singleton component ID
1214
+ * @param entityId - The entity identifier
1203
1215
  * @param componentType - The component type to check
1204
1216
  * @returns `true` if the entity has the component, `false` otherwise
1205
1217
  *
@@ -1208,55 +1220,73 @@ declare class World {
1208
1220
  * if (world.has(entity, Position)) {
1209
1221
  * const pos = world.get(entity, Position);
1210
1222
  * }
1223
+ */
1224
+ has<T>(entityId: EntityId, componentType: EntityId<T>): boolean;
1225
+ /**
1226
+ * Checks if a **singleton component** (component-as-entity) is present.
1227
+ * Equivalent to `has(componentId, componentId)`.
1211
1228
  *
1212
- * // Check a singleton component (component-as-entity)
1229
+ * Immediately reflects the current state without waiting for `sync()`.
1230
+ *
1231
+ * @template T - The component data type
1232
+ * @param componentId - The singleton component ID
1233
+ * @returns `true` if the singleton component exists, `false` otherwise
1234
+ *
1235
+ * @example
1213
1236
  * if (world.has(GlobalConfig)) {
1214
1237
  * const config = world.get(GlobalConfig);
1215
1238
  * }
1216
- *
1217
- * // Use exists() for entity liveness checks
1218
- * if (world.exists(entity)) { ... }
1219
1239
  */
1220
1240
  has<T>(componentId: ComponentId<T>): boolean;
1221
- has<T>(entityId: EntityId, componentType: EntityId<T>): boolean;
1222
1241
  /**
1223
1242
  * Retrieves a component from an entity.
1224
- * For wildcard relations, returns all relations of that type.
1225
1243
  * Throws an error if the component does not exist; use `has()` to check first or use `getOptional()`.
1226
1244
  *
1227
- * @overload get<T>(entityId: EntityId<T>): T
1228
- * When called with only an entity ID, retrieves the entity's primary component.
1245
+ * @template T - The component data type
1246
+ * @param entityId - The entity identifier
1247
+ * @param componentType - The component type to retrieve
1248
+ *
1249
+ * @throws {Error} If the entity does not exist
1250
+ * @throws {Error} If the component does not exist on the entity
1229
1251
  *
1230
- * @overload get<T>(entityId: EntityId, componentType: WildcardRelationId<T>): [EntityId<unknown>, T][]
1231
- * For wildcard relations, returns an array of [target entity, component value] pairs.
1252
+ * @example
1253
+ * const position = world.get(entity, Position);
1254
+ */
1255
+ get<T>(entityId: EntityId, componentType: EntityId<T>): T;
1256
+ /**
1257
+ * Retrieves all relations of a given wildcard type for an entity.
1258
+ * Returns an array of [target entity, component value] pairs.
1232
1259
  *
1233
- * @overload get<T>(entityId: EntityId, componentType: EntityId<T>): T
1234
- * Retrieves a specific component from the entity.
1260
+ * @template T - The component data type
1261
+ * @param entityId - The entity identifier
1262
+ * @param componentType - The wildcard relation type
1263
+ * @returns Array of [target entity, component value] pairs
1235
1264
  *
1236
1265
  * @throws {Error} If the entity does not exist
1237
- * @throws {Error} If the component does not exist on the entity
1238
1266
  *
1239
1267
  * @example
1240
- * const position = world.get(entity, Position); // Throws if no Position
1241
- * const relations = world.get(entity, relation(Parent, "*")); // Wildcard relation
1268
+ * const relations = world.get(entity, relation(Parent, "*"));
1242
1269
  */
1243
- get<T>(entityId: EntityId<T>): T;
1244
1270
  get<T>(entityId: EntityId, componentType: WildcardRelationId<T>): [EntityId<unknown>, T][];
1245
- get<T>(entityId: EntityId, componentType: EntityId<T>): T;
1246
1271
  /**
1247
- * Safely retrieves a component from an entity without throwing an error.
1248
- * Returns `undefined` if the component does not exist.
1249
- * For wildcard relations, returns `undefined` if there are no relations.
1272
+ * Retrieves the entity's primary component when called with only an entity ID.
1250
1273
  *
1251
1274
  * @template T - The component data type
1252
- * @overload getOptional<T>(entityId: EntityId<T>): { value: T } | undefined
1253
- * Retrieves the entity's primary component safely.
1275
+ * @param entityId - The entity identifier
1276
+ * @returns The component value
1254
1277
  *
1255
- * @overload getOptional<T>(entityId: EntityId, componentType: WildcardRelationId<T>): { value: [EntityId<unknown>, T][] } | undefined
1256
- * Retrieves all matching relation values safely.
1278
+ * @throws {Error} If the entity does not exist
1279
+ * @throws {Error} If the component does not exist on the entity
1280
+ */
1281
+ get<T>(entityId: EntityId<T>): T;
1282
+ /**
1283
+ * Safely retrieves a component from an entity without throwing an error.
1284
+ * Returns `undefined` if the component does not exist.
1257
1285
  *
1258
- * @overload getOptional<T>(entityId: EntityId, componentType: EntityId<T>): { value: T } | undefined
1259
- * Retrieves a specific component safely.
1286
+ * @template T - The component data type
1287
+ * @param entityId - The entity identifier
1288
+ * @param componentType - The component type to retrieve
1289
+ * @returns The component value wrapped in `{ value }`, or `undefined` if absent
1260
1290
  *
1261
1291
  * @throws {Error} If the entity does not exist
1262
1292
  *
@@ -1266,13 +1296,34 @@ declare class World {
1266
1296
  * console.log(position.value.x);
1267
1297
  * }
1268
1298
  */
1269
- getOptional<T>(entityId: EntityId<T>): {
1299
+ getOptional<T>(entityId: EntityId, componentType: EntityId<T>): {
1270
1300
  value: T;
1271
1301
  } | undefined;
1302
+ /**
1303
+ * Safely retrieves all matching relation values for a wildcard relation type.
1304
+ * Returns `undefined` if there are no relations.
1305
+ *
1306
+ * @template T - The component data type
1307
+ * @param entityId - The entity identifier
1308
+ * @param componentType - The wildcard relation type
1309
+ * @returns Array of [target, value] pairs wrapped in `{ value }`, or `undefined` if none
1310
+ *
1311
+ * @throws {Error} If the entity does not exist
1312
+ */
1272
1313
  getOptional<T>(entityId: EntityId, componentType: WildcardRelationId<T>): {
1273
1314
  value: [EntityId<unknown>, T][];
1274
1315
  } | undefined;
1275
- getOptional<T>(entityId: EntityId, componentType: EntityId<T>): {
1316
+ /**
1317
+ * Safely retrieves the entity's primary component without throwing an error.
1318
+ * Returns `undefined` if the component does not exist.
1319
+ *
1320
+ * @template T - The component data type
1321
+ * @param entityId - The entity identifier
1322
+ * @returns The component value wrapped in `{ value }`, or `undefined` if absent
1323
+ *
1324
+ * @throws {Error} If the entity does not exist
1325
+ */
1326
+ getOptional<T>(entityId: EntityId<T>): {
1276
1327
  value: T;
1277
1328
  } | undefined;
1278
1329
  /**
@@ -1377,21 +1428,15 @@ declare class World {
1377
1428
  }): void;
1378
1429
  /**
1379
1430
  * Registers a lifecycle hook that responds to component changes.
1380
- * The hook callback is invoked when components matching the specified types are added, updated, or removed.
1381
- * @overload hook<const T extends readonly ComponentType<any>[]>(
1382
- * componentTypes: T,
1383
- * hook: LifecycleHook<T> | LifecycleCallback<T>,
1384
- * filter?: QueryFilter,
1385
- * ): () => void
1386
- * Registers a hook for multiple component types.
1387
- * The hook is triggered when entities enter/exit the matching set.
1431
+ * The hook callback is invoked when components matching the specified types
1432
+ * are added, updated, or removed.
1388
1433
  *
1389
1434
  * @param componentTypes - Component types that define the matching entity set
1390
1435
  * @param hook - Either a hook object with on_init/on_set/on_remove handlers, or a callback function
1391
1436
  * @param filter - Optional query-style filter applied to the hook match set
1392
1437
  * @returns A function that unsubscribes the hook when called
1393
1438
  *
1394
- * @throws {Error} If no required components are specified in array overload
1439
+ * @throws {Error} If no required components are specified
1395
1440
  *
1396
1441
  * @example
1397
1442
  * const unsubscribe = world.hook([Position, Velocity], {
@@ -1526,32 +1571,30 @@ declare class World {
1526
1571
  getMatchingArchetypes(componentTypes: EntityId<any>[]): Archetype[];
1527
1572
  /**
1528
1573
  * Queries entities with specific components.
1529
- * For simpler use cases, prefer using `createQuery()` with `forEach()` which is cached and more efficient.
1530
- *
1531
- * @overload query(componentTypes: EntityId<any>[]): EntityId[]
1532
1574
  * Returns an array of entity IDs that have all specified components.
1533
- *
1534
- * @overload query<const T extends readonly EntityId<any>[]>(
1535
- * componentTypes: T,
1536
- * includeComponents: true,
1537
- * ): Array<{ entity: EntityId; components: ComponentTuple<T> }>
1538
- * Returns entities along with their component data.
1575
+ * For simpler use cases, prefer using `createQuery()` with `forEach()` which is cached and more efficient.
1539
1576
  *
1540
1577
  * @param componentTypes - Array of component types to query
1541
- * @param includeComponents - If true, includes component data in results
1542
- * @returns Array of entity IDs or objects with entities and components
1578
+ * @returns Array of entity IDs matching the query
1543
1579
  *
1544
1580
  * @example
1545
- * // Just entity IDs
1546
1581
  * const entities = world.query([Position, Velocity]);
1582
+ */
1583
+ query(componentTypes: EntityId<any>[]): EntityId[];
1584
+ /**
1585
+ * Queries entities with specific components and returns their component data.
1586
+ *
1587
+ * @template T - The tuple of component types
1588
+ * @param componentTypes - Array of component types to query
1589
+ * @param includeComponents - Must be `true` to include component data
1590
+ * @returns Array of objects with entity and component data
1547
1591
  *
1548
- * // With components
1592
+ * @example
1549
1593
  * const results = world.query([Position, Velocity], true);
1550
1594
  * results.forEach(({ entity, components: [pos, vel] }) => {
1551
1595
  * pos.x += vel.x;
1552
1596
  * });
1553
1597
  */
1554
- query(componentTypes: EntityId<any>[]): EntityId[];
1555
1598
  query<const T extends readonly EntityId<any>[]>(componentTypes: T, includeComponents: true): Array<{
1556
1599
  entity: EntityId;
1557
1600
  components: ComponentTuple<T>;
package/dist/world.mjs CHANGED
@@ -626,8 +626,9 @@ var EntityBuilder = class {
626
626
  /**
627
627
  * Explicit handle for a singleton component (component-as-entity).
628
628
  *
629
- * This provides an explicit and concise API for singleton components without
630
- * overloading `world.set()` semantics.
629
+ * This is the preferred API for singleton components.
630
+ * `world.set(componentId, value)` remains available only as a deprecated
631
+ * compatibility shorthand.
631
632
  *
632
633
  * @example
633
634
  * const config = world.singleton(Config);
@@ -2746,15 +2747,25 @@ function assertSetComponentTypeValid(componentType) {
2746
2747
  /**
2747
2748
  * Resolve the (entity, componentType, value) for a set() call.
2748
2749
  */
2749
- function resolveSetOperation(entityId, componentTypeOrComponent, maybeComponent, exists = () => true) {
2750
+ function resolveSetOperation(entityId, componentTypeOrComponent, maybeComponent, argCount = 3, exists = () => true) {
2750
2751
  const targetEntityId = entityId;
2752
+ if (argCount === 2 && isComponentId(targetEntityId) && typeof componentTypeOrComponent !== "number") {
2753
+ assertEntityExists(targetEntityId, "Component entity", exists);
2754
+ return {
2755
+ entityId: targetEntityId,
2756
+ componentType: targetEntityId,
2757
+ component: componentTypeOrComponent,
2758
+ deprecatedSingletonShorthand: true
2759
+ };
2760
+ }
2751
2761
  const componentType = componentTypeOrComponent;
2752
2762
  assertEntityExists(targetEntityId, "Entity", exists);
2753
2763
  assertSetComponentTypeValid(componentType);
2754
2764
  return {
2755
2765
  entityId: targetEntityId,
2756
2766
  componentType,
2757
- component: maybeComponent
2767
+ component: maybeComponent,
2768
+ deprecatedSingletonShorthand: false
2758
2769
  };
2759
2770
  }
2760
2771
  /**
@@ -2944,7 +2955,8 @@ function deserializeWorld(ctx, snapshot) {
2944
2955
  * World class for ECS architecture
2945
2956
  * Manages entities and components
2946
2957
  */
2947
- var World = class {
2958
+ var World = class World {
2959
+ static DEPRECATED_SINGLETON_SET_SHORTHAND_WARNING = "world.set(componentId, value) for singleton components is deprecated; use world.singleton(componentId).set(value) or world.set(componentId, componentId, value) instead.";
2948
2960
  entityIdManager = new EntityIdManager();
2949
2961
  entityReferences = /* @__PURE__ */ new Map();
2950
2962
  /** Sparse relation storage (for components created with `sparse: true`), shared with all Archetype instances */
@@ -3095,8 +3107,10 @@ var World = class {
3095
3107
  if (this.componentEntities.exists(entityId)) return true;
3096
3108
  return this.entityToArchetype.has(entityId);
3097
3109
  }
3110
+ /** Internal implementation for `set()` overloads. */
3098
3111
  set(entityId, componentTypeOrComponent, maybeComponent) {
3099
- const { entityId: targetEntityId, componentType, component } = resolveSetOperation(entityId, componentTypeOrComponent, maybeComponent, (id) => this.exists(id));
3112
+ const { entityId: targetEntityId, componentType, component, deprecatedSingletonShorthand } = resolveSetOperation(entityId, componentTypeOrComponent, maybeComponent, arguments.length, (id) => this.exists(id));
3113
+ if (deprecatedSingletonShorthand) console.warn(World.DEPRECATED_SINGLETON_SET_SHORTHAND_WARNING);
3100
3114
  this.commandBuffer.set(targetEntityId, componentType, component);
3101
3115
  }
3102
3116
  remove(entityId, componentType) {