archetype-ecs 1.3.1 → 1.3.2

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 (47) hide show
  1. package/README.md +104 -34
  2. package/bench/allocations-1m.js +1 -1
  3. package/bench/multi-ecs-bench.js +1 -1
  4. package/bench/typed-vs-bitecs-1m.js +1 -1
  5. package/bench/typed-vs-untyped.js +81 -81
  6. package/bench/vs-bitecs.js +31 -56
  7. package/dist/ComponentRegistry.d.ts +18 -0
  8. package/dist/ComponentRegistry.js +29 -0
  9. package/dist/EntityManager.d.ts +52 -0
  10. package/dist/EntityManager.js +891 -0
  11. package/dist/Profiler.d.ts +12 -0
  12. package/dist/Profiler.js +38 -0
  13. package/dist/System.d.ts +41 -0
  14. package/dist/System.js +159 -0
  15. package/dist/index.d.ts +12 -0
  16. package/dist/index.js +29 -0
  17. package/dist/src/ComponentRegistry.d.ts +18 -0
  18. package/dist/src/ComponentRegistry.js +29 -0
  19. package/dist/src/EntityManager.d.ts +49 -0
  20. package/dist/src/EntityManager.js +853 -0
  21. package/dist/src/Profiler.d.ts +12 -0
  22. package/dist/src/Profiler.js +38 -0
  23. package/dist/src/System.d.ts +37 -0
  24. package/dist/src/System.js +139 -0
  25. package/dist/src/index.d.ts +14 -0
  26. package/dist/src/index.js +29 -0
  27. package/dist/tests/EntityManager.test.d.ts +1 -0
  28. package/dist/tests/EntityManager.test.js +651 -0
  29. package/dist/tests/System.test.d.ts +1 -0
  30. package/dist/tests/System.test.js +630 -0
  31. package/dist/tests/types.d.ts +1 -0
  32. package/dist/tests/types.js +129 -0
  33. package/package.json +9 -7
  34. package/src/ComponentRegistry.ts +49 -0
  35. package/src/EntityManager.ts +1018 -0
  36. package/src/{Profiler.js → Profiler.ts} +18 -5
  37. package/src/System.ts +226 -0
  38. package/src/index.ts +44 -0
  39. package/tests/{EntityManager.test.js → EntityManager.test.ts} +338 -70
  40. package/tests/System.test.ts +730 -0
  41. package/tests/types.ts +67 -66
  42. package/tsconfig.json +8 -5
  43. package/tsconfig.test.json +13 -0
  44. package/src/ComponentRegistry.js +0 -21
  45. package/src/EntityManager.js +0 -578
  46. package/src/index.d.ts +0 -118
  47. package/src/index.js +0 -37
package/tests/types.ts CHANGED
@@ -1,101 +1,102 @@
1
1
  // Compile-time type tests — run with: npx tsc --noEmit
2
- // These tests validate TS generics flow correctly through the API.
2
+ // These tests validate TS types flow correctly through the API.
3
3
  // No runtime execution needed; if this file compiles, the types are correct.
4
4
 
5
- import { createEntityManager, component, type ComponentDef, type FieldRef, type EntityId } from '../src/index.js';
5
+ import {
6
+ createEntityManager, createSystem, createSystems, component,
7
+ System, OnAdded, OnRemoved,
8
+ type ComponentDef, type FieldRef, type EntityId,
9
+ type SystemContext, type FunctionalSystemConstructor, type FunctionalSystem, type Pipeline
10
+ } from '../src/index.js';
6
11
 
7
- // --- component() infers schema fields ---
12
+ // --- component() creates ComponentDef ---
8
13
  const Position = component('Position', 'f32', ['x', 'y']);
9
14
  const Velocity = component('Velocity', { vx: 'f32', vy: 'f32' });
10
15
  const Tag = component('Tag');
11
16
  const Name = component('Name', { name: 'string', title: 'string' });
12
17
  const Label = component('Label', 'string', ['text', 'color']);
13
18
 
14
- // Position should be ComponentDef<{ x: number, y: number }>
15
- type AssertPosition = typeof Position extends ComponentDef<{ x: number; y: number }> ? true : never;
16
- const _assertPos: AssertPosition = true;
17
-
18
- // Position.x should be a FieldRef<number>
19
- type AssertFieldRef = typeof Position.x extends FieldRef<number> ? true : never;
20
- const _assertField: AssertFieldRef = true;
21
-
22
- // Name should be ComponentDef<{ name: string, title: string }>
23
- type AssertName = typeof Name extends ComponentDef<{ name: string; title: string }> ? true : never;
24
- const _assertName: AssertName = true;
25
-
26
- // Name.name should be a FieldRef<string>
27
- type AssertNameField = typeof Name.name extends FieldRef<string> ? true : never;
28
- const _assertNameField: AssertNameField = true;
29
-
30
- // Label short form: ComponentDef<{ text: string, color: string }>
31
- type AssertLabel = typeof Label extends ComponentDef<{ text: string; color: string }> ? true : never;
32
- const _assertLabel: AssertLabel = true;
33
-
34
- // Tag should be ComponentDef<unknown> (no schema)
35
- type AssertTag = typeof Tag extends ComponentDef ? true : never;
36
- const _assertTag: AssertTag = true;
19
+ // component() returns ComponentDef
20
+ const _assertComp: ComponentDef = Position;
21
+ const _assertTag: ComponentDef = Tag;
37
22
 
38
23
  // --- EntityManager typed methods ---
39
24
  const em = createEntityManager();
40
25
  const id: EntityId = em.createEntity();
41
26
 
42
- // addComponent: accepts correct data shape
27
+ // addComponent: accepts data
43
28
  em.addComponent(id, Position, { x: 1, y: 2 });
44
29
  em.addComponent(id, Velocity, { vx: 0, vy: 0 });
45
30
  em.addComponent(id, Name, { name: 'Hero', title: 'Sir' });
46
31
 
47
- // getComponent: returns typed object or undefined
32
+ // getComponent: returns data or undefined
48
33
  const pos = em.getComponent(id, Position);
49
- if (pos) {
50
- const x: number = pos.x;
51
- const y: number = pos.y;
52
- }
53
-
54
- const nameComp = em.getComponent(id, Name);
55
- if (nameComp) {
56
- const n: string = nameComp.name;
57
- const t: string = nameComp.title;
58
- }
59
34
 
60
- // get/set: numeric field descriptor access
61
- const px: number | undefined = em.get(id, Position.x);
62
- const py: number | undefined = em.get(id, Position.y);
35
+ // get/set: field descriptor access
36
+ const px = em.get(id, Position.x);
63
37
  em.set(id, Position.x, 10);
64
- em.set(id, Position.y, 20);
65
-
66
- // get/set: string field descriptor access
67
- const nameVal: string | undefined = em.get(id, Name.name);
68
- em.set(id, Name.name, 'Villain');
69
38
 
70
39
  // forEach + field: accepts FieldRef
71
40
  em.forEach([Position, Velocity], (arch) => {
72
41
  const count: number = arch.count;
73
42
  const ids: EntityId[] = arch.entityIds;
74
43
  const arrX = arch.field(Position.x);
75
- const arrVx = arch.field(Velocity.vx);
76
44
  });
77
45
 
78
46
  // createEntityWith: alternating type, data
79
47
  em.createEntityWith(Position, { x: 0, y: 0 }, Velocity, { vx: 1, vy: 1 });
80
48
 
81
- // --- These should fail if uncommented (negative tests) ---
82
- // @ts-expect-error 'z' does not exist on Position
83
- em.get(id, Position.z);
84
-
85
- // @ts-expect-error 'z' does not exist on Position
86
- em.set(id, Position.z, 5);
87
-
88
- // @ts-expect-error 'x' does not exist on Velocity
89
- em.get(id, Velocity.x);
90
-
91
- // @ts-expect-error missing 'y' in Position data
92
- em.addComponent(id, Position, { x: 1 });
93
-
94
- // @ts-expect-error 'z' does not exist on Position
95
- em.forEach([Position], (arch) => { arch.field(Position.z); });
49
+ // --- createSystem returns FunctionalSystem ---
50
+ const sys: FunctionalSystem = createSystem(em, (s: SystemContext) => {
51
+ // onAdded with 1 type
52
+ s.onAdded(Position, (entityId: EntityId) => {});
53
+ // onAdded with 2 types
54
+ s.onAdded(Position, Velocity, (entityId: EntityId) => {});
55
+ // onRemoved with 1 type
56
+ s.onRemoved(Position, (entityId: EntityId) => {});
57
+ // onRemoved with 2 types
58
+ s.onRemoved(Position, Velocity, (entityId: EntityId) => {});
59
+
60
+ return () => {
61
+ s.forEach([Position, Velocity], (view) => {
62
+ const count: number = view.count;
63
+ });
64
+ s.forEach([Position], (_view) => {}, [Velocity]);
65
+ };
66
+ });
67
+ sys();
68
+ sys.dispose();
69
+
70
+ // --- FunctionalSystemConstructor type ---
71
+ const ctor: FunctionalSystemConstructor = (s) => {
72
+ s.onAdded(Position, (_id) => {});
73
+ };
74
+
75
+ // --- createSystems returns Pipeline ---
76
+ const pipeline: Pipeline = createSystems(em, [ctor]);
77
+ pipeline();
78
+ pipeline.dispose();
79
+
80
+ // --- Class-based System ---
81
+ class TestSystem extends System {
82
+ @OnAdded(Position)
83
+ handleAdd(id: EntityId) {}
84
+
85
+ @OnRemoved(Position)
86
+ handleRemove(id: EntityId) {}
87
+
88
+ tick() {
89
+ this.forEach([Position], (view) => {
90
+ const count: number = view.count;
91
+ });
92
+ }
93
+ }
96
94
 
97
- // @ts-expect-error wrong data shape for Name
98
- em.addComponent(id, Name, { foo: 'bar' });
95
+ const testSys = new TestSystem(em);
96
+ testSys.run();
97
+ testSys.dispose();
99
98
 
100
- // @ts-expect-error number not assignable to string field
101
- em.set(id, Name.name, 42);
99
+ // --- createSystems accepts mixed entries ---
100
+ const mixedPipeline: Pipeline = createSystems(em, [ctor, TestSystem]);
101
+ mixedPipeline();
102
+ mixedPipeline.dispose();
package/tsconfig.json CHANGED
@@ -1,10 +1,13 @@
1
1
  {
2
2
  "compilerOptions": {
3
3
  "strict": true,
4
- "noEmit": true,
5
- "moduleResolution": "node",
6
- "module": "ESNext",
7
- "target": "ESNext"
4
+ "target": "ES2022",
5
+ "module": "NodeNext",
6
+ "moduleResolution": "NodeNext",
7
+ "declaration": true,
8
+ "outDir": "dist",
9
+ "rootDir": "src",
10
+ "skipLibCheck": true
8
11
  },
9
- "include": ["tests/types.ts"]
12
+ "include": ["src/**/*.ts"]
10
13
  }
@@ -0,0 +1,13 @@
1
+ {
2
+ "compilerOptions": {
3
+ "strict": true,
4
+ "target": "ES2022",
5
+ "module": "NodeNext",
6
+ "moduleResolution": "NodeNext",
7
+ "declaration": false,
8
+ "outDir": "dist-test",
9
+ "rootDir": ".",
10
+ "skipLibCheck": true
11
+ },
12
+ "include": ["src/**/*.ts", "tests/**/*.ts"]
13
+ }
@@ -1,21 +0,0 @@
1
- export const TYPED = Symbol('typed');
2
-
3
- export const TYPE_MAP = {
4
- 'f32': Float32Array,
5
- 'f64': Float64Array,
6
- 'i8': Int8Array,
7
- 'i16': Int16Array,
8
- 'i32': Int32Array,
9
- 'u8': Uint8Array,
10
- 'u16': Uint16Array,
11
- 'u32': Uint32Array,
12
- 'string': Array,
13
- };
14
-
15
- /** @type {Map<symbol, Record<string, typeof Float32Array>>} */
16
- export const componentSchemas = new Map();
17
-
18
- /** Extract the underlying symbol from a component object or pass through a plain symbol */
19
- export function toSym(type) {
20
- return type._sym || type;
21
- }