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
@@ -47,38 +47,42 @@ export default class EntityManager<ComponentTypes> {
47
47
  * Called when a component is removed (explicit removal, entity destruction, or replacement).
48
48
  * Later registrations replace earlier ones for the same component type.
49
49
  * @param componentName The component type to register disposal for
50
- * @param callback Function receiving the component value being disposed
50
+ * @param callback Function receiving the component value being disposed and the entity ID
51
51
  */
52
- registerDispose<ComponentName extends keyof ComponentTypes>(componentName: ComponentName, callback: (value: ComponentTypes[ComponentName]) => void): void;
52
+ registerDispose<ComponentName extends keyof ComponentTypes>(componentName: ComponentName, callback: (ctx: {
53
+ value: ComponentTypes[ComponentName];
54
+ entityId: number;
55
+ }) => void): void;
53
56
  /**
54
57
  * Get all registered dispose callbacks.
55
- * @internal Used by ECSpresso for bundle installation
58
+ * @internal Used by ECSpresso for plugin installation
56
59
  */
57
- getDisposeCallbacks(): Map<keyof ComponentTypes, (value: any) => void>;
60
+ getDisposeCallbacks(): Map<keyof ComponentTypes, (ctx: {
61
+ value: unknown;
62
+ entityId: number;
63
+ }) => void>;
58
64
  /**
59
65
  * Invoke the dispose callback for a component, if registered.
60
66
  * Errors are caught and logged to prevent blocking removal.
61
67
  */
62
68
  private invokeDispose;
63
- private resolveEntity;
64
- private resolveEntityId;
65
- addComponent<ComponentName extends keyof ComponentTypes>(entityOrId: number | Entity<ComponentTypes>, componentName: ComponentName, data: ComponentTypes[ComponentName]): this;
69
+ addComponent<ComponentName extends keyof ComponentTypes>(entityId: number, componentName: ComponentName, data: ComponentTypes[ComponentName]): this;
66
70
  /**
67
71
  * Add multiple components to an entity at once
68
- * @param entityOrId Entity or entity ID to add components to
72
+ * @param entityId Entity ID to add components to
69
73
  * @param components Object with component names as keys and component data as values
70
74
  */
71
75
  addComponents<T extends {
72
76
  [K in keyof ComponentTypes]?: ComponentTypes[K];
73
- }>(entityOrId: number | Entity<ComponentTypes>, components: T & Record<Exclude<keyof T, keyof ComponentTypes>, never>): this;
74
- removeComponent<ComponentName extends keyof ComponentTypes>(entityOrId: number | Entity<ComponentTypes>, componentName: ComponentName): this;
77
+ }>(entityId: number, components: T & Record<Exclude<keyof T, keyof ComponentTypes>, never>): this;
78
+ removeComponent<ComponentName extends keyof ComponentTypes>(entityId: number, componentName: ComponentName): this;
75
79
  getComponent<ComponentName extends keyof ComponentTypes>(entityId: number, componentName: ComponentName): ComponentTypes[ComponentName] | undefined;
76
80
  getEntitiesWithQuery<WithComponents extends keyof ComponentTypes = never, WithoutComponents extends keyof ComponentTypes = never>(required?: ReadonlyArray<WithComponents>, excluded?: ReadonlyArray<WithoutComponents>, changed?: ReadonlyArray<keyof ComponentTypes>, changeThreshold?: number, parentHas?: ReadonlyArray<keyof ComponentTypes>): Array<FilteredEntity<ComponentTypes, WithComponents extends never ? never : WithComponents, WithoutComponents extends never ? never : WithoutComponents>>;
77
81
  /**
78
82
  * Check if an entity's direct parent has all specified components
79
83
  */
80
84
  private parentHasComponents;
81
- removeEntity(entityOrId: number | Entity<ComponentTypes>, options?: RemoveEntityOptions): boolean;
85
+ removeEntity(entityId: number, options?: RemoveEntityOptions): boolean;
82
86
  /**
83
87
  * Internal method to remove a single entity without cascade logic
84
88
  */
@@ -90,14 +94,20 @@ export default class EntityManager<ComponentTypes> {
90
94
  * @param handler Function receiving the new component value and the entity
91
95
  * @returns Unsubscribe function to remove the callback
92
96
  */
93
- onComponentAdded<ComponentName extends keyof ComponentTypes>(componentName: ComponentName, handler: (value: ComponentTypes[ComponentName], entity: Entity<ComponentTypes>) => void): () => void;
97
+ onComponentAdded<ComponentName extends keyof ComponentTypes>(componentName: ComponentName, handler: (ctx: {
98
+ value: ComponentTypes[ComponentName];
99
+ entity: Entity<ComponentTypes>;
100
+ }) => void): () => void;
94
101
  /**
95
102
  * Register a callback when a specific component is removed from any entity
96
103
  * @param componentName The component key
97
104
  * @param handler Function receiving the old component value and the entity
98
105
  * @returns Unsubscribe function to remove the callback
99
106
  */
100
- onComponentRemoved<ComponentName extends keyof ComponentTypes>(componentName: ComponentName, handler: (oldValue: ComponentTypes[ComponentName], entity: Entity<ComponentTypes>) => void): () => void;
107
+ onComponentRemoved<ComponentName extends keyof ComponentTypes>(componentName: ComponentName, handler: (ctx: {
108
+ value: ComponentTypes[ComponentName];
109
+ entity: Entity<ComponentTypes>;
110
+ }) => void): () => void;
101
111
  onAfterComponentAdded(hook: (entityId: number, componentName: keyof ComponentTypes) => void): () => void;
102
112
  onAfterEntityMutated(hook: (entityId: number) => void): () => void;
103
113
  onAfterComponentRemoved(hook: (entityId: number, componentName: keyof ComponentTypes) => void): () => void;
@@ -110,10 +120,10 @@ export default class EntityManager<ComponentTypes> {
110
120
  get changeSeq(): number;
111
121
  /**
112
122
  * Mark a component as changed on an entity, stamping the next sequence number.
113
- * @param entityOrId The entity or entity ID
123
+ * @param entityId The entity ID
114
124
  * @param componentName The component that changed
115
125
  */
116
- markChanged<K extends keyof ComponentTypes>(entityOrId: number | Entity<ComponentTypes>, componentName: K): void;
126
+ markChanged<K extends keyof ComponentTypes>(entityId: number, componentName: K): void;
117
127
  /**
118
128
  * Get the sequence number at which a component was last changed on an entity
119
129
  * @param entityId The entity ID
@@ -123,89 +133,89 @@ export default class EntityManager<ComponentTypes> {
123
133
  getChangeSeq<K extends keyof ComponentTypes>(entityId: number, componentName: K): number;
124
134
  /**
125
135
  * Create an entity as a child of another entity with initial components
126
- * @param parentOrId The parent entity or entity ID
136
+ * @param parentId The parent entity ID
127
137
  * @param components Initial components to add
128
138
  * @returns The created child entity
129
139
  */
130
140
  spawnChild<T extends {
131
141
  [K in keyof ComponentTypes]?: ComponentTypes[K];
132
- }>(parentOrId: number | Entity<ComponentTypes>, components: T & Record<Exclude<keyof T, keyof ComponentTypes>, never>): FilteredEntity<ComponentTypes, keyof T & keyof ComponentTypes>;
142
+ }>(parentId: number, components: T & Record<Exclude<keyof T, keyof ComponentTypes>, never>): FilteredEntity<ComponentTypes, keyof T & keyof ComponentTypes>;
133
143
  /**
134
144
  * Set the parent of an entity
135
- * @param childOrId The entity or entity ID to set as a child
136
- * @param parentOrId The entity or entity ID to set as the parent
145
+ * @param childId The entity ID to set as a child
146
+ * @param parentId The entity ID to set as the parent
137
147
  */
138
- setParent(childOrId: number | Entity<ComponentTypes>, parentOrId: number | Entity<ComponentTypes>): this;
148
+ setParent(childId: number, parentId: number): this;
139
149
  /**
140
150
  * Remove the parent relationship for an entity (orphan it)
141
- * @param childOrId The entity or entity ID to orphan
151
+ * @param childId The entity ID to orphan
142
152
  * @returns true if a parent was removed, false if entity had no parent
143
153
  */
144
- removeParent(childOrId: number | Entity<ComponentTypes>): boolean;
154
+ removeParent(childId: number): boolean;
145
155
  /**
146
156
  * Get the parent of an entity
147
- * @param entityOrId The entity or entity ID to get the parent of
157
+ * @param entityId The entity ID to get the parent of
148
158
  * @returns The parent entity ID, or null if no parent
149
159
  */
150
- getParent(entityOrId: number | Entity<ComponentTypes>): number | null;
160
+ getParent(entityId: number): number | null;
151
161
  /**
152
162
  * Get all children of an entity in insertion order
153
- * @param parentOrId The parent entity or entity ID
163
+ * @param parentId The parent entity ID
154
164
  * @returns Readonly array of child entity IDs
155
165
  */
156
- getChildren(parentOrId: number | Entity<ComponentTypes>): readonly number[];
166
+ getChildren(parentId: number): readonly number[];
157
167
  /**
158
168
  * Get a child at a specific index
159
- * @param parentOrId The parent entity or entity ID
169
+ * @param parentId The parent entity ID
160
170
  * @param index The index of the child
161
171
  * @returns The child entity ID, or null if index is out of bounds
162
172
  */
163
- getChildAt(parentOrId: number | Entity<ComponentTypes>, index: number): number | null;
173
+ getChildAt(parentId: number, index: number): number | null;
164
174
  /**
165
175
  * Get the index of a child within its parent's children list
166
- * @param parentOrId The parent entity or entity ID
167
- * @param childOrId The child entity or entity ID to find
176
+ * @param parentId The parent entity ID
177
+ * @param childId The child entity ID to find
168
178
  * @returns The index of the child, or -1 if not found
169
179
  */
170
- getChildIndex(parentOrId: number | Entity<ComponentTypes>, childOrId: number | Entity<ComponentTypes>): number;
180
+ getChildIndex(parentId: number, childId: number): number;
171
181
  /**
172
182
  * Get all ancestors of an entity in order [parent, grandparent, ...]
173
- * @param entityOrId The entity or entity ID to get ancestors of
183
+ * @param entityId The entity ID to get ancestors of
174
184
  * @returns Readonly array of ancestor entity IDs
175
185
  */
176
- getAncestors(entityOrId: number | Entity<ComponentTypes>): readonly number[];
186
+ getAncestors(entityId: number): readonly number[];
177
187
  /**
178
188
  * Get all descendants of an entity in depth-first order
179
- * @param entityOrId The entity or entity ID to get descendants of
189
+ * @param entityId The entity ID to get descendants of
180
190
  * @returns Readonly array of descendant entity IDs
181
191
  */
182
- getDescendants(entityOrId: number | Entity<ComponentTypes>): readonly number[];
192
+ getDescendants(entityId: number): readonly number[];
183
193
  /**
184
194
  * Get the root ancestor of an entity (topmost parent), or self if no parent
185
- * @param entityOrId The entity or entity ID to get the root of
195
+ * @param entityId The entity ID to get the root of
186
196
  * @returns The root entity ID
187
197
  */
188
- getRoot(entityOrId: number | Entity<ComponentTypes>): number;
198
+ getRoot(entityId: number): number;
189
199
  /**
190
200
  * Get siblings of an entity (other children of the same parent)
191
- * @param entityOrId The entity or entity ID to get siblings of
201
+ * @param entityId The entity ID to get siblings of
192
202
  * @returns Readonly array of sibling entity IDs
193
203
  */
194
- getSiblings(entityOrId: number | Entity<ComponentTypes>): readonly number[];
204
+ getSiblings(entityId: number): readonly number[];
195
205
  /**
196
206
  * Check if an entity is a descendant of another entity
197
- * @param entityOrId The potential descendant (entity or ID)
198
- * @param ancestorOrId The potential ancestor (entity or ID)
199
- * @returns true if entityOrId is a descendant of ancestorOrId
207
+ * @param entityId The potential descendant ID
208
+ * @param ancestorId The potential ancestor ID
209
+ * @returns true if entityId is a descendant of ancestorId
200
210
  */
201
- isDescendantOf(entityOrId: number | Entity<ComponentTypes>, ancestorOrId: number | Entity<ComponentTypes>): boolean;
211
+ isDescendantOf(entityId: number, ancestorId: number): boolean;
202
212
  /**
203
213
  * Check if an entity is an ancestor of another entity
204
- * @param entityOrId The potential ancestor (entity or ID)
205
- * @param descendantOrId The potential descendant (entity or ID)
206
- * @returns true if entityOrId is an ancestor of descendantOrId
214
+ * @param entityId The potential ancestor ID
215
+ * @param descendantId The potential descendant ID
216
+ * @returns true if entityId is an ancestor of descendantId
207
217
  */
208
- isAncestorOf(entityOrId: number | Entity<ComponentTypes>, descendantOrId: number | Entity<ComponentTypes>): boolean;
218
+ isAncestorOf(entityId: number, descendantId: number): boolean;
209
219
  /**
210
220
  * Get all root entities (entities that have children but no parent)
211
221
  * @returns Readonly array of root entity IDs
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import ECSpresso from './ecspresso';
2
- import { SystemBuilder } from './system-builder';
3
- import Bundle, { mergeBundles } from './bundle';
2
+ import { SystemBuilder, type ProcessContext } from './system-builder';
3
+ import { type Plugin, type BasePluginOptions, definePlugin } from './plugin';
4
4
  export * from './types';
5
5
  export * from './asset-types';
6
6
  export * from './screen-types';
@@ -8,6 +8,7 @@ export * from './utils/math';
8
8
  export type { ReactiveQueryDefinition } from './reactive-query-manager';
9
9
  export { default as AssetManager, createAssetConfigurator } from './asset-manager';
10
10
  export { default as ScreenManager, createScreenConfigurator } from './screen-manager';
11
- export { SystemBuilder };
12
- export { Bundle, mergeBundles };
11
+ export { SystemBuilder, type ProcessContext };
12
+ export { type Plugin, type BasePluginOptions, definePlugin };
13
+ export { directValue, type ResourceDirectValue } from './resource-manager';
13
14
  export default ECSpresso;
@@ -0,0 +1,61 @@
1
+ import type ECSpresso from './ecspresso';
2
+ import type { SystemPhase } from './types';
3
+ import type { WorldConfig, EmptyConfig, MergeConfigs, AnyECSpresso, ConfigOf } from './type-utils';
4
+ /**
5
+ * Plugin interface for ECSpresso. A plugin is a plain object with an `install`
6
+ * function that configures a world directly, plus phantom properties for
7
+ * compile-time type extraction.
8
+ *
9
+ * @typeParam Cfg - The WorldConfig this plugin provides (components, events, resources, etc.)
10
+ * @typeParam Requires - The WorldConfig this plugin requires from other plugins
11
+ */
12
+ export interface Plugin<Cfg extends WorldConfig = EmptyConfig, Requires extends WorldConfig = EmptyConfig, Labels extends string = never, Groups extends string = never, AssetGroupNames extends string = never, ReactiveQueryNames extends string = never> {
13
+ readonly id: string;
14
+ readonly install: (world: ECSpresso<MergeConfigs<Cfg, Requires>>) => void;
15
+ readonly _cfg?: Cfg;
16
+ readonly _requires?: Requires;
17
+ readonly _labels?: Labels;
18
+ readonly _groups?: Groups;
19
+ readonly _assetGroupNames?: AssetGroupNames;
20
+ readonly _reactiveQueryNames?: ReactiveQueryNames;
21
+ }
22
+ /**
23
+ * Common configuration options shared by most plugins.
24
+ * Plugin-specific options interfaces extend this with additional fields.
25
+ */
26
+ export interface BasePluginOptions<G extends string = string> {
27
+ /** System group name for all systems registered by this plugin */
28
+ systemGroup?: G;
29
+ /** Priority for the plugin's primary system (default varies per plugin) */
30
+ priority?: number;
31
+ /** Execution phase for the plugin's primary system */
32
+ phase?: SystemPhase;
33
+ }
34
+ /**
35
+ * Factory function to create a type-safe Plugin with phantom type parameters.
36
+ * The type assertion adds phantom types without runtime cost.
37
+ *
38
+ * @example
39
+ * ```typescript
40
+ * // Option 1: Explicit config type param
41
+ * const myPlugin = definePlugin<WorldConfigFrom<MyComponents, MyEvents, MyResources>>({
42
+ * id: 'my-plugin',
43
+ * install(world) { ... },
44
+ * });
45
+ *
46
+ * // Option 2: Single world type param (extracts config automatically)
47
+ * type MyWorld = typeof ecs;
48
+ * const myPlugin = definePlugin<MyWorld>({
49
+ * id: 'my-plugin',
50
+ * install(world) { ... },
51
+ * });
52
+ * ```
53
+ */
54
+ export declare function definePlugin<W extends AnyECSpresso, Requires extends WorldConfig = EmptyConfig, Labels extends string = never, Groups extends string = never, AssetGroupNames extends string = never, ReactiveQueryNames extends string = never>(config: {
55
+ id: string;
56
+ install: (world: W) => void;
57
+ }): Plugin<ConfigOf<W>, Requires, Labels, Groups, AssetGroupNames, ReactiveQueryNames>;
58
+ export declare function definePlugin<Cfg extends WorldConfig = EmptyConfig, Requires extends WorldConfig = EmptyConfig, Labels extends string = never, Groups extends string = never, AssetGroupNames extends string = never, ReactiveQueryNames extends string = never>(config: {
59
+ id: string;
60
+ install: (world: ECSpresso<MergeConfigs<Cfg, Requires>>) => void;
61
+ }): Plugin<Cfg, Requires, Labels, Groups, AssetGroupNames, ReactiveQueryNames>;
@@ -1,12 +1,13 @@
1
1
  /**
2
- * Audio Bundle for ECSpresso
2
+ * Audio Plugin for ECSpresso
3
3
  *
4
4
  * Web Audio API integration via Howler.js for sound effects and music playback.
5
5
  * User-defined channels with type-safe volume control, hybrid resource + component API,
6
6
  * and asset manager integration.
7
7
  */
8
- import { Bundle } from 'ecspresso';
9
- import type { SystemPhase, AssetsOfWorld } from 'ecspresso';
8
+ import { type Plugin, type BasePluginOptions } from 'ecspresso';
9
+ import type { AssetsOfWorld, AnyECSpresso, ChannelOfWorld } from 'ecspresso';
10
+ import type { WorldConfigFrom, EmptyConfig } from '../type-utils';
10
11
  import type { Howl } from 'howler';
11
12
  /**
12
13
  * Configuration for a single audio channel.
@@ -56,7 +57,7 @@ export interface AudioSource<Ch extends string = string> {
56
57
  _soundId: number;
57
58
  }
58
59
  /**
59
- * Component types provided by the audio bundle.
60
+ * Component types provided by the audio plugin.
60
61
  */
61
62
  export interface AudioComponentTypes<Ch extends string = string> {
62
63
  audioSource: AudioSource<Ch>;
@@ -93,7 +94,7 @@ export interface SoundEndedEvent {
93
94
  sound: string;
94
95
  }
95
96
  /**
96
- * Event types provided by the audio bundle.
97
+ * Event types provided by the audio plugin.
97
98
  */
98
99
  export interface AudioEventTypes<Ch extends string = string> {
99
100
  playSound: PlaySoundEvent<Ch>;
@@ -157,23 +158,17 @@ export interface AudioState<Ch extends string = string> {
157
158
  isMuted(): boolean;
158
159
  }
159
160
  /**
160
- * Resource types provided by the audio bundle.
161
+ * Resource types provided by the audio plugin.
161
162
  */
162
163
  export interface AudioResourceTypes<Ch extends string = string> {
163
164
  audioState: AudioState<Ch>;
164
165
  }
165
166
  /**
166
- * Configuration options for the audio bundle.
167
+ * Configuration options for the audio plugin.
167
168
  */
168
- export interface AudioBundleOptions<Ch extends string, G extends string = 'audio'> {
169
+ export interface AudioPluginOptions<Ch extends string, G extends string = 'audio'> extends BasePluginOptions<G> {
169
170
  /** Channel definitions from defineAudioChannels */
170
171
  channels: Readonly<Record<Ch, AudioChannelConfig>>;
171
- /** System group name (default: 'audio') */
172
- systemGroup?: G;
173
- /** Priority for audio sync system (default: 0) */
174
- priority?: number;
175
- /** Execution phase (default: 'update') */
176
- phase?: SystemPhase;
177
172
  }
178
173
  /**
179
174
  * Create an audioSource component for entity-attached audio.
@@ -219,7 +214,7 @@ export declare function loadSound(src: string | string[], options?: {
219
214
  preload?: boolean;
220
215
  }): () => Promise<Howl>;
221
216
  /**
222
- * Create an audio bundle for ECSpresso.
217
+ * Create an audio plugin for ECSpresso.
223
218
  *
224
219
  * Provides:
225
220
  * - `audioState` resource for fire-and-forget SFX and music
@@ -240,7 +235,7 @@ export declare function loadSound(src: string | string[], options?: {
240
235
  *
241
236
  * const ecs = ECSpresso.create()
242
237
  * .withAssets(a => a.add('explosion', loadSound('/sfx/boom.mp3')))
243
- * .withBundle(createAudioBundle({ channels }))
238
+ * .withPlugin(createAudioPlugin({ channels }))
244
239
  * .build();
245
240
  *
246
241
  * await ecs.initialize();
@@ -248,46 +243,31 @@ export declare function loadSound(src: string | string[], options?: {
248
243
  * audio.play('explosion', { channel: 'sfx' });
249
244
  * ```
250
245
  */
251
- export declare function createAudioBundle<Ch extends string, G extends string = 'audio'>(options: AudioBundleOptions<Ch, G>): Bundle<AudioComponentTypes<Ch>, AudioEventTypes<Ch>, AudioResourceTypes<Ch>, {}, {}, 'audio-sync', G, never, 'audio-sources'>;
252
- type AnyECSpresso = import('ecspresso').default<any, any, any, any, any, any, any>;
246
+ export declare function createAudioPlugin<Ch extends string, G extends string = 'audio'>(options: AudioPluginOptions<Ch, G>): Plugin<WorldConfigFrom<AudioComponentTypes<Ch>, AudioEventTypes<Ch>, AudioResourceTypes<Ch>>, EmptyConfig, 'audio-sync', G, never, 'audio-sources'>;
253
247
  /**
254
- * Kit result from createAudioKit.
255
- */
256
- export interface AudioKit<W extends AnyECSpresso, Ch extends string, G extends string = 'audio'> {
257
- bundle: Bundle<AudioComponentTypes<Ch>, AudioEventTypes<Ch>, AudioResourceTypes<Ch>, {}, {}, 'audio-sync', G, never, 'audio-sources'>;
258
- createAudioSource: (sound: keyof AssetsOfWorld<W> & string, channel: Ch, options?: {
259
- volume?: number;
260
- loop?: boolean;
261
- autoRemove?: boolean;
262
- }) => Pick<AudioComponentTypes<Ch>, 'audioSource'>;
263
- loadSound: typeof loadSound;
264
- }
265
- /**
266
- * Create a typed audio kit that captures the world type W and channel type Ch.
267
- *
268
- * The returned `createAudioSource` validates sound keys against the world's
269
- * asset types at compile time.
248
+ * Typed helpers for the audio plugin.
249
+ * Creates helpers that validate sound keys and channel names against the world type W.
250
+ * Call after .build() using typeof ecs.
270
251
  *
271
252
  * @template W - Concrete ECS world type (e.g. `typeof ecs`)
272
- * @template Ch - Channel name union from defineAudioChannels
273
- * @template G - System group name (default: 'audio')
274
- * @param options - Bundle configuration including channel definitions
275
- * @returns A kit object with bundle, createAudioSource, loadSound
276
253
  *
277
254
  * @example
278
255
  * ```typescript
279
- * const channels = defineAudioChannels({ sfx: { volume: 1 }, music: { volume: 0.7 } });
280
- * type Ch = ChannelsOf<typeof channels>;
281
- * const kit = createAudioKit<typeof ecs, Ch>({ channels });
282
- *
283
256
  * const ecs = ECSpresso.create()
284
- * .withBundle(kit.bundle)
257
+ * .withPlugin(createAudioPlugin({ channels }))
285
258
  * .withAssets(a => a.add('boom', loadSound('/sfx/boom.mp3')))
286
259
  * .build();
287
260
  *
288
- * // Type-safe: 'boom' must be a registered asset
289
- * kit.createAudioSource('boom', 'sfx');
261
+ * const { createAudioSource } = createAudioHelpers<typeof ecs>();
262
+ * // Type-safe: 'boom' must be a registered asset, 'sfx' a valid channel
263
+ * createAudioSource('boom', 'sfx');
290
264
  * ```
291
265
  */
292
- export declare function createAudioKit<W extends AnyECSpresso, Ch extends string, G extends string = 'audio'>(options: AudioBundleOptions<Ch, G>): AudioKit<W, Ch, G>;
293
- export {};
266
+ export interface AudioHelpers<W extends AnyECSpresso> {
267
+ createAudioSource: (sound: keyof AssetsOfWorld<W> & string, channel: ChannelOfWorld<W>, options?: {
268
+ volume?: number;
269
+ loop?: boolean;
270
+ autoRemove?: boolean;
271
+ }) => Pick<AudioComponentTypes<ChannelOfWorld<W>>, 'audioSource'>;
272
+ }
273
+ export declare function createAudioHelpers<W extends AnyECSpresso>(_world?: W): AudioHelpers<W>;
@@ -1,13 +1,13 @@
1
1
  /**
2
- * Bounds Bundle for ECSpresso
2
+ * Bounds Plugin for ECSpresso
3
3
  *
4
4
  * Provides screen bounds enforcement for entities with transforms.
5
5
  * Reads worldTransform for position checking; modifies localTransform for corrections.
6
6
  * Supports destroy, clamp, and wrap behaviors.
7
7
  */
8
- import { Bundle } from 'ecspresso';
9
- import type { SystemPhase } from 'ecspresso';
10
- import type { TransformComponentTypes } from './transform';
8
+ import { type Plugin, type BasePluginOptions } from 'ecspresso';
9
+ import type { WorldConfigFrom } from '../type-utils';
10
+ import type { TransformWorldConfig } from './transform';
11
11
  /**
12
12
  * Component that marks an entity for destruction when outside bounds.
13
13
  */
@@ -30,14 +30,14 @@ export interface WrapAtBounds {
30
30
  padding?: number;
31
31
  }
32
32
  /**
33
- * Component types provided by the bounds bundle.
34
- * Included automatically via `.withBundle(createBoundsBundle())`.
33
+ * Component types provided by the bounds plugin.
34
+ * Included automatically via `.withPlugin(createBoundsPlugin())`.
35
35
  *
36
36
  * @example
37
37
  * ```typescript
38
38
  * const ecs = ECSpresso.create()
39
- * .withBundle(createTransformBundle())
40
- * .withBundle(createBoundsBundle({ width: 800, height: 600 }))
39
+ * .withPlugin(createTransformPlugin())
40
+ * .withPlugin(createBoundsPlugin({ width: 800, height: 600 }))
41
41
  * .withComponentTypes<{ sprite: Sprite }>()
42
42
  * .build();
43
43
  * ```
@@ -61,7 +61,7 @@ export interface BoundsRect {
61
61
  height: number;
62
62
  }
63
63
  /**
64
- * Resource types provided by the bounds bundle.
64
+ * Resource types provided by the bounds plugin.
65
65
  */
66
66
  export interface BoundsResourceTypes {
67
67
  bounds: BoundsRect;
@@ -76,25 +76,19 @@ export interface EntityOutOfBoundsEvent {
76
76
  exitEdge: 'top' | 'bottom' | 'left' | 'right';
77
77
  }
78
78
  /**
79
- * Event types provided by the bounds bundle.
79
+ * Event types provided by the bounds plugin.
80
80
  */
81
81
  export interface BoundsEventTypes {
82
82
  entityOutOfBounds: EntityOutOfBoundsEvent;
83
83
  }
84
84
  /**
85
- * Configuration options for the bounds bundle.
85
+ * Configuration options for the bounds plugin.
86
86
  */
87
- export interface BoundsBundleOptions<G extends string = 'physics'> {
88
- /** System group name (default: 'physics') */
89
- systemGroup?: G;
90
- /** Priority for bounds systems (default: 50) */
91
- priority?: number;
87
+ export interface BoundsPluginOptions<G extends string = 'physics'> extends BasePluginOptions<G> {
92
88
  /** Resource key for bounds rectangle (default: 'bounds') */
93
89
  boundsResourceKey?: string;
94
90
  /** Whether to auto-remove entities when out of bounds (default: true) */
95
91
  autoRemove?: boolean;
96
- /** Execution phase (default: 'postUpdate') */
97
- phase?: SystemPhase;
98
92
  }
99
93
  /**
100
94
  * Create a bounds rectangle resource.
@@ -158,11 +152,10 @@ export declare function createClampToBounds(margin?: number): Pick<BoundsCompone
158
152
  * ```
159
153
  */
160
154
  export declare function createWrapAtBounds(padding?: number): Pick<BoundsComponentTypes, 'wrapAtBounds'>;
161
- type CombinedComponentTypes = BoundsComponentTypes & TransformComponentTypes;
162
155
  /**
163
- * Create a bounds bundle for ECSpresso.
156
+ * Create a bounds plugin for ECSpresso.
164
157
  *
165
- * This bundle provides:
158
+ * This plugin provides:
166
159
  * - Destroy out of bounds system - removes entities that exit bounds
167
160
  * - Clamp to bounds system - constrains entities within bounds
168
161
  * - Wrap at bounds system - wraps entities to opposite edge
@@ -176,8 +169,8 @@ type CombinedComponentTypes = BoundsComponentTypes & TransformComponentTypes;
176
169
  * const ecs = ECSpresso
177
170
  * .create<Components, Events, Resources>()
178
171
  * .withResource('bounds', createBounds(800, 600))
179
- * .withBundle(createTransformBundle())
180
- * .withBundle(createBoundsBundle())
172
+ * .withPlugin(createTransformPlugin())
173
+ * .withPlugin(createBoundsPlugin())
181
174
  * .build();
182
175
  *
183
176
  * // Entity that gets destroyed when leaving screen
@@ -187,5 +180,4 @@ type CombinedComponentTypes = BoundsComponentTypes & TransformComponentTypes;
187
180
  * });
188
181
  * ```
189
182
  */
190
- export declare function createBoundsBundle<ResourceTypes extends BoundsResourceTypes = BoundsResourceTypes, G extends string = 'physics'>(options?: BoundsBundleOptions<G>): Bundle<CombinedComponentTypes, BoundsEventTypes, ResourceTypes, {}, {}, 'bounds-destroy' | 'bounds-clamp' | 'bounds-wrap', G>;
191
- export {};
183
+ export declare function createBoundsPlugin<ResourceTypes extends BoundsResourceTypes = BoundsResourceTypes, G extends string = 'physics'>(options?: BoundsPluginOptions<G>): Plugin<WorldConfigFrom<BoundsComponentTypes, BoundsEventTypes, ResourceTypes>, TransformWorldConfig, 'bounds-destroy' | 'bounds-clamp' | 'bounds-wrap', G>;
@@ -1,20 +1,21 @@
1
1
  /**
2
- * Camera / Viewport Bundle for ECSpresso
2
+ * Camera / Viewport Plugin for ECSpresso
3
3
  *
4
4
  * Provides a camera entity with world/screen coordinate conversion, smooth follow,
5
5
  * trauma-based shake, bounds clamping, and logical viewport dimensions.
6
6
  *
7
- * This bundle is renderer-agnostic. PixiJS or other renderer integration (applying
7
+ * This plugin is renderer-agnostic. PixiJS or other renderer integration (applying
8
8
  * cameraState to a container/stage transform) is the consumer's responsibility.
9
9
  *
10
10
  * Camera uses its own x/y/zoom/rotation rather than localTransform/worldTransform.
11
11
  * It reads the target entity's worldTransform for follow, but doesn't participate
12
12
  * in the transform hierarchy itself.
13
13
  */
14
- import { Bundle } from 'ecspresso';
14
+ import { type Plugin } from 'ecspresso';
15
15
  import type { SystemPhase } from 'ecspresso';
16
16
  import type ECSpresso from 'ecspresso';
17
- import type { TransformComponentTypes } from './transform';
17
+ import type { WorldConfigFrom } from '../type-utils';
18
+ import type { TransformWorldConfig } from './transform';
18
19
  export interface Camera {
19
20
  x: number;
20
21
  y: number;
@@ -48,7 +49,6 @@ export interface CameraComponentTypes {
48
49
  cameraShake: CameraShake;
49
50
  cameraBounds: CameraBounds;
50
51
  }
51
- type CombinedComponentTypes = CameraComponentTypes & TransformComponentTypes;
52
52
  export interface CameraState {
53
53
  x: number;
54
54
  y: number;
@@ -63,7 +63,7 @@ export interface CameraState {
63
63
  export interface CameraResourceTypes {
64
64
  cameraState: CameraState;
65
65
  }
66
- export interface CameraBundleOptions<G extends string = 'camera'> {
66
+ export interface CameraPluginOptions<G extends string = 'camera'> {
67
67
  viewportWidth?: number;
68
68
  viewportHeight?: number;
69
69
  systemGroup?: G;
@@ -76,7 +76,7 @@ export declare function createCamera(x?: number, y?: number, zoom?: number, rota
76
76
  export declare function createCameraFollow(target: number, options?: Partial<Omit<CameraFollow, 'target'>>): Pick<CameraComponentTypes, 'cameraFollow'>;
77
77
  export declare function createCameraShake(options?: Partial<CameraShake>): Pick<CameraComponentTypes, 'cameraShake'>;
78
78
  export declare function createCameraBounds(minX: number, minY: number, maxX: number, maxY: number): Pick<CameraComponentTypes, 'cameraBounds'>;
79
- export declare function addTrauma<C extends CombinedComponentTypes, R extends CameraResourceTypes>(ecs: ECSpresso<C, any, R>, entityId: number, amount: number): void;
79
+ export declare function addTrauma<Cfg extends WorldConfigFrom<CameraComponentTypes, {}, CameraResourceTypes>>(ecs: ECSpresso<Cfg>, entityId: number, amount: number): void;
80
80
  export declare function worldToScreen(worldX: number, worldY: number, state: CameraState): {
81
81
  x: number;
82
82
  y: number;
@@ -85,5 +85,4 @@ export declare function screenToWorld(screenX: number, screenY: number, state: C
85
85
  x: number;
86
86
  y: number;
87
87
  };
88
- export declare function createCameraBundle<G extends string = 'camera'>(options?: CameraBundleOptions<G>): Bundle<CombinedComponentTypes, {}, CameraResourceTypes, {}, {}, 'camera-follow' | 'camera-shake-update' | 'camera-bounds' | 'camera-state-sync', G>;
89
- export {};
88
+ export declare function createCameraPlugin<G extends string = 'camera'>(options?: CameraPluginOptions<G>): Plugin<WorldConfigFrom<CameraComponentTypes, {}, CameraResourceTypes>, TransformWorldConfig, 'camera-follow' | 'camera-shake-update' | 'camera-bounds' | 'camera-state-sync', G>;