@codehz/ecs 0.9.0 → 0.10.0

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/README.md CHANGED
@@ -222,7 +222,7 @@ bun run examples/inventory-system-relations.ts
222
222
  | `spawnMany(count, configure)` | 批量创建多个实体 |
223
223
  | `exists(entity)` | 检查实体是否存在 |
224
224
  | `set(entity, componentId, data?)` | 添加/更新组件(缓冲,`sync()` 后生效)。对 `void` 组件可不传 data |
225
- | `set(componentId, data)` | 单例组件简写:`world.set(GlobalConfig, { ... })` |
225
+ | `singleton(componentId)` | 获取单例组件句柄,推荐用 `world.singleton(Config).set(value)` |
226
226
  | `get(entity, componentId?)` | 获取组件数据。**若组件不存在会抛出异常**,请先用 `has()` 检查或使用 `getOptional()` |
227
227
  | `getOptional(entity, componentId?)` | 安全获取组件,返回 `{ value: T } \| undefined` |
228
228
  | `has(entity, componentId?)` | 检查组件是否存在 |
@@ -236,6 +236,18 @@ bun run examples/inventory-system-relations.ts
236
236
  | `serialize()` | 序列化世界状态为快照对象 |
237
237
  | `sync()` | 执行所有延迟命令 |
238
238
 
239
+ 单例组件推荐写法:
240
+
241
+ ```ts
242
+ const config = world.singleton(GlobalConfig);
243
+ config.set({ debug: true });
244
+ world.sync();
245
+
246
+ if (config.has()) {
247
+ console.log(config.get());
248
+ }
249
+ ```
250
+
239
251
  ### Query
240
252
 
241
253
  查询通过 `world.createQuery()` 创建,应**跨帧复用**以获得最佳性能。
@@ -1009,6 +1009,41 @@ declare class Query {
1009
1009
  get disposed(): boolean;
1010
1010
  }
1011
1011
  //#endregion
1012
+ //#region src/world/singleton.d.ts
1013
+ interface SingletonHandleOps<T> {
1014
+ has(): boolean;
1015
+ get(): T;
1016
+ getOptional(): {
1017
+ value: T;
1018
+ } | undefined;
1019
+ remove(): void;
1020
+ set(value: T | undefined): void;
1021
+ }
1022
+ /**
1023
+ * Explicit handle for a singleton component (component-as-entity).
1024
+ *
1025
+ * This provides an explicit and concise API for singleton components without
1026
+ * overloading `world.set()` semantics.
1027
+ *
1028
+ * @example
1029
+ * const config = world.singleton(Config);
1030
+ * config.set({ debug: true });
1031
+ * world.sync();
1032
+ * console.log(config.get());
1033
+ */
1034
+ declare class SingletonHandle<T = void> {
1035
+ readonly componentId: ComponentId<T>;
1036
+ private readonly ops;
1037
+ constructor(componentId: ComponentId<T>, ops: SingletonHandleOps<T>);
1038
+ has(): boolean;
1039
+ get(): T;
1040
+ getOptional(): {
1041
+ value: T;
1042
+ } | undefined;
1043
+ remove(): void;
1044
+ set(...args: T extends void ? [] : [value: NoInfer<T>]): void;
1045
+ }
1046
+ //#endregion
1012
1047
  //#region src/world/world.d.ts
1013
1048
  /**
1014
1049
  * World class for ECS architecture
@@ -1016,32 +1051,22 @@ declare class Query {
1016
1051
  */
1017
1052
  declare class World {
1018
1053
  private entityIdManager;
1019
- private archetypes;
1020
- private archetypeBySignature;
1021
- private entityToArchetype;
1022
- private archetypesByComponent;
1023
1054
  private entityReferences;
1024
- /** Reverse index: entity ID → set of archetypes whose componentTypes include that entity ID */
1025
- private entityToReferencingArchetypes;
1026
1055
  /** Sparse relation storage (for components created with `sparse: true`), shared with all Archetype instances */
1027
1056
  private readonly sparseStore;
1028
1057
  /** Component entity (singleton) storage */
1029
1058
  private readonly componentEntities;
1059
+ private archetypeManager;
1060
+ private get archetypes();
1061
+ private get entityToArchetype();
1062
+ private get archetypesByComponent();
1063
+ private get entityToReferencingArchetypes();
1030
1064
  private readonly queryRegistry;
1031
1065
  private hooks;
1032
- private readonly _debugCollectors;
1033
- private _debugMigrations;
1034
- private _debugArchetypesCreated;
1035
- private _debugArchetypesRemoved;
1066
+ private readonly debugStats;
1036
1067
  private commandBuffer;
1037
- private readonly _changeset;
1038
- private readonly _removeChangeset;
1039
- /** Cached command processor context to avoid per-entity object allocation */
1040
- private readonly _commandCtx;
1041
- /** Cached hooks context to avoid per-entity object allocation */
1042
- private readonly _hooksCtx;
1068
+ private commandExecutor;
1043
1069
  constructor(snapshot?: SerializedWorld);
1044
- private createArchetypeSignature;
1045
1070
  /**
1046
1071
  * Creates a new entity.
1047
1072
  * The entity is created with an empty component set and can be configured using `set()`.
@@ -1086,11 +1111,6 @@ declare class World {
1086
1111
  * if (world.has(entity, Position)) { ... }
1087
1112
  */
1088
1113
  exists(entityId: EntityId): boolean;
1089
- private assertEntityExists;
1090
- private assertComponentTypeValid;
1091
- private assertSetComponentTypeValid;
1092
- private resolveSetOperation;
1093
- private resolveRemoveOperation;
1094
1114
  /**
1095
1115
  * Adds or updates a component on an entity (or marks void component as present).
1096
1116
  * The change is buffered and takes effect after calling `world.sync()`.
@@ -1102,21 +1122,17 @@ declare class World {
1102
1122
  * @overload set<T>(entityId: EntityId, componentType: EntityId<T>, component: NoInfer<T>): void
1103
1123
  * Adds or updates a component with data on the entity
1104
1124
  *
1105
- * @overload set<T>(componentId: ComponentId<T>, component: NoInfer<T>): void
1106
- * Adds or updates a singleton component (shorthand for set(componentId, componentId, component))
1107
- *
1108
1125
  * @throws {Error} If the entity does not exist
1109
1126
  * @throws {Error} If the component type is invalid or is a wildcard relation
1110
1127
  *
1111
1128
  * @example
1112
1129
  * world.set(entity, Position, { x: 10, y: 20 });
1113
1130
  * world.set(entity, Marker); // void component
1114
- * world.set(GlobalConfig, { debug: true }); // singleton component
1131
+ * world.singleton(GlobalConfig).set({ debug: true }); // singleton component
1115
1132
  * world.sync(); // Apply changes
1116
1133
  */
1117
1134
  set(entityId: EntityId, componentType: EntityId<void>): void;
1118
1135
  set<T>(entityId: EntityId, componentType: EntityId<T>, component: NoInfer<T>): void;
1119
- set<T>(componentId: ComponentId<T>, component: NoInfer<T>): void;
1120
1136
  /**
1121
1137
  * Removes a component from an entity.
1122
1138
  * The change is buffered and takes effect after calling `world.sync()`.
@@ -1154,6 +1170,18 @@ declare class World {
1154
1170
  * world.sync(); // Apply changes
1155
1171
  */
1156
1172
  delete(entityId: EntityId): void;
1173
+ /**
1174
+ * Returns an explicit handle for a singleton component (component-as-entity).
1175
+ *
1176
+ * This is the preferred API for singleton components.
1177
+ *
1178
+ * @example
1179
+ * const config = world.singleton(GlobalConfig);
1180
+ * config.set({ debug: true });
1181
+ * world.sync();
1182
+ * console.log(config.get());
1183
+ */
1184
+ singleton<T>(componentId: ComponentId<T>): SingletonHandle<T>;
1157
1185
  /**
1158
1186
  * Checks if a specific **component** is present on an entity.
1159
1187
  *
@@ -1401,8 +1429,6 @@ declare class World {
1401
1429
  * This is intended for development/debugging and leak detection.
1402
1430
  */
1403
1431
  createDebugStatsCollector(callback: (stats: SyncDebugStats) => void): DebugStatsCollector;
1404
- private _resetDebugActivityCounters;
1405
- private _deliverDebugStats;
1406
1432
  /**
1407
1433
  * Synchronizes all buffered commands (set/remove/delete) to the world.
1408
1434
  * This method must be called after making changes via `set()`, `remove()`, or `delete()` for them to take effect.
@@ -1498,7 +1524,6 @@ declare class World {
1498
1524
  * @internal
1499
1525
  */
1500
1526
  getMatchingArchetypes(componentTypes: EntityId<any>[]): Archetype[];
1501
- private getArchetypesWithComponents;
1502
1527
  /**
1503
1528
  * Queries entities with specific components.
1504
1529
  * For simpler use cases, prefer using `createQuery()` with `forEach()` which is cached and more efficient.
@@ -1531,21 +1556,9 @@ declare class World {
1531
1556
  entity: EntityId;
1532
1557
  components: ComponentTuple<T>;
1533
1558
  }>;
1534
- private executeEntityCommands;
1535
- private applyEntityCommands;
1536
- private createHooksContext;
1537
- private removeComponentImmediate;
1538
- private updateEntityReferences;
1539
1559
  private ensureArchetype;
1540
- /** Add componentType to the reverse index if it contains an entity ID */
1541
- private addToReferencingIndex;
1542
- /** Remove componentType from the reverse index */
1543
- private removeFromReferencingIndex;
1544
- private createNewArchetype;
1545
- private updateArchetypeHookMatches;
1546
- private archetypeMatchesHook;
1547
1560
  private cleanupArchetypesReferencingEntity;
1548
- private removeArchetype;
1561
+ private archetypeMatchesHook;
1549
1562
  /**
1550
1563
  * Serializes the entire world state to a plain JavaScript object.
1551
1564
  * This creates a "memory snapshot" that can be stored or transmitted.
@@ -1567,6 +1580,8 @@ declare class World {
1567
1580
  * const savedData = JSON.parse(localStorage.getItem('save'));
1568
1581
  * const newWorld = new World(savedData);
1569
1582
  */
1583
+ private removeComponentImmediate;
1584
+ private createHooksContext;
1570
1585
  serialize(): SerializedWorld;
1571
1586
  }
1572
1587
  //#endregion
@@ -1643,5 +1658,5 @@ declare class EntityBuilder {
1643
1658
  build(): EntityId;
1644
1659
  }
1645
1660
  //#endregion
1646
- export { WildcardRelationId as A, isWildcardRelationId as C, EntityId as D, ComponentRelationId as E, isEntityId as M, isRelationId as N, EntityRelationId as O, decodeRelationId as S, ComponentId as T, getComponentIdByName as _, ComponentTuple as a, isSparseRelation as b, LifecycleCallback as c, SerializedComponent as d, SerializedEntity as f, component as g, ComponentOptions as h, Query as i, isComponentId as j, RelationId as k, LifecycleHook as l, SerializedWorld as m, EntityBuilder as n, ComponentType as o, SerializedEntityId as p, World as r, DebugStatsCollector as s, ComponentDef as t, SyncDebugStats as u, getComponentNameById as v, relation as w, isSparseWildcard as x, isSparseComponent as y };
1661
+ export { EntityRelationId as A, isSparseWildcard as C, ComponentId as D, relation as E, isRelationId as F, WildcardRelationId as M, isComponentId as N, ComponentRelationId as O, isEntityId as P, isSparseRelation as S, isWildcardRelationId as T, ComponentOptions as _, SingletonHandleOps as a, getComponentNameById as b, ComponentType as c, LifecycleHook as d, SyncDebugStats as f, SerializedWorld as g, SerializedEntityId as h, SingletonHandle as i, RelationId as j, EntityId as k, DebugStatsCollector as l, SerializedEntity as m, EntityBuilder as n, Query as o, SerializedComponent as p, World as r, ComponentTuple as s, ComponentDef as t, LifecycleCallback as u, component as v, decodeRelationId as w, isSparseComponent as x, getComponentIdByName as y };
1647
1662
  //# sourceMappingURL=builder.d.mts.map
package/dist/index.d.mts CHANGED
@@ -1,2 +1,2 @@
1
- import { A as WildcardRelationId, C as isWildcardRelationId, D as EntityId, E as ComponentRelationId, M as isEntityId, N as isRelationId, O as EntityRelationId, S as decodeRelationId, T as ComponentId, _ as getComponentIdByName, a as ComponentTuple, b as isSparseRelation, c as LifecycleCallback, d as SerializedComponent, f as SerializedEntity, g as component, h as ComponentOptions, i as Query, j as isComponentId, k as RelationId, l as LifecycleHook, m as SerializedWorld, n as EntityBuilder, o as ComponentType, p as SerializedEntityId, r as World, s as DebugStatsCollector, t as ComponentDef, u as SyncDebugStats, v as getComponentNameById, w as relation, x as isSparseWildcard, y as isSparseComponent } from "./builder.mjs";
2
- export { type ComponentDef, type ComponentId, type ComponentOptions, type ComponentRelationId, type ComponentTuple, type ComponentType, type DebugStatsCollector, EntityBuilder, type EntityId, type EntityRelationId, type LifecycleCallback, type LifecycleHook, Query, type RelationId, type SerializedComponent, type SerializedEntity, type SerializedEntityId, type SerializedWorld, type SyncDebugStats, type WildcardRelationId, World, component, decodeRelationId, getComponentIdByName, getComponentNameById, isComponentId, isSparseComponent as isDontFragmentComponent, isSparseComponent, isSparseRelation as isDontFragmentRelation, isSparseRelation, isSparseWildcard as isDontFragmentWildcard, isSparseWildcard, isEntityId, isRelationId, isWildcardRelationId, relation };
1
+ import { A as EntityRelationId, C as isSparseWildcard, D as ComponentId, E as relation, F as isRelationId, M as WildcardRelationId, N as isComponentId, O as ComponentRelationId, P as isEntityId, S as isSparseRelation, T as isWildcardRelationId, _ as ComponentOptions, a as SingletonHandleOps, b as getComponentNameById, c as ComponentType, d as LifecycleHook, f as SyncDebugStats, g as SerializedWorld, h as SerializedEntityId, i as SingletonHandle, j as RelationId, k as EntityId, l as DebugStatsCollector, m as SerializedEntity, n as EntityBuilder, o as Query, p as SerializedComponent, r as World, s as ComponentTuple, t as ComponentDef, u as LifecycleCallback, v as component, w as decodeRelationId, x as isSparseComponent, y as getComponentIdByName } from "./builder.mjs";
2
+ export { type ComponentDef, type ComponentId, type ComponentOptions, type ComponentRelationId, type ComponentTuple, type ComponentType, type DebugStatsCollector, EntityBuilder, type EntityId, type EntityRelationId, type LifecycleCallback, type LifecycleHook, Query, type RelationId, type SerializedComponent, type SerializedEntity, type SerializedEntityId, type SerializedWorld, SingletonHandle, type SingletonHandleOps, type SyncDebugStats, type WildcardRelationId, World, component, decodeRelationId, getComponentIdByName, getComponentNameById, isComponentId, isSparseComponent as isDontFragmentComponent, isSparseComponent, isSparseRelation as isDontFragmentRelation, isSparseRelation, isSparseWildcard as isDontFragmentWildcard, isSparseWildcard, isEntityId, isRelationId, isWildcardRelationId, relation };
package/dist/index.mjs CHANGED
@@ -1,2 +1,2 @@
1
- import { a as getComponentIdByName, c as isSparseRelation, d as isWildcardRelationId, f as relation, h as isRelationId, i as component, l as isSparseWildcard, m as isEntityId, n as Query, o as getComponentNameById, p as isComponentId, r as EntityBuilder, s as isSparseComponent, t as World, u as decodeRelationId } from "./world.mjs";
2
- export { EntityBuilder, Query, World, component, decodeRelationId, getComponentIdByName, getComponentNameById, isComponentId, isSparseComponent as isDontFragmentComponent, isSparseComponent, isSparseRelation as isDontFragmentRelation, isSparseRelation, isSparseWildcard as isDontFragmentWildcard, isSparseWildcard, isEntityId, isRelationId, isWildcardRelationId, relation };
1
+ import { a as component, c as isSparseComponent, d as decodeRelationId, f as isWildcardRelationId, g as isRelationId, h as isEntityId, i as EntityBuilder, l as isSparseRelation, m as isComponentId, n as Query, o as getComponentIdByName, p as relation, r as SingletonHandle, s as getComponentNameById, t as World, u as isSparseWildcard } from "./world.mjs";
2
+ export { EntityBuilder, Query, SingletonHandle, World, component, decodeRelationId, getComponentIdByName, getComponentNameById, isComponentId, isSparseComponent as isDontFragmentComponent, isSparseComponent, isSparseRelation as isDontFragmentRelation, isSparseRelation, isSparseWildcard as isDontFragmentWildcard, isSparseWildcard, isEntityId, isRelationId, isWildcardRelationId, relation };
@@ -1,4 +1,4 @@
1
- import { A as WildcardRelationId, D as EntityId, T as ComponentId, c as LifecycleCallback, g as component, i as Query, k as RelationId, l as LifecycleHook, n as EntityBuilder, r as World, t as ComponentDef, w as relation } from "./builder.mjs";
1
+ import { D as ComponentId, E as relation, M as WildcardRelationId, d as LifecycleHook, j as RelationId, k as EntityId, n as EntityBuilder, o as Query, r as World, t as ComponentDef, u as LifecycleCallback, v as component } from "./builder.mjs";
2
2
 
3
3
  //#region src/testing/index.d.ts
4
4
  /**
package/dist/testing.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { d as isWildcardRelationId, f as relation, i as component, r as EntityBuilder, t as World } from "./world.mjs";
1
+ import { a as component, f as isWildcardRelationId, i as EntityBuilder, p as relation, t as World } from "./world.mjs";
2
2
  //#region src/testing/index.ts
3
3
  /**
4
4
  * A test fixture that manages a World instance and provides convenient