
Security News
Attackers Are Hunting High-Impact Node.js Maintainers in a Coordinated Social Engineering Campaign
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.
@funderforge/ecs
Advanced tools
A generic component framework implementing an Entity-Component System (ECS) pattern, optimized for performance and simplicity
A generic component framework, optimized for performance and simplicity.
This framework implements an Entity-Component System (ECS) pattern that allows you to build flexible, hierarchical structures where entities can contain multiple components. The framework is designed with performance in mind, featuring efficient traversal algorithms, cached property access, and method-based architecture.
The framework consists of two main classes:
Entity Tree Structure:
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
[Root Entity]
ββ Component A
ββ Component B
ββ Component C
β
βββββββββ΄ββββββββ
β β
[Child Entity 1] [Child Entity 2]
ββ Component X ββ Component Y
ββ Component Z ββ Component W
β β
[Grandchild] [Grandchild]
ββ Component ββ Component
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Key Concepts:
β’ Entities form a tree hierarchy (parent-child relationships)
β’ Each entity can have multiple components
β’ Components belong to exactly one entity
β’ Enabled/disabled state cascades down the tree
β’ Components respond to lifecycle events through virtual methods
onTick and onEnabledChanged virtual methodsnpm install @funderforge/ecs
# or
pnpm install @funderforge/ecs
# or
yarn add @funderforge/ecs
import { Entity } from '@funderforge/ecs';
// Create a root entity
const root = new Entity({ id: 'root' });
// Create child entities
const child1 = new Entity({ id: 'child1' });
const child2 = new Entity({ id: 'child2' });
// Build the hierarchy
root.addChild(child1);
root.addChild(child2);
console.log(root.childrenLength()); // 2
console.log(child1.parent === root); // true
import { Entity, Component } from '@funderforge/ecs';
// Define a custom component
class TransformComponent extends Component {
x: number = 0;
y: number = 0;
move(dx: number, dy: number) {
this.x += dx;
this.y += dy;
}
}
// Create an entity and attach components
const entity = new Entity({ id: 'player' });
const transform = new TransformComponent(entity);
const renderer = new RenderComponent(entity);
console.log(entity.componentsLength()); // 2
// Access components with type safety
const transformComp = entity.getComponent<TransformComponent>(
c => c instanceof TransformComponent
);
transformComp?.move(10, 20);
// Create a game scene hierarchy
const scene = new Entity({ id: 'scene' });
const player = new Entity({ id: 'player', parent: scene });
const enemy1 = new Entity({ id: 'enemy1', parent: scene });
const enemy2 = new Entity({ id: 'enemy2', parent: scene });
// Nested hierarchy
const weapon = new Entity({ id: 'weapon', parent: player });
const bullet = new Entity({ id: 'bullet', parent: weapon });
// Get hierarchy level
console.log(bullet.hierarchyLevel()); // 3 (scene -> player -> weapon -> bullet)
// Remove from hierarchy
scene.removeChild(enemy1);
console.log(enemy1.parent); // null
// Create entity with initial state
const entity = new Entity({ id: 'entity', enabledSelf: true });
const child = new Entity({ id: 'child', parent: entity });
console.log(entity.enabled); // true
console.log(child.enabled); // true (inherits from parent)
// Disable parent - children are also disabled
entity.enabledSelf = false;
console.log(entity.enabled); // false
console.log(child.enabled); // false
// Re-enable
entity.enabledSelf = true;
console.log(child.enabled); // true
// Components respect entity enabled state
const component = new MyComponent(entity, { enabledSelf: true });
entity.enabledSelf = false;
console.log(component.enabled); // false (disabled because entity is disabled)
class MovementComponent extends Component {
velocity: number = 5;
position: number = 0;
// Override the onTick virtual method
protected onTick(deltaTime: number): void {
// Update position based on deltaTime (in milliseconds)
this.position += this.velocity * (deltaTime / 1000);
}
}
// Create entities and components
const root = new Entity({ id: 'root' });
const entity = new Entity({ id: 'moving-entity', parent: root });
const movement = new MovementComponent(entity);
// Tick the root entity (traverses the entire tree)
// This will call onTick on all enabled components
(root as any)._tick(16.67); // ~60fps deltaTime
class AudioComponent extends Component {
// Override the onEnabledChanged virtual method
protected onEnabledChanged(newValue: boolean): void {
if (newValue) {
console.log('Component enabled, starting audio');
// Start audio playback
} else {
console.log('Component disabled, stopping audio');
// Stop audio playback
}
}
}
const entity = new Entity({ id: 'audio-entity' });
const audio = new AudioComponent(entity);
// Disable the component
audio.enabledSelf = false; // Calls onEnabledChanged(false)
// Disable the entity (also calls onEnabledChanged for all components)
entity.enabledSelf = false;
const entity = new Entity({ id: 'entity' });
const component = new MyComponent(entity);
// Use the component...
component.doSomething();
// Destroy the component when no longer needed
component.destroy();
// CRITICAL: After destroy(), the component is "dead" and must not be accessed
// Remove all references and never use it again
// component = null; // or let it go out of scope
// β WRONG - Never do this after destroy():
// component.doSomething(); // Undefined behavior - component is dead
// console.log(component.entity); // Undefined behavior - component is dead
Important: After calling destroy() on a component, you must remove all references to it and never access it again. The component is permanently removed from its entity and accessing it will result in undefined behavior.
const root = new Entity({ id: 'root' });
// ... build tree structure ...
// Traverse all children
for (const child of root.traverseChildren()) {
console.log(`Child: ${child.id}`);
}
// Traverse children with a filter
for (const enabledChild of root.traverseChildren(child => child.enabledSelf)) {
console.log(`Enabled child: ${enabledChild.id}`);
}
// Traverse all components in the tree
for (const component of root.traverseComponents()) {
console.log(`Component: ${component.constructor.name}`);
}
// Traverse specific component types with type safety
for (const transform of root.traverseComponents<TransformComponent>(
c => c instanceof TransformComponent
)) {
console.log(`Transform at entity: ${transform.entity.id}`);
}
// Find specific entities/components with type safety
const player = root.getChild(e => e.id === 'player');
const transform = player?.getComponent<TransformComponent>(
c => c instanceof TransformComponent
);
import { Entity, Component } from '@funderforge/ecs';
// Define components
class TransformComponent extends Component {
x: number = 0;
y: number = 0;
rotation: number = 0;
}
class RenderComponent extends Component {
color: string = '#ffffff';
protected onTick(): void {
const transform = this.entity.getComponent<TransformComponent>(
c => c instanceof TransformComponent
);
if (transform) {
console.log(`Rendering at (${transform.x}, ${transform.y})`);
}
}
}
class PhysicsComponent extends Component {
velocityX: number = 0;
velocityY: number = 0;
protected onTick(deltaTime: number): void {
const transform = this.entity.getComponent<TransformComponent>(
c => c instanceof TransformComponent
);
if (transform) {
transform.x += this.velocityX * (deltaTime / 1000);
transform.y += this.velocityY * (deltaTime / 1000);
}
}
}
// Create game scene
const scene = new Entity({ id: 'scene' });
// Create player entity
const player = new Entity({ id: 'player', parent: scene });
new TransformComponent(player);
new PhysicsComponent(player);
new RenderComponent(player);
// Create enemy entities
for (let i = 0; i < 5; i++) {
const enemy = new Entity({ id: `enemy-${i}`, parent: scene });
new TransformComponent(enemy);
new RenderComponent(enemy);
}
// Game loop
function gameLoop() {
const deltaTime = 16.67; // ~60fps
(scene as any)._tick(deltaTime);
}
// Run game loop
setInterval(gameLoop, 16.67);
new Entity(preset?: {
id?: string | number;
enabledSelf?: boolean;
parent?: Entity;
})
addChild(child: Entity): Add a child entityremoveChild(child: Entity): Remove a child entitysetParent(parent: Entity | null): Set or clear the parent entitychildByIdx<T extends Entity = Entity>(index: number): T | undefined: Get a child entity by indexgetChild<T extends Entity = Entity>(predicate: (child: Entity) => boolean): T | undefined: Find a child entitygetChildren<T extends Entity = Entity>(filter: (child: Entity) => boolean): T[]: Get filtered childrentraverseChildren<T extends Entity = Entity>(predicate?: (child: Entity) => boolean): Generator<T>: Traverse all descendantscomponentByIdx<T extends Component = Component>(index: number): T | undefined: Get a component by indexgetComponent<T extends Component = Component>(predicate: (component: Component) => boolean): T | undefined: Find a componentgetComponents<T extends Component = Component>(filter: (component: Component) => boolean): T[]: Get filtered componentstraverseComponents<T extends Component = Component>(predicate?: (component: Component) => boolean): Generator<T>: Traverse all components in treehierarchyLevel(): Get the depth level in the hierarchyid: string | number: Unique identifierenabledSelf: boolean: Whether this entity is enabled (setter/getter)enabled: boolean: Whether this entity is enabled (considering parent state)parent: Entity | null: Parent entity referencenew Component(entity: Entity, preset?: {
enabledSelf?: boolean;
precacheTypeLookup?: boolean;
})
protected onTick(deltaTime: number): void: Override this method to handle tick events. Called during entity update cycles.protected onEnabledChanged(newValue: boolean): void: Override this method to respond to enabled state changes.destroy(): void: Destroys this component, removing it from its entity. After calling destroy(), the component is considered "dead" and must not be accessed or used in any way. Remove all references to the component and never access its properties or methods again.entity: Entity: The entity this component belongs toenabledSelf: boolean: Whether this component is enabled (setter/getter)enabled: boolean: Whether this component is enabled (considering entity state)The framework is optimized for performance:
enabled property is cached and only recomputed when necessaryMIT
FAQs
A generic component framework implementing an Entity-Component System (ECS) pattern, optimized for performance and simplicity
We found that @funderforge/ecs demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago.Β It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.

Security News
Axios compromise traced to social engineering, showing how attacks on maintainers can bypass controls and expose the broader software supply chain.

Security News
Node.js has paused its bug bounty program after funding ended, removing payouts for vulnerability reports but keeping its security process unchanged.