ecspresso 0.0.2 → 0.0.4

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
@@ -2,7 +2,7 @@
2
2
 
3
3
  *(pronounced "ex-presso")*
4
4
 
5
- __Note: This is a VERY early work in progress.__
5
+ __Note: This is a VERY early work in progress. The documention is being autogenerated while this ECSpresso being iterated on.__
6
6
 
7
7
  To install dependencies:
8
8
 
@@ -49,6 +49,22 @@ The ECS pattern separates data (Components) from behavior (Systems) through Enti
49
49
  - Fluent builder API for creating systems and bundles
50
50
  - Event handling with lifecycle hooks for systems
51
51
  - Resource management for global state
52
+ - Simplified system API - all system methods receive the ECSpresso instance
53
+
54
+ ### System API Design
55
+
56
+ All system methods (process, onAttach, onDetach, and event handlers) receive the ECSpresso instance as a parameter, which provides:
57
+
58
+ - Access to entity management via `ecs.entityManager`
59
+ - Access to resources via `ecs.resourceManager`
60
+ - Access to events via `ecs.eventBus`
61
+
62
+ This design simplifies the API and allows systems access to all ECS functionality through a single reference. Benefits of this approach include:
63
+
64
+ - **Simpler method signatures**: Systems only need to deal with one additional parameter (the ECS instance) rather than multiple managers
65
+ - **Future extensibility**: New functionality added to the ECSpresso class is automatically available to all systems without changing method signatures
66
+ - **Consistency**: All system methods (process, lifecycle hooks, event handlers) use the same parameter pattern
67
+ - **Reduced verbosity**: Systems can be written more concisely while still having access to all ECS functionality
52
68
 
53
69
  ## Installation
54
70
 
@@ -98,11 +114,11 @@ const ecs = new ECSpresso<
98
114
  ecs.addResource('gameState', { score: 0, level: 1 });
99
115
 
100
116
  // Create an entity
101
- const entityId = ecs.createEntity();
117
+ const entity = ecs.entityManager.createEntity();
102
118
 
103
119
  // Add components to the entity
104
- ecs.addComponent(entityId, 'position', { x: 0, y: 0 });
105
- ecs.addComponent(entityId, 'velocity', { dx: 1, dy: 2 });
120
+ ecs.entityManager.addComponent(entity.id, 'position', { x: 0, y: 0 });
121
+ ecs.entityManager.addComponent(entity.id, 'velocity', { dx: 1, dy: 2 });
106
122
 
107
123
  // Run the simulation
108
124
  ecs.update(16.67); // Pass delta time in ms
@@ -110,14 +126,23 @@ ecs.update(16.67); // Pass delta time in ms
110
126
 
111
127
  ### Creating Systems
112
128
 
129
+ Systems should be created through a bundle. You can create a bundle and then add systems to it:
130
+
113
131
  ```typescript
114
- // Create a movement system
115
- const movementSystem = ecs.entityManager
116
- .createSystem('movement')
132
+ // Create a bundle
133
+ const gameBundle = new Bundle<
134
+ { position: Position; velocity: Velocity },
135
+ { collision: CollisionEvent },
136
+ { gameState: GameState }
137
+ >('gameBundle');
138
+
139
+ // Create a movement system within the bundle
140
+ const movementSystem = gameBundle
141
+ .addSystem('movement')
117
142
  .addQuery('movable', {
118
143
  with: ['position', 'velocity']
119
144
  })
120
- .setProcess((queries, deltaTime) => {
145
+ .setProcess((queries, deltaTime, ecs) => {
121
146
  // Process entities with both position and velocity
122
147
  for (const entity of queries.movable) {
123
148
  const { position, velocity } = entity.components;
@@ -126,8 +151,8 @@ const movementSystem = ecs.entityManager
126
151
  }
127
152
  });
128
153
 
129
- // Add the system to the ECS
130
- ecs.addSystem(movementSystem);
154
+ // Install the bundle to add the system to the ECS
155
+ ecs.install(gameBundle);
131
156
  ```
132
157
 
133
158
  ### Using Bundles
@@ -146,11 +171,11 @@ physicsBundle
146
171
  .addQuery('collidable', {
147
172
  with: ['position']
148
173
  })
149
- .setProcess((queries, deltaTime, entityManager, resourceManager, eventBus) => {
174
+ .setProcess((queries, deltaTime, ecs) => {
150
175
  // Check for collisions
151
176
  // ...
152
177
  // Emit collision events
153
- eventBus.emit('collision', { entityA: 1, entityB: 2 });
178
+ ecs.eventBus.publish('collision', { entityA: 1, entityB: 2 });
154
179
  });
155
180
 
156
181
  // Install the bundle
@@ -160,14 +185,21 @@ ecs.install(physicsBundle);
160
185
  ### Event Handling
161
186
 
162
187
  ```typescript
188
+ // Create a bundle for the score system
189
+ const gameUIBundle = new Bundle<
190
+ { position: Position; velocity: Velocity },
191
+ { collision: CollisionEvent },
192
+ { gameState: GameState }
193
+ >('gameUI');
194
+
163
195
  // Create a system that handles collision events
164
- const scoreSystem = ecs.entityManager
165
- .createSystem('score')
196
+ gameUIBundle
197
+ .addSystem('score')
166
198
  .setEventHandlers({
167
199
  collision: {
168
- handler: (event, entityManager, resourceManager) => {
200
+ handler: (event, ecs) => {
169
201
  // Handle collision event
170
- const gameState = resourceManager.getResource('gameState');
202
+ const gameState = ecs.resourceManager.getResource('gameState');
171
203
  if (gameState) {
172
204
  gameState.score += 10;
173
205
  }
@@ -176,7 +208,7 @@ const scoreSystem = ecs.entityManager
176
208
  });
177
209
 
178
210
  // Add the system to the ECS
179
- ecs.addSystem(scoreSystem);
211
+ ecs.install(gameUIBundle);
180
212
  ```
181
213
 
182
214
  ## Advanced Features
@@ -201,17 +233,109 @@ ecs.install(gameBundle);
201
233
  ### System Lifecycle Hooks
202
234
 
203
235
  ```typescript
236
+ // Create a bundle for the rendering systems
237
+ const renderBundle = new Bundle<
238
+ { position: Position; sprite: Sprite },
239
+ {},
240
+ { renderer: Renderer }
241
+ >('render');
242
+
204
243
  // Create a system with lifecycle hooks
205
- const renderSystem = ecs.entityManager
206
- .createSystem('render')
207
- .setOnAttach((entityManager, resourceManager, eventBus) => {
244
+ renderBundle
245
+ .addSystem('render')
246
+ .setOnAttach((ecs) => {
208
247
  // Initialize rendering resources
209
248
  console.log('Render system attached');
249
+ // You could initialize renderer resources here
250
+ ecs.addResource('renderer', new Renderer());
210
251
  })
211
- .setOnDetach((entityManager, resourceManager, eventBus) => {
252
+ .setOnDetach((ecs) => {
212
253
  // Clean up rendering resources
213
254
  console.log('Render system detached');
255
+ // You could clean up renderer resources here
256
+ const renderer = ecs.getResource('renderer');
257
+ if (renderer) {
258
+ renderer.dispose();
259
+ }
214
260
  });
261
+
262
+ // Install the bundle
263
+ ecs.install(renderBundle);
215
264
  ```
216
265
 
266
+ ### Complex System Example
267
+
268
+ The following example demonstrates a system that uses multiple aspects of the ECS (queries, resources, and events) with the simplified API:
269
+
270
+ ```typescript
271
+ // Create an AI bundle
272
+ const aiBundle = new Bundle<
273
+ {
274
+ position: Position;
275
+ ai: AIComponent;
276
+ health: Health;
277
+ stunned: StunnedEffect;
278
+ player: PlayerTag;
279
+ },
280
+ { enemySpottedPlayer: EnemySpottedEvent },
281
+ { gameConfig: GameConfig }
282
+ >('enemyAI');
283
+
284
+ // Create a complex AI system that needs access to multiple ECS features
285
+ aiBundle
286
+ .addSystem('enemyAI')
287
+ .addQuery('enemies', {
288
+ with: ['position', 'ai', 'health'],
289
+ without: ['stunned']
290
+ })
291
+ .addQuery('players', {
292
+ with: ['position', 'player']
293
+ })
294
+ .setProcess((queries, deltaTime, ecs) => {
295
+ // Access game configuration from resources
296
+ const config = ecs.resourceManager.get('gameConfig');
297
+ const difficultyMultiplier = config?.difficulty || 1.0;
298
+
299
+ // Process each enemy
300
+ for (const enemy of queries.enemies) {
301
+ // Find the nearest player
302
+ let nearestPlayer = null;
303
+ let shortestDistance = Infinity;
304
+
305
+ for (const player of queries.players) {
306
+ const distance = calculateDistance(
307
+ enemy.components.position,
308
+ player.components.position
309
+ );
310
+
311
+ if (distance < shortestDistance) {
312
+ nearestPlayer = player;
313
+ shortestDistance = distance;
314
+ }
315
+ }
316
+
317
+ if (nearestPlayer && shortestDistance < enemy.components.ai.detectionRange * difficultyMultiplier) {
318
+ // Enemy detected player, update AI state
319
+ if (enemy.components.ai.state !== 'chasing') {
320
+ enemy.components.ai.state = 'chasing';
321
+
322
+ // Emit event that enemy spotted player
323
+ ecs.eventBus.publish('enemySpottedPlayer', {
324
+ enemyId: enemy.id,
325
+ playerId: nearestPlayer.id
326
+ });
327
+ }
328
+
329
+ // Move enemy toward player
330
+ moveToward(enemy, nearestPlayer, deltaTime);
331
+ }
332
+ }
333
+ });
334
+
335
+ // Install the AI bundle
336
+ ecs.install(aiBundle);
337
+ ```
338
+
339
+ This example shows how having access to the entire ECS through a single parameter simplifies the code, as the system can easily work with entity queries, access resources, and publish events without needing separate parameters for each manager.
340
+
217
341
  This project was created using `bun init` in bun v1.2.4. [Bun](https://bun.sh) is a fast all-in-one JavaScript runtime.
@@ -2,6 +2,19 @@ import EntityManager from "./entity-manager";
2
2
  import EventBus from "./event-bus";
3
3
  import ResourceManager from "./resource-manager";
4
4
  import type Bundle from "./bundle";
5
+ /**
6
+ * The main ECS (Entity Component System) container class
7
+ *
8
+ * This class manages entities, components, systems, resources, and events.
9
+ *
10
+ * Systems interact with the ECS through a single parameter that provides access
11
+ * to the entire ECSpresso instance. This provides a simplified API for systems
12
+ * and allows them access to all ECS functionality through a single reference.
13
+ *
14
+ * @template ComponentTypes Record of component types used in this ECS instance
15
+ * @template EventTypes Record of event types used in this ECS instance
16
+ * @template ResourceTypes Record of resource types used in this ECS instance
17
+ */
5
18
  export default class ECSpresso<ComponentTypes extends Record<string, any> = Record<string, any>, EventTypes extends Record<string, any> = Record<string, any>, ResourceTypes extends Record<string, any> = Record<string, any>> {
6
19
  static readonly VERSION: string;
7
20
  private _entityManager;
@@ -12,10 +25,16 @@ export default class ECSpresso<ComponentTypes extends Record<string, any> = Reco
12
25
  constructor();
13
26
  /**
14
27
  * Install one or more bundles into this ECS instance
28
+ * Systems in the bundle will have their onAttach method called with this ECSpresso instance
29
+ * @param bundles One or more bundles to install
30
+ * @returns This ECSpresso instance for method chaining
15
31
  */
16
32
  install<Bundles extends Array<Bundle<any, any, any>>>(...bundles: Bundles): this;
17
33
  /**
18
34
  * Remove a system by its label
35
+ * Calls the system's onDetach method with this ECSpresso instance if defined
36
+ * @param label The unique label of the system to remove
37
+ * @returns true if the system was found and removed, false otherwise
19
38
  */
20
39
  removeSystem(label: string): boolean;
21
40
  /**
@@ -44,6 +63,8 @@ export default class ECSpresso<ComponentTypes extends Record<string, any> = Reco
44
63
  getEntitiesWithComponents(withComponents: (keyof ComponentTypes)[], withoutComponents?: (keyof ComponentTypes)[]): import("./types").FilteredEntity<ComponentTypes, keyof ComponentTypes extends infer T ? T extends keyof ComponentTypes ? T extends never ? never : T : never : never, keyof ComponentTypes extends infer T_1 ? T_1 extends keyof ComponentTypes ? T_1 extends never ? never : T_1 : never : never>[];
45
64
  /**
46
65
  * Update all systems
66
+ * Calls each system's process method with this ECSpresso instance
67
+ * @param deltaTime Time elapsed since the last update in seconds
47
68
  */
48
69
  update(deltaTime: number): void;
49
70
  get entityManager(): EntityManager<ComponentTypes>;
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- class M{nextId=1;entities=new Map;componentIndices=new Map;createEntity(){let f=this.nextId++,g={id:f,components:{}};return this.entities.set(f,g),g}addComponent(f,g,j){let J=typeof f==="number"?this.entities.get(f):f;if(!J)throw new Error(`Entity ${f} does not exist`);if(J.components[g]=j,!this.componentIndices.has(g))this.componentIndices.set(g,new Set);return this.componentIndices.get(g)?.add(J.id),this}removeComponent(f,g){let j=this.entities.get(f);if(!j)throw new Error(`Entity ${f} does not exist`);delete j.components[g],this.componentIndices.get(g)?.delete(f)}getComponent(f,g){let j=this.entities.get(f);if(!j)throw new Error(`Entity ${f} does not exist`);return j.components[g]||null}getEntitiesWithComponents(f=[],g=[]){if(f.length===0){if(g.length===0)return Array.from(this.entities.values());return Array.from(this.entities.values()).filter((E)=>{return g.every((F)=>!(F in E.components))})}let j=f.reduce((E,F)=>{let K=this.componentIndices.get(F),U=K?K.size:0,X=this.componentIndices.get(E)?.size??1/0;return U<X?F:E},f[0]);return Array.from(this.componentIndices.get(j)||[]).filter((E)=>{let F=this.entities.get(E);return F&&f.every((K)=>(K in F.components))&&g.every((K)=>!(K in F.components))}).map((E)=>this.entities.get(E))}removeEntity(f){let g=this.entities.get(f);if(!g)return!1;for(let j of Object.keys(g.components))this.componentIndices.get(j)?.delete(f);return this.entities.delete(f)}getEntity(f){return this.entities.get(f)}}class P{handlers=new Map;subscribe(f,g){return this.addHandler(f,g,!1)}once(f,g){return this.addHandler(f,g,!0)}addHandler(f,g,j){if(!this.handlers.has(f))this.handlers.set(f,[]);let J={callback:g,once:j};return this.handlers.get(f).push(J),()=>{let E=this.handlers.get(f);if(E){let F=E.indexOf(J);if(F!==-1)E.splice(F,1)}}}publish(f,g=void 0){let j=this.handlers.get(f);if(!j)return;let J=[...j],E=[];for(let F of J)if(F.callback(g),F.once)E.push(F);if(E.length>0)for(let F of E){let K=j.indexOf(F);if(K!==-1)j.splice(K,1)}}clear(){this.handlers.clear()}clearEvent(f){this.handlers.delete(f)}}class Q{resources=new Map;add(f,g){return this.resources.set(f,g),g}get(f){let g=this.resources.get(f);if(g===void 0)throw new Error(`Resource ${String(f)} not found`);return g}getOptional(f){return this.resources.get(f)}has(f){return this.resources.has(f)}remove(f){return this.resources.delete(f)}getKeys(){return Array.from(this.resources.keys())}}var Y="0.0.2";class V{static VERSION=Y;_entityManager;_systems=[];_eventBus;_resourceManager;_installedBundles=new Set;constructor(){this._entityManager=new M,this._eventBus=new P,this._resourceManager=new Q}install(...f){for(let g of f){if(this._installedBundles.has(g.id)){console.warn(`Bundle ${g.id} is already installed`);continue}let j=g.getSystems();if(!j.length)console.warn(`Bundle ${g.id} has no systems`);for(let E of j){let F=E;if(this._systems.push(F),F.onAttach)F.onAttach(this._entityManager,this._resourceManager,this._eventBus);if(F.eventHandlers)for(let K in F.eventHandlers){let U=F.eventHandlers[K];if(U?.handler){let X=(Z)=>{U.handler(Z,this._entityManager,this._resourceManager,this._eventBus)};this._eventBus.subscribe(K,X)}}}let J=g.getResources();for(let[E,F]of J.entries())this._resourceManager.add(E,F);this._installedBundles.add(g.id)}return this}removeSystem(f){let g=this._systems.findIndex((J)=>J.label===f);if(g===-1)return!1;let j=this._systems[g];if(!j)return!1;return j.onDetach?.(this._entityManager,this._resourceManager,this._eventBus),this._systems.splice(g,1),!0}hasResource(f){return this._resourceManager.has(f)}getResource(f){return this._resourceManager.getOptional(f)}getResourceOrThrow(f){return this._resourceManager.get(f)}addResource(f,g){return this._resourceManager.add(f,g),this}hasComponent(f,g){return this._entityManager.getComponent(f,g)!==null}getEntitiesWithComponents(f,g=[]){return this._entityManager.getEntitiesWithComponents(f,g)}update(f){for(let g of this._systems){if(!g.process)continue;let j={};if(g.entityQueries){for(let J in g.entityQueries){let E=g.entityQueries[J];if(E)j[J]=this._entityManager.getEntitiesWithComponents(E.with,E.without||[])}g.process(j,f,this._entityManager,this._resourceManager,this._eventBus)}else g.process([],f,this._entityManager,this._resourceManager,this._eventBus)}}get entityManager(){return this._entityManager}get eventBus(){return this._eventBus}get resourceManager(){return this._resourceManager}get installedBundles(){return Array.from(this._installedBundles)}}function $(){return`bundle_${Date.now().toString(36)}_${Math.random().toString(36).substring(2,9)}`}class L{_systems=[];_resources=new Map;_id;constructor(f){this._id=f||$()}get id(){return this._id}set id(f){this._id=f}addSystem(f){let g=new W(f,this);return this._systems.push(g),g}addResource(f,g){return this._resources.set(f,g),this}getSystems(){return this._systems.map((f)=>f.build())}getResources(){return new Map(this._resources)}getResource(f){return this._resources.get(f)}getSystemBuilders(){return[...this._systems]}hasResource(f){return this._resources.has(f)}}function D(f,...g){if(g.length===0)return new L(f);let j=new L(f);for(let J of g){for(let E of J.getSystemBuilders())j.addSystem(E);for(let[E,F]of J.getResources().entries())j.addResource(E,F)}return j}class W{_label;_bundle;queries={};processFunction;attachFunction;detachFunction;eventHandlers;constructor(f,g=new L){this._label=f;this._bundle=g}get label(){return this._label}get bundle(){return this._bundle}addQuery(f,g){let j=this;return j.queries={...this.queries,[f]:g},j}setProcess(f){return this.processFunction=f,this}setOnAttach(f){return this.attachFunction=f,this}setOnDetach(f){return this.detachFunction=f,this}setEventHandlers(f){return this.eventHandlers=f,this}build(){let f={label:this._label,entityQueries:this.queries};if(this.processFunction)f.process=this.processFunction;if(this.attachFunction)f.onAttach=this.attachFunction;if(this.detachFunction)f.onDetach=this.detachFunction;if(this.eventHandlers)f.eventHandlers=this.eventHandlers;return f}}var i=V;export{D as mergeBundles,i as default,W as SystemBuilder,Q as ResourceManager,P as EventBus,M as EntityManager,L as Bundle};
1
+ class P{nextId=1;entities=new Map;componentIndices=new Map;createEntity(){let f=this.nextId++,g={id:f,components:{}};return this.entities.set(f,g),g}addComponent(f,g,j){let K=typeof f==="number"?this.entities.get(f):f;if(!K)throw new Error(`Entity ${f} does not exist`);if(K.components[g]=j,!this.componentIndices.has(g))this.componentIndices.set(g,new Set);return this.componentIndices.get(g)?.add(K.id),this}removeComponent(f,g){let j=this.entities.get(f);if(!j)throw new Error(`Entity ${f} does not exist`);delete j.components[g],this.componentIndices.get(g)?.delete(f)}getComponent(f,g){let j=this.entities.get(f);if(!j)throw new Error(`Entity ${f} does not exist`);return j.components[g]||null}getEntitiesWithComponents(f=[],g=[]){if(f.length===0){if(g.length===0)return Array.from(this.entities.values());return Array.from(this.entities.values()).filter((F)=>{return g.every((J)=>!(J in F.components))})}let j=f.reduce((F,J)=>{let L=this.componentIndices.get(J),V=L?L.size:0,Y=this.componentIndices.get(F)?.size??1/0;return V<Y?J:F},f[0]);return Array.from(this.componentIndices.get(j)||[]).filter((F)=>{let J=this.entities.get(F);return J&&f.every((L)=>(L in J.components))&&g.every((L)=>!(L in J.components))}).map((F)=>this.entities.get(F))}removeEntity(f){let g=this.entities.get(f);if(!g)return!1;for(let j of Object.keys(g.components))this.componentIndices.get(j)?.delete(f);return this.entities.delete(f)}getEntity(f){return this.entities.get(f)}}class Q{handlers=new Map;subscribe(f,g){return this.addHandler(f,g,!1)}once(f,g){return this.addHandler(f,g,!0)}addHandler(f,g,j){if(!this.handlers.has(f))this.handlers.set(f,[]);let K={callback:g,once:j};return this.handlers.get(f).push(K),()=>{let F=this.handlers.get(f);if(F){let J=F.indexOf(K);if(J!==-1)F.splice(J,1)}}}publish(f,g=void 0){let j=this.handlers.get(f);if(!j)return;let K=[...j],F=[];for(let J of K)if(J.callback(g),J.once)F.push(J);if(F.length>0)for(let J of F){let L=j.indexOf(J);if(L!==-1)j.splice(L,1)}}clear(){this.handlers.clear()}clearEvent(f){this.handlers.delete(f)}}class U{resources=new Map;add(f,g){return this.resources.set(f,g),this}get(f){let g=this.resources.get(f);if(g===void 0)throw new Error(`Resource ${String(f)} not found`);return g}getOptional(f){return this.resources.get(f)}has(f){return this.resources.has(f)}remove(f){return this.resources.delete(f)}getKeys(){return Array.from(this.resources.keys())}}var Z="0.0.4";class W{static VERSION=Z;_entityManager;_systems=[];_eventBus;_resourceManager;_installedBundles=new Set;constructor(){this._entityManager=new P,this._eventBus=new Q,this._resourceManager=new U}install(...f){for(let g of f){if(this._installedBundles.has(g.id)){console.warn(`Bundle ${g.id} is already installed`);continue}let j=g.getSystems();if(!j.length)console.warn(`Bundle ${g.id} has no systems`);for(let F of j){let J=F;if(this._systems.push(J),J.onAttach)J.onAttach(this);if(J.eventHandlers)for(let L in J.eventHandlers){let V=J.eventHandlers[L];if(V?.handler){let Y=(_)=>{V.handler(_,this)};this._eventBus.subscribe(L,Y)}}}let K=g.getResources();for(let[F,J]of K.entries())this._resourceManager.add(F,J);this._installedBundles.add(g.id)}return this}removeSystem(f){let g=this._systems.findIndex((K)=>K.label===f);if(g===-1)return!1;let j=this._systems[g];if(!j)return!1;return j.onDetach?.(this),this._systems.splice(g,1),!0}hasResource(f){return this._resourceManager.has(f)}getResource(f){return this._resourceManager.getOptional(f)}getResourceOrThrow(f){return this._resourceManager.get(f)}addResource(f,g){return this._resourceManager.add(f,g),this}hasComponent(f,g){return this._entityManager.getComponent(f,g)!==null}getEntitiesWithComponents(f,g=[]){return this._entityManager.getEntitiesWithComponents(f,g)}update(f){for(let g of this._systems){if(!g.process)continue;let j={};if(g.entityQueries){for(let K in g.entityQueries){let F=g.entityQueries[K];if(F)j[K]=this._entityManager.getEntitiesWithComponents(F.with,F.without||[])}g.process(j,f,this)}else g.process([],f,this)}}get entityManager(){return this._entityManager}get eventBus(){return this._eventBus}get resourceManager(){return this._resourceManager}get installedBundles(){return Array.from(this._installedBundles)}}function D(){return`bundle_${Date.now().toString(36)}_${Math.random().toString(36).substring(2,9)}`}class M{_systems=[];_resources=new Map;_id;constructor(f){this._id=f||D()}get id(){return this._id}set id(f){this._id=f}addSystem(f){let g=new X(f,this);return this._systems.push(g),g}addResource(f,g){return this._resources.set(f,g),this}getSystems(){return this._systems.map((f)=>f.build())}getResources(){return new Map(this._resources)}getResource(f){return this._resources.get(f)}getSystemBuilders(){return[...this._systems]}hasResource(f){return this._resources.has(f)}}function H(f,...g){if(g.length===0)return new M(f);let j=new M(f);for(let K of g){for(let F of K.getSystemBuilders())j.addSystem(F);for(let[F,J]of K.getResources().entries())j.addResource(F,J)}return j}class X{_label;_bundle;queries={};processFunction;attachFunction;detachFunction;eventHandlers;constructor(f,g=new M){this._label=f;this._bundle=g}get label(){return this._label}get bundle(){return this._bundle}addQuery(f,g){let j=this;return j.queries={...this.queries,[f]:g},j}setProcess(f){return this.processFunction=f,this}setOnAttach(f){return this.attachFunction=f,this}setOnDetach(f){return this.detachFunction=f,this}setEventHandlers(f){return this.eventHandlers=f,this}build(){let f={label:this._label,entityQueries:this.queries};if(this.processFunction)f.process=this.processFunction;if(this.attachFunction)f.onAttach=this.attachFunction;if(this.detachFunction)f.onDetach=this.detachFunction;if(this.eventHandlers)f.eventHandlers=this.eventHandlers;return f}}var i=W;export{H as mergeBundles,i as default,X as SystemBuilder,U as ResourceManager,Q as EventBus,P as EntityManager,M as Bundle};
2
2
 
3
- //# debugId=3C890DE03A8FCB0764756E2164756E21
3
+ //# debugId=51C1642754D171C064756E2164756E21
4
4
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -4,13 +4,13 @@
4
4
  "sourcesContent": [
5
5
  "import type { Entity, FilteredEntity } from \"./types\";\n\nexport default\nclass EntityManager<ComponentTypes> {\n\tprivate nextId: number = 1;\n\tprivate entities: Map<number, Entity<ComponentTypes>> = new Map();\n\tprivate componentIndices: Map<keyof ComponentTypes, Set<number>> = new Map();\n\t\n\tcreateEntity(): Entity<ComponentTypes> {\n\t\tconst id = this.nextId++;\n\t\tconst entity: Entity<ComponentTypes> = { id, components: {} };\n\t\tthis.entities.set(id, entity);\n\t\treturn entity;\n\t}\n\n\t// TODO: Component object pooling if(/when) garbage collection is an issue...?\n\taddComponent<ComponentName extends keyof ComponentTypes>(\n\t\tentityOrId: number | Entity<ComponentTypes>,\n\t\tcomponentName: ComponentName,\n\t\tdata: ComponentTypes[ComponentName]\n\t) {\n\t\tconst entity = typeof entityOrId === 'number' ?\n\t\t\tthis.entities.get(entityOrId) :\n\t\t\tentityOrId;\n\n\t\tif (!entity) throw new Error(`Entity ${entityOrId} does not exist`);\n\n\t\tentity.components[componentName] = data;\n\t\t\n\t\t// Update component index\n\t\tif (!this.componentIndices.has(componentName)) {\n\t\t\tthis.componentIndices.set(componentName, new Set());\n\t\t}\n\t\tthis.componentIndices.get(componentName)?.add(entity.id);\n\t\treturn this;\n\t}\n\n\tremoveComponent<ComponentName extends keyof ComponentTypes>(entityId: number, componentName: ComponentName) {\n\t\tconst entity = this.entities.get(entityId);\n\t\tif (!entity) throw new Error(`Entity ${entityId} does not exist`);\n\n\t\tdelete entity.components[componentName];\n\t\t\n\t\t// Update component index\n\t\tthis.componentIndices.get(componentName)?.delete(entityId);\n\t}\n\n\tgetComponent<ComponentName extends keyof ComponentTypes>(entityId: number, componentName: ComponentName): ComponentTypes[ComponentName] | null {\n\t\tconst entity = this.entities.get(entityId);\n\n\t\tif (!entity) throw new Error(`Entity ${entityId} does not exist`);\n\n\t\treturn entity.components[componentName] || null;\n\t}\n\n\tgetEntitiesWithComponents<\n\t\tWithComponents extends keyof ComponentTypes = never,\n\t\tWithoutComponents extends keyof ComponentTypes = never\n\t>(\n\t\trequired: ReadonlyArray<WithComponents> = [] as any, \n\t\texcluded: ReadonlyArray<WithoutComponents> = [] as any,\n\t): Array<FilteredEntity<ComponentTypes, WithComponents extends never ? never : WithComponents, WithoutComponents extends never ? never : WithoutComponents>> {\n\t\t// Use the smallest component set as base for better performance\n\t\tif (required.length === 0) {\n\t\t\tif (excluded.length === 0) {\n\t\t\t\treturn Array.from(this.entities.values()) as any;\n\t\t\t}\n\n\t\t\treturn Array\n\t\t\t\t.from(this.entities.values())\n\t\t\t\t.filter((entity) => {\n\t\t\t\t\treturn excluded.every(comp => !(comp in entity.components));\n\t\t\t\t}) as any;\n\t\t}\n\t\t\n\t\t// Find the component with the smallest entity set to start with\n\t\tconst smallestComponent = required.reduce((smallest, comp) => {\n\t\t\tconst set = this.componentIndices.get(comp);\n\t\t\tconst currentSize = set ? set.size : 0;\n\t\t\tconst smallestSize = this.componentIndices.get(smallest!)?.size ?? Infinity;\n\t\t\t\n\t\t\treturn currentSize < smallestSize ? comp : smallest;\n\t\t}, required[0])!;\n\n\t\t// Start with the entities from the smallest component set\n\t\tconst candidates = Array.from(this.componentIndices.get(smallestComponent) || []);\n\n\t\t// Return full entity objects, not just IDs\n\t\treturn candidates\n\t\t\t.filter(id => {\n\t\t\t\tconst entity = this.entities.get(id);\n\t\t\t\treturn (\n\t\t\t\t\tentity &&\n\t\t\t\t\trequired.every(comp => comp in entity.components) && \n\t\t\t\t\texcluded.every(comp => !(comp in entity.components))\n\t\t\t\t);\n\t\t\t})\n\t\t\t.map(id => this.entities.get(id)!) as Array<FilteredEntity<ComponentTypes, WithComponents extends never ? never : WithComponents, WithoutComponents extends never ? never : WithoutComponents>>;\n\t}\n\n\tremoveEntity(entityId: number): boolean {\n\t\tconst entity = this.entities.get(entityId);\n\t\tif (!entity) return false;\n\t\t\n\t\t// Remove entity from all component indices\n\t\tfor (const componentName of Object.keys(entity.components) as Array<keyof ComponentTypes>) {\n\t\t\tthis.componentIndices.get(componentName)?.delete(entityId);\n\t\t}\n\t\t\n\t\t// Remove the entity itself\n\t\treturn this.entities.delete(entityId);\n\t}\n\n\tgetEntity(entityId: number): Entity<ComponentTypes> | undefined {\n\t\treturn this.entities.get(entityId);\n\t}\n}\n",
6
6
  "import type { EventHandler } from \"./types\";\n\nexport default\nclass EventBus<EventTypes> {\n\tprivate handlers: Map<keyof EventTypes, Array<EventHandler<any>>> = new Map();\n\n\t/**\n\t * Subscribe to an event\n\t */\n\tsubscribe<E extends keyof EventTypes>(\n\t\teventType: E,\n\t\tcallback: (data: EventTypes[E]) => void\n\t): () => void {\n\t\treturn this.addHandler(eventType, callback, false);\n\t}\n\n\t/**\n\t * Subscribe to an event once\n\t */\n\tonce<E extends keyof EventTypes>(\n\t\teventType: E,\n\t\tcallback: (data: EventTypes[E]) => void\n\t): () => void {\n\t\treturn this.addHandler(eventType, callback, true);\n\t}\n\n\t/**\n\t * Internal method to add an event handler\n\t */\n\tprivate addHandler<E extends keyof EventTypes>(\n\t\teventType: E,\n\t\tcallback: (data: EventTypes[E]) => void,\n\t\tonce: boolean\n\t): () => void {\n\t\tif (!this.handlers.has(eventType)) {\n\t\t\tthis.handlers.set(eventType, []);\n\t\t}\n\n\t\tconst handler: EventHandler<EventTypes[E]> = {\n\t\t\tcallback,\n\t\t\tonce\n\t\t};\n\n\t\tthis.handlers.get(eventType)!.push(handler);\n\n\t\t// Return unsubscribe function\n\t\treturn () => {\n\t\t\tconst handlers = this.handlers.get(eventType);\n\t\t\tif (handlers) {\n\t\t\t\tconst index = handlers.indexOf(handler);\n\t\t\t\tif (index !== -1) {\n\t\t\t\t\thandlers.splice(index, 1);\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\t}\n\n\tpublish<E extends keyof EventTypes>(\n\t\teventType: E,\n\t\tdata: EventTypes[E] = undefined as EventTypes[E]\n\t): void {\n\t\tconst handlers = this.handlers.get(eventType);\n\t\tif (!handlers) return;\n\n\t\t// Create a copy of handlers to avoid issues with handlers that modify the array\n\t\tconst handlersToCall = [...handlers];\n\t\t\n\t\t// Call all handlers and collect handlers to remove\n\t\tconst handlersToRemove: EventHandler<any>[] = [];\n\t\t\n\t\tfor (const handler of handlersToCall) {\n\t\t\thandler.callback(data);\n\t\t\tif (handler.once) {\n\t\t\t\thandlersToRemove.push(handler);\n\t\t\t}\n\t\t}\n\t\t\n\t\tif (handlersToRemove.length > 0) {\n\t\t\tfor (const handler of handlersToRemove) {\n\t\t\t\tconst index = handlers.indexOf(handler);\n\t\t\t\tif (index !== -1) {\n\t\t\t\t\thandlers.splice(index, 1);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tclear(): void {\n\t\tthis.handlers.clear();\n\t}\n\n\tclearEvent<E extends keyof EventTypes>(eventType: E): void {\n\t\tthis.handlers.delete(eventType);\n\t}\n}\n",
7
- "export default\nclass ResourceManager<ResourceTypes extends Record<string, any> = Record<string, any>> {\n\tprivate resources: Map<keyof ResourceTypes, ResourceTypes[keyof ResourceTypes]> = new Map();\n\n\t/**\n\t * Add a resource to the manager\n\t * @param label The resource key\n\t * @param resource The resource value\n\t * @returns The added resource\n\t */\n\tadd<K extends keyof ResourceTypes>(label: K, resource: ResourceTypes[K]): ResourceTypes[K] {\n\t\tthis.resources.set(label, resource);\n\t\treturn resource;\n\t}\n\n\t/**\n\t * Get a resource from the manager\n\t * @param label The resource key\n\t * @returns The resource value\n\t * @throws Error if resource not found\n\t */\n\tget<K extends keyof ResourceTypes>(label: K): ResourceTypes[K] {\n\t\tconst resource = this.resources.get(label);\n\t\tif (resource === undefined) {\n\t\t\tthrow new Error(`Resource ${String(label)} not found`);\n\t\t}\n\t\treturn resource as ResourceTypes[K];\n\t}\n\n\t/**\n\t * Get a resource from the manager, returning undefined if not found\n\t * @param label The resource key\n\t * @returns The resource value or undefined if not found\n\t */\n\tgetOptional<K extends keyof ResourceTypes>(label: K): ResourceTypes[K] | undefined {\n\t\tconst resource = this.resources.get(label);\n\t\treturn resource as ResourceTypes[K] | undefined;\n\t}\n\n\t/**\n\t * Check if a resource exists\n\t * @param label The resource key\n\t * @returns True if the resource exists\n\t */\n\thas<K extends keyof ResourceTypes>(label: K): boolean {\n\t\treturn this.resources.has(label);\n\t}\n\n\t/**\n\t * Remove a resource\n\t * @param label The resource key\n\t * @returns True if the resource was removed\n\t */\n\tremove<K extends keyof ResourceTypes>(label: K): boolean {\n\t\treturn this.resources.delete(label);\n\t}\n\n\t/**\n\t * Get all resource keys\n\t * @returns Array of resource keys\n\t */\n\tgetKeys(): Array<keyof ResourceTypes> {\n\t\treturn Array.from(this.resources.keys());\n\t}\n}\n",
8
- "import EntityManager from \"./entity-manager\";\nimport EventBus from \"./event-bus\";\nimport ResourceManager from \"./resource-manager\";\nimport type { System } from \"./types\";\nimport type Bundle from \"./bundle\";\nimport { version } from \"../package.json\";\n\n\nexport default\nclass ECSpresso<\n\tComponentTypes extends Record<string, any> = Record<string, any>,\n\tEventTypes extends Record<string, any> = Record<string, any>,\n\tResourceTypes extends Record<string, any> = Record<string, any>,\n> {\n\tpublic static readonly VERSION = version;\n\tprivate _entityManager: EntityManager<ComponentTypes>;\n\tprivate _systems: System<ComponentTypes, any, any, EventTypes, ResourceTypes>[] = [];\n\tprivate _eventBus: EventBus<EventTypes>;\n\tprivate _resourceManager: ResourceManager<ResourceTypes>;\n\tprivate _installedBundles: Set<string> = new Set();\n\n\tconstructor() {\n\t\tthis._entityManager = new EntityManager<ComponentTypes>();\n\t\tthis._eventBus = new EventBus<EventTypes>();\n\t\tthis._resourceManager = new ResourceManager<ResourceTypes>();\n\t}\n\n\t/**\n\t * Install one or more bundles into this ECS instance\n\t */\n\tinstall<\n\t\tBundles extends Array<Bundle<any, any, any>>\n\t>(...bundles: Bundles): this {\n\t\tfor (const bundle of bundles) {\n\t\t\t// Check if this bundle is already installed\n\t\t\tif (this._installedBundles.has(bundle.id)) {\n\t\t\t\tconsole.warn(`Bundle ${bundle.id} is already installed`);\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst systems = bundle.getSystems();\n\n\t\t\t// Check if bundle has systems\n\t\t\tif (!systems.length) {\n\t\t\t\tconsole.warn(`Bundle ${bundle.id} has no systems`);\n\t\t\t}\n\n\t\t\t// Register all systems from the bundle\n\t\t\tfor (const system of systems) {\n\t\t\t\t// Need to cast here because we can't fully type the system generics\n\t\t\t\tconst typedSystem = system as unknown as System<ComponentTypes, any, any, EventTypes, ResourceTypes>;\n\t\t\t\tthis._systems.push(typedSystem);\n\n\t\t\t\t// Call onAttach lifecycle hook if defined\n\t\t\t\tif (typedSystem.onAttach) {\n\t\t\t\t\ttypedSystem.onAttach(\n\t\t\t\t\t\tthis._entityManager,\n\t\t\t\t\t\tthis._resourceManager,\n\t\t\t\t\t\tthis._eventBus\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\t// Auto-subscribe to events if eventHandlers are defined\n\t\t\t\tif (typedSystem.eventHandlers) {\n\t\t\t\t\tfor (const eventName in typedSystem.eventHandlers) {\n\t\t\t\t\t\tconst handler = typedSystem.eventHandlers[eventName];\n\t\t\t\t\t\tif (handler?.handler) {\n\t\t\t\t\t\t\t// Create a wrapper that passes the additional parameters to the handler\n\t\t\t\t\t\t\tconst wrappedHandler = (data: any) => {\n\t\t\t\t\t\t\t\thandler.handler(\n\t\t\t\t\t\t\t\t\tdata,\n\t\t\t\t\t\t\t\t\tthis._entityManager,\n\t\t\t\t\t\t\t\t\tthis._resourceManager,\n\t\t\t\t\t\t\t\t\tthis._eventBus\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t};\n\n\t\t\t\t\t\t\tthis._eventBus.subscribe(eventName, wrappedHandler);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Register all resources from the bundle\n\t\t\tconst resources = bundle.getResources();\n\t\t\tfor (const [key, value] of resources.entries()) {\n\t\t\t\t// We need to cast here because TypeScript can't verify the type compatibility\n\t\t\t\t// between bundles, but we trust that the bundle's resource types are compatible\n\t\t\t\tthis._resourceManager.add(key as unknown as keyof ResourceTypes, value as any);\n\t\t\t}\n\n\t\t\t// Mark this bundle as installed\n\t\t\tthis._installedBundles.add(bundle.id);\n\t\t}\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * Remove a system by its label\n\t */\n\tremoveSystem(label: string): boolean {\n\t\tconst index = this._systems.findIndex(system => system.label === label);\n\t\tif (index === -1) return false;\n\n\t\tconst system = this._systems[index];\n\t\tif (!system) return false;\n\n\t\tsystem.onDetach?.(\n\t\t\tthis._entityManager,\n\t\t\tthis._resourceManager,\n\t\t\tthis._eventBus\n\t\t);\n\n\t\t// Remove system\n\t\tthis._systems.splice(index, 1);\n\t\treturn true;\n\t}\n\n\t/**\n\t * Check if a resource exists\n\t */\n\thasResource<K extends keyof ResourceTypes>(key: K): boolean {\n\t\treturn this._resourceManager.has(key);\n\t}\n\n\t/**\n\t * Get a resource if it exists, or undefined if not\n\t */\n\tgetResource<K extends keyof ResourceTypes>(key: K): ResourceTypes[K] | undefined {\n\t\treturn this._resourceManager.getOptional(key);\n\t}\n\n\t/**\n\t * Get a resource, throws error if not found\n\t */\n\tgetResourceOrThrow<K extends keyof ResourceTypes>(key: K): ResourceTypes[K] {\n\t\treturn this._resourceManager.get(key);\n\t}\n\n\t/**\n\t * Add a resource to the ECS instance\n\t */\n\taddResource<K extends keyof ResourceTypes>(key: K, resource: ResourceTypes[K]): this {\n\t\tthis._resourceManager.add(key, resource);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Check if an entity has a component\n\t */\n\thasComponent<K extends keyof ComponentTypes>(\n\t\tentityId: number,\n\t\tcomponentName: K\n\t): boolean {\n\t\tconst component = this._entityManager.getComponent(entityId, componentName);\n\t\treturn component !== null;\n\t}\n\n\t/**\n\t * Get all entities with specific components\n\t */\n\tgetEntitiesWithComponents(\n\t\twithComponents: (keyof ComponentTypes)[],\n\t\twithoutComponents: (keyof ComponentTypes)[] = []\n\t) {\n\t\treturn this._entityManager.getEntitiesWithComponents(\n\t\t\twithComponents,\n\t\t\twithoutComponents\n\t\t);\n\t}\n\n\t/**\n\t * Update all systems\n\t */\n\tupdate(deltaTime: number) {\n\t\tfor (const system of this._systems) {\n\t\t\tif (!system.process) continue;\n\n\t\t\t// Prepare query results\n\t\t\tconst queryResults: Record<string, any[]> = {};\n\n\t\t\t// Process entity queries if defined\n\t\t\tif (system.entityQueries) {\n\t\t\t\tfor (const queryName in system.entityQueries) {\n\t\t\t\t\tconst query = system.entityQueries[queryName];\n\t\t\t\t\tif (query) {\n\t\t\t\t\t\tqueryResults[queryName] = this._entityManager.getEntitiesWithComponents(\n\t\t\t\t\t\t\tquery.with,\n\t\t\t\t\t\t\tquery.without || []\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Call the system's process method\n\t\t\t\tsystem.process(\n\t\t\t\t\tqueryResults,\n\t\t\t\t\tdeltaTime,\n\t\t\t\t\tthis._entityManager,\n\t\t\t\t\tthis._resourceManager,\n\t\t\t\t\tthis._eventBus\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\t// No queries defined, pass an empty array\n\t\t\t\tsystem.process(\n\t\t\t\t\t[],\n\t\t\t\t\tdeltaTime,\n\t\t\t\t\tthis._entityManager,\n\t\t\t\t\tthis._resourceManager,\n\t\t\t\t\tthis._eventBus\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\n\t// Getters for the internal managers\n\tget entityManager() {\n\t\treturn this._entityManager;\n\t}\n\n\tget eventBus() {\n\t\treturn this._eventBus;\n\t}\n\n\tget resourceManager() {\n\t\treturn this._resourceManager;\n\t}\n\n\t/**\n\t * Get all installed bundle IDs\n\t */\n\tget installedBundles(): string[] {\n\t\treturn Array.from(this._installedBundles);\n\t}\n}\n",
7
+ "export default\nclass ResourceManager<ResourceTypes extends Record<string, any> = Record<string, any>> {\n\tprivate resources: Map<keyof ResourceTypes, ResourceTypes[keyof ResourceTypes]> = new Map();\n\n\t/**\n\t * Add a resource to the manager\n\t * @param label The resource key\n\t * @param resource The resource value\n\t * @returns The resource manager instance for chaining\n\t */\n\tadd<K extends keyof ResourceTypes>(label: K, resource: ResourceTypes[K]) {\n\t\tthis.resources.set(label, resource);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Get a resource from the manager\n\t * @param label The resource key\n\t * @returns The resource value\n\t * @throws Error if resource not found\n\t */\n\tget<K extends keyof ResourceTypes>(label: K): ResourceTypes[K] {\n\t\tconst resource = this.resources.get(label);\n\n\t\tif (resource === undefined) {\n\t\t\tthrow new Error(`Resource ${String(label)} not found`);\n\t\t}\n\n\t\treturn resource;\n\t}\n\n\t/**\n\t * Get a resource from the manager, returning undefined if not found\n\t * @param label The resource key\n\t * @returns The resource value or undefined if not found\n\t */\n\tgetOptional<K extends keyof ResourceTypes>(label: K): ResourceTypes[K] | undefined {\n\t\tconst resource = this.resources.get(label);\n\t\treturn resource as ResourceTypes[K] | undefined;\n\t}\n\n\t/**\n\t * Check if a resource exists\n\t * @param label The resource key\n\t * @returns True if the resource exists\n\t */\n\thas<K extends keyof ResourceTypes>(label: K): boolean {\n\t\treturn this.resources.has(label);\n\t}\n\n\t/**\n\t * Remove a resource\n\t * @param label The resource key\n\t * @returns True if the resource was removed\n\t */\n\tremove<K extends keyof ResourceTypes>(label: K): boolean {\n\t\treturn this.resources.delete(label);\n\t}\n\n\t/**\n\t * Get all resource keys\n\t * @returns Array of resource keys\n\t */\n\tgetKeys(): Array<keyof ResourceTypes> {\n\t\treturn Array.from(this.resources.keys());\n\t}\n}\n",
8
+ "import EntityManager from \"./entity-manager\";\nimport EventBus from \"./event-bus\";\nimport ResourceManager from \"./resource-manager\";\nimport type { System } from \"./types\";\nimport type Bundle from \"./bundle\";\nimport { version } from \"../package.json\";\n\n\n/**\n * The main ECS (Entity Component System) container class\n *\n * This class manages entities, components, systems, resources, and events.\n *\n * Systems interact with the ECS through a single parameter that provides access\n * to the entire ECSpresso instance. This provides a simplified API for systems\n * and allows them access to all ECS functionality through a single reference.\n *\n * @template ComponentTypes Record of component types used in this ECS instance\n * @template EventTypes Record of event types used in this ECS instance\n * @template ResourceTypes Record of resource types used in this ECS instance\n */\nexport default\nclass ECSpresso<\n\tComponentTypes extends Record<string, any> = Record<string, any>,\n\tEventTypes extends Record<string, any> = Record<string, any>,\n\tResourceTypes extends Record<string, any> = Record<string, any>,\n> {\n\tpublic static readonly VERSION = version;\n\tprivate _entityManager: EntityManager<ComponentTypes>;\n\tprivate _systems: System<ComponentTypes, any, any, EventTypes, ResourceTypes>[] = [];\n\tprivate _eventBus: EventBus<EventTypes>;\n\tprivate _resourceManager: ResourceManager<ResourceTypes>;\n\tprivate _installedBundles: Set<string> = new Set();\n\n\tconstructor() {\n\t\tthis._entityManager = new EntityManager<ComponentTypes>();\n\t\tthis._eventBus = new EventBus<EventTypes>();\n\t\tthis._resourceManager = new ResourceManager<ResourceTypes>();\n\t}\n\n\t/**\n\t * Install one or more bundles into this ECS instance\n\t * Systems in the bundle will have their onAttach method called with this ECSpresso instance\n\t * @param bundles One or more bundles to install\n\t * @returns This ECSpresso instance for method chaining\n\t */\n\tinstall<\n\t\tBundles extends Array<Bundle<any, any, any>>\n\t>(...bundles: Bundles): this {\n\t\tfor (const bundle of bundles) {\n\t\t\t// Check if this bundle is already installed\n\t\t\tif (this._installedBundles.has(bundle.id)) {\n\t\t\t\tconsole.warn(`Bundle ${bundle.id} is already installed`);\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst systems = bundle.getSystems();\n\n\t\t\t// Check if bundle has systems\n\t\t\tif (!systems.length) {\n\t\t\t\tconsole.warn(`Bundle ${bundle.id} has no systems`);\n\t\t\t}\n\n\t\t\t// Register all systems from the bundle\n\t\t\tfor (const system of systems) {\n\t\t\t\t// Need to cast here because we can't fully type the system generics\n\t\t\t\tconst typedSystem = system as unknown as System<ComponentTypes, any, any, EventTypes, ResourceTypes>;\n\t\t\t\tthis._systems.push(typedSystem);\n\n\t\t\t\t// Call onAttach lifecycle hook if defined\n\t\t\t\tif (typedSystem.onAttach) {\n\t\t\t\t\ttypedSystem.onAttach(\n\t\t\t\t\t\tthis\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\t// Auto-subscribe to events if eventHandlers are defined\n\t\t\t\tif (typedSystem.eventHandlers) {\n\t\t\t\t\tfor (const eventName in typedSystem.eventHandlers) {\n\t\t\t\t\t\tconst handler = typedSystem.eventHandlers[eventName];\n\t\t\t\t\t\tif (handler?.handler) {\n\t\t\t\t\t\t\t// Create a wrapper that passes the additional parameters to the handler\n\t\t\t\t\t\t\tconst wrappedHandler = (data: any) => {\n\t\t\t\t\t\t\t\thandler.handler(\n\t\t\t\t\t\t\t\t\tdata,\n\t\t\t\t\t\t\t\t\tthis\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t};\n\n\t\t\t\t\t\t\tthis._eventBus.subscribe(eventName, wrappedHandler);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Register all resources from the bundle\n\t\t\tconst resources = bundle.getResources();\n\t\t\tfor (const [key, value] of resources.entries()) {\n\t\t\t\t// We need to cast here because TypeScript can't verify the type compatibility\n\t\t\t\t// between bundles, but we trust that the bundle's resource types are compatible\n\t\t\t\tthis._resourceManager.add(key as unknown as keyof ResourceTypes, value as any);\n\t\t\t}\n\n\t\t\t// Mark this bundle as installed\n\t\t\tthis._installedBundles.add(bundle.id);\n\t\t}\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * Remove a system by its label\n\t * Calls the system's onDetach method with this ECSpresso instance if defined\n\t * @param label The unique label of the system to remove\n\t * @returns true if the system was found and removed, false otherwise\n\t */\n\tremoveSystem(label: string): boolean {\n\t\tconst index = this._systems.findIndex(system => system.label === label);\n\t\tif (index === -1) return false;\n\n\t\tconst system = this._systems[index];\n\t\tif (!system) return false;\n\n\t\tsystem.onDetach?.(\n\t\t\tthis\n\t\t);\n\n\t\t// Remove system\n\t\tthis._systems.splice(index, 1);\n\t\treturn true;\n\t}\n\n\t/**\n\t * Check if a resource exists\n\t */\n\thasResource<K extends keyof ResourceTypes>(key: K): boolean {\n\t\treturn this._resourceManager.has(key);\n\t}\n\n\t/**\n\t * Get a resource if it exists, or undefined if not\n\t */\n\tgetResource<K extends keyof ResourceTypes>(key: K): ResourceTypes[K] | undefined {\n\t\treturn this._resourceManager.getOptional(key);\n\t}\n\n\t/**\n\t * Get a resource, throws error if not found\n\t */\n\tgetResourceOrThrow<K extends keyof ResourceTypes>(key: K): ResourceTypes[K] {\n\t\treturn this._resourceManager.get(key);\n\t}\n\n\t/**\n\t * Add a resource to the ECS instance\n\t */\n\taddResource<K extends keyof ResourceTypes>(key: K, resource: ResourceTypes[K]): this {\n\t\tthis._resourceManager.add(key, resource);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Check if an entity has a component\n\t */\n\thasComponent<K extends keyof ComponentTypes>(\n\t\tentityId: number,\n\t\tcomponentName: K\n\t): boolean {\n\t\tconst component = this._entityManager.getComponent(entityId, componentName);\n\t\treturn component !== null;\n\t}\n\n\t/**\n\t * Get all entities with specific components\n\t */\n\tgetEntitiesWithComponents(\n\t\twithComponents: (keyof ComponentTypes)[],\n\t\twithoutComponents: (keyof ComponentTypes)[] = []\n\t) {\n\t\treturn this._entityManager.getEntitiesWithComponents(\n\t\t\twithComponents,\n\t\t\twithoutComponents\n\t\t);\n\t}\n\n\t/**\n\t * Update all systems\n\t * Calls each system's process method with this ECSpresso instance\n\t * @param deltaTime Time elapsed since the last update in seconds\n\t */\n\tupdate(deltaTime: number) {\n\t\tfor (const system of this._systems) {\n\t\t\tif (!system.process) continue;\n\n\t\t\t// Prepare query results\n\t\t\tconst queryResults: Record<string, any[]> = {};\n\n\t\t\t// Process entity queries if defined\n\t\t\tif (system.entityQueries) {\n\t\t\t\tfor (const queryName in system.entityQueries) {\n\t\t\t\t\tconst query = system.entityQueries[queryName];\n\t\t\t\t\tif (query) {\n\t\t\t\t\t\tqueryResults[queryName] = this._entityManager.getEntitiesWithComponents(\n\t\t\t\t\t\t\tquery.with,\n\t\t\t\t\t\t\tquery.without || []\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Call the system's process method\n\t\t\t\tsystem.process(\n\t\t\t\t\tqueryResults,\n\t\t\t\t\tdeltaTime,\n\t\t\t\t\tthis\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\t// No queries defined, pass an empty array\n\t\t\t\tsystem.process(\n\t\t\t\t\t[],\n\t\t\t\t\tdeltaTime,\n\t\t\t\t\tthis\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\n\t// Getters for the internal managers\n\tget entityManager() {\n\t\treturn this._entityManager;\n\t}\n\n\tget eventBus() {\n\t\treturn this._eventBus;\n\t}\n\n\tget resourceManager() {\n\t\treturn this._resourceManager;\n\t}\n\n\t/**\n\t * Get all installed bundle IDs\n\t */\n\tget installedBundles(): string[] {\n\t\treturn Array.from(this._installedBundles);\n\t}\n}\n",
9
9
  "import { SystemBuilder } from './system-builder';\n\n/**\n * Generates a unique ID for a bundle\n */\nfunction generateBundleId(): string {\n\treturn `bundle_${Date.now().toString(36)}_${Math.random().toString(36).substring(2, 9)}`;\n}\n\n/**\n * Bundle class that encapsulates a set of components, resources, events, and systems\n * that can be merged into a ECSpresso instance\n */\nexport default class Bundle<\n\tComponentTypes extends Record<string, any> = Record<string, any>,\n\tEventTypes extends Record<string, any> = Record<string, any>,\n\tResourceTypes extends Record<string, any> = Record<string, any>,\n> {\n\tprivate _systems: SystemBuilder<ComponentTypes, EventTypes, ResourceTypes, any>[] = [];\n\tprivate _resources: Map<keyof ResourceTypes, ResourceTypes[keyof ResourceTypes]> = new Map();\n\tprivate _id: string;\n\n\tconstructor(id?: string) {\n\t\tthis._id = id || generateBundleId();\n\t}\n\n\t/**\n\t * Get the unique ID of this bundle\n\t */\n\tget id(): string {\n\t\treturn this._id;\n\t}\n\n\t/**\n\t * Set the ID of this bundle\n\t * @internal Used by combineBundles\n\t */\n\tset id(value: string) {\n\t\tthis._id = value;\n\t}\n\n\t/**\n\t * Add a system to this bundle\n\t */\n\taddSystem(label: string) {\n\t\tconst system = new SystemBuilder(label, this);\n\n\t\tthis._systems.push(system);\n\n\t\treturn system;\n\t}\n\n\t/**\n\t * Add a resource to this bundle\n\t * @param label The resource key\n\t * @param resource The resource value\n\t */\n\taddResource<K extends keyof ResourceTypes>(label: K, resource: ResourceTypes[K]) {\n\t\tthis._resources.set(label, resource);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Get all systems defined in this bundle\n\t * Returns built System objects instead of SystemBuilders\n\t */\n\tgetSystems() {\n\t\treturn this._systems.map(system => system.build());\n\t}\n\n\t/**\n\t * Get all resources defined in this bundle\n\t */\n\tgetResources(): Map<keyof ResourceTypes, ResourceTypes[keyof ResourceTypes]> {\n\t\treturn new Map(this._resources);\n\t}\n\n\t/**\n\t * Get a specific resource by key\n\t * @param key The resource key\n\t * @returns The resource value or undefined if not found\n\t */\n\tgetResource<K extends keyof ResourceTypes>(key: K): ResourceTypes[K] | undefined {\n\t\treturn this._resources.get(key) as ResourceTypes[K] | undefined;\n\t}\n\n\t/**\n\t * Get all system builders in this bundle\n\t */\n\tgetSystemBuilders(): SystemBuilder<ComponentTypes, EventTypes, ResourceTypes, any>[] {\n\t\treturn [...this._systems];\n\t}\n\n\t/**\n\t * Check if this bundle has a specific resource\n\t * @param key The resource key to check\n\t * @returns True if the resource exists\n\t */\n\thasResource<K extends keyof ResourceTypes>(key: K): boolean {\n\t\treturn this._resources.has(key);\n\t}\n}\n\n/**\n * Utility type for merging two types\n */\n// This sets props with the same name but different type to \"never\". Maybe we want this?\nexport type Merge<T1, T2> = T1 & T2;\n// This makes the later prop types override the earlier ones. Maybe we want this instead?\n// export type Merge<T1, T2> = Omit<T1, keyof T2> & T2;\n// Or maybe this, which sets props with the same name to a union of the two types\n// export type Merge<T1, T2> = {\n// \t[K in keyof T1 | keyof T2]: K extends keyof T1 & keyof T2\n// \t\t? T1[K] | T2[K]\n// \t\t: K extends keyof T1\n// \t\t\t? T1[K]\n// \t\t\t: K extends keyof T2\n// \t\t\t\t? T2[K]\n// \t\t\t\t: never;\n// };\n\nexport type MergeAll<T extends any[]> = T extends [infer First, ...infer Rest] ?\n\tRest extends [] ?\n\t\tFirst: Merge<First, MergeAll<Rest>>:\n\t{};\n\nexport function mergeBundles<\n\tBundles extends Array<Bundle<any, any, any>>\n>(\n\tid: string,\n\t...bundles: Bundles\n): Bundle<\n\tMergeAll<{ [K in keyof Bundles]: Bundles[K] extends Bundle<infer C, any, any> ? C : never }>,\n\tMergeAll<{ [K in keyof Bundles]: Bundles[K] extends Bundle<any, infer E, any> ? E : never }>,\n\tMergeAll<{ [K in keyof Bundles]: Bundles[K] extends Bundle<any, any, infer R> ? R : never }>\n> {\n\tif (bundles.length === 0) {\n\t\treturn new Bundle(id);\n\t}\n\n\tconst combined = new Bundle(id);\n\n\tfor (const bundle of bundles) {\n\t\tfor (const system of bundle.getSystemBuilders()) {\n\t\t\tcombined.addSystem(system as any);\n\t\t}\n\n\t\t// Add resources from this bundle\n\t\tfor (const [label, resource] of bundle.getResources().entries()) {\n\t\t\tcombined.addResource(label as any, resource);\n\t\t}\n\t}\n\n\treturn combined as any;\n}\n",
10
- "import Bundle from \"./bundle\";\nimport type EntityManager from \"./entity-manager\";\nimport type EventBus from \"./event-bus\";\nimport type ResourceManager from \"./resource-manager\";\nimport type { FilteredEntity, System } from \"./types\";\n\n/**\n * Builder class for creating type-safe ECS Systems with proper query inference\n */\nexport class SystemBuilder<\n\tComponentTypes extends Record<string, any> = Record<string, any>,\n\tEventTypes extends Record<string, any> = Record<string, any>,\n\tResourceTypes extends Record<string, any> = Record<string, any>,\n\tQueries extends Record<string, QueryDefinition<ComponentTypes>> = {},\n> {\n\tprivate queries: Queries = {} as Queries;\n\tprivate processFunction?: ProcessFunction<ComponentTypes, EventTypes, ResourceTypes, Queries>;\n\tprivate attachFunction?: LifecycleFunction<ComponentTypes, EventTypes, ResourceTypes>;\n\tprivate detachFunction?: LifecycleFunction<ComponentTypes, EventTypes, ResourceTypes>;\n\tprivate eventHandlers?: {\n\t\t[EventName in keyof EventTypes]?: {\n\t\t\thandler(\n\t\t\t\tdata: EventTypes[EventName],\n\t\t\t\tentityManager: EntityManager<ComponentTypes>,\n\t\t\t\tresourceManager: ResourceManager<ResourceTypes>,\n\t\t\t\teventBus: EventBus<EventTypes>,\n\t\t\t): void;\n\t\t};\n\t};\n\n\tconstructor(\n\t\tprivate _label: string,\n\t\tprivate _bundle = new Bundle<ComponentTypes, EventTypes, ResourceTypes>()\n\t) {}\n\n\tget label() {\n\t\treturn this._label;\n\t}\n\n\tget bundle() {\n\t\treturn this._bundle;\n\t}\n\n\t/**\n\t * Add a query definition to the system\n\t */\n\taddQuery<\n\t\tQueryName extends string,\n\t\tWithComponents extends keyof ComponentTypes,\n\t\tWithoutComponents extends keyof ComponentTypes = never,\n\t>(\n\t\tname: QueryName,\n\t\tdefinition: {\n\t\t\twith: ReadonlyArray<WithComponents>;\n\t\t\twithout?: ReadonlyArray<WithoutComponents>;\n\t\t}\n\t): SystemBuilder<\n\t\tComponentTypes,\n\t\tEventTypes,\n\t\tResourceTypes,\n\t\tQueries & Record<QueryName, QueryDefinition<ComponentTypes, WithComponents, WithoutComponents>>\n\t> {\n\t\t// Cast is needed because TypeScript can't preserve the type information\n\t\t// when modifying an object property\n\t\tconst newBuilder = this as any;\n\t\tnewBuilder.queries = {\n\t\t\t...this.queries,\n\t\t\t[name]: definition,\n\t\t};\n\t\treturn newBuilder;\n\t}\n\n\t/**\n\t * Set the system's process function that runs each update\n\t */\n\tsetProcess(\n\t\tprocess: ProcessFunction<ComponentTypes, EventTypes, ResourceTypes, Queries>\n\t): SystemBuilder<ComponentTypes, EventTypes, ResourceTypes, Queries> {\n\t\tthis.processFunction = process;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Set the onAttach lifecycle hook\n\t */\n\tsetOnAttach(\n\t\tonAttach: LifecycleFunction<ComponentTypes, EventTypes, ResourceTypes>\n\t): SystemBuilder<ComponentTypes, EventTypes, ResourceTypes, Queries> {\n\t\tthis.attachFunction = onAttach;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Set the onDetach lifecycle hook\n\t */\n\tsetOnDetach(\n\t\tonDetach: LifecycleFunction<ComponentTypes, EventTypes, ResourceTypes>\n\t): SystemBuilder<ComponentTypes, EventTypes, ResourceTypes, Queries> {\n\t\tthis.detachFunction = onDetach;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Set event handlers for the system\n\t */\n\tsetEventHandlers(\n\t\thandlers: {\n\t\t\t[EventName in keyof EventTypes]?: {\n\t\t\t\thandler(\n\t\t\t\t\tdata: EventTypes[EventName],\n\t\t\t\t\tentityManager: EntityManager<ComponentTypes>,\n\t\t\t\t\tresourceManager: ResourceManager<ResourceTypes>,\n\t\t\t\t\teventBus: EventBus<EventTypes>,\n\t\t\t\t): void;\n\t\t\t};\n\t\t}\n\t): SystemBuilder<ComponentTypes, EventTypes, ResourceTypes, Queries> {\n\t\tthis.eventHandlers = handlers;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Build the final system object\n\t */\n\tbuild(): System<ComponentTypes, any, any, EventTypes, ResourceTypes> {\n\t\tconst system: System<ComponentTypes, any, any, EventTypes, ResourceTypes> = {\n\t\t\tlabel: this._label,\n\t\t\tentityQueries: this.queries as any,\n\t\t};\n\n\t\tif (this.processFunction) {\n\t\t\tsystem.process = this.processFunction as any;\n\t\t}\n\n\t\tif (this.attachFunction) {\n\t\t\tsystem.onAttach = this.attachFunction;\n\t\t}\n\n\t\tif (this.detachFunction) {\n\t\t\tsystem.onDetach = this.detachFunction;\n\t\t}\n\n\t\tif (this.eventHandlers) {\n\t\t\tsystem.eventHandlers = this.eventHandlers;\n\t\t}\n\n\t\treturn system;\n\t}\n}\n\n// Helper type definitions\ntype QueryDefinition<\n\tComponentTypes,\n\tWithComponents extends keyof ComponentTypes = any,\n\tWithoutComponents extends keyof ComponentTypes = any,\n> = {\n\twith: ReadonlyArray<WithComponents>;\n\twithout?: ReadonlyArray<WithoutComponents>;\n};\n\ntype QueryResults<\n\tComponentTypes,\n\tQueries extends Record<string, QueryDefinition<ComponentTypes>>,\n> = {\n\t[QueryName in keyof Queries]: QueryName extends string\n\t\t? FilteredEntity<\n\t\t\tComponentTypes,\n\t\t\tQueries[QueryName] extends QueryDefinition<ComponentTypes, infer W, any> ? W : never,\n\t\t\tQueries[QueryName] extends QueryDefinition<ComponentTypes, any, infer WO> ? WO : never\n\t\t>[]\n\t\t: never;\n};\n\ntype ProcessFunction<\n\tComponentTypes,\n\tEventTypes extends Record<string, any>,\n\tResourceTypes extends Record<string, any>,\n\tQueries extends Record<string, QueryDefinition<ComponentTypes>>,\n> = (\n\tqueries: QueryResults<ComponentTypes, Queries>,\n\tdeltaTime: number,\n\tentityManager: EntityManager<ComponentTypes>,\n\tresourceManager: ResourceManager<ResourceTypes>,\n\teventBus: EventBus<EventTypes>,\n) => void;\n\ntype LifecycleFunction<\n\tComponentTypes,\n\tEventTypes extends Record<string, any>,\n\tResourceTypes extends Record<string, any>,\n> = (\n\tentityManager: EntityManager<ComponentTypes>,\n\tresourceManager: ResourceManager<ResourceTypes>,\n\teventBus: EventBus<EventTypes>,\n) => void;\n\n// // Factory function for easier creation\n// function createSystem<\n// \tComponentTypes,\n// \tEventTypes = any,\n// \tResourceTypes = any\n// >(\n// \tlabel: string\n// ): SystemBuilder<ComponentTypes, EventTypes, ResourceTypes> {\n// \treturn new SystemBuilder<ComponentTypes, EventTypes, ResourceTypes>(label);\n// }\n",
10
+ "import Bundle from \"./bundle\";\nimport type { FilteredEntity, System } from \"./types\";\n\n/**\n * Builder class for creating type-safe ECS Systems with proper query inference\n */\nexport class SystemBuilder<\n\tComponentTypes extends Record<string, any> = Record<string, any>,\n\tEventTypes extends Record<string, any> = Record<string, any>,\n\tResourceTypes extends Record<string, any> = Record<string, any>,\n\tQueries extends Record<string, QueryDefinition<ComponentTypes>> = {},\n> {\n\tprivate queries: Queries = {} as Queries;\n\tprivate processFunction?: ProcessFunction<ComponentTypes, EventTypes, ResourceTypes, Queries>;\n\tprivate attachFunction?: LifecycleFunction<ComponentTypes, EventTypes, ResourceTypes>;\n\tprivate detachFunction?: LifecycleFunction<ComponentTypes, EventTypes, ResourceTypes>;\n\tprivate eventHandlers?: {\n\t\t[EventName in keyof EventTypes]?: {\n\t\t\thandler(\n\t\t\t\tdata: EventTypes[EventName],\n\t\t\t\tecs: import(\"./ecspresso\").default<\n\t\t\t\t\tComponentTypes & Record<string, any>,\n\t\t\t\t\tEventTypes,\n\t\t\t\t\tResourceTypes\n\t\t\t\t>\n\t\t\t): void;\n\t\t};\n\t};\n\n\tconstructor(\n\t\tprivate _label: string,\n\t\tprivate _bundle = new Bundle<ComponentTypes, EventTypes, ResourceTypes>()\n\t) {}\n\n\tget label() {\n\t\treturn this._label;\n\t}\n\n\tget bundle() {\n\t\treturn this._bundle;\n\t}\n\n\t/**\n\t * Add a query definition to the system\n\t */\n\taddQuery<\n\t\tQueryName extends string,\n\t\tWithComponents extends keyof ComponentTypes,\n\t\tWithoutComponents extends keyof ComponentTypes = never,\n\t>(\n\t\tname: QueryName,\n\t\tdefinition: {\n\t\t\twith: ReadonlyArray<WithComponents>;\n\t\t\twithout?: ReadonlyArray<WithoutComponents>;\n\t\t}\n\t): SystemBuilder<\n\t\tComponentTypes,\n\t\tEventTypes,\n\t\tResourceTypes,\n\t\tQueries & Record<QueryName, QueryDefinition<ComponentTypes, WithComponents, WithoutComponents>>\n\t> {\n\t\t// Cast is needed because TypeScript can't preserve the type information\n\t\t// when modifying an object property\n\t\tconst newBuilder = this as any;\n\t\tnewBuilder.queries = {\n\t\t\t...this.queries,\n\t\t\t[name]: definition,\n\t\t};\n\t\treturn newBuilder;\n\t}\n\n\t/**\n\t * Set the system's process function that runs each update\n\t * @param process Function to process entities matching the system's queries each update\n\t * @returns This SystemBuilder instance for method chaining\n\t */\n\tsetProcess(\n\t\tprocess: ProcessFunction<ComponentTypes, EventTypes, ResourceTypes, Queries>\n\t): SystemBuilder<ComponentTypes, EventTypes, ResourceTypes, Queries> {\n\t\tthis.processFunction = process;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Set the onAttach lifecycle hook\n\t * Called when the system is attached to the ECS\n\t * @param onAttach Function to run when this system is attached to the ECS\n\t * @returns This SystemBuilder instance for method chaining\n\t */\n\tsetOnAttach(\n\t\tonAttach: LifecycleFunction<ComponentTypes, EventTypes, ResourceTypes>\n\t): SystemBuilder<ComponentTypes, EventTypes, ResourceTypes, Queries> {\n\t\tthis.attachFunction = onAttach;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Set the onDetach lifecycle hook\n\t * Called when the system is removed from the ECS\n\t * @param onDetach Function to run when this system is detached from the ECS\n\t * @returns This SystemBuilder instance for method chaining\n\t */\n\tsetOnDetach(\n\t\tonDetach: LifecycleFunction<ComponentTypes, EventTypes, ResourceTypes>\n\t): SystemBuilder<ComponentTypes, EventTypes, ResourceTypes, Queries> {\n\t\tthis.detachFunction = onDetach;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Set event handlers for the system\n\t * These handlers will be automatically subscribed when the system is attached\n\t * @param handlers Object mapping event names to handler functions\n\t * @returns This SystemBuilder instance for method chaining\n\t */\n\tsetEventHandlers(\n\t\thandlers: {\n\t\t\t[EventName in keyof EventTypes]?: {\n\t\t\t\thandler(\n\t\t\t\t\tdata: EventTypes[EventName],\n\t\t\t\t\tecs: import(\"./ecspresso\").default<\n\t\t\t\t\t\tComponentTypes & Record<string, any>,\n\t\t\t\t\t\tEventTypes,\n\t\t\t\t\t\tResourceTypes\n\t\t\t\t\t>\n\t\t\t\t): void;\n\t\t\t};\n\t\t}\n\t): SystemBuilder<ComponentTypes, EventTypes, ResourceTypes, Queries> {\n\t\tthis.eventHandlers = handlers;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Build the final system object\n\t */\n\tbuild(): System<ComponentTypes, any, any, EventTypes, ResourceTypes> {\n\t\tconst system: System<ComponentTypes, any, any, EventTypes, ResourceTypes> = {\n\t\t\tlabel: this._label,\n\t\t\tentityQueries: this.queries as any,\n\t\t};\n\n\t\tif (this.processFunction) {\n\t\t\tsystem.process = this.processFunction as any;\n\t\t}\n\n\t\tif (this.attachFunction) {\n\t\t\tsystem.onAttach = this.attachFunction;\n\t\t}\n\n\t\tif (this.detachFunction) {\n\t\t\tsystem.onDetach = this.detachFunction;\n\t\t}\n\n\t\tif (this.eventHandlers) {\n\t\t\tsystem.eventHandlers = this.eventHandlers;\n\t\t}\n\n\t\treturn system;\n\t}\n}\n\n// Helper type definitions\ntype QueryDefinition<\n\tComponentTypes,\n\tWithComponents extends keyof ComponentTypes = any,\n\tWithoutComponents extends keyof ComponentTypes = any,\n> = {\n\twith: ReadonlyArray<WithComponents>;\n\twithout?: ReadonlyArray<WithoutComponents>;\n};\n\ntype QueryResults<\n\tComponentTypes,\n\tQueries extends Record<string, QueryDefinition<ComponentTypes>>,\n> = {\n\t[QueryName in keyof Queries]: QueryName extends string\n\t\t? FilteredEntity<\n\t\t\tComponentTypes,\n\t\t\tQueries[QueryName] extends QueryDefinition<ComponentTypes, infer W, any> ? W : never,\n\t\t\tQueries[QueryName] extends QueryDefinition<ComponentTypes, any, infer WO> ? WO : never\n\t\t>[]\n\t\t: never;\n};\n\n/**\n * Function signature for system process methods\n * @param queries Results of entity queries defined by the system\n * @param deltaTime Time elapsed since last update in seconds\n * @param ecs The ECSpresso instance providing access to all ECS functionality\n */\ntype ProcessFunction<\n\tComponentTypes,\n\tEventTypes extends Record<string, any>,\n\tResourceTypes extends Record<string, any>,\n\tQueries extends Record<string, QueryDefinition<ComponentTypes>>,\n> = (\n\tqueries: QueryResults<ComponentTypes, Queries>,\n\tdeltaTime: number,\n\tecs: import(\"./ecspresso\").default<\n\t\tComponentTypes & Record<string, any>,\n\t\tEventTypes,\n\t\tResourceTypes\n\t>\n) => void;\n\n/**\n * Function signature for system lifecycle hooks (onAttach and onDetach)\n * @param ecs The ECSpresso instance providing access to all ECS functionality\n */\ntype LifecycleFunction<\n\tComponentTypes,\n\tEventTypes extends Record<string, any>,\n\tResourceTypes extends Record<string, any>,\n> = (\n\tecs: import(\"./ecspresso\").default<\n\t\tComponentTypes & Record<string, any>,\n\t\tEventTypes,\n\t\tResourceTypes\n\t>\n) => void;\n\n// // Factory function for easier creation\n// function createSystem<\n// \tComponentTypes,\n// \tEventTypes = any,\n// \tResourceTypes = any\n// >(\n// \tlabel: string\n// ): SystemBuilder<ComponentTypes, EventTypes, ResourceTypes> {\n// \treturn new SystemBuilder<ComponentTypes, EventTypes, ResourceTypes>(label);\n// }\n",
11
11
  "import ECSpresso from './ecspresso';\nimport { SystemBuilder } from './system-builder';\nimport Bundle, { mergeBundles } from './bundle';\n\nexport * from './types';\nexport { default as EntityManager } from './entity-manager';\nexport { default as EventBus } from './event-bus';\nexport { default as ResourceManager } from './resource-manager';\nexport { SystemBuilder };\nexport { Bundle, mergeBundles };\nexport default ECSpresso;\n"
12
12
  ],
13
- "mappings": "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",
14
- "debugId": "3C890DE03A8FCB0764756E2164756E21",
13
+ "mappings": "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",
14
+ "debugId": "51C1642754D171C064756E2164756E21",
15
15
  "names": []
16
16
  }
@@ -4,9 +4,9 @@ export default class ResourceManager<ResourceTypes extends Record<string, any> =
4
4
  * Add a resource to the manager
5
5
  * @param label The resource key
6
6
  * @param resource The resource value
7
- * @returns The added resource
7
+ * @returns The resource manager instance for chaining
8
8
  */
9
- add<K extends keyof ResourceTypes>(label: K, resource: ResourceTypes[K]): ResourceTypes[K];
9
+ add<K extends keyof ResourceTypes>(label: K, resource: ResourceTypes[K]): this;
10
10
  /**
11
11
  * Get a resource from the manager
12
12
  * @param label The resource key
@@ -1,7 +1,4 @@
1
1
  import Bundle from "./bundle";
2
- import type EntityManager from "./entity-manager";
3
- import type EventBus from "./event-bus";
4
- import type ResourceManager from "./resource-manager";
5
2
  import type { FilteredEntity, System } from "./types";
6
3
  /**
7
4
  * Builder class for creating type-safe ECS Systems with proper query inference
@@ -26,22 +23,33 @@ export declare class SystemBuilder<ComponentTypes extends Record<string, any> =
26
23
  }): SystemBuilder<ComponentTypes, EventTypes, ResourceTypes, Queries & Record<QueryName, QueryDefinition<ComponentTypes, WithComponents, WithoutComponents>>>;
27
24
  /**
28
25
  * Set the system's process function that runs each update
26
+ * @param process Function to process entities matching the system's queries each update
27
+ * @returns This SystemBuilder instance for method chaining
29
28
  */
30
29
  setProcess(process: ProcessFunction<ComponentTypes, EventTypes, ResourceTypes, Queries>): SystemBuilder<ComponentTypes, EventTypes, ResourceTypes, Queries>;
31
30
  /**
32
31
  * Set the onAttach lifecycle hook
32
+ * Called when the system is attached to the ECS
33
+ * @param onAttach Function to run when this system is attached to the ECS
34
+ * @returns This SystemBuilder instance for method chaining
33
35
  */
34
36
  setOnAttach(onAttach: LifecycleFunction<ComponentTypes, EventTypes, ResourceTypes>): SystemBuilder<ComponentTypes, EventTypes, ResourceTypes, Queries>;
35
37
  /**
36
38
  * Set the onDetach lifecycle hook
39
+ * Called when the system is removed from the ECS
40
+ * @param onDetach Function to run when this system is detached from the ECS
41
+ * @returns This SystemBuilder instance for method chaining
37
42
  */
38
43
  setOnDetach(onDetach: LifecycleFunction<ComponentTypes, EventTypes, ResourceTypes>): SystemBuilder<ComponentTypes, EventTypes, ResourceTypes, Queries>;
39
44
  /**
40
45
  * Set event handlers for the system
46
+ * These handlers will be automatically subscribed when the system is attached
47
+ * @param handlers Object mapping event names to handler functions
48
+ * @returns This SystemBuilder instance for method chaining
41
49
  */
42
50
  setEventHandlers(handlers: {
43
51
  [EventName in keyof EventTypes]?: {
44
- handler(data: EventTypes[EventName], entityManager: EntityManager<ComponentTypes>, resourceManager: ResourceManager<ResourceTypes>, eventBus: EventBus<EventTypes>): void;
52
+ handler(data: EventTypes[EventName], ecs: import("./ecspresso").default<ComponentTypes & Record<string, any>, EventTypes, ResourceTypes>): void;
45
53
  };
46
54
  }): SystemBuilder<ComponentTypes, EventTypes, ResourceTypes, Queries>;
47
55
  /**
@@ -56,6 +64,16 @@ type QueryDefinition<ComponentTypes, WithComponents extends keyof ComponentTypes
56
64
  type QueryResults<ComponentTypes, Queries extends Record<string, QueryDefinition<ComponentTypes>>> = {
57
65
  [QueryName in keyof Queries]: QueryName extends string ? FilteredEntity<ComponentTypes, Queries[QueryName] extends QueryDefinition<ComponentTypes, infer W, any> ? W : never, Queries[QueryName] extends QueryDefinition<ComponentTypes, any, infer WO> ? WO : never>[] : never;
58
66
  };
59
- type ProcessFunction<ComponentTypes, EventTypes extends Record<string, any>, ResourceTypes extends Record<string, any>, Queries extends Record<string, QueryDefinition<ComponentTypes>>> = (queries: QueryResults<ComponentTypes, Queries>, deltaTime: number, entityManager: EntityManager<ComponentTypes>, resourceManager: ResourceManager<ResourceTypes>, eventBus: EventBus<EventTypes>) => void;
60
- type LifecycleFunction<ComponentTypes, EventTypes extends Record<string, any>, ResourceTypes extends Record<string, any>> = (entityManager: EntityManager<ComponentTypes>, resourceManager: ResourceManager<ResourceTypes>, eventBus: EventBus<EventTypes>) => void;
67
+ /**
68
+ * Function signature for system process methods
69
+ * @param queries Results of entity queries defined by the system
70
+ * @param deltaTime Time elapsed since last update in seconds
71
+ * @param ecs The ECSpresso instance providing access to all ECS functionality
72
+ */
73
+ type ProcessFunction<ComponentTypes, EventTypes extends Record<string, any>, ResourceTypes extends Record<string, any>, Queries extends Record<string, QueryDefinition<ComponentTypes>>> = (queries: QueryResults<ComponentTypes, Queries>, deltaTime: number, ecs: import("./ecspresso").default<ComponentTypes & Record<string, any>, EventTypes, ResourceTypes>) => void;
74
+ /**
75
+ * Function signature for system lifecycle hooks (onAttach and onDetach)
76
+ * @param ecs The ECSpresso instance providing access to all ECS functionality
77
+ */
78
+ type LifecycleFunction<ComponentTypes, EventTypes extends Record<string, any>, ResourceTypes extends Record<string, any>> = (ecs: import("./ecspresso").default<ComponentTypes & Record<string, any>, EventTypes, ResourceTypes>) => void;
61
79
  export {};
package/dist/types.d.ts CHANGED
@@ -1,6 +1,3 @@
1
- import type ResourceManager from "./resource-manager";
2
- import type EventBus from "./event-bus";
3
- import type EntityManager from "./entity-manager";
4
1
  export interface Entity<ComponentTypes> {
5
2
  id: number;
6
3
  components: Partial<ComponentTypes>;
@@ -24,14 +21,36 @@ export interface System<ComponentTypes, WithComponents extends keyof ComponentTy
24
21
  entityQueries?: {
25
22
  [queryName: string]: QueryConfig<ComponentTypes, WithComponents, WithoutComponents>;
26
23
  };
24
+ /**
25
+ * Process method that runs during each update cycle
26
+ * @param queries The entity queries results based on system's entityQueries definition
27
+ * @param deltaTime Time elapsed since the last update in seconds
28
+ * @param ecs The ECSpresso instance providing access to all ECS functionality
29
+ */
27
30
  process?(queries: {
28
31
  [queryName: string]: Array<FilteredEntity<ComponentTypes, WithComponents, WithoutComponents>>;
29
- } | Array<FilteredEntity<ComponentTypes, WithComponents, WithoutComponents>>, deltaTime: number, entityManager: EntityManager<ComponentTypes>, resourceManager: ResourceManager<ResourceTypes>, eventBus: EventBus<EventTypes>): void;
30
- onAttach?(entityManager: EntityManager<ComponentTypes>, resourceManager: ResourceManager<ResourceTypes>, eventBus: EventBus<EventTypes>): void;
31
- onDetach?(entityManager: EntityManager<ComponentTypes>, resourceManager: ResourceManager<ResourceTypes>, eventBus: EventBus<EventTypes>): void;
32
+ } | Array<FilteredEntity<ComponentTypes, WithComponents, WithoutComponents>>, deltaTime: number, ecs: import("./ecspresso").default<ComponentTypes & Record<string, any>, EventTypes, ResourceTypes>): void;
33
+ /**
34
+ * Lifecycle hook called when the system is attached to the ECS
35
+ * @param ecs The ECSpresso instance providing access to all ECS functionality
36
+ */
37
+ onAttach?(ecs: import("./ecspresso").default<ComponentTypes & Record<string, any>, EventTypes, ResourceTypes>): void;
38
+ /**
39
+ * Lifecycle hook called when the system is detached from the ECS
40
+ * @param ecs The ECSpresso instance providing access to all ECS functionality
41
+ */
42
+ onDetach?(ecs: import("./ecspresso").default<ComponentTypes & Record<string, any>, EventTypes, ResourceTypes>): void;
43
+ /**
44
+ * Event handlers for specific event types
45
+ */
32
46
  eventHandlers?: {
33
47
  [EventName in keyof EventTypes]?: {
34
- handler(data: EventTypes[EventName], entityManager: EntityManager<ComponentTypes>, resourceManager: ResourceManager<ResourceTypes>, eventBus: EventBus<EventTypes>): void;
48
+ /**
49
+ * Event handler function
50
+ * @param data The event data specific to this event type
51
+ * @param ecs The ECSpresso instance providing access to all ECS functionality
52
+ */
53
+ handler(data: EventTypes[EventName], ecs: import("./ecspresso").default<ComponentTypes & Record<string, any>, EventTypes, ResourceTypes>): void;
35
54
  };
36
55
  };
37
56
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ecspresso",
3
- "version": "0.0.2",
3
+ "version": "0.0.4",
4
4
  "main": "dist/index.js",
5
5
  "module": "dist/index.js",
6
6
  "types": "dist/index.d.ts",