archetype-ecs 1.2.0 → 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 (48) hide show
  1. package/README.md +126 -63
  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/.claude/settings.local.json +0 -32
  45. package/src/ComponentRegistry.js +0 -21
  46. package/src/EntityManager.js +0 -462
  47. package/src/index.d.ts +0 -111
  48. package/src/index.js +0 -37
@@ -0,0 +1,129 @@
1
+ // Compile-time type tests — run with: npx tsc --noEmit
2
+ // These tests validate TS types flow correctly through the API.
3
+ // No runtime execution needed; if this file compiles, the types are correct.
4
+ var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
5
+ var useValue = arguments.length > 2;
6
+ for (var i = 0; i < initializers.length; i++) {
7
+ value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
8
+ }
9
+ return useValue ? value : void 0;
10
+ };
11
+ var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
12
+ function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
13
+ var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
14
+ var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
15
+ var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
16
+ var _, done = false;
17
+ for (var i = decorators.length - 1; i >= 0; i--) {
18
+ var context = {};
19
+ for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
20
+ for (var p in contextIn.access) context.access[p] = contextIn.access[p];
21
+ context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
22
+ var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
23
+ if (kind === "accessor") {
24
+ if (result === void 0) continue;
25
+ if (result === null || typeof result !== "object") throw new TypeError("Object expected");
26
+ if (_ = accept(result.get)) descriptor.get = _;
27
+ if (_ = accept(result.set)) descriptor.set = _;
28
+ if (_ = accept(result.init)) initializers.unshift(_);
29
+ }
30
+ else if (_ = accept(result)) {
31
+ if (kind === "field") initializers.unshift(_);
32
+ else descriptor[key] = _;
33
+ }
34
+ }
35
+ if (target) Object.defineProperty(target, contextIn.name, descriptor);
36
+ done = true;
37
+ };
38
+ import { createEntityManager, createSystem, createSystems, component, System, OnAdded, OnRemoved } from '../src/index.js';
39
+ // --- component() creates ComponentDef ---
40
+ const Position = component('Position', 'f32', ['x', 'y']);
41
+ const Velocity = component('Velocity', { vx: 'f32', vy: 'f32' });
42
+ const Tag = component('Tag');
43
+ const Name = component('Name', { name: 'string', title: 'string' });
44
+ const Label = component('Label', 'string', ['text', 'color']);
45
+ // component() returns ComponentDef
46
+ const _assertComp = Position;
47
+ const _assertTag = Tag;
48
+ // --- EntityManager typed methods ---
49
+ const em = createEntityManager();
50
+ const id = em.createEntity();
51
+ // addComponent: accepts data
52
+ em.addComponent(id, Position, { x: 1, y: 2 });
53
+ em.addComponent(id, Velocity, { vx: 0, vy: 0 });
54
+ em.addComponent(id, Name, { name: 'Hero', title: 'Sir' });
55
+ // getComponent: returns data or undefined
56
+ const pos = em.getComponent(id, Position);
57
+ // get/set: field descriptor access
58
+ const px = em.get(id, Position.x);
59
+ em.set(id, Position.x, 10);
60
+ // forEach + field: accepts FieldRef
61
+ em.forEach([Position, Velocity], (arch) => {
62
+ const count = arch.count;
63
+ const ids = arch.entityIds;
64
+ const arrX = arch.field(Position.x);
65
+ });
66
+ // createEntityWith: alternating type, data
67
+ em.createEntityWith(Position, { x: 0, y: 0 }, Velocity, { vx: 1, vy: 1 });
68
+ // --- createSystem returns FunctionalSystem ---
69
+ const sys = createSystem(em, (s) => {
70
+ // onAdded with 1 type
71
+ s.onAdded(Position, (entityId) => { });
72
+ // onAdded with 2 types
73
+ s.onAdded(Position, Velocity, (entityId) => { });
74
+ // onRemoved with 1 type
75
+ s.onRemoved(Position, (entityId) => { });
76
+ // onRemoved with 2 types
77
+ s.onRemoved(Position, Velocity, (entityId) => { });
78
+ return () => {
79
+ s.forEach([Position, Velocity], (view) => {
80
+ const count = view.count;
81
+ });
82
+ s.forEach([Position], (_view) => { }, [Velocity]);
83
+ };
84
+ });
85
+ sys();
86
+ sys.dispose();
87
+ // --- FunctionalSystemConstructor type ---
88
+ const ctor = (s) => {
89
+ s.onAdded(Position, (_id) => { });
90
+ };
91
+ // --- createSystems returns Pipeline ---
92
+ const pipeline = createSystems(em, [ctor]);
93
+ pipeline();
94
+ pipeline.dispose();
95
+ // --- Class-based System ---
96
+ let TestSystem = (() => {
97
+ let _classSuper = System;
98
+ let _instanceExtraInitializers = [];
99
+ let _handleAdd_decorators;
100
+ let _handleRemove_decorators;
101
+ return class TestSystem extends _classSuper {
102
+ static {
103
+ const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
104
+ _handleAdd_decorators = [OnAdded(Position)];
105
+ _handleRemove_decorators = [OnRemoved(Position)];
106
+ __esDecorate(this, null, _handleAdd_decorators, { kind: "method", name: "handleAdd", static: false, private: false, access: { has: obj => "handleAdd" in obj, get: obj => obj.handleAdd }, metadata: _metadata }, null, _instanceExtraInitializers);
107
+ __esDecorate(this, null, _handleRemove_decorators, { kind: "method", name: "handleRemove", static: false, private: false, access: { has: obj => "handleRemove" in obj, get: obj => obj.handleRemove }, metadata: _metadata }, null, _instanceExtraInitializers);
108
+ if (_metadata) Object.defineProperty(this, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
109
+ }
110
+ handleAdd(id) { }
111
+ handleRemove(id) { }
112
+ tick() {
113
+ this.forEach([Position], (view) => {
114
+ const count = view.count;
115
+ });
116
+ }
117
+ constructor() {
118
+ super(...arguments);
119
+ __runInitializers(this, _instanceExtraInitializers);
120
+ }
121
+ };
122
+ })();
123
+ const testSys = new TestSystem(em);
124
+ testSys.run();
125
+ testSys.dispose();
126
+ // --- createSystems accepts mixed entries ---
127
+ const mixedPipeline = createSystems(em, [ctor, TestSystem]);
128
+ mixedPipeline();
129
+ mixedPipeline.dispose();
package/package.json CHANGED
@@ -1,19 +1,19 @@
1
1
  {
2
2
  "name": "archetype-ecs",
3
- "version": "1.2.0",
3
+ "version": "1.3.2",
4
4
  "description": "Lightweight archetype-based Entity Component System",
5
5
  "type": "module",
6
- "main": "src/index.js",
7
- "module": "src/index.js",
8
- "types": "src/index.d.ts",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
9
8
  "exports": {
10
9
  ".": {
11
- "types": "./src/index.d.ts",
12
- "default": "./src/index.js"
10
+ "types": "./dist/index.d.ts",
11
+ "default": "./dist/index.js"
13
12
  }
14
13
  },
15
14
  "scripts": {
16
- "test": "node --test tests/ && npx tsc --noEmit",
15
+ "build": "tsc",
16
+ "test": "tsc && tsc -p tsconfig.test.json && node --test dist-test/tests/EntityManager.test.js dist-test/tests/System.test.js",
17
17
  "bench": "node --expose-gc bench/multi-ecs-bench.js"
18
18
  },
19
19
  "keywords": [
@@ -26,9 +26,11 @@
26
26
  ],
27
27
  "license": "MIT",
28
28
  "devDependencies": {
29
+ "@types/node": "^25.2.3",
29
30
  "bitecs": "^0.4.0",
30
31
  "harmony-ecs": "^0.0.12",
31
32
  "miniplex": "^2.0.0",
33
+ "typescript": "^5.9.3",
32
34
  "wolf-ecs": "^2.0.0"
33
35
  }
34
36
  }
@@ -0,0 +1,49 @@
1
+ export const TYPED: unique symbol = Symbol('typed');
2
+
3
+ type TypedArrayConstructor =
4
+ | typeof Float32Array | typeof Float64Array
5
+ | typeof Int8Array | typeof Int16Array | typeof Int32Array
6
+ | typeof Uint8Array | typeof Uint16Array | typeof Uint32Array
7
+ | typeof Array;
8
+
9
+ export const TYPE_MAP: Record<string, TypedArrayConstructor> = {
10
+ 'f32': Float32Array,
11
+ 'f64': Float64Array,
12
+ 'i8': Int8Array,
13
+ 'i16': Int16Array,
14
+ 'i32': Int32Array,
15
+ 'u8': Uint8Array,
16
+ 'u16': Uint16Array,
17
+ 'u32': Uint32Array,
18
+ 'string': Array,
19
+ };
20
+
21
+ export type TypeSpec = TypedArrayConstructor | [TypedArrayConstructor, number];
22
+
23
+ export function parseTypeSpec(typeStr: string): TypeSpec {
24
+ const match = typeStr.match(/^(\w+)\[(\d+)\]$/);
25
+ if (match) {
26
+ const Ctor = TYPE_MAP[match[1]];
27
+ if (!Ctor) throw new Error(`Unknown base type "${match[1]}"`);
28
+ return [Ctor, parseInt(match[2])];
29
+ }
30
+ const Ctor = TYPE_MAP[typeStr];
31
+ if (!Ctor) throw new Error(`Unknown type "${typeStr}"`);
32
+ return Ctor;
33
+ }
34
+
35
+ export const componentSchemas = new Map<symbol, Record<string, TypeSpec>>();
36
+
37
+ export interface FieldRef {
38
+ readonly _sym: symbol;
39
+ readonly _field: string;
40
+ }
41
+
42
+ export type ComponentDef<F extends string = never> = {
43
+ readonly _sym: symbol;
44
+ readonly _name: string;
45
+ } & { readonly [K in F]: FieldRef };
46
+
47
+ export function toSym(type: ComponentDef | symbol): symbol {
48
+ return (type as ComponentDef)._sym || (type as symbol);
49
+ }