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
@@ -8,11 +8,12 @@
8
8
  */
9
9
  import type { Application, ApplicationOptions, Container, Sprite, Graphics } from 'pixi.js';
10
10
  import { Bundle } from 'ecspresso';
11
- import { type LocalTransform, type WorldTransform, type TransformComponentTypes, type TransformBundleOptions } from 'ecspresso/bundles/utils/transform';
12
- import { type BoundsRect } from 'ecspresso/bundles/utils/bounds';
11
+ import { type LocalTransform, type WorldTransform, type TransformComponentTypes, type TransformBundleOptions } from 'ecspresso/bundles/transform';
12
+ import { type BoundsRect } from 'ecspresso/bundles/bounds';
13
+ import type { CameraResourceTypes } from 'ecspresso/bundles/camera';
13
14
  export type { LocalTransform, WorldTransform, TransformComponentTypes };
14
15
  export type { BoundsRect };
15
- export { createTransform, createLocalTransform, createWorldTransform } from 'ecspresso/bundles/utils/transform';
16
+ export { createTransform, createLocalTransform, createWorldTransform, DEFAULT_LOCAL_TRANSFORM, DEFAULT_WORLD_TRANSFORM } from 'ecspresso/bundles/transform';
16
17
  /**
17
18
  * Visibility and alpha component
18
19
  */
@@ -22,14 +23,14 @@ export interface Visible {
22
23
  }
23
24
  /**
24
25
  * Aggregate component types for the 2D renderer bundle.
25
- * Users should extend this interface with their own component types.
26
+ * Included automatically via `.withBundle(createRenderer2DBundle({ ... }))`.
26
27
  *
27
28
  * @example
28
29
  * ```typescript
29
- * interface GameComponents extends Renderer2DComponentTypes {
30
- * velocity: { x: number; y: number };
31
- * player: true;
32
- * }
30
+ * const ecs = ECSpresso.create()
31
+ * .withBundle(createRenderer2DBundle({ ... }))
32
+ * .withComponentTypes<{ velocity: { x: number; y: number }; player: true }>()
33
+ * .build();
33
34
  * ```
34
35
  */
35
36
  export interface Renderer2DComponentTypes extends TransformComponentTypes {
@@ -59,14 +60,33 @@ export interface Renderer2DResourceTypes {
59
60
  /** Screen bounds derived from PixiJS screen dimensions, updated on resize */
60
61
  bounds: BoundsRect;
61
62
  }
63
+ export type ScaleMode = 'fit' | 'cover' | 'stretch';
64
+ export interface ScreenScaleOptions {
65
+ readonly width: number;
66
+ readonly height: number;
67
+ readonly mode?: ScaleMode;
68
+ }
69
+ export interface ViewportScale {
70
+ scaleX: number;
71
+ scaleY: number;
72
+ offsetX: number;
73
+ offsetY: number;
74
+ physicalWidth: number;
75
+ physicalHeight: number;
76
+ readonly designWidth: number;
77
+ readonly designHeight: number;
78
+ }
79
+ export interface ViewportScaleResourceTypes {
80
+ viewportScale: ViewportScale;
81
+ }
62
82
  /**
63
83
  * Common options shared between both initialization modes
64
84
  */
65
- interface Renderer2DBundleCommonOptions {
85
+ interface Renderer2DBundleCommonOptions<G extends string = 'renderer2d'> {
66
86
  /** Optional custom root container (defaults to app.stage) */
67
87
  rootContainer?: Container;
68
88
  /** System group name (default: 'renderer2d') */
69
- systemGroup?: string;
89
+ systemGroup?: G;
70
90
  /** Priority for render sync system (default: 500) */
71
91
  renderSyncPriority?: number;
72
92
  /** Options for the included transform bundle */
@@ -75,11 +95,17 @@ interface Renderer2DBundleCommonOptions {
75
95
  startLoop?: boolean;
76
96
  /** Ordered render layer names (back-to-front). Entities with a renderLayer component are placed in the corresponding container. */
77
97
  renderLayers?: string[];
98
+ /** Automatically apply cameraState resource to rootContainer each frame.
99
+ * Requires the camera bundle to be installed. (default: false) */
100
+ camera?: boolean;
101
+ /** Enforce a logical design resolution with automatic aspect-ratio-aware scaling.
102
+ * When set, systems work in design-resolution coordinate space. */
103
+ screenScale?: ScreenScaleOptions;
78
104
  }
79
105
  /**
80
106
  * Options when providing a pre-initialized PixiJS Application
81
107
  */
82
- export interface Renderer2DBundleAppOptions extends Renderer2DBundleCommonOptions {
108
+ export interface Renderer2DBundleAppOptions<G extends string = 'renderer2d'> extends Renderer2DBundleCommonOptions<G> {
83
109
  /** The PixiJS Application instance (already initialized) */
84
110
  app: Application;
85
111
  init?: never;
@@ -88,7 +114,7 @@ export interface Renderer2DBundleAppOptions extends Renderer2DBundleCommonOption
88
114
  /**
89
115
  * Options when letting the bundle create and manage the PixiJS Application
90
116
  */
91
- export interface Renderer2DBundleManagedOptions extends Renderer2DBundleCommonOptions {
117
+ export interface Renderer2DBundleManagedOptions<G extends string = 'renderer2d'> extends Renderer2DBundleCommonOptions<G> {
92
118
  app?: never;
93
119
  /** PixiJS ApplicationOptions - bundle will create and initialize the Application */
94
120
  init: Partial<ApplicationOptions>;
@@ -108,31 +134,25 @@ export interface Renderer2DBundleManagedOptions extends Renderer2DBundleCommonOp
108
134
  * ```typescript
109
135
  * const app = new Application();
110
136
  * await app.init({ resizeTo: window });
111
- * const ecs = ECSpresso.create<GameComponents, {}, {}>()
137
+ * const ecs = ECSpresso.create()
112
138
  * .withBundle(createRenderer2DBundle({ app }))
139
+ * .withComponentTypes<{ player: true }>()
113
140
  * .build();
114
141
  * ```
115
142
  *
116
143
  * @example Managed mode (convenience)
117
144
  * ```typescript
118
- * const ecs = ECSpresso.create<GameComponents, {}, {}>()
145
+ * const ecs = ECSpresso.create()
119
146
  * .withBundle(createRenderer2DBundle({
120
147
  * init: { background: '#1099bb', resizeTo: window },
121
148
  * container: document.body,
122
149
  * }))
150
+ * .withComponentTypes<{ player: true }>()
123
151
  * .build();
124
152
  * await ecs.initialize(); // Application created here
125
153
  * ```
126
154
  */
127
- export type Renderer2DBundleOptions = Renderer2DBundleAppOptions | Renderer2DBundleManagedOptions;
128
- /**
129
- * Default local transform values
130
- */
131
- export declare const DEFAULT_LOCAL_TRANSFORM: Readonly<LocalTransform>;
132
- /**
133
- * Default world transform values
134
- */
135
- export declare const DEFAULT_WORLD_TRANSFORM: Readonly<WorldTransform>;
155
+ export type Renderer2DBundleOptions<G extends string = 'renderer2d'> = Renderer2DBundleAppOptions<G> | Renderer2DBundleManagedOptions<G>;
136
156
  interface PositionOption {
137
157
  x?: number;
138
158
  y?: number;
@@ -188,6 +208,15 @@ export declare function createGraphicsComponents(graphics: Graphics, position?:
188
208
  * ```
189
209
  */
190
210
  export declare function createContainerComponents(container: Container, position?: PositionOption, options?: TransformOptions): Pick<Renderer2DComponentTypes, 'container' | 'localTransform' | 'worldTransform' | 'visible'>;
211
+ export declare function computeViewportScale(physicalW: number, physicalH: number, designW: number, designH: number, mode: ScaleMode): ViewportScale;
212
+ /**
213
+ * Convert physical canvas pixel coordinates to design-resolution (logical) coordinates.
214
+ * Compose with camera `screenToWorld()` for full physical→world conversion.
215
+ */
216
+ export declare function physicalToLogical(physicalX: number, physicalY: number, viewport: ViewportScale): {
217
+ x: number;
218
+ y: number;
219
+ };
191
220
  /**
192
221
  * Create a 2D rendering bundle for ECSpresso.
193
222
  *
@@ -217,4 +246,16 @@ export declare function createContainerComponents(container: Container, position
217
246
  * await ecs.initialize();
218
247
  * ```
219
248
  */
220
- export declare function createRenderer2DBundle(options: Renderer2DBundleOptions): Bundle<Renderer2DComponentTypes, Renderer2DEventTypes, Renderer2DResourceTypes>;
249
+ type Renderer2DLabels = 'renderer2d-sync' | 'renderer2d-scene-graph' | 'renderer2d-camera-sync' | 'transform-propagation';
250
+ type Renderer2DReactiveQueryNames = 'renderer2d-sprites' | 'renderer2d-graphics' | 'renderer2d-containers';
251
+ export declare function createRenderer2DBundle<G extends string = 'renderer2d'>(options: Renderer2DBundleOptions<G> & {
252
+ screenScale: ScreenScaleOptions;
253
+ camera: true;
254
+ }): Bundle<Renderer2DComponentTypes, Renderer2DEventTypes, Renderer2DResourceTypes & ViewportScaleResourceTypes & CameraResourceTypes, {}, {}, Renderer2DLabels, G, never, Renderer2DReactiveQueryNames>;
255
+ export declare function createRenderer2DBundle<G extends string = 'renderer2d'>(options: Renderer2DBundleOptions<G> & {
256
+ screenScale: ScreenScaleOptions;
257
+ }): Bundle<Renderer2DComponentTypes, Renderer2DEventTypes, Renderer2DResourceTypes & ViewportScaleResourceTypes, {}, {}, Renderer2DLabels, G, never, Renderer2DReactiveQueryNames>;
258
+ export declare function createRenderer2DBundle<G extends string = 'renderer2d'>(options: Renderer2DBundleOptions<G> & {
259
+ camera: true;
260
+ }): Bundle<Renderer2DComponentTypes, Renderer2DEventTypes, Renderer2DResourceTypes & CameraResourceTypes, {}, {}, Renderer2DLabels, G, never, Renderer2DReactiveQueryNames>;
261
+ export declare function createRenderer2DBundle<G extends string = 'renderer2d'>(options: Renderer2DBundleOptions<G>): Bundle<Renderer2DComponentTypes, Renderer2DEventTypes, Renderer2DResourceTypes, {}, {}, Renderer2DLabels, G, never, Renderer2DReactiveQueryNames>;
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Spatial Index Bundle for ECSpresso
3
+ *
4
+ * Provides a uniform-grid spatial hash for broadphase collision detection
5
+ * and proximity queries. Replaces O(n²) brute-force with O(n·d) where
6
+ * d = local density.
7
+ *
8
+ * Standalone usage: queryRect / queryRadius for proximity queries.
9
+ * Automatic acceleration: collision and physics2D bundles detect the
10
+ * spatialIndex resource at runtime and use it for broadphase when present.
11
+ */
12
+ import { Bundle } from 'ecspresso';
13
+ import type { TransformComponentTypes } from './transform';
14
+ import type { CollisionComponentTypes } from './collision';
15
+ import { type SpatialIndex } from '../utils/spatial-hash';
16
+ export interface SpatialIndexResourceTypes {
17
+ spatialIndex: SpatialIndex;
18
+ }
19
+ type SpatialIndexComponentTypes = TransformComponentTypes & Pick<CollisionComponentTypes<string>, 'aabbCollider' | 'circleCollider'>;
20
+ export type SpatialIndexPhase = 'fixedUpdate' | 'postUpdate';
21
+ type SpatialIndexLabel = `spatial-index-rebuild-${SpatialIndexPhase}`;
22
+ export interface SpatialIndexBundleOptions<G extends string = 'spatialIndex'> {
23
+ /** Cell size for the spatial hash grid (default: 64) */
24
+ cellSize?: number;
25
+ /** System group name (default: 'spatialIndex') */
26
+ systemGroup?: G;
27
+ /** Priority for rebuild systems (default: 2000, before collision) */
28
+ priority?: number;
29
+ /** Phases to register rebuild systems in (default: ['fixedUpdate', 'postUpdate']) */
30
+ phases?: ReadonlyArray<SpatialIndexPhase>;
31
+ }
32
+ /**
33
+ * Create a spatial index bundle for ECSpresso.
34
+ *
35
+ * Provides a uniform-grid spatial hash that accelerates collision detection.
36
+ * When installed alongside the collision or physics2D bundles, they
37
+ * automatically use the spatial index for broadphase instead of O(n²)
38
+ * brute-force.
39
+ *
40
+ * Also provides proximity query methods for game logic (e.g. "find all
41
+ * enemies within 200 units").
42
+ *
43
+ * @example
44
+ * ```typescript
45
+ * const ecs = ECSpresso.create()
46
+ * .withBundle(createTransformBundle())
47
+ * .withBundle(createCollisionBundle({ layers }))
48
+ * .withBundle(createSpatialIndexBundle({ cellSize: 128 }))
49
+ * .build();
50
+ *
51
+ * // Proximity query in a system:
52
+ * const si = ecs.getResource('spatialIndex');
53
+ * const nearby = si.queryRadius(playerX, playerY, 200);
54
+ * ```
55
+ */
56
+ export declare function createSpatialIndexBundle<G extends string = 'spatialIndex'>(options?: SpatialIndexBundleOptions<G>): Bundle<SpatialIndexComponentTypes, {}, SpatialIndexResourceTypes, {}, {}, SpatialIndexLabel, G>;
57
+ export {};
@@ -0,0 +1,298 @@
1
+ /**
2
+ * State Machine Bundle for ECSpresso
3
+ *
4
+ * Provides ECS-native finite state machines with guard-based transitions,
5
+ * event-driven transitions, and lifecycle hooks (onEnter, onExit, onUpdate).
6
+ *
7
+ * Each entity gets a `stateMachine` component referencing a shared definition.
8
+ * One system processes all state machine entities each tick.
9
+ */
10
+ import { Bundle } from 'ecspresso';
11
+ import type { SystemPhase } from 'ecspresso';
12
+ /**
13
+ * Structural interface for ECS methods available inside state machine hooks.
14
+ * Uses method syntax for bivariant parameter checking under strictFunctionTypes,
15
+ * allowing users to annotate hooks with their concrete ECSpresso type.
16
+ */
17
+ export interface StateMachineWorld {
18
+ entityManager: {
19
+ getComponent(entityId: number, componentName: string): unknown | undefined;
20
+ };
21
+ eventBus: {
22
+ publish(eventType: string, data: unknown): void;
23
+ };
24
+ spawn(components: Record<string, unknown>): {
25
+ id: number;
26
+ };
27
+ removeEntity(entityOrId: number): boolean;
28
+ hasComponent(entityId: number, componentName: string): boolean;
29
+ getResource(key: string): unknown;
30
+ hasResource(key: string): boolean;
31
+ markChanged(entityId: number, componentName: string): void;
32
+ commands: {
33
+ spawn(components: Record<string, unknown>): void;
34
+ removeEntity(entityId: number): void;
35
+ };
36
+ }
37
+ /**
38
+ * Configuration for a single state in a state machine definition.
39
+ *
40
+ * @template S - Union of state name strings
41
+ * @template W - World interface type for hooks/guards (default: StateMachineWorld)
42
+ */
43
+ export interface StateConfig<S extends string, W extends StateMachineWorld = StateMachineWorld> {
44
+ /** Called when entering this state */
45
+ onEnter?(ecs: W, entityId: number): void;
46
+ /** Called when exiting this state */
47
+ onExit?(ecs: W, entityId: number): void;
48
+ /** Called each tick while in this state */
49
+ onUpdate?(ecs: W, entityId: number, deltaTime: number): void;
50
+ /** Guard-based transitions evaluated each tick. First passing guard wins. */
51
+ transitions?: ReadonlyArray<{
52
+ target: S;
53
+ guard(ecs: W, entityId: number): boolean;
54
+ }>;
55
+ /** Event-based transition map: eventName → target state or guarded transition */
56
+ on?: Record<string, S | {
57
+ target: S;
58
+ guard(ecs: W, entityId: number): boolean;
59
+ }>;
60
+ }
61
+ /**
62
+ * Immutable definition of a state machine. Shared across entities.
63
+ *
64
+ * @template S - Union of state name strings
65
+ */
66
+ export interface StateMachineDefinition<S extends string> {
67
+ readonly id: string;
68
+ readonly initial: S;
69
+ readonly states: {
70
+ readonly [K in S]: StateConfig<S>;
71
+ };
72
+ }
73
+ /**
74
+ * Runtime state machine component stored on each entity.
75
+ *
76
+ * @template S - Union of state name strings (default: string)
77
+ */
78
+ export interface StateMachine<S extends string = string> {
79
+ readonly definition: StateMachineDefinition<string>;
80
+ current: S;
81
+ previous: S | null;
82
+ stateTime: number;
83
+ }
84
+ /**
85
+ * Component types provided by the state machine bundle.
86
+ *
87
+ * @template S - Union of state name strings (default: string)
88
+ */
89
+ export interface StateMachineComponentTypes<S extends string = string> {
90
+ stateMachine: StateMachine<S>;
91
+ }
92
+ /**
93
+ * Event published on every state transition.
94
+ *
95
+ * @template S - Union of state name strings (default: string)
96
+ */
97
+ export interface StateTransitionEvent<S extends string = string> {
98
+ entityId: number;
99
+ from: S;
100
+ to: S;
101
+ definitionId: string;
102
+ }
103
+ /**
104
+ * Event types provided by the state machine bundle.
105
+ *
106
+ * @template S - Union of state name strings (default: string)
107
+ */
108
+ export interface StateMachineEventTypes<S extends string = string> {
109
+ stateTransition: StateTransitionEvent<S>;
110
+ }
111
+ /**
112
+ * Extract the state name union from a StateMachineDefinition.
113
+ *
114
+ * @example
115
+ * ```typescript
116
+ * const enemyFSM = defineStateMachine('enemy', { initial: 'idle', states: { idle: {}, chase: {} } });
117
+ * type EnemyStates = StatesOf<typeof enemyFSM>; // 'idle' | 'chase'
118
+ * type AllStates = StatesOf<typeof enemyFSM> | StatesOf<typeof playerFSM>;
119
+ * ```
120
+ */
121
+ export type StatesOf<D> = D extends StateMachineDefinition<infer S> ? S : never;
122
+ /**
123
+ * Configuration options for the state machine bundle.
124
+ */
125
+ export interface StateMachineBundleOptions<G extends string = 'stateMachine'> {
126
+ /** System group name (default: 'stateMachine') */
127
+ systemGroup?: G;
128
+ /** Priority for state machine system (default: 0) */
129
+ priority?: number;
130
+ /** Execution phase (default: 'update') */
131
+ phase?: SystemPhase;
132
+ }
133
+ /**
134
+ * Define a state machine with type-safe state names.
135
+ *
136
+ * @template S - Union of state name strings, inferred from `states` keys
137
+ * @param id - Unique identifier for this definition
138
+ * @param config - Initial state and state configurations
139
+ * @returns A frozen StateMachineDefinition
140
+ *
141
+ * @example
142
+ * ```typescript
143
+ * const enemyFSM = defineStateMachine('enemy', {
144
+ * initial: 'idle',
145
+ * states: {
146
+ * idle: {
147
+ * onEnter: (ecs, id) => { ... },
148
+ * transitions: [{ target: 'chase', guard: (ecs, id) => playerNearby(ecs, id) }],
149
+ * },
150
+ * chase: {
151
+ * onUpdate: (ecs, id, dt) => { ... },
152
+ * on: { playerLost: 'idle' },
153
+ * },
154
+ * },
155
+ * });
156
+ * ```
157
+ */
158
+ export declare function defineStateMachine<S extends string>(id: string, config: {
159
+ initial: NoInfer<S>;
160
+ states: Record<S, StateConfig<NoInfer<S>>>;
161
+ }): StateMachineDefinition<S>;
162
+ /**
163
+ * Create a stateMachine component from a definition.
164
+ *
165
+ * @param definition - The state machine definition to use
166
+ * @param options - Optional overrides (e.g., initial state)
167
+ * @returns Component object suitable for spreading into spawn()
168
+ *
169
+ * @example
170
+ * ```typescript
171
+ * ecs.spawn({
172
+ * ...createStateMachine(enemyFSM),
173
+ * position: { x: 100, y: 200 },
174
+ * });
175
+ * ```
176
+ */
177
+ export declare function createStateMachine<S extends string>(definition: StateMachineDefinition<S>, options?: {
178
+ initial?: S;
179
+ }): Pick<StateMachineComponentTypes<S>, 'stateMachine'>;
180
+ /**
181
+ * Directly transition an entity's state machine to a target state.
182
+ * Fires onExit, onEnter hooks and publishes stateTransition event.
183
+ *
184
+ * @param ecs - ECS instance (structural typing)
185
+ * @param entityId - Entity to transition
186
+ * @param targetState - State to transition to
187
+ * @returns true if transition succeeded, false if entity has no stateMachine or target state doesn't exist
188
+ */
189
+ export declare function transitionTo(ecs: StateMachineWorld, entityId: number, targetState: string): boolean;
190
+ /**
191
+ * Send a named event to an entity's state machine.
192
+ * Checks the current state's `on` handlers for a matching event.
193
+ *
194
+ * @param ecs - ECS instance (structural typing)
195
+ * @param entityId - Entity to send event to
196
+ * @param eventName - Event name to match against `on` handlers
197
+ * @returns true if a transition occurred, false otherwise
198
+ */
199
+ export declare function sendEvent(ecs: StateMachineWorld, entityId: number, eventName: string): boolean;
200
+ /**
201
+ * Get the current state of an entity's state machine.
202
+ *
203
+ * @param ecs - ECS instance (structural typing)
204
+ * @param entityId - Entity to query
205
+ * @returns The current state string, or undefined if entity has no stateMachine
206
+ */
207
+ export declare function getStateMachineState(ecs: StateMachineWorld, entityId: number): string | undefined;
208
+ /**
209
+ * A typed kit that captures the world type W once, providing helpers
210
+ * where hooks/guards contextually receive W instead of StateMachineWorld.
211
+ *
212
+ * @template W - Concrete ECS world type
213
+ */
214
+ export interface StateMachineKit<W extends StateMachineWorld, S extends string = string> {
215
+ bundle: Bundle<StateMachineComponentTypes<S>, StateMachineEventTypes<S>, {}, {}, {}, 'state-machine-update', 'stateMachine'>;
216
+ defineStateMachine: <DS extends S>(id: string, config: {
217
+ initial: NoInfer<DS>;
218
+ states: Record<DS, StateConfig<NoInfer<DS>, W>>;
219
+ }) => StateMachineDefinition<DS>;
220
+ createStateMachine: <DS extends S>(definition: StateMachineDefinition<DS>, options?: {
221
+ initial?: DS;
222
+ }) => Pick<StateMachineComponentTypes<S>, 'stateMachine'>;
223
+ }
224
+ /**
225
+ * Create a typed state machine kit that captures the world type W.
226
+ *
227
+ * Hooks and guards in definitions created via the kit's `defineStateMachine`
228
+ * contextually receive W as their `ecs` parameter — no manual annotations needed.
229
+ *
230
+ * @template W - Concrete ECS world type
231
+ * @param options - Optional bundle configuration (same as createStateMachineBundle)
232
+ * @returns A kit object with bundle, defineStateMachine, createStateMachine, transitionTo, sendEvent, getStateMachineState
233
+ *
234
+ * @example
235
+ * ```typescript
236
+ * const ecs = ECSpresso.create()
237
+ * .withBundle(createStateMachineBundle())
238
+ * .withComponentTypes<{ enemy: true }>()
239
+ * .build();
240
+ *
241
+ * type ECS = typeof ecs;
242
+ * const { bundle, defineStateMachine, createStateMachine } =
243
+ * createStateMachineKit<ECS>();
244
+ *
245
+ * const enemyFSM = defineStateMachine('enemy', {
246
+ * initial: 'patrol',
247
+ * states: {
248
+ * patrol: {
249
+ * onEnter(ecs, entityId) {
250
+ * ecs.getResource('bounds'); // fully typed
251
+ * },
252
+ * transitions: [{
253
+ * target: 'chase',
254
+ * guard: (ecs, entityId) => distanceToPlayer(ecs, entityId) < 180,
255
+ * }],
256
+ * },
257
+ * chase: {},
258
+ * },
259
+ * });
260
+ * ```
261
+ */
262
+ export declare function createStateMachineKit<W extends StateMachineWorld = StateMachineWorld, S extends string = string>(options?: StateMachineBundleOptions): StateMachineKit<W, S>;
263
+ /**
264
+ * Create a state machine bundle for ECSpresso.
265
+ *
266
+ * Provides:
267
+ * - Lifecycle hooks (onEnter, onExit, onUpdate) per state
268
+ * - Guard-based automatic transitions evaluated each tick
269
+ * - Event-based transitions via `sendEvent()`
270
+ * - Direct transitions via `transitionTo()`
271
+ * - stateTransition events published on every transition
272
+ *
273
+ * @example
274
+ * ```typescript
275
+ * const ecs = ECSpresso.create()
276
+ * .withBundle(createStateMachineBundle())
277
+ * .build();
278
+ *
279
+ * const fsm = defineStateMachine('enemy', {
280
+ * initial: 'idle',
281
+ * states: {
282
+ * idle: {
283
+ * transitions: [{ target: 'chase', guard: (ecs, id) => playerNearby(ecs, id) }],
284
+ * },
285
+ * chase: {
286
+ * onUpdate: (ecs, id, dt) => moveTowardPlayer(ecs, id, dt),
287
+ * on: { playerLost: 'idle' },
288
+ * },
289
+ * },
290
+ * });
291
+ *
292
+ * ecs.spawn({
293
+ * ...createStateMachine(fsm),
294
+ * position: { x: 0, y: 0 },
295
+ * });
296
+ * ```
297
+ */
298
+ export declare function createStateMachineBundle<S extends string = string, G extends string = 'stateMachine'>(options?: StateMachineBundleOptions<G>): Bundle<StateMachineComponentTypes<S>, StateMachineEventTypes<S>, {}, {}, {}, 'state-machine-update', G>;
@@ -55,16 +55,17 @@ export interface Timer<EventTypes extends Record<string, any>> {
55
55
  }
56
56
  /**
57
57
  * Component types provided by the timer bundle.
58
- * Extend your component types with this interface.
58
+ * Included automatically via `.withBundle(createTimerBundle<Events>())`.
59
+ * The EventTypes generic constrains which events can be used with `onComplete`.
59
60
  *
60
61
  * @template EventTypes The event types from your ECS
61
62
  *
62
63
  * @example
63
64
  * ```typescript
64
- * interface GameComponents extends TimerComponentTypes<GameEvents> {
65
- * velocity: { x: number; y: number };
66
- * player: true;
67
- * }
65
+ * const ecs = ECSpresso.create()
66
+ * .withBundle(createTimerBundle<{ respawn: TimerEventData }>())
67
+ * .withComponentTypes<{ velocity: { x: number; y: number }; player: true }>()
68
+ * .build();
68
69
  * ```
69
70
  */
70
71
  export interface TimerComponentTypes<EventTypes extends Record<string, any>> {
@@ -73,9 +74,9 @@ export interface TimerComponentTypes<EventTypes extends Record<string, any>> {
73
74
  /**
74
75
  * Configuration options for the timer bundle.
75
76
  */
76
- export interface TimerBundleOptions {
77
+ export interface TimerBundleOptions<G extends string = 'timers'> {
77
78
  /** System group name (default: 'timers') */
78
- systemGroup?: string;
79
+ systemGroup?: G;
79
80
  /** Priority for timer update system (default: 0) */
80
81
  priority?: number;
81
82
  /** Execution phase (default: 'preUpdate') */
@@ -169,4 +170,4 @@ export declare function createRepeatingTimer<EventTypes extends Record<string, a
169
170
  * });
170
171
  * ```
171
172
  */
172
- export declare function createTimerBundle<EventTypes extends Record<string, any>>(options?: TimerBundleOptions): Bundle<TimerComponentTypes<EventTypes>, EventTypes, {}>;
173
+ export declare function createTimerBundle<EventTypes extends Record<string, any>, G extends string = 'timers'>(options?: TimerBundleOptions<G>): Bundle<TimerComponentTypes<EventTypes>, EventTypes, {}, {}, {}, 'timer-update', G>;
@@ -32,14 +32,14 @@ export interface WorldTransform {
32
32
  }
33
33
  /**
34
34
  * Component types provided by the transform bundle.
35
- * Extend your component types with this interface.
35
+ * Included automatically via `.withBundle(createTransformBundle())`.
36
36
  *
37
37
  * @example
38
38
  * ```typescript
39
- * interface GameComponents extends TransformComponentTypes {
40
- * sprite: Sprite;
41
- * velocity: { x: number; y: number };
42
- * }
39
+ * const ecs = ECSpresso.create()
40
+ * .withBundle(createTransformBundle())
41
+ * .withComponentTypes<{ sprite: Sprite; velocity: { x: number; y: number } }>()
42
+ * .build();
43
43
  * ```
44
44
  */
45
45
  export interface TransformComponentTypes {
@@ -49,10 +49,10 @@ export interface TransformComponentTypes {
49
49
  /**
50
50
  * Configuration options for the transform bundle.
51
51
  */
52
- export interface TransformBundleOptions {
52
+ export interface TransformBundleOptions<G extends string = 'transform'> {
53
53
  /** System group name (default: 'transform') */
54
- systemGroup?: string;
55
- /** Priority for transform propagation (default: 500, runs after physics/movement) */
54
+ systemGroup?: G;
55
+ /** Priority for transform propagation (default: 500, runs after physics) */
56
56
  priority?: number;
57
57
  /** Execution phase (default: 'postUpdate') */
58
58
  phase?: SystemPhase;
@@ -138,7 +138,7 @@ export declare function createTransform(x: number, y: number, options?: Transfor
138
138
  * const ecs = ECSpresso
139
139
  * .create<Components, Events, Resources>()
140
140
  * .withBundle(createTransformBundle())
141
- * .withBundle(createMovementBundle())
141
+ * .withBundle(createPhysics2DBundle())
142
142
  * .build();
143
143
  *
144
144
  * // Spawn entity with transform
@@ -148,4 +148,4 @@ export declare function createTransform(x: number, y: number, options?: Transfor
148
148
  * });
149
149
  * ```
150
150
  */
151
- export declare function createTransformBundle(options?: TransformBundleOptions): Bundle<TransformComponentTypes, {}, {}>;
151
+ export declare function createTransformBundle<G extends string = 'transform'>(options?: TransformBundleOptions<G>): Bundle<TransformComponentTypes, {}, {}, {}, {}, 'transform-propagation', G>;