ecspresso 0.11.0 → 0.12.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 (96) hide show
  1. package/README.md +200 -148
  2. package/dist/asset-manager.d.ts +1 -1
  3. package/dist/asset-types.d.ts +2 -2
  4. package/dist/command-buffer.d.ts +34 -24
  5. package/dist/ecspresso-builder.d.ts +100 -72
  6. package/dist/ecspresso.d.ts +257 -122
  7. package/dist/entity-manager.d.ts +57 -47
  8. package/dist/index.d.ts +5 -4
  9. package/dist/plugin.d.ts +61 -0
  10. package/dist/{bundles → plugins}/audio.d.ts +27 -47
  11. package/dist/{bundles → plugins}/bounds.d.ts +17 -25
  12. package/dist/{bundles → plugins}/camera.d.ts +8 -9
  13. package/dist/{bundles → plugins}/collision.d.ts +22 -26
  14. package/dist/plugins/coroutine.d.ts +126 -0
  15. package/dist/{bundles → plugins}/diagnostics.d.ts +5 -4
  16. package/dist/{bundles → plugins}/input.d.ts +9 -15
  17. package/dist/plugins/particles.d.ts +225 -0
  18. package/dist/{bundles → plugins}/physics2D.d.ts +27 -23
  19. package/dist/{bundles → plugins}/renderers/renderer2D.d.ts +40 -39
  20. package/dist/{bundles → plugins}/spatial-index.d.ts +11 -10
  21. package/dist/plugins/sprite-animation.d.ts +150 -0
  22. package/dist/{bundles → plugins}/state-machine.d.ts +50 -104
  23. package/dist/plugins/timers.d.ts +151 -0
  24. package/dist/{bundles → plugins}/transform.d.ts +18 -19
  25. package/dist/{bundles → plugins}/tween.d.ts +36 -71
  26. package/dist/resource-manager.d.ts +32 -7
  27. package/dist/screen-manager.d.ts +17 -11
  28. package/dist/screen-types.d.ts +5 -2
  29. package/dist/src/index.js +2 -2
  30. package/dist/src/index.js.map +17 -17
  31. package/dist/src/plugins/audio.js +4 -0
  32. package/dist/src/plugins/audio.js.map +10 -0
  33. package/dist/src/plugins/bounds.js +4 -0
  34. package/dist/src/plugins/bounds.js.map +10 -0
  35. package/dist/src/plugins/camera.js +4 -0
  36. package/dist/src/plugins/camera.js.map +10 -0
  37. package/dist/src/plugins/collision.js +4 -0
  38. package/dist/src/plugins/collision.js.map +11 -0
  39. package/dist/src/plugins/coroutine.js +4 -0
  40. package/dist/src/plugins/coroutine.js.map +10 -0
  41. package/dist/src/plugins/diagnostics.js +5 -0
  42. package/dist/src/plugins/diagnostics.js.map +10 -0
  43. package/dist/src/plugins/input.js +4 -0
  44. package/dist/src/plugins/input.js.map +10 -0
  45. package/dist/src/plugins/particles.js +4 -0
  46. package/dist/src/plugins/particles.js.map +10 -0
  47. package/dist/src/plugins/physics2D.js +4 -0
  48. package/dist/src/plugins/physics2D.js.map +11 -0
  49. package/dist/src/plugins/renderers/renderer2D.js +4 -0
  50. package/dist/src/plugins/renderers/renderer2D.js.map +10 -0
  51. package/dist/src/plugins/spatial-index.js +4 -0
  52. package/dist/src/plugins/spatial-index.js.map +11 -0
  53. package/dist/src/plugins/sprite-animation.js +4 -0
  54. package/dist/src/plugins/sprite-animation.js.map +10 -0
  55. package/dist/src/plugins/state-machine.js +4 -0
  56. package/dist/src/plugins/state-machine.js.map +10 -0
  57. package/dist/src/plugins/timers.js +4 -0
  58. package/dist/src/plugins/timers.js.map +10 -0
  59. package/dist/src/plugins/transform.js +4 -0
  60. package/dist/src/plugins/transform.js.map +10 -0
  61. package/dist/src/plugins/tween.js +4 -0
  62. package/dist/src/plugins/tween.js.map +11 -0
  63. package/dist/system-builder.d.ts +66 -97
  64. package/dist/type-utils.d.ts +218 -27
  65. package/dist/types.d.ts +52 -24
  66. package/dist/utils/check-required-cycle.d.ts +1 -1
  67. package/dist/utils/narrowphase.d.ts +7 -7
  68. package/package.json +53 -45
  69. package/dist/bundle.d.ts +0 -173
  70. package/dist/bundles/timers.d.ts +0 -173
  71. package/dist/src/bundles/audio.js +0 -4
  72. package/dist/src/bundles/audio.js.map +0 -10
  73. package/dist/src/bundles/bounds.js +0 -4
  74. package/dist/src/bundles/bounds.js.map +0 -10
  75. package/dist/src/bundles/camera.js +0 -4
  76. package/dist/src/bundles/camera.js.map +0 -10
  77. package/dist/src/bundles/collision.js +0 -4
  78. package/dist/src/bundles/collision.js.map +0 -11
  79. package/dist/src/bundles/diagnostics.js +0 -5
  80. package/dist/src/bundles/diagnostics.js.map +0 -10
  81. package/dist/src/bundles/input.js +0 -4
  82. package/dist/src/bundles/input.js.map +0 -10
  83. package/dist/src/bundles/physics2D.js +0 -4
  84. package/dist/src/bundles/physics2D.js.map +0 -11
  85. package/dist/src/bundles/renderers/renderer2D.js +0 -4
  86. package/dist/src/bundles/renderers/renderer2D.js.map +0 -10
  87. package/dist/src/bundles/spatial-index.js +0 -4
  88. package/dist/src/bundles/spatial-index.js.map +0 -11
  89. package/dist/src/bundles/state-machine.js +0 -4
  90. package/dist/src/bundles/state-machine.js.map +0 -10
  91. package/dist/src/bundles/timers.js +0 -4
  92. package/dist/src/bundles/timers.js.map +0 -10
  93. package/dist/src/bundles/transform.js +0 -4
  94. package/dist/src/bundles/transform.js.map +0 -10
  95. package/dist/src/bundles/tween.js +0 -4
  96. package/dist/src/bundles/tween.js.map +0 -11
@@ -0,0 +1,151 @@
1
+ /**
2
+ * Timer Plugin for ECSpresso
3
+ *
4
+ * Provides ECS-native timers following the "data, not callbacks" philosophy.
5
+ * Timers are components processed each frame, automatically cleaned up when entities are removed.
6
+ */
7
+ import { type Plugin, type BasePluginOptions } from 'ecspresso';
8
+ import type { WorldConfigFrom, EmptyConfig } from '../type-utils';
9
+ /**
10
+ * Data structure passed to onComplete callbacks when a timer completes.
11
+ *
12
+ * @example
13
+ * ```typescript
14
+ * createTimer(1.5, {
15
+ * onComplete: (data) => {
16
+ * console.log(`Timer on entity ${data.entityId} finished after ${data.elapsed}s`);
17
+ * }
18
+ * });
19
+ * ```
20
+ */
21
+ export interface TimerEventData {
22
+ /** The entity ID that the timer belongs to */
23
+ entityId: number;
24
+ /** The timer's configured duration in seconds */
25
+ duration: number;
26
+ /** The actual elapsed time (may exceed duration slightly) */
27
+ elapsed: number;
28
+ }
29
+ /**
30
+ * Timer component data structure.
31
+ * Use `justFinished` to detect timer completion in your systems.
32
+ */
33
+ export interface Timer {
34
+ /** Time accumulated so far (seconds) */
35
+ elapsed: number;
36
+ /** Target duration (seconds) */
37
+ duration: number;
38
+ /** Whether timer repeats after completion */
39
+ repeat: boolean;
40
+ /** Whether timer is currently running */
41
+ active: boolean;
42
+ /** True for one frame after timer completes */
43
+ justFinished: boolean;
44
+ /** Optional callback invoked when timer completes */
45
+ onComplete?: (data: TimerEventData) => void;
46
+ }
47
+ /**
48
+ * Component types provided by the timer plugin.
49
+ * Included automatically via `.withPlugin(createTimerPlugin())`.
50
+ *
51
+ * @example
52
+ * ```typescript
53
+ * const ecs = ECSpresso.create()
54
+ * .withPlugin(createTimerPlugin())
55
+ * .withComponentTypes<{ velocity: { x: number; y: number }; player: true }>()
56
+ * .build();
57
+ * ```
58
+ */
59
+ export interface TimerComponentTypes {
60
+ timer: Timer;
61
+ }
62
+ /**
63
+ * Configuration options for the timer plugin.
64
+ */
65
+ export interface TimerPluginOptions<G extends string = 'timers'> extends BasePluginOptions<G> {
66
+ }
67
+ /**
68
+ * Options for timer creation
69
+ */
70
+ export interface TimerOptions {
71
+ /** Callback invoked when timer completes */
72
+ onComplete?: (data: TimerEventData) => void;
73
+ }
74
+ /**
75
+ * Create a one-shot timer that fires once after the specified duration.
76
+ *
77
+ * @param duration Duration in seconds until the timer completes
78
+ * @param options Optional configuration including onComplete callback
79
+ * @returns Component object suitable for spreading into spawn()
80
+ *
81
+ * @example
82
+ * ```typescript
83
+ * // Timer without callback
84
+ * ecs.spawn({
85
+ * ...createTimer(2),
86
+ * explosion: true,
87
+ * });
88
+ *
89
+ * // Timer with onComplete callback
90
+ * ecs.spawn({
91
+ * ...createTimer(1.5, { onComplete: (data) => console.log('done', data.entityId) }),
92
+ * });
93
+ * ```
94
+ */
95
+ export declare function createTimer(duration: number, options?: TimerOptions): Pick<TimerComponentTypes, 'timer'>;
96
+ /**
97
+ * Create a repeating timer that fires every `duration` seconds.
98
+ *
99
+ * @param duration Duration in seconds between each timer completion
100
+ * @param options Optional configuration including onComplete callback
101
+ * @returns Component object suitable for spreading into spawn()
102
+ *
103
+ * @example
104
+ * ```typescript
105
+ * // Timer without callback
106
+ * ecs.spawn({
107
+ * ...createRepeatingTimer(5),
108
+ * spawner: true,
109
+ * });
110
+ *
111
+ * // Repeating timer with onComplete callback
112
+ * ecs.spawn({
113
+ * ...createRepeatingTimer(3, { onComplete: (data) => console.log('cycle', data.elapsed) }),
114
+ * });
115
+ * ```
116
+ */
117
+ export declare function createRepeatingTimer(duration: number, options?: TimerOptions): Pick<TimerComponentTypes, 'timer'>;
118
+ /**
119
+ * Create a timer plugin for ECSpresso.
120
+ *
121
+ * This plugin provides:
122
+ * - Timer update system that processes all timer components each frame
123
+ * - `justFinished` flag pattern for one-frame completion detection
124
+ * - Automatic cleanup when entities are removed
125
+ *
126
+ * @example
127
+ * ```typescript
128
+ * const ecs = ECSpresso
129
+ * .create<Components, Events, Resources>()
130
+ * .withPlugin(createTimerPlugin())
131
+ * .build();
132
+ *
133
+ * // Spawn entity with timer
134
+ * ecs.spawn({
135
+ * ...createRepeatingTimer(5),
136
+ * spawner: true,
137
+ * });
138
+ *
139
+ * // React to timer completion in a system
140
+ * ecs.addSystem('spawn-on-timer')
141
+ * .addQuery('spawners', { with: ['timer', 'spawner'] })
142
+ * .setProcess((queries, _dt, ecs) => {
143
+ * for (const { components } of queries.spawners) {
144
+ * if (components.timer.justFinished) {
145
+ * ecs.spawn({ enemy: true });
146
+ * }
147
+ * }
148
+ * });
149
+ * ```
150
+ */
151
+ export declare function createTimerPlugin<G extends string = 'timers'>(options?: TimerPluginOptions<G>): Plugin<WorldConfigFrom<TimerComponentTypes>, EmptyConfig, 'timer-update', G>;
@@ -1,13 +1,13 @@
1
1
  /**
2
- * Transform Bundle for ECSpresso
2
+ * Transform Plugin for ECSpresso
3
3
  *
4
4
  * Provides hierarchical transform propagation following Bevy's Transform/GlobalTransform pattern.
5
5
  * LocalTransform is modified by user code; WorldTransform is computed automatically.
6
6
  *
7
7
  * @see https://docs.rs/bevy/latest/bevy/transform/components/struct.GlobalTransform.html
8
8
  */
9
- import { Bundle } from 'ecspresso';
10
- import type { SystemPhase } from 'ecspresso';
9
+ import { type Plugin, type BasePluginOptions } from 'ecspresso';
10
+ import type { WorldConfigFrom, EmptyConfig } from '../type-utils';
11
11
  /**
12
12
  * Local transform relative to parent (or world if no parent).
13
13
  * This is the transform you modify directly.
@@ -31,13 +31,13 @@ export interface WorldTransform {
31
31
  scaleY: number;
32
32
  }
33
33
  /**
34
- * Component types provided by the transform bundle.
35
- * Included automatically via `.withBundle(createTransformBundle())`.
34
+ * Component types provided by the transform plugin.
35
+ * Included automatically via `.withPlugin(createTransformPlugin())`.
36
36
  *
37
37
  * @example
38
38
  * ```typescript
39
39
  * const ecs = ECSpresso.create()
40
- * .withBundle(createTransformBundle())
40
+ * .withPlugin(createTransformPlugin())
41
41
  * .withComponentTypes<{ sprite: Sprite; velocity: { x: number; y: number } }>()
42
42
  * .build();
43
43
  * ```
@@ -47,15 +47,14 @@ export interface TransformComponentTypes {
47
47
  worldTransform: WorldTransform;
48
48
  }
49
49
  /**
50
- * Configuration options for the transform bundle.
50
+ * WorldConfig representing the transform plugin's provided components.
51
+ * Used as the `Requires` type parameter by plugins that depend on transform.
51
52
  */
52
- export interface TransformBundleOptions<G extends string = 'transform'> {
53
- /** System group name (default: 'transform') */
54
- systemGroup?: G;
55
- /** Priority for transform propagation (default: 500, runs after physics) */
56
- priority?: number;
57
- /** Execution phase (default: 'postUpdate') */
58
- phase?: SystemPhase;
53
+ export type TransformWorldConfig = WorldConfigFrom<TransformComponentTypes>;
54
+ /**
55
+ * Configuration options for the transform plugin.
56
+ */
57
+ export interface TransformPluginOptions<G extends string = 'transform'> extends BasePluginOptions<G> {
59
58
  }
60
59
  /**
61
60
  * Default local transform values.
@@ -126,9 +125,9 @@ export interface TransformOptions {
126
125
  */
127
126
  export declare function createTransform(x: number, y: number, options?: TransformOptions): TransformComponentTypes;
128
127
  /**
129
- * Create a transform bundle for ECSpresso.
128
+ * Create a transform plugin for ECSpresso.
130
129
  *
131
- * This bundle provides:
130
+ * This plugin provides:
132
131
  * - Transform propagation system that computes world transforms from local transforms
133
132
  * - Parent-first traversal ensures parents are processed before children
134
133
  * - Supports full transform hierarchy (position, rotation, scale)
@@ -137,8 +136,8 @@ export declare function createTransform(x: number, y: number, options?: Transfor
137
136
  * ```typescript
138
137
  * const ecs = ECSpresso
139
138
  * .create<Components, Events, Resources>()
140
- * .withBundle(createTransformBundle())
141
- * .withBundle(createPhysics2DBundle())
139
+ * .withPlugin(createTransformPlugin())
140
+ * .withPlugin(createPhysics2DPlugin())
142
141
  * .build();
143
142
  *
144
143
  * // Spawn entity with transform
@@ -148,4 +147,4 @@ export declare function createTransform(x: number, y: number, options?: Transfor
148
147
  * });
149
148
  * ```
150
149
  */
151
- export declare function createTransformBundle<G extends string = 'transform'>(options?: TransformBundleOptions<G>): Bundle<TransformComponentTypes, {}, {}, {}, {}, 'transform-propagation', G>;
150
+ export declare function createTransformPlugin<G extends string = 'transform'>(options?: TransformPluginOptions<G>): Plugin<WorldConfigFrom<TransformComponentTypes>, EmptyConfig, 'transform-propagation', G>;
@@ -1,11 +1,12 @@
1
1
  /**
2
- * Tween Bundle for ECSpresso
2
+ * Tween Plugin for ECSpresso
3
3
  *
4
4
  * Declarative property animation within the ECS. Tween any numeric component
5
5
  * field over time with standard easing functions, sequences, and completion events.
6
6
  */
7
- import { Bundle } from 'ecspresso';
8
- import type { SystemPhase, ComponentsOfWorld, EventsOfWorld } from 'ecspresso';
7
+ import { type Plugin, type BasePluginOptions } from 'ecspresso';
8
+ import type { ComponentsOfWorld, AnyECSpresso } from 'ecspresso';
9
+ import type { WorldConfigFrom, EmptyConfig } from '../type-utils';
9
10
  import { type EasingFn } from '../utils/easing';
10
11
  /**
11
12
  * Data structure published when a tween completes.
@@ -17,15 +18,6 @@ export interface TweenEventData {
17
18
  /** Number of steps in the tween */
18
19
  stepCount: number;
19
20
  }
20
- /**
21
- * Extracts event names from EventTypes that have TweenEventData as their payload.
22
- * This ensures only compatible events can be used with tween.onComplete.
23
- * Uses `keyof EventTypes & string` to exclude number/symbol keys, ensuring the
24
- * result is always a string subtype and Tween<E> is assignable to Tween<Record<string, any>>.
25
- */
26
- export type TweenEventName<EventTypes extends Record<string, any>> = {
27
- [K in keyof EventTypes & string]: EventTypes[K] extends TweenEventData ? K : never;
28
- }[keyof EventTypes & string];
29
21
  export interface TweenTarget {
30
22
  /** Component name on the entity */
31
23
  component: string;
@@ -41,7 +33,7 @@ export interface TweenStep {
41
33
  duration: number;
42
34
  easing: EasingFn;
43
35
  }
44
- export interface Tween<EventTypes extends Record<string, any> = Record<string, any>> {
36
+ export interface Tween {
45
37
  steps: TweenStep[];
46
38
  currentStep: number;
47
39
  elapsed: number;
@@ -50,25 +42,19 @@ export interface Tween<EventTypes extends Record<string, any> = Record<string, a
50
42
  completedLoops: number;
51
43
  direction: 1 | -1;
52
44
  state: 'pending' | 'active' | 'complete';
53
- onComplete?: TweenEventName<EventTypes>;
45
+ onComplete?: (data: TweenEventData) => void;
54
46
  justFinished: boolean;
55
47
  }
56
48
  export type LoopMode = 'once' | 'loop' | 'yoyo';
57
49
  /**
58
- * Component types provided by the tween bundle.
50
+ * Component types provided by the tween plugin.
59
51
  */
60
- export interface TweenComponentTypes<EventTypes extends Record<string, any> = Record<string, any>> {
61
- tween: Tween<EventTypes>;
52
+ export interface TweenComponentTypes {
53
+ tween: Tween;
62
54
  }
63
- export interface TweenBundleOptions<G extends string = 'tweens'> {
64
- /** System group name (default: 'tweens') */
65
- systemGroup?: G;
66
- /** Priority for tween update system (default: 0) */
67
- priority?: number;
68
- /** Execution phase (default: 'update') */
69
- phase?: SystemPhase;
55
+ export interface TweenPluginOptions<G extends string = 'tweens'> extends BasePluginOptions<G> {
70
56
  }
71
- export interface TweenOptions<EventTypes extends Record<string, any> = Record<string, any>> {
57
+ export interface TweenOptions {
72
58
  /** Explicit starting value (default: captures current value on first tick) */
73
59
  from?: number;
74
60
  /** Easing function (default: linear) */
@@ -77,8 +63,8 @@ export interface TweenOptions<EventTypes extends Record<string, any> = Record<st
77
63
  loop?: LoopMode;
78
64
  /** Number of loops. -1 = infinite (default: 1) */
79
65
  loops?: number;
80
- /** Event name to publish when tween completes */
81
- onComplete?: TweenEventName<EventTypes>;
66
+ /** Callback invoked when tween completes */
67
+ onComplete?: (data: TweenEventData) => void;
82
68
  }
83
69
  /**
84
70
  * Create a single-target tween component.
@@ -90,7 +76,7 @@ export interface TweenOptions<EventTypes extends Record<string, any> = Record<st
90
76
  * @param options Optional configuration
91
77
  * @returns Component object suitable for spreading into spawn()
92
78
  */
93
- export declare function createTween<EventTypes extends Record<string, any> = Record<string, any>>(component: string, field: string, to: number, duration: number, options?: TweenOptions<EventTypes>): Pick<TweenComponentTypes<EventTypes>, 'tween'>;
79
+ export declare function createTween(component: string, field: string, to: number, duration: number, options?: TweenOptions): Pick<TweenComponentTypes, 'tween'>;
94
80
  export interface TweenSequenceStepInput {
95
81
  targets: ReadonlyArray<{
96
82
  component: string;
@@ -101,13 +87,13 @@ export interface TweenSequenceStepInput {
101
87
  duration: number;
102
88
  easing?: EasingFn;
103
89
  }
104
- export interface TweenSequenceOptions<EventTypes extends Record<string, any> = Record<string, any>> {
90
+ export interface TweenSequenceOptions {
105
91
  /** Loop mode (default: 'once') */
106
92
  loop?: LoopMode;
107
93
  /** Number of loops. -1 = infinite (default: 1) */
108
94
  loops?: number;
109
- /** Event name to publish when tween completes */
110
- onComplete?: TweenEventName<EventTypes>;
95
+ /** Callback invoked when tween completes */
96
+ onComplete?: (data: TweenEventData) => void;
111
97
  }
112
98
  /**
113
99
  * Create a multi-step tween sequence. Each step can have parallel targets.
@@ -116,8 +102,7 @@ export interface TweenSequenceOptions<EventTypes extends Record<string, any> = R
116
102
  * @param options Optional configuration
117
103
  * @returns Component object suitable for spreading into spawn()
118
104
  */
119
- export declare function createTweenSequence<EventTypes extends Record<string, any> = Record<string, any>>(steps: ReadonlyArray<TweenSequenceStepInput>, options?: TweenSequenceOptions<EventTypes>): Pick<TweenComponentTypes<EventTypes>, 'tween'>;
120
- type AnyECSpresso = import('ecspresso').default<any, any, any, any, any, any, any>;
105
+ export declare function createTweenSequence(steps: ReadonlyArray<TweenSequenceStepInput>, options?: TweenSequenceOptions): Pick<TweenComponentTypes, 'tween'>;
121
106
  /**
122
107
  * Recursively produce a union of dot-separated paths that resolve to `number`
123
108
  * within type T. Depth-limited to 4 levels to prevent TS recursion errors.
@@ -147,51 +132,31 @@ export interface TypedTweenSequenceStepInput<C extends Record<string, any>> {
147
132
  duration: number;
148
133
  easing?: EasingFn;
149
134
  }
150
- export interface TweenKit<W extends AnyECSpresso, G extends string = 'tweens'> {
151
- bundle: Bundle<TweenComponentTypes<EventsOfWorld<W>>, EventsOfWorld<W>, {}, {}, {}, 'tween-update', G>;
152
- createTween: <K extends keyof ComponentsOfWorld<W> & string>(component: K, field: NumericPaths<ComponentsOfWorld<W>[K]>, to: number, duration: number, options?: TweenOptions<EventsOfWorld<W>>) => Pick<TweenComponentTypes<EventsOfWorld<W>>, 'tween'>;
153
- createTweenSequence: (steps: ReadonlyArray<TypedTweenSequenceStepInput<ComponentsOfWorld<W>>>, options?: TweenSequenceOptions<EventsOfWorld<W>>) => Pick<TweenComponentTypes<EventsOfWorld<W>>, 'tween'>;
135
+ export interface TweenHelpers<W extends AnyECSpresso> {
136
+ createTween: <K extends keyof ComponentsOfWorld<W> & string>(component: K, field: NumericPaths<ComponentsOfWorld<W>[K]>, to: number, duration: number, options?: {
137
+ from?: number;
138
+ easing?: EasingFn;
139
+ loop?: LoopMode;
140
+ loops?: number;
141
+ onComplete?: (data: TweenEventData) => void;
142
+ }) => Pick<TweenComponentTypes, 'tween'>;
143
+ createTweenSequence: (steps: ReadonlyArray<TypedTweenSequenceStepInput<ComponentsOfWorld<W>>>, options?: {
144
+ loop?: LoopMode;
145
+ loops?: number;
146
+ onComplete?: (data: TweenEventData) => void;
147
+ }) => Pick<TweenComponentTypes, 'tween'>;
154
148
  }
149
+ export declare function createTweenHelpers<W extends AnyECSpresso>(_world?: W): TweenHelpers<W>;
155
150
  /**
156
- * Create a typed tween kit that captures the world type W.
157
- *
158
- * The returned `createTween` and `createTweenSequence` validate component names
159
- * and field paths at compile time. Runtime behavior is identical to the standalone
160
- * functions — all validation is type-level only.
161
- *
162
- * @template W - Concrete ECS world type (e.g. `typeof ecs`)
163
- * @template G - System group name (default: 'tweens')
164
- * @param options - Optional bundle configuration (same as createTweenBundle)
165
- * @returns A kit object with bundle, createTween, createTweenSequence
166
- *
167
- * @example
168
- * ```typescript
169
- * const kit = createTweenKit<typeof ecs>();
170
- * // or: const kit = createTweenKit<ECS>();
171
- *
172
- * const ecs = ECSpresso.create()
173
- * .withBundle(kit.bundle)
174
- * .build();
175
- *
176
- * // Type-safe: 'position' must be a component, 'x' must be a numeric field
177
- * kit.createTween('position', 'x', 100, 1);
178
- *
179
- * // Type error: 'z' is not a field of position
180
- * kit.createTween('position', 'z', 100, 1);
181
- * ```
182
- */
183
- export declare function createTweenKit<W extends AnyECSpresso, G extends string = 'tweens'>(options?: TweenBundleOptions<G>): TweenKit<W, G>;
184
- /**
185
- * Create a tween bundle for ECSpresso.
151
+ * Create a tween plugin for ECSpresso.
186
152
  *
187
- * This bundle provides:
153
+ * This plugin provides:
188
154
  * - Tween system that processes all tween components each frame
189
155
  * - Support for single-field, multi-target, and multi-step sequences
190
156
  * - 31 standard easing functions
191
157
  * - Loop modes: once, loop, yoyo
192
158
  * - `justFinished` flag for one-frame completion detection
193
- * - `onComplete` event publishing
159
+ * - `onComplete` callback on completion
194
160
  * - Change detection via markChanged
195
161
  */
196
- export declare function createTweenBundle<EventTypes extends Record<string, any> = Record<string, any>, G extends string = 'tweens'>(options?: TweenBundleOptions<G>): Bundle<TweenComponentTypes<EventTypes>, EventTypes, {}, {}, {}, 'tween-update', G>;
197
- export {};
162
+ export declare function createTweenPlugin<G extends string = 'tweens'>(options?: TweenPluginOptions<G>): Plugin<WorldConfigFrom<TweenComponentTypes>, EmptyConfig, 'tween-update', G>;
@@ -6,6 +6,29 @@ export interface ResourceFactoryWithDeps<T, Context = unknown, D extends string
6
6
  factory: (context: Context) => T | Promise<T>;
7
7
  onDispose?: (resource: T, context: Context) => void | Promise<void>;
8
8
  }
9
+ /** @internal */
10
+ export declare const RESOURCE_DIRECT: unique symbol;
11
+ /**
12
+ * Branded wrapper for storing a value as-is, bypassing factory detection.
13
+ * The value is carried on the symbol key to avoid structural conflicts
14
+ * with user resource types that have a `value` property.
15
+ * Create via the `directValue()` helper.
16
+ */
17
+ export interface ResourceDirectValue<T> {
18
+ [RESOURCE_DIRECT]: T;
19
+ }
20
+ /**
21
+ * Wrap a value to store it as-is, bypassing factory detection.
22
+ * Use when the resource itself is a function or class that should not be invoked.
23
+ *
24
+ * @example
25
+ * ```ts
26
+ * import { directValue } from 'ecspresso';
27
+ * world.addResource('handler', directValue(myFunction));
28
+ * world.addResource('MyClass', directValue(MyClass));
29
+ * ```
30
+ */
31
+ export declare function directValue<T>(value: T): ResourceDirectValue<T>;
9
32
  /**
10
33
  * When Context is unknown (default), context args are optional.
11
34
  * When Context is a specific type (e.g. ECSpresso<...>), context is required.
@@ -18,17 +41,19 @@ export default class ResourceManager<ResourceTypes extends Record<string, any> =
18
41
  private resourceDisposers;
19
42
  private initializedResourceKeys;
20
43
  /**
21
- * Add a resource to the manager
44
+ * Add a resource to the manager.
45
+ *
46
+ * Resolution order:
47
+ * 1. `{ factory, dependsOn?, onDispose? }` → factory with optional deps/disposal
48
+ * 2. `{ value }` → direct value wrapper (use to store functions/classes as-is)
49
+ * 3. `typeof === 'function'` → bare factory (no deps)
50
+ * 4. Anything else → direct value
51
+ *
22
52
  * @param label The resource key
23
53
  * @param resource The resource value, a factory function, or a factory with dependencies
24
54
  * @returns The resource manager instance for chaining
25
55
  */
26
- add<K extends keyof ResourceTypes>(label: K, resource: ResourceTypes[K] | ((context: Context) => ResourceTypes[K] | Promise<ResourceTypes[K]>) | ResourceFactoryWithDeps<ResourceTypes[K], Context, keyof ResourceTypes & string>): this;
27
- /**
28
- * Improved detection of factory functions vs direct values/classes
29
- * @private
30
- */
31
- private _isFactoryFunction;
56
+ add<K extends keyof ResourceTypes>(label: K, resource: ResourceTypes[K] | ((context: Context) => ResourceTypes[K] | Promise<ResourceTypes[K]>) | ResourceFactoryWithDeps<ResourceTypes[K], Context, keyof ResourceTypes & string> | ResourceDirectValue<ResourceTypes[K]>): this;
32
57
  /**
33
58
  * Try to get a resource from the manager.
34
59
  * Returns the resource value if it exists, or undefined if not found.
@@ -4,7 +4,7 @@
4
4
  import type EventBus from './event-bus';
5
5
  import type AssetManager from './asset-manager';
6
6
  import type ECSpresso from './ecspresso';
7
- import type { ScreenDefinition, ScreenResource, ScreenEvents, ScreenConfigurator } from './screen-types';
7
+ import type { ScreenDefinition, ScreenResource, ScreenEvents, ScreenConfigurator, ScreenConfig, ScreenState } from './screen-types';
8
8
  /**
9
9
  * Manages screen/state transitions for ECSpresso
10
10
  */
@@ -20,6 +20,7 @@ export default class ScreenManager<Screens extends Record<string, ScreenDefiniti
20
20
  * @internal
21
21
  */
22
22
  setDependencies(eventBus: EventBus<ScreenEvents<keyof Screens & string>>, assetManager: AssetManager<any> | null, ecs: ECSpresso<any, any, any, any, any>): void;
23
+ private requireEcs;
23
24
  /**
24
25
  * Register a screen definition
25
26
  */
@@ -49,25 +50,30 @@ export default class ScreenManager<Screens extends Record<string, ScreenDefiniti
49
50
  */
50
51
  getCurrentScreen(): keyof Screens | null;
51
52
  /**
52
- * Get the current screen config (immutable)
53
+ * Get the current screen config (immutable).
54
+ * If `screen` is provided, asserts that the current screen matches.
53
55
  */
54
- getConfig<K extends keyof Screens>(): Screens[K] extends ScreenDefinition<infer C, any> ? Readonly<C> : never;
56
+ getConfig(screen?: keyof Screens): Readonly<ScreenConfig<Screens[keyof Screens]>>;
55
57
  /**
56
- * Get the current screen config or null
58
+ * Get the current screen config or undefined.
59
+ * If `screen` is provided, returns undefined when the current screen doesn't match.
57
60
  */
58
- getConfigOrNull<K extends keyof Screens>(): (Screens[K] extends ScreenDefinition<infer C, any> ? Readonly<C> : never) | null;
61
+ tryGetConfig(screen?: keyof Screens): Readonly<ScreenConfig<Screens[keyof Screens]>> | undefined;
59
62
  /**
60
- * Get the current screen state (mutable)
63
+ * Get the current screen state (mutable).
64
+ * If `screen` is provided, asserts that the current screen matches.
61
65
  */
62
- getState<K extends keyof Screens>(): Screens[K] extends ScreenDefinition<any, infer S> ? S : never;
66
+ getState(screen?: keyof Screens): ScreenState<Screens[keyof Screens]>;
63
67
  /**
64
- * Get the current screen state or null
68
+ * Get the current screen state or undefined.
69
+ * If `screen` is provided, returns undefined when the current screen doesn't match.
65
70
  */
66
- getStateOrNull<K extends keyof Screens>(): (Screens[K] extends ScreenDefinition<any, infer S> ? S : never) | null;
71
+ tryGetState(screen?: keyof Screens): ScreenState<Screens[keyof Screens]> | undefined;
67
72
  /**
68
- * Update the current screen state
73
+ * Update the current screen state.
74
+ * If `screen` is provided, asserts that the current screen matches.
69
75
  */
70
- updateState<K extends keyof Screens>(update: Partial<Screens[K] extends ScreenDefinition<any, infer S> ? S : never> | ((current: Screens[K] extends ScreenDefinition<any, infer S> ? S : never) => Partial<Screens[K] extends ScreenDefinition<any, infer S> ? S : never>)): void;
76
+ updateState(update: unknown, screen?: keyof Screens): void;
71
77
  /**
72
78
  * Get the screen stack depth
73
79
  */
@@ -5,7 +5,7 @@ import type ECSpresso from './ecspresso';
5
5
  /**
6
6
  * Definition for a screen including its state, lifecycle hooks, and requirements
7
7
  */
8
- export interface ScreenDefinition<Config extends Record<string, unknown> = Record<string, never>, State extends Record<string, unknown> = Record<string, never>, W = ECSpresso<any, any, any, any, any>> {
8
+ export interface ScreenDefinition<Config extends Record<string, unknown> = Record<string, never>, State extends Record<string, unknown> = Record<string, never>, W = ECSpresso<any>> {
9
9
  /**
10
10
  * Function to create initial state from config
11
11
  */
@@ -13,7 +13,10 @@ export interface ScreenDefinition<Config extends Record<string, unknown> = Recor
13
13
  /**
14
14
  * Lifecycle hook called when entering this screen
15
15
  */
16
- readonly onEnter?: (config: Config, ecs: W) => void | Promise<void>;
16
+ readonly onEnter?: (ctx: {
17
+ config: Config;
18
+ ecs: W;
19
+ }) => void | Promise<void>;
17
20
  /**
18
21
  * Lifecycle hook called when exiting this screen
19
22
  */