@oasys/oecs 0.1.2 → 0.2.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 (41) hide show
  1. package/README.md +131 -164
  2. package/dist/archetype.d.ts +50 -13
  3. package/dist/archetype.d.ts.map +1 -1
  4. package/dist/component.d.ts +27 -10
  5. package/dist/component.d.ts.map +1 -1
  6. package/dist/ecs.d.ts +104 -0
  7. package/dist/ecs.d.ts.map +1 -0
  8. package/dist/entity.d.ts.map +1 -1
  9. package/dist/event.d.ts +2 -2
  10. package/dist/event.d.ts.map +1 -1
  11. package/dist/index.cjs +1 -1
  12. package/dist/index.d.ts +8 -7
  13. package/dist/index.d.ts.map +1 -1
  14. package/dist/index.js +673 -321
  15. package/dist/query.d.ts +28 -31
  16. package/dist/query.d.ts.map +1 -1
  17. package/dist/ref.d.ts +19 -0
  18. package/dist/ref.d.ts.map +1 -0
  19. package/dist/resource.d.ts +23 -0
  20. package/dist/resource.d.ts.map +1 -0
  21. package/dist/schedule.d.ts +3 -3
  22. package/dist/schedule.d.ts.map +1 -1
  23. package/dist/store.d.ts +48 -29
  24. package/dist/store.d.ts.map +1 -1
  25. package/dist/system.d.ts +2 -2
  26. package/dist/system.d.ts.map +1 -1
  27. package/dist/type_primitives/assertions.d.ts +1 -0
  28. package/dist/type_primitives/assertions.d.ts.map +1 -1
  29. package/dist/type_primitives/bitset/bitset.d.ts +2 -2
  30. package/dist/type_primitives/bitset/bitset.d.ts.map +1 -1
  31. package/dist/type_primitives/sparse_map/sparse_map.d.ts.map +1 -1
  32. package/dist/type_primitives/sparse_set/sparse_set.d.ts.map +1 -1
  33. package/dist/type_primitives/typed_arrays/typed_arrays.d.ts +9 -0
  34. package/dist/type_primitives/typed_arrays/typed_arrays.d.ts.map +1 -1
  35. package/dist/utils/constants.d.ts +20 -0
  36. package/dist/utils/constants.d.ts.map +1 -0
  37. package/dist/utils/error.d.ts +2 -9
  38. package/dist/utils/error.d.ts.map +1 -1
  39. package/package.json +2 -1
  40. package/dist/world.d.ts +0 -73
  41. package/dist/world.d.ts.map +0 -1
package/README.md CHANGED
@@ -2,27 +2,41 @@
2
2
 
3
3
  A fast, minimal archetype-based Entity Component System written in TypeScript.
4
4
 
5
- - **Zero-copy archetype transitions** — component data is indexed by entity, not archetype row. Moving between archetypes copies only the relevant column data.
6
- - **Structure-of-Arrays (SoA)** — each component field is a contiguous `number[]` column, enabling tight inner loops.
7
- - **Phantom-typed components** — `ComponentDef<["x", "y"]>` is just a number at runtime, but enforces field names at compile time.
8
- - **Batch iteration** — `query.each()` calls your function once per archetype with column arrays and entity count. You write the inner loop.
5
+ - **Structure-of-Arrays (SoA)** — each component field is a contiguous typed array column (`Float64Array`, `Int32Array`, etc.), enabling cache-friendly inner loops.
6
+ - **Phantom-typed components** — `ComponentDef<{x:"f64",y:"f64"}>` is just a number at runtime, but enforces field names and types at compile time.
7
+ - **Batch iteration** — `for..of` over a query yields non-empty archetypes. Access SoA columns via `get_column()` and write the inner loop.
8
+ - **Single-entity refs** — `ctx.ref(Pos, entity)` gives you a cached accessor with prototype-backed getters/setters.
9
+ - **Resources** — typed global singletons (time, input, config) with live readers.
10
+ - **Events & signals** — fire-and-forget SoA channels, auto-cleared each frame.
9
11
  - **Deferred structural changes** — add/remove component and destroy entity are buffered during system execution and flushed between phases.
10
12
  - **Topological system ordering** — systems within a phase are sorted by before/after constraints using Kahn's algorithm.
13
+ - **Fixed timestep** — configurable fixed update loop with accumulator and interpolation alpha.
11
14
 
12
15
  ## Quick start
13
16
 
14
17
  ```ts
15
- import { World, SCHEDULE } from "@oasys/oecs";
18
+ import { ECS, SCHEDULE } from "@oasys/oecs-typed2";
16
19
 
17
- const world = new World();
20
+ const world = new ECS();
18
21
 
19
- // Define components as field-name arrays
20
- const Pos = world.register_component(["x", "y"] as const);
22
+ // Record syntax per-field type control
23
+ const Pos = world.register_component({ x: "f64", y: "f64" });
24
+
25
+ // Array shorthand — uniform type, defaults to "f64"
21
26
  const Vel = world.register_component(["vx", "vy"] as const);
22
27
 
23
28
  // Tags have no fields
24
29
  const IsEnemy = world.register_tag();
25
30
 
31
+ // Resources are global singletons
32
+ const Time = world.register_resource(["delta", "elapsed"] as const, {
33
+ delta: 0,
34
+ elapsed: 0,
35
+ });
36
+
37
+ // Events are fire-and-forget messages
38
+ const Damage = world.register_event(["target", "amount"] as const);
39
+
26
40
  // Create entities and attach components
27
41
  const e = world.create_entity();
28
42
  world.add_component(e, Pos, { x: 0, y: 0 });
@@ -32,12 +46,17 @@ world.add_component(e, IsEnemy);
32
46
  // Register a system with a typed query
33
47
  const moveSys = world.register_system(
34
48
  (q, _ctx, dt) => {
35
- q.each((pos, vel, n) => {
49
+ for (const arch of q) {
50
+ const px = arch.get_column(Pos, "x");
51
+ const py = arch.get_column(Pos, "y");
52
+ const vx = arch.get_column(Vel, "vx");
53
+ const vy = arch.get_column(Vel, "vy");
54
+ const n = arch.entity_count;
36
55
  for (let i = 0; i < n; i++) {
37
- pos.x[i] += vel.vx[i] * dt;
38
- pos.y[i] += vel.vy[i] * dt;
56
+ px[i] += vx[i] * dt;
57
+ py[i] += vy[i] * dt;
39
58
  }
40
- });
59
+ }
41
60
  },
42
61
  (qb) => qb.every(Pos, Vel),
43
62
  );
@@ -50,245 +69,193 @@ world.startup();
50
69
 
51
70
  // Game loop
52
71
  function frame(dt: number) {
72
+ world.set_resource(Time, { delta: dt, elapsed: performance.now() / 1000 });
53
73
  world.update(dt);
54
74
  }
55
75
  ```
56
76
 
57
77
  ## Components
58
78
 
59
- Components are defined as readonly string arrays of field names. All field values are `number`.
79
+ Components map field names to typed array tags. All field values are `number`, but storage uses the specified typed array (`Float64Array`, `Int32Array`, etc.) for cache-friendly iteration.
60
80
 
61
81
  ```ts
62
- const Position = world.register_component(["x", "y"] as const);
63
- const Health = world.register_component(["current", "max"] as const);
64
- const IsEnemy = world.register_tag(); // no fields
82
+ // Record syntax per-field type control
83
+ const Position = world.register_component({ x: "f64", y: "f64" });
84
+ const Health = world.register_component({ current: "i32", max: "i32" });
85
+
86
+ // Array shorthand — all fields default to "f64"
87
+ const Vel = world.register_component(["vx", "vy"] as const);
88
+
89
+ // Tags — no fields
90
+ const IsEnemy = world.register_tag();
65
91
  ```
66
92
 
67
- The `as const` is required so TypeScript infers the literal tuple type `["x", "y"]` instead of `string[]`. This enables type-safe field access throughout the API.
93
+ Supported typed array tags: `"f32"`, `"f64"`, `"i8"`, `"i16"`, `"i32"`, `"u8"`, `"u16"`, `"u32"`.
68
94
 
69
- ### Adding components
95
+ Add components individually or in batch (single archetype transition):
70
96
 
71
97
  ```ts
72
- const e = world.create_entity();
73
-
74
- // Data components require a values object
75
98
  world.add_component(e, Position, { x: 10, y: 20 });
76
- world.add_component(e, Health, { current: 100, max: 100 });
77
-
78
- // Tags require no values
79
- world.add_component(e, IsEnemy);
80
-
81
- // Add multiple at once (single archetype transition)
82
99
  world.add_components(e, [
83
100
  { def: Position, values: { x: 10, y: 20 } },
84
101
  { def: Health, values: { current: 100, max: 100 } },
85
102
  ]);
86
103
  ```
87
104
 
88
- ### Removing and checking components
89
-
90
- ```ts
91
- world.remove_component(e, Health);
92
- world.has_component(e, IsEnemy); // true
93
- ```
105
+ See [docs/api/components.md](docs/api/components.md) for full API.
94
106
 
95
107
  ## Queries
96
108
 
97
- Queries are live views over all archetypes matching a component mask. They update automatically as new archetypes are created.
98
-
99
- ### Batch iteration with `each()`
100
-
101
- `each()` calls your function once per matching archetype, passing column-group objects and the entity count. You write the inner loop.
109
+ Queries are live views over all archetypes matching a component mask.
102
110
 
103
111
  ```ts
104
112
  const q = world.query(Position, Velocity);
105
113
 
106
- q.each((pos, vel, n) => {
107
- // pos.x, pos.y, vel.vx, vel.vy are all number[]
108
- // n is the entity count in this archetype
114
+ // Iterate non-empty archetypes, access SoA columns, write the inner loop
115
+ for (const arch of q) {
116
+ const px = arch.get_column(Position, "x");
117
+ const py = arch.get_column(Position, "y");
118
+ const vx = arch.get_column(Velocity, "vx");
119
+ const vy = arch.get_column(Velocity, "vy");
120
+ const n = arch.entity_count;
109
121
  for (let i = 0; i < n; i++) {
110
- pos.x[i] += vel.vx[i];
111
- pos.y[i] += vel.vy[i];
122
+ px[i] += vx[i];
123
+ py[i] += vy[i];
112
124
  }
113
- });
114
- ```
115
-
116
- ### Query chaining
117
-
118
- ```ts
119
- // Extend required components
120
- const q = world.query(Position).and(Velocity);
121
-
122
- // Exclude archetypes that have a component
123
- const alive = world.query(Position).not(Dead);
124
-
125
- // Require at least one of these
126
- const damaged = world.query(Health).or(Poison, Fire);
125
+ }
127
126
 
128
- // Combine
127
+ // Chaining
129
128
  const targets = world.query(Position).and(Health).not(Shield).or(IsEnemy, IsBoss);
130
129
  ```
131
130
 
132
- ### Manual iteration
133
-
134
- For dynamic component access, iterate archetypes directly:
135
-
136
- ```ts
137
- for (const arch of world.query(Position)) {
138
- const px = arch.get_column(Position, "x");
139
- const py = arch.get_column(Position, "y");
140
- const count = arch.entity_count;
141
-
142
- for (let i = 0; i < count; i++) {
143
- px[i] += 1;
144
- py[i] += 1;
145
- }
146
- }
147
- ```
131
+ See [docs/api/queries.md](docs/api/queries.md) for full API.
148
132
 
149
133
  ## Systems
150
134
 
151
135
  Systems are plain functions registered with a query and scheduled into lifecycle phases.
152
136
 
153
- ### Registration
154
-
155
- Two registration styles:
156
-
157
137
  ```ts
158
- // Style 1: Function + query builder (typed, preferred)
138
+ // With a typed query
159
139
  const moveSys = world.register_system(
160
- (q, ctx, dt) => {
161
- q.each((pos, vel, n) => {
162
- for (let i = 0; i < n; i++) {
163
- pos.x[i] += vel.vx[i] * dt;
164
- pos.y[i] += vel.vy[i] * dt;
165
- }
166
- });
167
- },
140
+ (q, ctx, dt) => { for (const arch of q) { /* ... */ } },
168
141
  (qb) => qb.every(Pos, Vel),
169
142
  );
170
143
 
171
- // Style 2: Config object (for systems that don't need a query)
144
+ // Without a query
172
145
  const logSys = world.register_system({
173
- fn(ctx, dt) {
174
- console.log("frame", dt);
175
- },
146
+ fn(ctx, dt) { console.log("frame", dt); },
176
147
  });
177
148
  ```
178
149
 
179
- ### Deferred operations
150
+ Inside systems, use `ctx` for deferred structural changes and per-entity access:
151
+
152
+ ```ts
153
+ const e = ctx.create_entity();
154
+ ctx.add_component(e, Pos, { x: 0, y: 0 });
155
+ ctx.destroy_entity(someEntity);
156
+ ctx.remove_component(entity, Health);
157
+ ```
158
+
159
+ See [docs/api/systems.md](docs/api/systems.md) for full API.
180
160
 
181
- Inside systems, use `ctx` (SystemContext) for structural changes. These are buffered and applied between phases.
161
+ ## Resources
162
+
163
+ Resources are typed global singletons — time, input state, camera config.
182
164
 
183
165
  ```ts
184
- const spawnSys = world.register_system({
185
- fn(ctx, _dt) {
186
- const e = ctx.create_entity();
187
- ctx.add_component(e, Position, { x: 0, y: 0 });
188
- ctx.destroy_entity(someOtherEntity);
189
- ctx.remove_component(anotherEntity, Health);
190
- // Changes are applied after all systems in this phase complete
191
- },
166
+ const Time = world.register_resource(["delta", "elapsed"] as const, {
167
+ delta: 0, elapsed: 0,
192
168
  });
169
+
170
+ // Write
171
+ world.set_resource(Time, { delta: dt, elapsed: total });
172
+
173
+ // Read — scalar values, not arrays
174
+ const time = world.resource(Time);
175
+ time.delta; // number
176
+ time.elapsed; // number
193
177
  ```
194
178
 
195
- ### Per-entity field access
179
+ See [docs/api/resources.md](docs/api/resources.md) for full API.
180
+
181
+ ## Events & Signals
196
182
 
197
- For reading/writing individual entity fields (not batch):
183
+ Events are fire-and-forget SoA channels, auto-cleared each frame.
198
184
 
199
185
  ```ts
200
- const damageSys = world.register_system({
201
- fn(ctx, _dt) {
202
- const hp = ctx.get_field(Health, targetEntity, "current");
203
- ctx.set_field(Health, targetEntity, "current", hp - 10);
204
- },
205
- });
186
+ // Data events carry fields
187
+ const Damage = world.register_event(["target", "amount"] as const);
188
+ ctx.emit(Damage, { target: entityId, amount: 50 });
189
+
190
+ const dmg = ctx.read(Damage);
191
+ for (let i = 0; i < dmg.length; i++) {
192
+ dmg.target[i]; // number
193
+ dmg.amount[i]; // number
194
+ }
195
+
196
+ // Signals carry no data — just a count
197
+ const OnReset = world.register_signal();
198
+ ctx.emit(OnReset);
199
+ if (ctx.read(OnReset).length > 0) { /* fired */ }
200
+ ```
201
+
202
+ See [docs/api/events.md](docs/api/events.md) for full API.
203
+
204
+ ## Refs
205
+
206
+ Refs provide cached single-entity field access — faster than `get_field`/`set_field` for repeated access.
207
+
208
+ ```ts
209
+ const pos = ctx.ref(Pos, entity);
210
+ const vel = ctx.ref(Vel, entity);
211
+ pos.x += vel.vx * dt;
212
+ pos.y += vel.vy * dt;
206
213
  ```
207
214
 
215
+ See [docs/api/refs.md](docs/api/refs.md) for full API.
216
+
208
217
  ## Schedule
209
218
 
210
- Six lifecycle phases, executed in order:
219
+ Seven lifecycle phases, executed in order:
211
220
 
212
221
  | Phase | When | Use case |
213
222
  |---|---|---|
214
- | `PRE_STARTUP` | Once, before startup | Resource loading, allocation |
223
+ | `PRE_STARTUP` | Once, before startup | Resource loading |
215
224
  | `STARTUP` | Once | Initial entity spawning |
216
- | `POST_STARTUP` | Once, after startup | Validation, index building |
217
- | `PRE_UPDATE` | Every frame, first | Input handling, time management |
218
- | `UPDATE` | Every frame | Game logic, physics, AI |
225
+ | `POST_STARTUP` | Once, after startup | Validation |
226
+ | `FIXED_UPDATE` | Every tick (fixed dt) | Physics, simulation |
227
+ | `PRE_UPDATE` | Every frame, first | Input handling |
228
+ | `UPDATE` | Every frame | Game logic |
219
229
  | `POST_UPDATE` | Every frame, last | Rendering, cleanup |
220
230
 
221
- Deferred changes are flushed automatically after each phase.
222
-
223
231
  ```ts
224
232
  world.add_systems(SCHEDULE.UPDATE, moveSys, physicsSys);
225
233
  world.add_systems(SCHEDULE.POST_UPDATE, renderSys);
226
- ```
227
234
 
228
- ### Ordering constraints
229
-
230
- ```ts
235
+ // Ordering constraints
231
236
  world.add_systems(SCHEDULE.UPDATE, moveSys, {
232
237
  system: physicsSys,
233
238
  ordering: { after: [moveSys] },
234
239
  });
235
-
236
- world.add_systems(SCHEDULE.UPDATE, {
237
- system: aiSys,
238
- ordering: { before: [moveSys] },
239
- });
240
240
  ```
241
241
 
242
- Systems with ordering constraints are topologically sorted. Systems with no constraints run in registration order.
242
+ See [docs/api/schedule.md](docs/api/schedule.md) for full API.
243
243
 
244
244
  ## Entity lifecycle
245
245
 
246
246
  ```ts
247
247
  const e = world.create_entity();
248
-
249
248
  world.is_alive(e); // true
250
-
251
- world.destroy_entity(e); // deferred
252
- world.flush(); // apply now
253
-
249
+ world.destroy_entity_deferred(e); // deferred
250
+ world.flush();
254
251
  world.is_alive(e); // false
255
252
  ```
256
253
 
257
- Entity IDs are generational: destroying an entity increments its slot's generation, so stale IDs are safely detected as dead.
258
-
259
- ## Lifecycle hooks
260
-
261
- Systems can define lifecycle hooks:
262
-
263
- ```ts
264
- const sys = world.register_system({
265
- fn(ctx, dt) { /* runs every frame */ },
266
-
267
- on_added(store) {
268
- // Called once during world.startup()
269
- },
270
-
271
- on_removed() {
272
- // Called when system is unregistered via world.remove_system()
273
- },
274
-
275
- dispose() {
276
- // Called during world.dispose()
277
- },
278
- });
279
- ```
254
+ Entity IDs are generational: destroying an entity increments its slot's generation, so stale IDs are detected as dead.
280
255
 
281
256
  ## Dev / Prod modes
282
257
 
283
- The codebase uses `__DEV__` compile-time flags. In development builds, you get:
284
-
285
- - Bounds checking on entity IDs
286
- - Validation on branded type construction
287
- - Duplicate system detection
288
- - Circular dependency detection
289
- - Helpful error messages with context
290
-
291
- All dev-only checks are tree-shaken in production builds.
258
+ `__DEV__` compile-time flags enable bounds checking, dead entity detection, and duplicate system detection. Circular dependency detection is always active (not tree-shaken). All other dev checks are tree-shaken in production builds.
292
259
 
293
260
  ## Development
294
261
 
@@ -302,4 +269,4 @@ pnpm tsc --noEmit # type check
302
269
 
303
270
  ## Architecture
304
271
 
305
- See [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) for a thorough explanation of the internal design.
272
+ See [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) for internal design details.
@@ -1,41 +1,52 @@
1
- import { Brand, BitSet } from './type_primitives';
2
- import { ComponentID, ComponentFields, ComponentDef, ColumnsForSchema } from './component';
1
+ import { Brand, GrowableTypedArray, AnyTypedArray, TypedArrayTag, BitSet } from './type_primitives';
2
+ import { ComponentID, ComponentDef, ComponentSchema, TagToTypedArray } from './component';
3
3
  import { EntityID } from './entity';
4
4
  export type ArchetypeID = Brand<number, "archetype_id">;
5
5
  export declare const as_archetype_id: (value: number) => ArchetypeID;
6
6
  export interface ArchetypeEdge {
7
7
  add: ArchetypeID | null;
8
8
  remove: ArchetypeID | null;
9
+ /** Pre-computed column mapping for add direction: this → add target. */
10
+ add_map: Int16Array | null;
11
+ /** Pre-computed column mapping for remove direction: this → remove target. */
12
+ remove_map: Int16Array | null;
9
13
  }
10
14
  export interface ArchetypeColumnLayout {
11
15
  component_id: ComponentID;
12
16
  field_names: string[];
13
17
  field_index: Record<string, number>;
18
+ field_types: TypedArrayTag[];
14
19
  }
15
20
  interface ArchetypeColumnGroup {
16
21
  layout: ArchetypeColumnLayout;
17
- columns: number[][];
18
- record: Record<string, number[]>;
22
+ columns: GrowableTypedArray<AnyTypedArray>[];
19
23
  }
20
24
  export declare class Archetype {
21
25
  readonly id: ArchetypeID;
22
26
  readonly mask: BitSet;
23
27
  readonly has_columns: boolean;
24
- entity_ids: EntityID[];
28
+ private readonly _entity_ids;
25
29
  length: number;
26
- private edges;
30
+ private readonly edges;
31
+ readonly _flat_columns: GrowableTypedArray<AnyTypedArray>[];
32
+ readonly _col_offset: number[];
33
+ readonly _field_count: number[];
34
+ private readonly _field_index;
35
+ private readonly _field_names;
27
36
  readonly column_groups: (ArchetypeColumnGroup | undefined)[];
28
- private _column_ids;
29
- constructor(id: ArchetypeID, mask: BitSet, layouts?: ArchetypeColumnLayout[]);
37
+ readonly _column_ids: number[];
38
+ constructor(id: ArchetypeID, mask: BitSet, layouts?: ArchetypeColumnLayout[], initial_capacity?: number);
30
39
  get entity_count(): number;
31
- get entity_list(): readonly EntityID[];
40
+ /** Raw entity ID buffer. Valid data at indices 0..entity_count-1. */
41
+ get entity_ids(): Uint32Array;
42
+ get entity_list(): Uint32Array;
32
43
  has_component(id: ComponentID): boolean;
33
44
  matches(required: BitSet): boolean;
34
45
  /** Get a single field's column. Valid data: indices 0..entity_count-1. */
35
- get_column<F extends ComponentFields, Field extends F[number]>(def: ComponentDef<F>, field: Field): number[];
36
- /** Get all columns for a component as { fieldName: number[] }. */
37
- get_column_group<F extends ComponentFields>(def: ComponentDef<F>): ColumnsForSchema<F>;
46
+ get_column<S extends ComponentSchema, K extends string & keyof S>(def: ComponentDef<S>, field: K): TagToTypedArray[S[K]];
38
47
  write_fields(row: number, component_id: ComponentID, values: Record<string, number>): void;
48
+ /** Fast positional write: values[i] → field[i] in declaration order. No string lookup. */
49
+ write_fields_positional(row: number, component_id: ComponentID, values: ArrayLike<number>): void;
39
50
  read_field(row: number, component_id: ComponentID, field: string): number;
40
51
  /** Copy all shared component columns from source archetype at src_row into dst_row. */
41
52
  copy_shared_from(source: Archetype, src_row: number, dst_row: number): void;
@@ -47,15 +58,41 @@ export declare class Archetype {
47
58
  /**
48
59
  * Remove entity at row via swap-and-pop. Swaps the last entity into the
49
60
  * vacated row to keep data dense. Returns the entity_index of the swapped
50
- * entity (so Store can update its row), or -1 if no swap was needed.
61
+ * entity (so Store can update its row), or NO_SWAP if no swap was needed.
51
62
  */
52
63
  remove_entity(row: number): number;
53
64
  /** Tag-optimized add: skip column push entirely (no data to store). */
54
65
  add_entity_tag(entity_id: EntityID): number;
55
66
  /** Tag-optimized remove via swap-and-pop: skip column swap/pop entirely. */
56
67
  remove_entity_tag(row: number): number;
68
+ /**
69
+ * Move an entity from src archetype into this archetype in a single pass.
70
+ * Combines add_entity + copy_shared_from + remove_entity(src).
71
+ * Uses a pre-computed transition map for branchless column copy.
72
+ * Writes dst_row to _move_result[0], swapped entity index to _move_result[1].
73
+ */
74
+ move_entity_from(src: Archetype, src_row: number, entity_id: EntityID, transition_map: Int16Array): void;
75
+ /**
76
+ * Move an entity from src into this archetype (tag-only: no columns to copy).
77
+ * Writes dst_row to _move_result[0], swapped entity index to _move_result[1].
78
+ */
79
+ move_entity_from_tag(src: Archetype, src_row: number, entity_id: EntityID): void;
80
+ /**
81
+ * Bulk-move ALL entities from src into this archetype using TypedArray.set().
82
+ * Much faster than per-entity move_entity_from when the entire source is moving.
83
+ * After this call, src is empty. Returns the starting dst_row for the batch.
84
+ */
85
+ bulk_move_all_from(src: Archetype, transition_map: Int16Array): number;
57
86
  get_edge(component_id: ComponentID): ArchetypeEdge | undefined;
58
87
  set_edge(component_id: ComponentID, edge: ArchetypeEdge): void;
59
88
  }
89
+ /** Reusable result buffer for move_entity_from/move_entity_from_tag. [dst_row, swapped_index] */
90
+ export declare const _move_result: [number, number];
91
+ /**
92
+ * Build a transition map from src archetype to dst archetype.
93
+ * For each column in dst, stores the index of the corresponding column in src,
94
+ * or -1 if the column is new (no source).
95
+ */
96
+ export declare function build_transition_map(src: Archetype, dst: Archetype): Int16Array;
60
97
  export {};
61
98
  //# sourceMappingURL=archetype.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"archetype.d.ts","sourceRoot":"","sources":["../src/archetype.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"archetype.d.ts","sourceRoot":"","sources":["../src/archetype.ts"],"names":[],"mappings":"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"}
@@ -1,18 +1,35 @@
1
- import { Brand } from './type_primitives';
1
+ import { Brand, TypedArrayTag } from './type_primitives';
2
2
  export type ComponentID = Brand<number, "component_id">;
3
3
  export declare const as_component_id: (value: number) => ComponentID;
4
- export type ComponentFields = readonly string[];
5
- /** Maps component fields to their value object: { x: number, y: number }. */
6
- export type FieldValues<F extends ComponentFields> = {
7
- readonly [K in F[number]]: number;
4
+ /** Core schema type: maps field names to typed array tags. */
5
+ export type ComponentSchema = Readonly<Record<string, TypedArrayTag>>;
6
+ /** Compile-time tag TypedArray mapping. */
7
+ export type TagToTypedArray = {
8
+ f32: Float32Array;
9
+ f64: Float64Array;
10
+ i8: Int8Array;
11
+ i16: Int16Array;
12
+ i32: Int32Array;
13
+ u8: Uint8Array;
14
+ u16: Uint16Array;
15
+ u32: Uint32Array;
16
+ };
17
+ /** Maps schema fields to their value object: { x: number, y: number }. */
18
+ export type FieldValues<S extends ComponentSchema> = {
19
+ readonly [K in keyof S]: number;
8
20
  };
9
- /** Maps component fields to column arrays: { x: number[], y: number[] }. */
10
- export type ColumnsForSchema<F extends ComponentFields> = {
11
- readonly [K in F[number]]: number[];
21
+ /** Maps schema fields to their specific typed array columns. */
22
+ export type ColumnsForSchema<S extends ComponentSchema> = {
23
+ readonly [K in keyof S]: TagToTypedArray[S[K]];
24
+ };
25
+ export type ComponentFields = readonly string[];
26
+ /** Maps component fields to column arrays (used by events — always Float64Array). */
27
+ export type ColumnsForFields<F extends ComponentFields> = {
28
+ readonly [K in F[number]]: Float64Array;
12
29
  };
13
30
  declare const __schema: unique symbol;
14
- export type ComponentDef<F extends ComponentFields = ComponentFields> = ComponentID & {
15
- readonly [__schema]: F;
31
+ export type ComponentDef<S extends ComponentSchema = ComponentSchema> = ComponentID & {
32
+ readonly [__schema]: S;
16
33
  };
17
34
  export {};
18
35
  //# sourceMappingURL=component.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"component.d.ts","sourceRoot":"","sources":["../src/component.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;KAkBK;AAEL,OAAO,EACL,KAAK,EAGN,MAAM,iBAAiB,CAAC;AAEzB,MAAM,MAAM,WAAW,GAAG,KAAK,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;AACxD,eAAO,MAAM,eAAe,UAAW,MAAM,gBAK1C,CAAC;AAEJ,MAAM,MAAM,eAAe,GAAG,SAAS,MAAM,EAAE,CAAC;AAEhD,6EAA6E;AAC7E,MAAM,MAAM,WAAW,CAAC,CAAC,SAAS,eAAe,IAAI;IACnD,QAAQ,EAAE,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,GAAG,MAAM;CAClC,CAAC;AAEF,4EAA4E;AAC5E,MAAM,MAAM,gBAAgB,CAAC,CAAC,SAAS,eAAe,IAAI;IACxD,QAAQ,EAAE,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,GAAG,MAAM,EAAE;CACpC,CAAC;AAKF,OAAO,CAAC,MAAM,QAAQ,EAAE,OAAO,MAAM,CAAC;AAEtC,MAAM,MAAM,YAAY,CAAC,CAAC,SAAS,eAAe,GAAG,eAAe,IAClE,WAAW,GAAG;IAAE,QAAQ,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAA;CAAE,CAAC"}
1
+ {"version":3,"file":"component.d.ts","sourceRoot":"","sources":["../src/component.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;KAuBK;AAEL,OAAO,EACL,KAAK,EAGL,KAAK,aAAa,EACnB,MAAM,iBAAiB,CAAC;AAEzB,MAAM,MAAM,WAAW,GAAG,KAAK,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;AACxD,eAAO,MAAM,eAAe,UAAW,MAAM,gBAK1C,CAAC;AAEJ,8DAA8D;AAC9D,MAAM,MAAM,eAAe,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC,CAAC;AAEtE,6CAA6C;AAC7C,MAAM,MAAM,eAAe,GAAG;IAC5B,GAAG,EAAE,YAAY,CAAC;IAClB,GAAG,EAAE,YAAY,CAAC;IAClB,EAAE,EAAE,SAAS,CAAC;IACd,GAAG,EAAE,UAAU,CAAC;IAChB,GAAG,EAAE,UAAU,CAAC;IAChB,EAAE,EAAE,UAAU,CAAC;IACf,GAAG,EAAE,WAAW,CAAC;IACjB,GAAG,EAAE,WAAW,CAAC;CAClB,CAAC;AAEF,0EAA0E;AAC1E,MAAM,MAAM,WAAW,CAAC,CAAC,SAAS,eAAe,IAAI;IACnD,QAAQ,EAAE,CAAC,IAAI,MAAM,CAAC,GAAG,MAAM;CAChC,CAAC;AAEF,gEAAgE;AAChE,MAAM,MAAM,gBAAgB,CAAC,CAAC,SAAS,eAAe,IAAI;IACxD,QAAQ,EAAE,CAAC,IAAI,MAAM,CAAC,GAAG,eAAe,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;CAC/C,CAAC;AAGF,MAAM,MAAM,eAAe,GAAG,SAAS,MAAM,EAAE,CAAC;AAEhD,qFAAqF;AACrF,MAAM,MAAM,gBAAgB,CAAC,CAAC,SAAS,eAAe,IAAI;IACxD,QAAQ,EAAE,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,GAAG,YAAY;CACxC,CAAC;AAMF,OAAO,CAAC,MAAM,QAAQ,EAAE,OAAO,MAAM,CAAC;AAEtC,MAAM,MAAM,YAAY,CAAC,CAAC,SAAS,eAAe,GAAG,eAAe,IAClE,WAAW,GAAG;IAAE,QAAQ,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAA;CAAE,CAAC"}