# Component bundles A bundle names one reusable component set: ```nupp local playerBundle = world:newBundle("Player", { required = {tecs.ecs.Transform2D, Health}, with = { [tecs.gfx.Tint] = function() return tecs.gfx.Tint(1, 1, 1, 1) end, [tecs.gfx.Renderable2D] = true, }, }) local player = playerBundle:spawn( tecs.ecs.Transform2D(100, 200), Health(100) ) ``` `required` sets the positional arguments to `spawn`. `with` creates the rest for every entity. ## Required components Declaration order controls spawn order: ```nupp local enemyBundle = world:newBundle("Enemy", { required = {tecs.ecs.Transform2D, Health, Damage}, }) local enemy = enemyBundle:spawn( tecs.ecs.Transform2D(100, 200), Health(50), Damage(10) ) ``` Every argument must match its declared component. Move any value that varies per spawn into `required`. ## Bundle defaults Each `with` value must hold a factory or `true`. A factory runs once per spawn and returns a fresh instance: ```nupp local bulletBundle = world:newBundle("Bullet", { required = {tecs.ecs.Transform2D}, with = { [Velocity] = function() return Velocity(100, 0) end, [Damage] = function() return Damage(25) end, }, }) ``` `true` uses the component's default value. It suits tags and components whose declared defaults already have the right value: ```nupp local propBundle = world:newBundle("Prop", { required = {tecs.ecs.Transform2D}, with = { [tecs.gfx.Renderable2D] = true, [Static] = true, }, }) ``` A spawn cannot override a component from `with`. Put that component in `required` when callers need to supply it. The definition may name each component once across `required` and `with`. Registration rejects duplicates, invalid `with` values, and duplicate bundle names. ## Staged spawning The bundle object and the world registry call the same spawn path: ```nupp local first = playerBundle:spawn( tecs.ecs.Transform2D(0, 0), Health(100) ) local second = world:spawnBundle( "Player", tecs.ecs.Transform2D(20, 0), Health(100) ) ``` Bundle spawns follow `world:spawn` timing. They reserve an ID immediately and stage placement until the next pipeline barrier. The returned ID works immediately for later staged operations: ```nupp for _archetype, _length in query:iter() do local id = playerBundle:spawn( tecs.ecs.Transform2D(0, 0), Health(100) ) world:set(id, Selected) end ``` Plain bundles cache their resolved signature, including transitive requirements and the active state tag. They enqueue raw values in a retained, fixed-stride queue and publish directly into columns. Ordinary plain spawns and scalar mutations use the same flattened transaction storage. Pending edits coalesce before publication; relationships and durable keys retain their required indexing and validation paths. A state change resolves the next bundle burst without retagging earlier reservations. ## Registry lookup `world:getBundle(name)` returns one bundle or `nil`. `world:getBundles()` returns a fresh name-to-bundle map, so changing the map does not change the registry.