ecspresso
Version:
A minimal Entity-Component-System library for typescript and javascript.
208 lines (207 loc) • 10.3 kB
TypeScript
import EntityManager from "./entity-manager";
import EventBus from "./event-bus";
import type { FilteredEntity, Entity } from "./types";
import type Bundle from "./bundle";
/**
* Type helper to detect conflicting types between two record types.
* Returns a union of keys that exist in both T and U but have incompatible types.
*/
type GetConflictingKeys<T, U> = {
[K in keyof T & keyof U]: T[K] extends U[K] ? U[K] extends T[K] ? never : K : K;
}[keyof T & keyof U];
/**
* Simplified type helper to check bundle type compatibility.
* Returns true if bundles can be merged without type conflicts.
*/
type BundlesAreCompatible<C1 extends Record<string, any>, C2 extends Record<string, any>, E1 extends Record<string, any>, E2 extends Record<string, any>, R1 extends Record<string, any>, R2 extends Record<string, any>> = [
keyof C1
] extends [never] ? [keyof E1] extends [never] ? [keyof R1] extends [never] ? true : GetConflictingKeys<R1, R2> extends never ? true : false : GetConflictingKeys<E1, E2> extends never ? GetConflictingKeys<R1, R2> extends never ? true : false : false : GetConflictingKeys<C1, C2> extends never ? GetConflictingKeys<E1, E2> extends never ? GetConflictingKeys<R1, R2> extends never ? true : false : false : false;
/**
* Interface declaration for ECSpresso constructor to ensure type augmentation works properly.
* This merges with the class declaration below.
*/
export default interface ECSpresso<ComponentTypes extends Record<string, any> = {}, EventTypes extends Record<string, any> = {}, ResourceTypes extends Record<string, any> = {}> {
/**
* Default constructor
*/
new (): ECSpresso<ComponentTypes, EventTypes, ResourceTypes>;
}
/**
* ECSpresso is the central ECS framework class that connects all features.
* It handles creation and management of entities, components, and systems, and provides lifecycle hooks.
*/
export default class ECSpresso<ComponentTypes extends Record<string, any> = {}, EventTypes extends Record<string, any> = {}, ResourceTypes extends Record<string, any> = {}> {
/** Library version*/
static readonly VERSION: string;
/** Access/modify stored components and entities*/
private _entityManager;
/** Publish/subscribe to events*/
private _eventBus;
/** Access/modify registered resources*/
private _resourceManager;
/** Registered systems that will be updated in order*/
private _systems;
/** Cached sorted systems for efficient updates */
private _sortedSystems;
/** Track installed bundles to prevent duplicates*/
private _installedBundles;
/**
* Creates a new ECSpresso instance.
*/
constructor();
/**
* Creates a new ECSpresso builder for type-safe bundle installation.
* This is the preferred way to create an ECSpresso instance with bundles.
*
* @returns A builder instance for fluent method chaining
*
* @example
* ```typescript
* const ecs = ECSpresso.create<BaseComponents, BaseEvents, BaseResources>()
* .withBundle(bundle1)
* .withBundle(bundle2)
* .build();
* ```
*/
static create<C extends Record<string, any> = {}, E extends Record<string, any> = {}, R extends Record<string, any> = {}>(): ECSpressoBuilder<C, E, R>;
/**
* Adds a system directly to this ECSpresso instance
* @param label Unique name to identify the system
* @returns A SystemBuilder instance for method chaining
*/
addSystem(label: string): import("./system-builder").SystemBuilderWithEcspresso<ComponentTypes, EventTypes, ResourceTypes, {}>;
/**
* Update all systems, passing deltaTime and query results to each system's process function
* @param deltaTime Time elapsed since the last update (in seconds)
*/
update(deltaTime: number): void;
/**
* Initialize all resources and systems
* This method:
* 1. Initializes all resources that were added as factory functions
* 2. Calls the onInitialize lifecycle hook on all systems
*
* This is useful for game startup to ensure all resources are ready
* and systems are properly initialized before the game loop begins.
*
* @param resourceKeys Optional array of specific resource keys to initialize
* @returns Promise that resolves when everything is initialized
*/
initialize(): Promise<void>;
/**
* Initialize specific resources or all resources that were added as factory functions but haven't been initialized yet.
* This is useful when you need to ensure resources are ready before proceeding.
* @param keys Optional array of resource keys to initialize. If not provided, all pending resources will be initialized.
* @returns Promise that resolves when the specified resources are initialized
*/
initializeResources<K extends keyof ResourceTypes>(...keys: K[]): Promise<void>;
/**
* Sort the systems array by priority (higher priority first)
* Called internally when system list changes
* @private
*/
private _sortSystems;
/**
* Update the priority of a system
* @param label The unique label of the system to update
* @param priority The new priority value (higher values execute first)
* @returns true if the system was found and updated, false otherwise
*/
updateSystemPriority(label: string, priority: number): boolean;
/**
* Remove a system by its label
* Calls the system's onDetach method with this ECSpresso instance if defined
* @param label The unique label of the system to remove
* @returns true if the system was found and removed, false otherwise
*/
removeSystem(label: string): boolean;
/**
* Check if a resource exists
*/
hasResource<K extends keyof ResourceTypes>(key: K): boolean;
/**
* Get a resource if it exists, or undefined if not
*/
getResource<K extends keyof ResourceTypes>(key: K): ResourceTypes[K];
/**
* Add a resource to the ECS instance
*/
addResource<K extends keyof ResourceTypes>(key: K, resource: ResourceTypes[K] | ((ecs: ECSpresso<ComponentTypes, EventTypes, ResourceTypes>) => ResourceTypes[K] | Promise<ResourceTypes[K]>)): this;
/**
* Remove a resource from the ECS instance
* @param key The resource key to remove
* @returns True if the resource was removed, false if it didn't exist
*/
removeResource<K extends keyof ResourceTypes>(key: K): boolean;
/**
* Update an existing resource using an updater function
* @param key The resource key to update
* @param updater Function that receives the current resource value and returns the new value
* @returns This ECSpresso instance for chaining
* @throws Error if the resource doesn't exist
*/
updateResource<K extends keyof ResourceTypes>(key: K, updater: (current: ResourceTypes[K]) => ResourceTypes[K]): this;
/**
* Get all resource keys that are currently registered
* @returns Array of resource keys
*/
getResourceKeys(): Array<keyof ResourceTypes>;
/**
* Check if a resource needs initialization (was added as a factory function)
* @param key The resource key to check
* @returns True if the resource needs initialization
*/
resourceNeedsInitialization<K extends keyof ResourceTypes>(key: K): boolean;
/**
* Check if an entity has a component
*/
hasComponent<K extends keyof ComponentTypes>(entityId: number, componentName: K): boolean;
/**
* Create an entity and add components to it in one call
* @param components Object with component names as keys and component data as values
* @returns The created entity with all components added
*/
spawn<T extends {
[K in keyof ComponentTypes]?: ComponentTypes[K];
}>(components: T & Record<Exclude<keyof T, keyof ComponentTypes>, never>): Entity<ComponentTypes>;
/**
* Get all entities with specific components
*/
getEntitiesWithQuery<WithComponents extends keyof ComponentTypes, WithoutComponents extends keyof ComponentTypes = never>(withComponents: ReadonlyArray<WithComponents>, withoutComponents?: ReadonlyArray<WithoutComponents>): Array<FilteredEntity<ComponentTypes, WithComponents, WithoutComponents>>;
/**
* Get all installed bundle IDs
*/
get installedBundles(): string[];
get entityManager(): EntityManager<ComponentTypes>;
get eventBus(): EventBus<EventTypes>;
/**
* Internal method to install a bundle into this ECSpresso instance.
* Called by the ECSpressoBuilder during the build process.
* The type safety is guaranteed by the builder's type system.
*/
_installBundle<C extends Record<string, any>, E extends Record<string, any>, R extends Record<string, any>>(bundle: Bundle<C, E, R>): this;
}
/**
* Builder class for ECSpresso that provides fluent type-safe bundle installation.
* Handles type checking during build process to ensure type safety.
*/
export declare class ECSpressoBuilder<C extends Record<string, any> = {}, E extends Record<string, any> = {}, R extends Record<string, any> = {}> {
/** The ECSpresso instance being built*/
private ecspresso;
constructor();
/**
* Add the first bundle when starting with empty types.
* This overload allows any bundle to be added to an empty ECSpresso instance.
*/
withBundle<BC extends Record<string, any>, BE extends Record<string, any>, BR extends Record<string, any>>(this: ECSpressoBuilder<{}, {}, {}>, bundle: Bundle<BC, BE, BR>): ECSpressoBuilder<BC, BE, BR>;
/**
* Add a subsequent bundle with type checking.
* This overload enforces bundle type compatibility.
*/
withBundle<BC extends Record<string, any>, BE extends Record<string, any>, BR extends Record<string, any>>(bundle: BundlesAreCompatible<C, BC, E, BE, R, BR> extends true ? Bundle<BC, BE, BR> : never): ECSpressoBuilder<C & BC, E & BE, R & BR>;
/**
* Complete the build process and return the built ECSpresso instance
*/
build(): ECSpresso<C, E, R>;
}
export {};