ecspresso 0.10.2 → 0.11.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.
Files changed (82) hide show
  1. package/README.md +73 -17
  2. package/dist/asset-manager.d.ts +15 -15
  3. package/dist/asset-types.d.ts +16 -14
  4. package/dist/bundle.d.ts +66 -16
  5. package/dist/bundles/audio.d.ts +293 -0
  6. package/dist/bundles/{utils/bounds.d.ts → bounds.d.ts} +9 -7
  7. package/dist/bundles/camera.d.ts +89 -0
  8. package/dist/bundles/collision.d.ts +289 -0
  9. package/dist/bundles/diagnostics.d.ts +48 -0
  10. package/dist/bundles/{utils/input.d.ts → input.d.ts} +16 -17
  11. package/dist/bundles/physics2D.d.ts +159 -0
  12. package/dist/bundles/renderers/renderer2D.d.ts +65 -24
  13. package/dist/bundles/spatial-index.d.ts +57 -0
  14. package/dist/bundles/state-machine.d.ts +298 -0
  15. package/dist/bundles/{utils/timers.d.ts → timers.d.ts} +9 -8
  16. package/dist/bundles/{utils/transform.d.ts → transform.d.ts} +10 -10
  17. package/dist/bundles/tween.d.ts +197 -0
  18. package/dist/command-buffer.d.ts +20 -20
  19. package/dist/ecspresso-builder.d.ts +165 -0
  20. package/dist/ecspresso.d.ts +157 -178
  21. package/dist/entity-manager.d.ts +76 -40
  22. package/dist/event-bus.d.ts +6 -1
  23. package/dist/index.d.ts +1 -9
  24. package/dist/reactive-query-manager.d.ts +14 -3
  25. package/dist/resource-manager.d.ts +35 -19
  26. package/dist/screen-manager.d.ts +4 -4
  27. package/dist/screen-types.d.ts +12 -11
  28. package/dist/src/bundles/audio.js +4 -0
  29. package/dist/src/bundles/audio.js.map +10 -0
  30. package/dist/src/bundles/bounds.js +4 -0
  31. package/dist/src/bundles/bounds.js.map +10 -0
  32. package/dist/src/bundles/camera.js +4 -0
  33. package/dist/src/bundles/camera.js.map +10 -0
  34. package/dist/src/bundles/collision.js +4 -0
  35. package/dist/src/bundles/collision.js.map +11 -0
  36. package/dist/src/bundles/diagnostics.js +5 -0
  37. package/dist/src/bundles/diagnostics.js.map +10 -0
  38. package/dist/src/bundles/input.js +4 -0
  39. package/dist/src/bundles/input.js.map +10 -0
  40. package/dist/src/bundles/physics2D.js +4 -0
  41. package/dist/src/bundles/physics2D.js.map +11 -0
  42. package/dist/src/bundles/renderers/renderer2D.js +4 -0
  43. package/dist/src/bundles/renderers/renderer2D.js.map +10 -0
  44. package/dist/src/bundles/spatial-index.js +4 -0
  45. package/dist/src/bundles/spatial-index.js.map +11 -0
  46. package/dist/src/bundles/state-machine.js +4 -0
  47. package/dist/src/bundles/state-machine.js.map +10 -0
  48. package/dist/src/bundles/timers.js +4 -0
  49. package/dist/src/bundles/timers.js.map +10 -0
  50. package/dist/src/bundles/transform.js +4 -0
  51. package/dist/src/bundles/transform.js.map +10 -0
  52. package/dist/src/bundles/tween.js +4 -0
  53. package/dist/src/bundles/tween.js.map +11 -0
  54. package/dist/src/index.js +4 -0
  55. package/dist/src/index.js.map +25 -0
  56. package/dist/system-builder.d.ts +36 -42
  57. package/dist/type-utils.d.ts +52 -3
  58. package/dist/types.d.ts +10 -19
  59. package/dist/utils/check-required-cycle.d.ts +12 -0
  60. package/dist/utils/easing.d.ts +71 -0
  61. package/dist/utils/math.d.ts +67 -0
  62. package/dist/utils/narrowphase.d.ts +63 -0
  63. package/dist/utils/spatial-hash.d.ts +53 -0
  64. package/package.json +50 -20
  65. package/dist/bundles/renderers/renderer2D.js +0 -4
  66. package/dist/bundles/renderers/renderer2D.js.map +0 -10
  67. package/dist/bundles/utils/bounds.js +0 -4
  68. package/dist/bundles/utils/bounds.js.map +0 -10
  69. package/dist/bundles/utils/collision.d.ts +0 -204
  70. package/dist/bundles/utils/collision.js +0 -4
  71. package/dist/bundles/utils/collision.js.map +0 -10
  72. package/dist/bundles/utils/input.js +0 -4
  73. package/dist/bundles/utils/input.js.map +0 -10
  74. package/dist/bundles/utils/movement.d.ts +0 -86
  75. package/dist/bundles/utils/movement.js +0 -4
  76. package/dist/bundles/utils/movement.js.map +0 -10
  77. package/dist/bundles/utils/timers.js +0 -4
  78. package/dist/bundles/utils/timers.js.map +0 -10
  79. package/dist/bundles/utils/transform.js +0 -4
  80. package/dist/bundles/utils/transform.js.map +0 -10
  81. package/dist/index.js +0 -4
  82. package/dist/index.js.map +0 -22
package/README.md CHANGED
@@ -20,6 +20,7 @@ A type-safe, modular, and extensible Entity Component System (ECS) framework for
20
20
  - **Reactive Queries**: Enter/exit callbacks when entities match or unmatch queries
21
21
  - **System Groups**: Enable/disable groups of systems at runtime
22
22
  - **Component Lifecycle**: Callbacks for component add/remove with unsubscribe support
23
+ - **Required Components**: Auto-add dependent components on spawn/addComponent (e.g. `localTransform` implies `worldTransform`)
23
24
  - **Command Buffer**: Deferred structural changes for safe entity/component operations during systems
24
25
  - **Timer Bundle**: ECS-native timers with event-based completion notifications
25
26
 
@@ -47,7 +48,7 @@ npm install ecspresso
47
48
  - [Entity Hierarchy](#entity-hierarchy) -- [Traversal](#traversal), [Parent-First Traversal](#parent-first-traversal), [Cascade Deletion](#cascade-deletion)
48
49
  - [Change Detection](#change-detection) -- [Marking Changes](#marking-changes), [Changed Query Filter](#changed-query-filter), [Sequence Timing](#sequence-timing)
49
50
  - [Command Buffer](#command-buffer) -- [Available Commands](#available-commands)
50
- - [Bundles](#bundles) -- [Built-in Bundles](#built-in-bundles), [Timer Bundle](#timer-bundle)
51
+ - [Bundles](#bundles) -- [Required Components](#required-components), [Built-in Bundles](#built-in-bundles), [Timer Bundle](#timer-bundle)
51
52
  - [Asset Management](#asset-management)
52
53
  - [Screen Management](#screen-management) -- [Screen-Scoped Systems](#screen-scoped-systems), [Screen Resource](#screen-resource)
53
54
  - [Type Safety](#type-safety)
@@ -107,7 +108,7 @@ const entity = world.spawn({
107
108
  // Add components later
108
109
  world.entityManager.addComponent(entity.id, 'velocity', { x: 5, y: 0 });
109
110
 
110
- // Get component data (returns null if not found)
111
+ // Get component data (returns undefined if not found)
111
112
  const position = world.entityManager.getComponent(entity.id, 'position');
112
113
 
113
114
  // Remove components or entities
@@ -687,16 +688,66 @@ const game = ECSpresso.create<GameComponents, {}, GameResources>()
687
688
  .build();
688
689
  ```
689
690
 
691
+ ### Required Components
692
+
693
+ Bundles can declare that certain components depend on others. When an entity gains a trigger component, any required components that aren't already present are auto-added with default values:
694
+
695
+ ```typescript
696
+ const transformBundle = new Bundle<TransformComponents>('transform')
697
+ .registerRequired('localTransform', 'worldTransform', () => ({
698
+ x: 0, y: 0, rotation: 0, scaleX: 1, scaleY: 1,
699
+ }));
700
+
701
+ const world = ECSpresso.create()
702
+ .withBundle(transformBundle)
703
+ .build();
704
+
705
+ // worldTransform is auto-added with defaults
706
+ const entity = world.spawn({
707
+ localTransform: { x: 100, y: 200, rotation: 0, scaleX: 1, scaleY: 1 },
708
+ });
709
+
710
+ // Explicit values always win — no auto-add if already provided
711
+ const entity2 = world.spawn({
712
+ localTransform: { x: 100, y: 200, rotation: 0, scaleX: 1, scaleY: 1 },
713
+ worldTransform: { x: 50, y: 50, rotation: 0, scaleX: 2, scaleY: 2 }, // used as-is
714
+ });
715
+ ```
716
+
717
+ Requirements can also be registered via the builder or at runtime:
718
+
719
+ ```typescript
720
+ // Builder
721
+ const world = ECSpresso.create()
722
+ .withComponentTypes<Components>()
723
+ .withRequired('rigidBody', 'velocity', () => ({ x: 0, y: 0 }))
724
+ .withRequired('rigidBody', 'force', () => ({ x: 0, y: 0 }))
725
+ .build();
726
+
727
+ // Runtime
728
+ world.registerRequired('position', 'velocity', () => ({ x: 0, y: 0 }));
729
+ ```
730
+
731
+ **Behavior:**
732
+ - Enforced at insertion time (`spawn`, `addComponent`, `addComponents`, `spawnChild`, command buffer)
733
+ - Removal is unrestricted — removing a required component does not cascade
734
+ - Transitive requirements resolve automatically (A requires B, B requires C → all three added)
735
+ - Circular dependencies are detected and rejected at registration time
736
+ - Auto-added components are marked as changed and trigger reactive queries
737
+ - Component names and factory return types are fully type-checked
738
+
739
+ **Built-in requirements:** The Transform bundle registers `localTransform` → `worldTransform`. The Physics 2D bundle registers `rigidBody` → `velocity` and `rigidBody` → `force`.
740
+
690
741
  ### Built-in Bundles
691
742
 
692
743
  | Bundle | Import | Default Phase | Description |
693
744
  |--------|--------|---------------|-------------|
694
- | **Input** | `ecspresso/bundles/utils/input` | `preUpdate` | Frame-accurate keyboard/pointer input with action mapping |
695
- | **Timers** | `ecspresso/bundles/utils/timers` | `preUpdate` | ECS-native timers with event-based completion |
696
- | **Movement** | `ecspresso/bundles/utils/movement` | `fixedUpdate` | Velocity-based movement integration |
697
- | **Transform** | `ecspresso/bundles/utils/transform` | `postUpdate` | Hierarchical transform propagation (local/world transforms) |
698
- | **Bounds** | `ecspresso/bundles/utils/bounds` | `postUpdate` | Screen bounds enforcement (destroy, clamp, wrap) |
699
- | **Collision** | `ecspresso/bundles/utils/collision` | `postUpdate` | Layer-based AABB/circle collision detection with events |
745
+ | **Input** | `ecspresso/bundles/input` | `preUpdate` | Frame-accurate keyboard/pointer input with action mapping |
746
+ | **Timers** | `ecspresso/bundles/timers` | `preUpdate` | ECS-native timers with event-based completion |
747
+ | **Movement** | `ecspresso/bundles/movement` | `fixedUpdate` | Velocity-based movement integration |
748
+ | **Transform** | `ecspresso/bundles/transform` | `postUpdate` | Hierarchical transform propagation (local/world transforms) |
749
+ | **Bounds** | `ecspresso/bundles/bounds` | `postUpdate` | Screen bounds enforcement (destroy, clamp, wrap) |
750
+ | **Collision** | `ecspresso/bundles/collision` | `postUpdate` | Layer-based AABB/circle collision detection with events |
700
751
  | **2D Renderer** | `ecspresso/bundles/renderers/renderer2D` | `render` | Automated PixiJS scene graph wiring |
701
752
 
702
753
  Each bundle accepts a `phase` option to override its default.
@@ -709,11 +760,9 @@ The input bundle provides frame-accurate keyboard, pointer (mouse + touch via Po
709
760
  import {
710
761
  createInputBundle,
711
762
  type InputResourceTypes, type KeyCode
712
- } from 'ecspresso/bundles/utils/input';
713
-
714
- interface Resources extends InputResourceTypes {}
763
+ } from 'ecspresso/bundles/input';
715
764
 
716
- const world = ECSpresso.create<Components, Events, Resources>()
765
+ const world = ECSpresso.create()
717
766
  .withBundle(createInputBundle({
718
767
  actions: {
719
768
  jump: { keys: [' ', 'ArrowUp'] },
@@ -730,10 +779,17 @@ if (input.actions.justActivated('jump')) { /* ... */ }
730
779
  if (input.keyboard.isDown('ArrowRight')) { /* ... */ }
731
780
  if (input.pointer.justPressed(0)) { /* ... */ }
732
781
 
733
- // Runtime remapping
734
- input.setActionMap({ jump: { keys: ['w'] } });
782
+ // Runtime remapping — must include all configured actions
783
+ input.setActionMap({
784
+ jump: { keys: ['w'] },
785
+ shoot: { keys: ['z'], buttons: [0] },
786
+ moveLeft: { keys: ['a'] },
787
+ moveRight: { keys: ['d'] },
788
+ });
735
789
  ```
736
790
 
791
+ Action names are type-safe — `isActive`, `justActivated`, `justDeactivated`, `setActionMap`, and `getActionMap` only accept action names from the config. The type parameter `A` is inferred from the `actions` object keys passed to `createInputBundle`. Defaults to `string` when no actions are configured.
792
+
737
793
  Key values use the `KeyCode` type — a union of all standard `KeyboardEvent.key` values — providing autocomplete and compile-time validation. Note that the space bar key is `' '` (a space character), not `'Space'`.
738
794
 
739
795
  ### Timer Bundle
@@ -744,7 +800,7 @@ The timer bundle provides ECS-native timers that follow the "data, not callbacks
744
800
  import {
745
801
  createTimerBundle, createTimer, createRepeatingTimer,
746
802
  type TimerComponentTypes, type TimerEventData
747
- } from 'ecspresso/bundles/utils/timers';
803
+ } from 'ecspresso/bundles/timers';
748
804
 
749
805
  // Events used with onComplete must have TimerEventData payload
750
806
  interface Events {
@@ -949,8 +1005,8 @@ world.getResource('nonexistent');
949
1005
  world.entityManager.addComponent(999, 'position', { x: 0, y: 0 });
950
1006
  // → "Cannot add component 'position': Entity with ID 999 does not exist"
951
1007
 
952
- // Component not found returns null (no throw)
953
- world.entityManager.getComponent(123, 'position'); // null
1008
+ // Component not found returns undefined (no throw)
1009
+ world.entityManager.getComponent(123, 'position'); // undefined
954
1010
  ```
955
1011
 
956
1012
  ## Performance Tips
@@ -6,7 +6,7 @@ import type { AssetStatus, AssetDefinition, AssetHandle, AssetsResource, AssetEv
6
6
  /**
7
7
  * Manages asset loading and access for ECSpresso
8
8
  */
9
- export default class AssetManager<AssetTypes extends Record<string, unknown> = Record<string, never>> {
9
+ export default class AssetManager<AssetTypes extends Record<string, unknown> = Record<string, never>, AssetGroupNames extends string = string> {
10
10
  private readonly assets;
11
11
  private readonly groups;
12
12
  private eventBus;
@@ -14,7 +14,7 @@ export default class AssetManager<AssetTypes extends Record<string, unknown> = R
14
14
  * Set the event bus for asset events
15
15
  * @internal
16
16
  */
17
- setEventBus(eventBus: EventBus<AssetEvents>): void;
17
+ setEventBus(eventBus: EventBus<AssetEvents<keyof AssetTypes & string, AssetGroupNames>>): void;
18
18
  /**
19
19
  * Register an asset definition
20
20
  */
@@ -30,7 +30,7 @@ export default class AssetManager<AssetTypes extends Record<string, unknown> = R
30
30
  /**
31
31
  * Load all assets in a group
32
32
  */
33
- loadAssetGroup(groupName: string): Promise<void>;
33
+ loadAssetGroup(groupName: AssetGroupNames): Promise<void>;
34
34
  /**
35
35
  * Get a loaded asset. Throws if not loaded.
36
36
  */
@@ -54,15 +54,15 @@ export default class AssetManager<AssetTypes extends Record<string, unknown> = R
54
54
  /**
55
55
  * Check if all assets in a group are loaded
56
56
  */
57
- isGroupLoaded(groupName: string): boolean;
57
+ isGroupLoaded(groupName: AssetGroupNames): boolean;
58
58
  /**
59
59
  * Get the loading progress of a group (0-1)
60
60
  */
61
- getGroupProgress(groupName: string): number;
61
+ getGroupProgress(groupName: AssetGroupNames): number;
62
62
  /**
63
63
  * Get detailed group progress
64
64
  */
65
- getGroupProgressDetails(groupName: string): {
65
+ getGroupProgressDetails(groupName: AssetGroupNames): {
66
66
  loaded: number;
67
67
  total: number;
68
68
  progress: number;
@@ -74,7 +74,7 @@ export default class AssetManager<AssetTypes extends Record<string, unknown> = R
74
74
  /**
75
75
  * Create the $assets resource object
76
76
  */
77
- createResource(): AssetsResource<AssetTypes>;
77
+ createResource(): AssetsResource<AssetTypes, AssetGroupNames>;
78
78
  /**
79
79
  * Get all registered asset keys
80
80
  */
@@ -91,21 +91,21 @@ export default class AssetManager<AssetTypes extends Record<string, unknown> = R
91
91
  /**
92
92
  * Implementation of AssetConfigurator for builder pattern
93
93
  */
94
- export declare class AssetConfiguratorImpl<A extends Record<string, unknown>> implements AssetConfigurator<A> {
94
+ export declare class AssetConfiguratorImpl<A extends Record<string, unknown>, G extends string = never> implements AssetConfigurator<A, G> {
95
95
  private readonly manager;
96
- constructor(manager: AssetManager<A>);
97
- add<K extends string, T>(key: K, loader: () => Promise<T>): AssetConfigurator<A & Record<K, T>>;
98
- addWithConfig<K extends string, T>(key: K, definition: AssetDefinition<T>): AssetConfigurator<A & Record<K, T>>;
99
- addGroup<G extends string, T extends Record<string, () => Promise<unknown>>>(groupName: G, assets: T): AssetConfigurator<A & {
96
+ constructor(manager: AssetManager<A, G>);
97
+ add<K extends string, T>(key: K, loader: () => Promise<T>): AssetConfigurator<A & Record<K, T>, G>;
98
+ addWithConfig<K extends string, T>(key: K, definition: AssetDefinition<T>): AssetConfigurator<A & Record<K, T>, G>;
99
+ addGroup<GN extends string, T extends Record<string, () => Promise<unknown>>>(groupName: GN, assets: T): AssetConfigurator<A & {
100
100
  [K in keyof T]: Awaited<ReturnType<T[K]>>;
101
- }>;
101
+ }, G | GN>;
102
102
  /**
103
103
  * Get the underlying manager
104
104
  * @internal
105
105
  */
106
- getManager(): AssetManager<A>;
106
+ getManager(): AssetManager<A, G>;
107
107
  }
108
108
  /**
109
109
  * Create a new AssetConfigurator for builder pattern usage
110
110
  */
111
- export declare function createAssetConfigurator<A extends Record<string, unknown> = Record<string, never>>(manager?: AssetManager<A>): AssetConfiguratorImpl<A>;
111
+ export declare function createAssetConfigurator<A extends Record<string, unknown> = Record<string, never>, G extends string = never>(manager?: AssetManager<A, G>): AssetConfiguratorImpl<A, G>;
@@ -32,7 +32,7 @@ export interface AssetHandle<T> {
32
32
  * Resource interface for accessing assets in systems
33
33
  * Exposed as $assets resource
34
34
  */
35
- export interface AssetsResource<A extends Record<string, unknown>> {
35
+ export interface AssetsResource<A extends Record<string, unknown>, G extends string = string> {
36
36
  /**
37
37
  * Get the loading status of an asset
38
38
  */
@@ -44,11 +44,11 @@ export interface AssetsResource<A extends Record<string, unknown>> {
44
44
  /**
45
45
  * Check if all assets in a group are loaded
46
46
  */
47
- isGroupLoaded(groupName: string): boolean;
47
+ isGroupLoaded(groupName: G): boolean;
48
48
  /**
49
49
  * Get the loading progress of a group (0-1)
50
50
  */
51
- getGroupProgress(groupName: string): number;
51
+ getGroupProgress(groupName: G): number;
52
52
  /**
53
53
  * Get a loaded asset. Throws if not loaded.
54
54
  */
@@ -63,21 +63,23 @@ export interface AssetsResource<A extends Record<string, unknown>> {
63
63
  getHandle<K extends keyof A>(key: K): AssetHandle<A[K]>;
64
64
  }
65
65
  /**
66
- * Events emitted by the asset system
66
+ * Events emitted by the asset system.
67
+ * @typeParam K - Asset key type (defaults to `string` for backward compatibility)
68
+ * @typeParam G - Asset group name type (defaults to `string` for backward compatibility)
67
69
  */
68
- export interface AssetEvents {
70
+ export interface AssetEvents<K extends string = string, G extends string = string> {
69
71
  assetLoaded: {
70
- key: string;
72
+ key: K;
71
73
  };
72
74
  assetFailed: {
73
- key: string;
75
+ key: K;
74
76
  error: Error;
75
77
  };
76
78
  assetGroupLoaded: {
77
- group: string;
79
+ group: G;
78
80
  };
79
81
  assetGroupProgress: {
80
- group: string;
82
+ group: G;
81
83
  progress: number;
82
84
  loaded: number;
83
85
  total: number;
@@ -86,19 +88,19 @@ export interface AssetEvents {
86
88
  /**
87
89
  * Configuration for asset definitions during builder setup
88
90
  */
89
- export interface AssetConfigurator<A extends Record<string, unknown>> {
91
+ export interface AssetConfigurator<A extends Record<string, unknown>, G extends string = never> {
90
92
  /**
91
93
  * Add a single eager asset
92
94
  */
93
- add<K extends string, T>(key: K, loader: () => Promise<T>): AssetConfigurator<A & Record<K, T>>;
95
+ add<K extends string, T>(key: K, loader: () => Promise<T>): AssetConfigurator<A & Record<K, T>, G>;
94
96
  /**
95
97
  * Add a single asset with full configuration
96
98
  */
97
- addWithConfig<K extends string, T>(key: K, definition: AssetDefinition<T>): AssetConfigurator<A & Record<K, T>>;
99
+ addWithConfig<K extends string, T>(key: K, definition: AssetDefinition<T>): AssetConfigurator<A & Record<K, T>, G>;
98
100
  /**
99
101
  * Add a group of assets that can be loaded together
100
102
  */
101
- addGroup<G extends string, T extends Record<string, () => Promise<unknown>>>(groupName: G, assets: T): AssetConfigurator<A & {
103
+ addGroup<GN extends string, T extends Record<string, () => Promise<unknown>>>(groupName: GN, assets: T): AssetConfigurator<A & {
102
104
  [K in keyof T]: Awaited<ReturnType<T[K]>>;
103
- }>;
105
+ }, G | GN>;
104
106
  }
package/dist/bundle.d.ts CHANGED
@@ -1,18 +1,22 @@
1
1
  import { SystemBuilderWithBundle } from './system-builder';
2
2
  import type ECSpresso from './ecspresso';
3
+ import type { ResourceFactoryWithDeps } from './resource-manager';
3
4
  import type { AssetDefinition } from './asset-types';
4
5
  import type { ScreenDefinition } from './screen-types';
5
6
  import type { BundlesAreCompatible } from './type-utils';
7
+ import type { QueryDefinition } from './types';
6
8
  /**
7
9
  * Bundle class that encapsulates a set of components, resources, events, and systems
8
10
  * that can be merged into a ECSpresso instance
9
11
  */
10
- export default class Bundle<ComponentTypes extends Record<string, any> = {}, EventTypes extends Record<string, any> = {}, ResourceTypes extends Record<string, any> = {}, AssetTypes extends Record<string, unknown> = {}, ScreenStates extends Record<string, ScreenDefinition<any, any>> = {}> {
12
+ export default class Bundle<ComponentTypes extends Record<string, any> = {}, EventTypes extends Record<string, any> = {}, ResourceTypes extends Record<string, any> = {}, AssetTypes extends Record<string, unknown> = {}, ScreenStates extends Record<string, ScreenDefinition<any, any>> = {}, Labels extends string = never, Groups extends string = never, AssetGroupNames extends string = never, ReactiveQueryNames extends string = never> {
11
13
  private _systems;
12
14
  private _resources;
13
15
  private _assets;
14
16
  private _assetGroups;
15
17
  private _screens;
18
+ private _disposeCallbacks;
19
+ private _requiredComponents;
16
20
  private _id;
17
21
  constructor(id?: string);
18
22
  /**
@@ -27,17 +31,14 @@ export default class Bundle<ComponentTypes extends Record<string, any> = {}, Eve
27
31
  /**
28
32
  * Add a system to this bundle, by label (creating a new builder) or by reusing an existing one
29
33
  */
30
- addSystem<Q extends Record<string, any>>(builder: SystemBuilderWithBundle<ComponentTypes, EventTypes, ResourceTypes, Q>): SystemBuilderWithBundle<ComponentTypes, EventTypes, ResourceTypes, Q>;
31
- addSystem(label: string): SystemBuilderWithBundle<ComponentTypes, EventTypes, ResourceTypes, {}>;
34
+ addSystem<Q extends Record<string, QueryDefinition<ComponentTypes>>, BL extends string, BG extends string, L extends string, SG extends string>(builder: SystemBuilderWithBundle<ComponentTypes, EventTypes, ResourceTypes, AssetTypes, ScreenStates, Q, BL, BG, L, SG, any, any>): SystemBuilderWithBundle<ComponentTypes, EventTypes, ResourceTypes, AssetTypes, ScreenStates, Q, Labels, Groups, L, SG, AssetGroupNames, ReactiveQueryNames>;
35
+ addSystem<L extends string>(label: L): SystemBuilderWithBundle<ComponentTypes, EventTypes, ResourceTypes, AssetTypes, ScreenStates, {}, Labels, Groups, L, never, AssetGroupNames, ReactiveQueryNames>;
32
36
  /**
33
37
  * Add a resource to this bundle
34
38
  * @param label The resource key
35
39
  * @param resource The resource value, a factory function, or a factory with dependencies
36
40
  */
37
- addResource<K extends keyof ResourceTypes>(label: K, resource: ResourceTypes[K] | ((ecs: ECSpresso<ComponentTypes, EventTypes, ResourceTypes>) => ResourceTypes[K] | Promise<ResourceTypes[K]>) | {
38
- dependsOn: readonly string[];
39
- factory: (ecs: ECSpresso<ComponentTypes, EventTypes, ResourceTypes>) => ResourceTypes[K] | Promise<ResourceTypes[K]>;
40
- }): this;
41
+ addResource<K extends keyof ResourceTypes>(label: K, resource: ResourceTypes[K] | ((ecs: ECSpresso<ComponentTypes, EventTypes, ResourceTypes, AssetTypes, ScreenStates>) => ResourceTypes[K] | Promise<ResourceTypes[K]>) | ResourceFactoryWithDeps<ResourceTypes[K], ECSpresso<ComponentTypes, EventTypes, ResourceTypes, AssetTypes, ScreenStates>, keyof ResourceTypes & string>): this;
41
42
  /**
42
43
  * Add an asset to this bundle
43
44
  * @param key The asset key
@@ -47,21 +48,21 @@ export default class Bundle<ComponentTypes extends Record<string, any> = {}, Eve
47
48
  addAsset<K extends string, T>(key: K, loader: () => Promise<T>, options?: {
48
49
  eager?: boolean;
49
50
  group?: string;
50
- }): Bundle<ComponentTypes, EventTypes, ResourceTypes, AssetTypes & Record<K, T>, ScreenStates>;
51
+ }): Bundle<ComponentTypes, EventTypes, ResourceTypes, AssetTypes & Record<K, T>, ScreenStates, Labels, Groups, AssetGroupNames, ReactiveQueryNames>;
51
52
  /**
52
53
  * Add a group of assets to this bundle
53
54
  * @param groupName The group name
54
55
  * @param assets Object mapping asset keys to loader functions
55
56
  */
56
- addAssetGroup<G extends string, T extends Record<string, () => Promise<unknown>>>(groupName: G, assets: T): Bundle<ComponentTypes, EventTypes, ResourceTypes, AssetTypes & {
57
+ addAssetGroup<GN extends string, T extends Record<string, () => Promise<unknown>>>(groupName: GN, assets: T): Bundle<ComponentTypes, EventTypes, ResourceTypes, AssetTypes & {
57
58
  [K in keyof T]: Awaited<ReturnType<T[K]>>;
58
- }, ScreenStates>;
59
+ }, ScreenStates, Labels, Groups, AssetGroupNames | GN, ReactiveQueryNames>;
59
60
  /**
60
61
  * Add a screen to this bundle
61
62
  * @param name The screen name
62
63
  * @param definition The screen definition
63
64
  */
64
- addScreen<K extends string, Config extends Record<string, unknown>, State extends Record<string, unknown>>(name: K, definition: ScreenDefinition<Config, State>): Bundle<ComponentTypes, EventTypes, ResourceTypes, AssetTypes, ScreenStates & Record<K, ScreenDefinition<Config, State>>>;
65
+ addScreen<K extends string, Config extends Record<string, unknown>, State extends Record<string, unknown>>(name: K, definition: ScreenDefinition<Config, State, ECSpresso<ComponentTypes, EventTypes, ResourceTypes, AssetTypes, Record<string, ScreenDefinition>>>): Bundle<ComponentTypes, EventTypes, ResourceTypes, AssetTypes, ScreenStates & Record<K, ScreenDefinition<Config, State>>, Labels, Groups, AssetGroupNames, ReactiveQueryNames>;
65
66
  /**
66
67
  * Get all asset definitions in this bundle
67
68
  */
@@ -70,6 +71,50 @@ export default class Bundle<ComponentTypes extends Record<string, any> = {}, Eve
70
71
  * Get all screen definitions in this bundle
71
72
  */
72
73
  getScreens(): Map<string, ScreenDefinition<any, any>>;
74
+ /**
75
+ * Register a dispose callback for a component type in this bundle.
76
+ * Called when a component is removed (explicit removal, entity destruction, or replacement).
77
+ * @param componentName The component type to register disposal for
78
+ * @param callback Function receiving the component value being disposed
79
+ * @returns This bundle for method chaining
80
+ */
81
+ registerDispose<K extends keyof ComponentTypes>(componentName: K, callback: (value: ComponentTypes[K]) => void): this;
82
+ /**
83
+ * Get all registered dispose callbacks in this bundle
84
+ */
85
+ getDisposeCallbacks(): Map<string, (value: unknown) => void>;
86
+ /**
87
+ * Register a required component relationship.
88
+ * When an entity gains `trigger`, the `required` component is auto-added
89
+ * (using `factory` for the default value) if not already present.
90
+ * @param trigger The component whose presence triggers auto-addition
91
+ * @param required The component to auto-add
92
+ * @param factory Function that creates the default value for the required component
93
+ * @returns This bundle for method chaining
94
+ */
95
+ registerRequired<Trigger extends keyof ComponentTypes, Required extends keyof ComponentTypes>(trigger: Trigger, required: Required, factory: (triggerValue: ComponentTypes[Trigger]) => ComponentTypes[Required]): this;
96
+ /**
97
+ * Declare reactive query names that this bundle will register at runtime.
98
+ * This is a pure type-level operation with no runtime cost.
99
+ */
100
+ withReactiveQueryNames<N extends string>(): Bundle<ComponentTypes, EventTypes, ResourceTypes, AssetTypes, ScreenStates, Labels, Groups, AssetGroupNames, ReactiveQueryNames | N>;
101
+ /**
102
+ * Get all registered required component mappings in this bundle
103
+ */
104
+ getRequiredComponents(): Map<string, Array<{
105
+ component: string;
106
+ factory: (triggerValue: any) => unknown;
107
+ }>>;
108
+ /**
109
+ * Check for circular dependencies in the required components graph
110
+ * @throws Error if adding the new edge would create a cycle
111
+ */
112
+ private _checkRequiredCycle;
113
+ /**
114
+ * Internal method to set a dispose callback
115
+ * @internal Used by mergeBundles
116
+ */
117
+ _setDisposeCallback(name: string, callback: (value: unknown) => void): void;
73
118
  /**
74
119
  * Internal method to set a resource
75
120
  * @internal Used by mergeBundles
@@ -85,16 +130,21 @@ export default class Bundle<ComponentTypes extends Record<string, any> = {}, Eve
85
130
  * @internal Used by mergeBundles
86
131
  */
87
132
  _setScreen(name: string, definition: ScreenDefinition<any, any>): void;
133
+ /**
134
+ * Internal method to add a required component entry
135
+ * @internal Used by mergeBundles
136
+ */
137
+ _addRequired(trigger: string, component: string, factory: (triggerValue: any) => unknown): void;
88
138
  /**
89
139
  * Get all systems defined in this bundle
90
140
  * Returns built System objects instead of SystemBuilders
91
141
  */
92
- getSystems(): SystemBuilderWithBundle<ComponentTypes, EventTypes, ResourceTypes, any>[];
142
+ getSystems(): SystemBuilderWithBundle<ComponentTypes, EventTypes, ResourceTypes, AssetTypes, ScreenStates, any, any, any, any, any, any, any>[];
93
143
  /**
94
144
  * Register all systems in this bundle with an ECSpresso instance
95
145
  * @internal Used by ECSpresso when adding a bundle
96
146
  */
97
- registerSystemsWithEcspresso(ecspresso: ECSpresso<ComponentTypes, EventTypes, ResourceTypes>): void;
147
+ registerSystemsWithEcspresso(ecspresso: ECSpresso<ComponentTypes, EventTypes, ResourceTypes, AssetTypes, ScreenStates>): void;
98
148
  /**
99
149
  * Get all resources defined in this bundle
100
150
  */
@@ -108,7 +158,7 @@ export default class Bundle<ComponentTypes extends Record<string, any> = {}, Eve
108
158
  /**
109
159
  * Get all system builders in this bundle
110
160
  */
111
- getSystemBuilders(): SystemBuilderWithBundle<ComponentTypes, EventTypes, ResourceTypes, any>[];
161
+ getSystemBuilders(): SystemBuilderWithBundle<ComponentTypes, EventTypes, ResourceTypes, AssetTypes, ScreenStates, any, any, any, any, any, any, any>[];
112
162
  /**
113
163
  * Check if this bundle has a specific resource
114
164
  * @param key The resource key to check
@@ -119,5 +169,5 @@ export default class Bundle<ComponentTypes extends Record<string, any> = {}, Eve
119
169
  /**
120
170
  * Function that merges multiple bundles into a single bundle
121
171
  */
122
- export declare function mergeBundles<C1 extends Record<string, any>, E1 extends Record<string, any>, R1 extends Record<string, any>, A1 extends Record<string, unknown>, S1 extends Record<string, ScreenDefinition<any, any>>, C2 extends Record<string, any>, E2 extends Record<string, any>, R2 extends Record<string, any>, A2 extends Record<string, unknown>, S2 extends Record<string, ScreenDefinition<any, any>>>(id: string, bundle1: Bundle<C1, E1, R1, A1, S1>, bundle2: BundlesAreCompatible<C1, C2, E1, E2, R1, R2, A1, A2, S1, S2> extends true ? Bundle<C2, E2, R2, A2, S2> : never): Bundle<C1 & C2, E1 & E2, R1 & R2, A1 & A2, S1 & S2>;
123
- export declare function mergeBundles<ComponentTypes extends Record<string, any>, EventTypes extends Record<string, any>, ResourceTypes extends Record<string, any>, AssetTypes extends Record<string, unknown>, ScreenStates extends Record<string, ScreenDefinition<any, any>>>(id: string, ...bundles: Array<Bundle<ComponentTypes, EventTypes, ResourceTypes, AssetTypes, ScreenStates>>): Bundle<ComponentTypes, EventTypes, ResourceTypes, AssetTypes, ScreenStates>;
172
+ export declare function mergeBundles<C1 extends Record<string, any>, E1 extends Record<string, any>, R1 extends Record<string, any>, A1 extends Record<string, unknown>, S1 extends Record<string, ScreenDefinition<any, any>>, L1 extends string, G1 extends string, AG1 extends string, RQ1 extends string, C2 extends Record<string, any>, E2 extends Record<string, any>, R2 extends Record<string, any>, A2 extends Record<string, unknown>, S2 extends Record<string, ScreenDefinition<any, any>>, L2 extends string, G2 extends string, AG2 extends string, RQ2 extends string>(id: string, bundle1: Bundle<C1, E1, R1, A1, S1, L1, G1, AG1, RQ1>, bundle2: BundlesAreCompatible<C1, C2, E1, E2, R1, R2, A1, A2, S1, S2> extends true ? Bundle<C2, E2, R2, A2, S2, L2, G2, AG2, RQ2> : never): Bundle<C1 & C2, E1 & E2, R1 & R2, A1 & A2, S1 & S2, L1 | L2, G1 | G2, AG1 | AG2, RQ1 | RQ2>;
173
+ export declare function mergeBundles<ComponentTypes extends Record<string, any>, EventTypes extends Record<string, any>, ResourceTypes extends Record<string, any>, AssetTypes extends Record<string, unknown>, ScreenStates extends Record<string, ScreenDefinition<any, any>>, Labels extends string, Groups extends string, AssetGroupNamesParam extends string, ReactiveQueryNamesParam extends string>(id: string, ...bundles: Array<Bundle<ComponentTypes, EventTypes, ResourceTypes, AssetTypes, ScreenStates, Labels, Groups, AssetGroupNamesParam, ReactiveQueryNamesParam>>): Bundle<ComponentTypes, EventTypes, ResourceTypes, AssetTypes, ScreenStates, Labels, Groups, AssetGroupNamesParam, ReactiveQueryNamesParam>;