@oasys/oecs 0.1.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 (45) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +305 -0
  3. package/dist/archetype.d.ts +61 -0
  4. package/dist/archetype.d.ts.map +1 -0
  5. package/dist/component.d.ts +18 -0
  6. package/dist/component.d.ts.map +1 -0
  7. package/dist/entity.d.ts +11 -0
  8. package/dist/entity.d.ts.map +1 -0
  9. package/dist/event.d.ts +24 -0
  10. package/dist/event.d.ts.map +1 -0
  11. package/dist/index.cjs +1 -0
  12. package/dist/index.d.ts +9 -0
  13. package/dist/index.d.ts.map +1 -0
  14. package/dist/index.js +1182 -0
  15. package/dist/query.d.ts +73 -0
  16. package/dist/query.d.ts.map +1 -0
  17. package/dist/schedule.d.ts +40 -0
  18. package/dist/schedule.d.ts.map +1 -0
  19. package/dist/store.d.ts +92 -0
  20. package/dist/store.d.ts.map +1 -0
  21. package/dist/system.d.ts +16 -0
  22. package/dist/system.d.ts.map +1 -0
  23. package/dist/type_primitives/assertions.d.ts +14 -0
  24. package/dist/type_primitives/assertions.d.ts.map +1 -0
  25. package/dist/type_primitives/bitset/bitset.d.ts +37 -0
  26. package/dist/type_primitives/bitset/bitset.d.ts.map +1 -0
  27. package/dist/type_primitives/brand.d.ts +18 -0
  28. package/dist/type_primitives/brand.d.ts.map +1 -0
  29. package/dist/type_primitives/error.d.ts +10 -0
  30. package/dist/type_primitives/error.d.ts.map +1 -0
  31. package/dist/type_primitives/index.d.ts +8 -0
  32. package/dist/type_primitives/index.d.ts.map +1 -0
  33. package/dist/type_primitives/sparse_map/sparse_map.d.ts +26 -0
  34. package/dist/type_primitives/sparse_map/sparse_map.d.ts.map +1 -0
  35. package/dist/type_primitives/sparse_set/sparse_set.d.ts +24 -0
  36. package/dist/type_primitives/sparse_set/sparse_set.d.ts.map +1 -0
  37. package/dist/type_primitives/typed_arrays/typed_arrays.d.ts +79 -0
  38. package/dist/type_primitives/typed_arrays/typed_arrays.d.ts.map +1 -0
  39. package/dist/utils/arrays.d.ts +6 -0
  40. package/dist/utils/arrays.d.ts.map +1 -0
  41. package/dist/utils/error.d.ts +28 -0
  42. package/dist/utils/error.d.ts.map +1 -0
  43. package/dist/world.d.ts +63 -0
  44. package/dist/world.d.ts.map +1 -0
  45. package/package.json +53 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Oasys
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,305 @@
1
+ # OECS
2
+
3
+ A fast, minimal archetype-based Entity Component System written in TypeScript.
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.
9
+ - **Deferred structural changes** — add/remove component and destroy entity are buffered during system execution and flushed between phases.
10
+ - **Topological system ordering** — systems within a phase are sorted by before/after constraints using Kahn's algorithm.
11
+
12
+ ## Quick start
13
+
14
+ ```ts
15
+ import { World, SCHEDULE } from "@oasys/oecs";
16
+
17
+ const world = new World();
18
+
19
+ // Define components as field-name arrays
20
+ const Pos = world.register_component(["x", "y"] as const);
21
+ const Vel = world.register_component(["vx", "vy"] as const);
22
+
23
+ // Tags have no fields
24
+ const IsEnemy = world.register_tag();
25
+
26
+ // Create entities and attach components
27
+ const e = world.create_entity();
28
+ world.add_component(e, Pos, { x: 0, y: 0 });
29
+ world.add_component(e, Vel, { vx: 100, vy: 50 });
30
+ world.add_component(e, IsEnemy);
31
+
32
+ // Register a system with a typed query
33
+ const moveSys = world.register_system(
34
+ (q, _ctx, dt) => {
35
+ q.each((pos, vel, n) => {
36
+ for (let i = 0; i < n; i++) {
37
+ pos.x[i] += vel.vx[i] * dt;
38
+ pos.y[i] += vel.vy[i] * dt;
39
+ }
40
+ });
41
+ },
42
+ (qb) => qb.every(Pos, Vel),
43
+ );
44
+
45
+ // Schedule the system
46
+ world.add_systems(SCHEDULE.UPDATE, moveSys);
47
+
48
+ // Initialize
49
+ world.startup();
50
+
51
+ // Game loop
52
+ function frame(dt: number) {
53
+ world.update(dt);
54
+ }
55
+ ```
56
+
57
+ ## Components
58
+
59
+ Components are defined as readonly string arrays of field names. All field values are `number`.
60
+
61
+ ```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
65
+ ```
66
+
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.
68
+
69
+ ### Adding components
70
+
71
+ ```ts
72
+ const e = world.create_entity();
73
+
74
+ // Data components require a values object
75
+ 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
+ world.add_components(e, [
83
+ { def: Position, values: { x: 10, y: 20 } },
84
+ { def: Health, values: { current: 100, max: 100 } },
85
+ ]);
86
+ ```
87
+
88
+ ### Removing and checking components
89
+
90
+ ```ts
91
+ world.remove_component(e, Health);
92
+ world.has_component(e, IsEnemy); // true
93
+ ```
94
+
95
+ ## Queries
96
+
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.
102
+
103
+ ```ts
104
+ const q = world.query(Position, Velocity);
105
+
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
109
+ for (let i = 0; i < n; i++) {
110
+ pos.x[i] += vel.vx[i];
111
+ pos.y[i] += vel.vy[i];
112
+ }
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);
127
+
128
+ // Combine
129
+ const targets = world.query(Position).and(Health).not(Shield).or(IsEnemy, IsBoss);
130
+ ```
131
+
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
+ ```
148
+
149
+ ## Systems
150
+
151
+ Systems are plain functions registered with a query and scheduled into lifecycle phases.
152
+
153
+ ### Registration
154
+
155
+ Two registration styles:
156
+
157
+ ```ts
158
+ // Style 1: Function + query builder (typed, preferred)
159
+ 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
+ },
168
+ (qb) => qb.every(Pos, Vel),
169
+ );
170
+
171
+ // Style 2: Config object (for systems that don't need a query)
172
+ const logSys = world.register_system({
173
+ fn(ctx, dt) {
174
+ console.log("frame", dt);
175
+ },
176
+ });
177
+ ```
178
+
179
+ ### Deferred operations
180
+
181
+ Inside systems, use `ctx` (SystemContext) for structural changes. These are buffered and applied between phases.
182
+
183
+ ```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
+ },
192
+ });
193
+ ```
194
+
195
+ ### Per-entity field access
196
+
197
+ For reading/writing individual entity fields (not batch):
198
+
199
+ ```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
+ });
206
+ ```
207
+
208
+ ## Schedule
209
+
210
+ Six lifecycle phases, executed in order:
211
+
212
+ | Phase | When | Use case |
213
+ |---|---|---|
214
+ | `PRE_STARTUP` | Once, before startup | Resource loading, allocation |
215
+ | `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 |
219
+ | `POST_UPDATE` | Every frame, last | Rendering, cleanup |
220
+
221
+ Deferred changes are flushed automatically after each phase.
222
+
223
+ ```ts
224
+ world.add_systems(SCHEDULE.UPDATE, moveSys, physicsSys);
225
+ world.add_systems(SCHEDULE.POST_UPDATE, renderSys);
226
+ ```
227
+
228
+ ### Ordering constraints
229
+
230
+ ```ts
231
+ world.add_systems(SCHEDULE.UPDATE, moveSys, {
232
+ system: physicsSys,
233
+ ordering: { after: [moveSys] },
234
+ });
235
+
236
+ world.add_systems(SCHEDULE.UPDATE, {
237
+ system: aiSys,
238
+ ordering: { before: [moveSys] },
239
+ });
240
+ ```
241
+
242
+ Systems with ordering constraints are topologically sorted. Systems with no constraints run in registration order.
243
+
244
+ ## Entity lifecycle
245
+
246
+ ```ts
247
+ const e = world.create_entity();
248
+
249
+ world.is_alive(e); // true
250
+
251
+ world.destroy_entity(e); // deferred
252
+ world.flush(); // apply now
253
+
254
+ world.is_alive(e); // false
255
+ ```
256
+
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
+ ```
280
+
281
+ ## Dev / Prod modes
282
+
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.
292
+
293
+ ## Development
294
+
295
+ ```bash
296
+ pnpm install
297
+ pnpm test # vitest in watch mode
298
+ pnpm bench # run benchmarks
299
+ pnpm build # vite library build
300
+ pnpm tsc --noEmit # type check
301
+ ```
302
+
303
+ ## Architecture
304
+
305
+ See [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) for a thorough explanation of the internal design.
@@ -0,0 +1,61 @@
1
+ import { Brand, BitSet } from './type_primitives';
2
+ import { ComponentID, ComponentFields, ComponentDef, ColumnsForSchema } from './component';
3
+ import { EntityID } from './entity';
4
+ export type ArchetypeID = Brand<number, "archetype_id">;
5
+ export declare const as_archetype_id: (value: number) => ArchetypeID;
6
+ export interface ArchetypeEdge {
7
+ add: ArchetypeID | null;
8
+ remove: ArchetypeID | null;
9
+ }
10
+ export interface ArchetypeColumnLayout {
11
+ component_id: ComponentID;
12
+ field_names: string[];
13
+ field_index: Record<string, number>;
14
+ }
15
+ interface ArchetypeColumnGroup {
16
+ layout: ArchetypeColumnLayout;
17
+ columns: number[][];
18
+ record: Record<string, number[]>;
19
+ }
20
+ export declare class Archetype {
21
+ readonly id: ArchetypeID;
22
+ readonly mask: BitSet;
23
+ readonly has_columns: boolean;
24
+ entity_ids: EntityID[];
25
+ length: number;
26
+ private edges;
27
+ readonly column_groups: (ArchetypeColumnGroup | undefined)[];
28
+ private _column_ids;
29
+ constructor(id: ArchetypeID, mask: BitSet, layouts?: ArchetypeColumnLayout[]);
30
+ get entity_count(): number;
31
+ get entity_list(): readonly EntityID[];
32
+ has_component(id: ComponentID): boolean;
33
+ matches(required: BitSet): boolean;
34
+ /** 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>;
38
+ write_fields(row: number, component_id: ComponentID, values: Record<string, number>): void;
39
+ read_field(row: number, component_id: ComponentID, field: string): number;
40
+ /** Copy all shared component columns from source archetype at src_row into dst_row. */
41
+ copy_shared_from(source: Archetype, src_row: number, dst_row: number): void;
42
+ /**
43
+ * Add an entity. Pushes zeroes into all columns and returns the assigned row.
44
+ * Store is responsible for tracking entity_index → row.
45
+ */
46
+ add_entity(entity_id: EntityID): number;
47
+ /**
48
+ * Remove entity at row via swap-and-pop. Swaps the last entity into the
49
+ * 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.
51
+ */
52
+ remove_entity(row: number): number;
53
+ /** Tag-optimized add: skip column push entirely (no data to store). */
54
+ add_entity_tag(entity_id: EntityID): number;
55
+ /** Tag-optimized remove via swap-and-pop: skip column swap/pop entirely. */
56
+ remove_entity_tag(row: number): number;
57
+ get_edge(component_id: ComponentID): ArchetypeEdge | undefined;
58
+ set_edge(component_id: ComponentID, edge: ArchetypeEdge): void;
59
+ }
60
+ export {};
61
+ //# sourceMappingURL=archetype.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"archetype.d.ts","sourceRoot":"","sources":["../src/archetype.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ import { Brand } from './type_primitives';
2
+ export type ComponentID = Brand<number, "component_id">;
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;
8
+ };
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[];
12
+ };
13
+ declare const __schema: unique symbol;
14
+ export type ComponentDef<F extends ComponentFields = ComponentFields> = ComponentID & {
15
+ readonly [__schema]: F;
16
+ };
17
+ export {};
18
+ //# sourceMappingURL=component.d.ts.map
@@ -0,0 +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"}
@@ -0,0 +1,11 @@
1
+ import { Brand } from './type_primitives';
2
+ export type EntityID = Brand<number, "entity_id">;
3
+ export declare const INDEX_BITS = 20;
4
+ export declare const INDEX_MASK: number;
5
+ export declare const MAX_INDEX: number;
6
+ export declare const GENERATION_BITS: number;
7
+ export declare const MAX_GENERATION: number;
8
+ export declare const create_entity_id: (index: number, generation: number) => EntityID;
9
+ export declare const get_entity_index: (id: EntityID) => number;
10
+ export declare const get_entity_generation: (id: EntityID) => number;
11
+ //# sourceMappingURL=entity.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"entity.d.ts","sourceRoot":"","sources":["../src/entity.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;KAkBK;AAEL,OAAO,EAAE,KAAK,EAAe,MAAM,iBAAiB,CAAC;AAGrD,MAAM,MAAM,QAAQ,GAAG,KAAK,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;AAElD,eAAO,MAAM,UAAU,KAAK,CAAC;AAC7B,eAAO,MAAM,UAAU,QAAwB,CAAC;AAChD,eAAO,MAAM,SAAS,QAAa,CAAC;AACpC,eAAO,MAAM,eAAe,QAAkB,CAAC;AAC/C,eAAO,MAAM,cAAc,QAA6B,CAAC;AAEzD,eAAO,MAAM,gBAAgB,UACpB,MAAM,cACD,MAAM,KACjB,QAWF,CAAC;AAEF,eAAO,MAAM,gBAAgB,OAAQ,QAAQ,KAAG,MAAyB,CAAC;AAE1E,eAAO,MAAM,qBAAqB,OAAQ,QAAQ,KAAG,MACvB,CAAC"}
@@ -0,0 +1,24 @@
1
+ import { Brand } from './type_primitives';
2
+ import { ComponentFields, ColumnsForSchema } from './component';
3
+ export type EventID = Brand<number, "event_id">;
4
+ export declare const as_event_id: (value: number) => EventID;
5
+ declare const __event_schema: unique symbol;
6
+ export type EventDef<F extends ComponentFields = ComponentFields> = EventID & {
7
+ readonly [__event_schema]: F;
8
+ };
9
+ /** Reader view over an event channel's SoA columns. */
10
+ export type EventReader<F extends ComponentFields> = {
11
+ length: number;
12
+ } & ColumnsForSchema<F>;
13
+ export declare class EventChannel {
14
+ readonly field_names: string[];
15
+ readonly columns: number[][];
16
+ readonly reader: EventReader<any>;
17
+ constructor(field_names: string[]);
18
+ emit(values: Record<string, number>): void;
19
+ /** Emit a signal (zero-field event). */
20
+ emit_signal(): void;
21
+ clear(): void;
22
+ }
23
+ export {};
24
+ //# sourceMappingURL=event.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"event.d.ts","sourceRoot":"","sources":["../src/event.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;KAyBK;AAEL,OAAO,EACL,KAAK,EAGN,MAAM,iBAAiB,CAAC;AACzB,OAAO,KAAK,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAErE,MAAM,MAAM,OAAO,GAAG,KAAK,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;AAChD,eAAO,MAAM,WAAW,UAAW,MAAM,YAKtC,CAAC;AAGJ,OAAO,CAAC,MAAM,cAAc,EAAE,OAAO,MAAM,CAAC;AAE5C,MAAM,MAAM,QAAQ,CAAC,CAAC,SAAS,eAAe,GAAG,eAAe,IAC9D,OAAO,GAAG;IAAE,QAAQ,CAAC,CAAC,cAAc,CAAC,EAAE,CAAC,CAAA;CAAE,CAAC;AAE7C,uDAAuD;AACvD,MAAM,MAAM,WAAW,CAAC,CAAC,SAAS,eAAe,IAAI;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE,GAAG,gBAAgB,CAAC,CAAC,CAAC,CAAC;AAE9F,qBAAa,YAAY;IACvB,QAAQ,CAAC,WAAW,EAAE,MAAM,EAAE,CAAC;IAC/B,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,CAAC;IAC7B,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC,GAAG,CAAC,CAAC;gBAEtB,WAAW,EAAE,MAAM,EAAE;IAejC,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI;IAS1C,wCAAwC;IACxC,WAAW,IAAI,IAAI;IAInB,KAAK,IAAI,IAAI;CAOd"}
package/dist/index.cjs ADDED
@@ -0,0 +1 @@
1
+ "use strict";var $=Object.defineProperty;var z=(a,t,e)=>t in a?$(a,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):a[t]=e;var l=(a,t,e)=>z(a,typeof t!="symbol"?t+"":t,e);Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});class Y extends Error{constructor(t,e,s){super(t),this.is_operational=e,this.context=s,this.name=this.constructor.name,Error.captureStackTrace(this,this.constructor)}}var u=(a=>(a.EID_MAX_INDEX_OVERFLOW="EID_MAX_INDEX_OVERFLOW",a.EID_MAX_GEN_OVERFLOW="EID_MAX_GEN_OVERFLOW",a.COMPONENT_NOT_REGISTERED="COMPONENT_NOT_REGISTERED",a.ENTITY_NOT_ALIVE="ENTITY_NOT_ALIVE",a.CIRCULAR_SYSTEM_DEPENDENCY="CIRCULAR_SYSTEM_DEPENDENCY",a.DUPLICATE_SYSTEM="DUPLICATE_SYSTEM",a.ARCHETYPE_NOT_FOUND="ARCHETYPE_NOT_FOUND",a))(u||{});class g extends Y{constructor(t,e,s){super(e??t,!0,s),this.category=t}}var C=(a=>(a.ASSERTION_FAIL_CONDITION="ASSERTION_FAIL_CONDITION",a.VALIDATION_FAIL_CONDITION="VALIDATION_FAIL_CONDITION",a))(C||{});class B extends Y{constructor(t,e,s){super(e,!1,s),this.category=t}}const P=a=>Number.isInteger(a)&&a>=0;function b(a,t,e){if(process.env.NODE_ENV!=="production"&&!t(a))throw new B(C.VALIDATION_FAIL_CONDITION,`Expected value to meet validation: ${e}`);return a}const Q=4;class T{constructor(t){l(this,"_words");this._words=t??new Array(Q).fill(0)}has(t){const e=t>>>5;return e>=this._words.length?!1:(this._words[e]&1<<(t&31))!==0}set(t){const e=t>>>5;e>=this._words.length&&this.grow(e+1),this._words[e]|=1<<(t&31)}clear(t){const e=t>>>5;e>=this._words.length||(this._words[e]&=~(1<<(t&31)))}overlaps(t){const e=this._words,s=t._words,n=e.length<s.length?e.length:s.length;for(let r=0;r<n;r++)if((e[r]&s[r])!==0)return!0;return!1}contains(t){const e=t._words,s=this._words,n=s.length;for(let r=0;r<e.length;r++){const o=e[r];if(o!==0&&(r>=n||(s[r]&o)!==o))return!1}return!0}equals(t){const e=this._words,s=t._words,n=e.length>s.length?e.length:s.length;for(let r=0;r<n;r++){const o=r<e.length?e[r]:0,_=r<s.length?s[r]:0;if(o!==_)return!1}return!0}copy(){return new T(this._words.slice())}copy_with_set(t){const e=t>>>5,s=e+1,n=this._words.length>s?this._words.length:s,r=new Array(n).fill(0);for(let o=0;o<this._words.length;o++)r[o]=this._words[o];return r[e]|=1<<(t&31),new T(r)}copy_with_clear(t){const e=this._words.slice(),s=t>>>5;return s<e.length&&(e[s]&=~(1<<(t&31))),new T(e)}hash(){let t=2166136261;const e=this._words;let s=e.length-1;for(;s>=0&&e[s]===0;)s--;for(let n=0;n<=s;n++)t^=e[n],t=Math.imul(t,16777619);return t}for_each(t){const e=this._words;for(let s=0;s<e.length;s++){let n=e[s];if(n===0)continue;const r=s<<5;for(;n!==0;){const o=n&-n>>>0,_=31-Math.clz32(o);t(r+_),n^=o}}}grow(t){let e=this._words.length;for(;e<t;)e*=2;const s=new Array(e).fill(0);for(let n=0;n<this._words.length;n++)s[n]=this._words[n];this._words=s}}const D=20,S=(1<<D)-1,K=S,J=31-D,k=(1<<J)-1,Z=(a,t)=>{if(process.env.NODE_ENV!=="production"){if(a<0||a>K)throw new g(u.EID_MAX_INDEX_OVERFLOW);if(t<0||t>k)throw new g(u.EID_MAX_GEN_OVERFLOW)}return t<<D|a},m=a=>a&S,R=a=>a>>D,H=a=>b(a,P,"ComponentID must be a non-negative integer"),tt=a=>b(a,P,"EventID must be a non-negative integer");class et{constructor(t){l(this,"field_names");l(this,"columns");l(this,"reader");this.field_names=t,this.columns=[];for(let s=0;s<t.length;s++)this.columns.push([]);const e={length:0};for(let s=0;s<t.length;s++)e[t[s]]=this.columns[s];this.reader=e}emit(t){const e=this.field_names,s=this.columns;for(let n=0;n<e.length;n++)s[n].push(t[e[n]]);this.reader.length++}emit_signal(){this.reader.length++}clear(){this.reader.length=0;const t=this.columns;for(let e=0;e<t.length;e++)t[e].length=0}}const st=a=>b(a,P,"ArchetypeID must be a non-negative integer");class nt{constructor(t,e,s){l(this,"id");l(this,"mask");l(this,"has_columns");l(this,"entity_ids",[]);l(this,"length",0);l(this,"edges",[]);l(this,"column_groups",[]);l(this,"_column_ids",[]);if(this.id=t,this.mask=e,s)for(let n=0;n<s.length;n++){const r=s[n],o=new Array(r.field_names.length);for(let h=0;h<r.field_names.length;h++)o[h]=[];const _=Object.create(null);for(let h=0;h<r.field_names.length;h++)_[r.field_names[h]]=o[h];this.column_groups[r.component_id]={layout:r,columns:o,record:_},this._column_ids.push(r.component_id)}this.has_columns=this._column_ids.length>0}get entity_count(){return this.length}get entity_list(){return this.entity_ids}has_component(t){return this.mask.has(t)}matches(t){return this.mask.contains(t)}get_column(t,e){const s=this.column_groups[t];if(process.env.NODE_ENV!=="production"&&!s)throw new g(u.COMPONENT_NOT_REGISTERED,`Component ${t} not in archetype ${this.id}`);const n=s.layout.field_index[e];if(process.env.NODE_ENV!=="production"&&n===void 0)throw new g(u.COMPONENT_NOT_REGISTERED,`Field "${e}" does not exist on component`);return s.columns[n]}get_column_group(t){const e=this.column_groups[t];return e?e.record:{}}write_fields(t,e,s){const n=this.column_groups[e];if(!n)return;const{field_names:r}=n.layout;for(let o=0;o<r.length;o++)n.columns[o][t]=s[r[o]]}read_field(t,e,s){const n=this.column_groups[e];if(!n)return NaN;const r=n.layout.field_index[s];return r===void 0?NaN:n.columns[r][t]??NaN}copy_shared_from(t,e,s){const n=t.column_groups,r=this._column_ids;for(let o=0;o<r.length;o++){const _=r[o],h=n[_];if(!h)continue;const i=this.column_groups[_];for(let c=0;c<i.columns.length;c++)i.columns[c][s]=h.columns[c][e]}}add_entity(t){const e=this.length;this.entity_ids.push(t);const s=this._column_ids;for(let n=0;n<s.length;n++){const r=this.column_groups[s[n]];for(let o=0;o<r.columns.length;o++)r.columns[o].push(0)}return this.length++,e}remove_entity(t){const e=this.length-1;let s=-1;const n=this._column_ids;if(t!==e){this.entity_ids[t]=this.entity_ids[e],s=m(this.entity_ids[t]);for(let r=0;r<n.length;r++){const o=this.column_groups[n[r]];for(let _=0;_<o.columns.length;_++)o.columns[_][t]=o.columns[_][e],o.columns[_].pop()}}else for(let r=0;r<n.length;r++){const o=this.column_groups[n[r]];for(let _=0;_<o.columns.length;_++)o.columns[_].pop()}return this.entity_ids.pop(),this.length--,s}add_entity_tag(t){const e=this.length;return this.entity_ids.push(t),this.length++,e}remove_entity_tag(t){const e=this.length-1;let s=-1;return t!==e&&(this.entity_ids[t]=this.entity_ids[e],s=m(this.entity_ids[t])),this.entity_ids.pop(),this.length--,s}get_edge(t){return this.edges[t]}set_edge(t,e){this.edges[t]=e}}function j(a,t,e){const s=a.get(t);s!==void 0?s.push(e):a.set(t,[e])}const w=-1,M=Object.freeze(Object.create(null));class rt{constructor(){l(this,"entity_generations",[]);l(this,"entity_high_water",0);l(this,"entity_free_indices",[]);l(this,"entity_alive_count",0);l(this,"component_metas",[]);l(this,"component_count",0);l(this,"event_channels",[]);l(this,"event_count",0);l(this,"archetypes",[]);l(this,"archetype_map",new Map);l(this,"next_archetype_id",0);l(this,"component_index",new Map);l(this,"registered_queries",[]);l(this,"empty_archetype_id");l(this,"entity_archetype",[]);l(this,"entity_row",[]);l(this,"pending_destroy",[]);l(this,"pending_add_ids",[]);l(this,"pending_add_defs",[]);l(this,"pending_add_values",[]);l(this,"pending_remove_ids",[]);l(this,"pending_remove_defs",[]);this.empty_archetype_id=this.arch_get_or_create_from_mask(new T)}arch_get(t){if(process.env.NODE_ENV!=="production"&&(t<0||t>=this.archetypes.length))throw new g(u.ARCHETYPE_NOT_FOUND,`Archetype with ID ${t} not found`);return this.archetypes[t]}arch_get_or_create_from_mask(t){const e=t.hash(),s=this.archetype_map.get(e);if(s!==void 0){for(let h=0;h<s.length;h++)if(this.archetypes[s[h]].mask.equals(t))return s[h]}const n=st(this.next_archetype_id++),r=[];t.for_each(h=>{const i=h,c=this.component_metas[i];c&&c.field_names.length>0&&r.push({component_id:i,field_names:c.field_names,field_index:c.field_index})});const o=new nt(n,t,r);this.archetypes.push(o),j(this.archetype_map,e,n),t.for_each(h=>{const i=h;let c=this.component_index.get(i);c||(c=new Set,this.component_index.set(i,c)),c.add(n)});const _=this.registered_queries;for(let h=0;h<_.length;h++){const i=_[h];o.matches(i.include_mask)&&(!i.exclude_mask||!o.mask.overlaps(i.exclude_mask))&&(!i.any_of_mask||o.mask.overlaps(i.any_of_mask))&&i.result.push(o)}return n}arch_resolve_add(t,e){const s=this.arch_get(t);if(s.mask.has(e))return t;const n=s.get_edge(e);if((n==null?void 0:n.add)!=null)return n.add;const r=this.arch_get_or_create_from_mask(s.mask.copy_with_set(e));return this.arch_cache_edge(s,this.arch_get(r),e),r}arch_resolve_remove(t,e){const s=this.arch_get(t);if(!s.mask.has(e))return t;const n=s.get_edge(e);if((n==null?void 0:n.remove)!=null)return n.remove;const r=this.arch_get_or_create_from_mask(s.mask.copy_with_clear(e));return this.arch_cache_edge(this.arch_get(r),s,e),r}arch_cache_edge(t,e,s){const n=t.get_edge(s)??{add:null,remove:null};n.add=e.id,t.set_edge(s,n);const r=e.get_edge(s)??{add:null,remove:null};r.remove=t.id,e.set_edge(s,r)}create_entity(){let t,e;this.entity_free_indices.length>0?(t=this.entity_free_indices.pop(),e=this.entity_generations[t]):(t=this.entity_high_water++,this.entity_generations[t]=0,e=0),this.entity_alive_count++;const s=Z(t,e);return this.entity_archetype[t]=this.empty_archetype_id,this.entity_row[t]=w,s}destroy_entity(t){if(!this.is_alive(t)){if(process.env.NODE_ENV!=="production")throw new g(u.ENTITY_NOT_ALIVE);return}const e=m(t),s=this.entity_row[e];if(s!==w){const o=this.arch_get(this.entity_archetype[e]).remove_entity(s);o!==-1&&(this.entity_row[o]=s)}this.entity_archetype[e]=w,this.entity_row[e]=w;const n=R(t);if(process.env.NODE_ENV!=="production"&&n>=k)throw new g(u.EID_MAX_GEN_OVERFLOW);this.entity_generations[e]=n+1&k,this.entity_free_indices.push(e),this.entity_alive_count--}is_alive(t){const e=m(t);return e<this.entity_high_water&&this.entity_generations[e]===R(t)}get entity_count(){return this.entity_alive_count}destroy_entity_deferred(t){if(process.env.NODE_ENV!=="production"&&!this.is_alive(t))throw new g(u.ENTITY_NOT_ALIVE);this.pending_destroy.push(t)}flush_destroyed(){const t=this.pending_destroy;if(t.length===0)return;const e=this.entity_archetype,s=this.entity_row,n=this.entity_generations,r=this.archetypes,o=this.entity_high_water;for(let _=0;_<t.length;_++){const h=t[_],i=h&S,c=h>>D;if(i>=o||n[i]!==c)continue;const d=s[i];if(d!==w){const f=r[e[i]],p=f.has_columns?f.remove_entity(d):f.remove_entity_tag(d);p!==-1&&(s[p]=d)}if(e[i]=w,s[i]=w,process.env.NODE_ENV!=="production"&&c>=k)throw new g(u.EID_MAX_GEN_OVERFLOW);n[i]=c+1&k,this.entity_free_indices.push(i),this.entity_alive_count--}t.length=0}get pending_destroy_count(){return this.pending_destroy.length}add_component_deferred(t,e,s){if(process.env.NODE_ENV!=="production"&&!this.is_alive(t))throw new g(u.ENTITY_NOT_ALIVE);this.pending_add_ids.push(t),this.pending_add_defs.push(e),this.pending_add_values.push(s??M)}remove_component_deferred(t,e){if(process.env.NODE_ENV!=="production"&&!this.is_alive(t))throw new g(u.ENTITY_NOT_ALIVE);this.pending_remove_ids.push(t),this.pending_remove_defs.push(e)}flush_structural(){this.pending_add_ids.length>0&&this._flush_adds(),this.pending_remove_ids.length>0&&this._flush_removes()}_flush_adds(){const t=this.pending_add_ids,e=this.pending_add_defs,s=this.pending_add_values,n=t.length,r=this.entity_archetype,o=this.entity_row,_=this.entity_generations,h=this.archetypes,i=this.component_metas,c=this.entity_high_water;for(let d=0;d<n;d++){const f=t[d],p=f&S,v=f>>D;if(p>=c||_[p]!==v)continue;const O=r[p],E=e[d],y=h[O];if(y.mask.has(E)){i[E].field_names.length>0&&y.write_fields(o[p],E,s[d]);continue}const A=this.arch_resolve_add(O,E),N=h[A],I=o[p],x=!N.has_columns&&!y.has_columns,V=x?N.add_entity_tag(f):N.add_entity(f);if(I!==w){x||N.copy_shared_from(y,I,V);const q=x?y.remove_entity_tag(I):y.remove_entity(I);q!==-1&&(o[q]=I)}i[E].field_names.length>0&&N.write_fields(V,E,s[d]),r[p]=A,o[p]=V}t.length=0,e.length=0,s.length=0}_flush_removes(){const t=this.pending_remove_ids,e=this.pending_remove_defs,s=t.length,n=this.entity_archetype,r=this.entity_row,o=this.entity_generations,_=this.archetypes,h=this.entity_high_water;for(let i=0;i<s;i++){const c=t[i],d=c&S,f=c>>D;if(d>=h||o[d]!==f)continue;const p=n[d],v=e[i],O=_[p];if(!O.mask.has(v))continue;const E=this.arch_resolve_remove(p,v),y=_[E],A=r[d],N=!y.has_columns&&!O.has_columns,I=N?y.add_entity_tag(c):y.add_entity(c);N||y.copy_shared_from(O,A,I);const x=N?O.remove_entity_tag(A):O.remove_entity(A);x!==-1&&(r[x]=A),n[d]=E,r[d]=I}t.length=0,e.length=0}get pending_structural_count(){return this.pending_add_ids.length+this.pending_remove_ids.length}register_component(t){const e=H(this.component_count++),s=t,n=Object.create(null);for(let r=0;r<s.length;r++)n[s[r]]=r;return this.component_metas.push({field_names:s,field_index:n}),e}add_component(t,e,s){if(!this.is_alive(t)){if(process.env.NODE_ENV!=="production")throw new g(u.ENTITY_NOT_ALIVE);return}const n=m(t),r=this.entity_archetype[n],o=this.arch_get(r);if(o.has_component(e)){o.write_fields(this.entity_row[n],e,s);return}const _=this.arch_resolve_add(r,e),h=this.arch_get(_),i=this.entity_row[n],c=h.add_entity(t);if(i!==w){h.copy_shared_from(o,i,c);const d=o.remove_entity(i);d!==-1&&(this.entity_row[d]=i)}h.write_fields(c,e,s),this.entity_archetype[n]=_,this.entity_row[n]=c}add_components(t,e){if(!this.is_alive(t)){if(process.env.NODE_ENV!=="production")throw new g(u.ENTITY_NOT_ALIVE);return}const s=m(t),n=this.entity_archetype[s];let r=n;for(let o=0;o<e.length;o++)r=this.arch_resolve_add(r,e[o].def);if(r!==n){const o=this.arch_get(n),_=this.arch_get(r),h=this.entity_row[s],i=_.add_entity(t);if(h!==w){_.copy_shared_from(o,h,i);const c=o.remove_entity(h);c!==-1&&(this.entity_row[c]=h)}for(let c=0;c<e.length;c++)_.write_fields(i,e[c].def,e[c].values??M);this.entity_archetype[s]=r,this.entity_row[s]=i}else{const o=this.arch_get(n),_=this.entity_row[s];for(let h=0;h<e.length;h++)o.write_fields(_,e[h].def,e[h].values??M)}}remove_component(t,e){if(!this.is_alive(t)){if(process.env.NODE_ENV!=="production")throw new g(u.ENTITY_NOT_ALIVE);return}const s=m(t),n=this.entity_archetype[s],r=this.arch_get(n);if(!r.has_component(e))return;const o=this.arch_resolve_remove(n,e),_=this.arch_get(o),h=this.entity_row[s],i=_.add_entity(t);_.copy_shared_from(r,h,i);const c=r.remove_entity(h);c!==-1&&(this.entity_row[c]=h),this.entity_archetype[s]=o,this.entity_row[s]=i}remove_components(t,e){if(!this.is_alive(t)){if(process.env.NODE_ENV!=="production")throw new g(u.ENTITY_NOT_ALIVE);return}const s=m(t),n=this.entity_archetype[s];let r=n;for(let d=0;d<e.length;d++)r=this.arch_resolve_remove(r,e[d]);if(r===n)return;const o=this.arch_get(n),_=this.arch_get(r),h=this.entity_row[s],i=_.add_entity(t);_.copy_shared_from(o,h,i);const c=o.remove_entity(h);c!==-1&&(this.entity_row[c]=h),this.entity_archetype[s]=r,this.entity_row[s]=i}has_component(t,e){if(!this.is_alive(t)){if(process.env.NODE_ENV!=="production")throw new g(u.ENTITY_NOT_ALIVE);return!1}const s=m(t);return this.arch_get(this.entity_archetype[s]).has_component(e)}get_entity_archetype(t){return this.arch_get(this.entity_archetype[m(t)])}get_entity_row(t){return this.entity_row[m(t)]}get_matching_archetypes(t,e,s){const n=t._words;let r=!1;for(let i=0;i<n.length;i++)if(n[i]!==0){r=!0;break}if(!r)return this.archetypes.filter(i=>(!e||!i.mask.overlaps(e))&&(!s||i.mask.overlaps(s)));let o,_=!1;for(let i=0;i<n.length;i++){let c=n[i];if(c===0)continue;const d=i<<5;for(;c!==0;){const f=c&-c>>>0,p=d+(31-Math.clz32(f));c^=f;const v=this.component_index.get(p);if(!v||v.size===0){_=!0;break}(!o||v.size<o.size)&&(o=v)}if(_)break}if(_||!o)return[];const h=[];for(const i of o){const c=this.arch_get(i);c.matches(t)&&(!e||!c.mask.overlaps(e))&&(!s||c.mask.overlaps(s))&&h.push(c)}return h}register_query(t,e,s){const n=this.get_matching_archetypes(t,e,s);return this.registered_queries.push({include_mask:t.copy(),exclude_mask:e?e.copy():null,any_of_mask:s?s.copy():null,result:n}),n}get archetype_count(){return this.archetypes.length}register_event(t){const e=tt(this.event_count++),s=new et(t);return this.event_channels.push(s),e}emit_event(t,e){this.event_channels[t].emit(e)}emit_signal(t){this.event_channels[t].emit_signal()}get_event_reader(t){return this.event_channels[t].reader}clear_events(){const t=this.event_channels;for(let e=0;e<t.length;e++)t[e].clear()}}var F=(a=>(a.PRE_STARTUP="PRE_STARTUP",a.STARTUP="STARTUP",a.POST_STARTUP="POST_STARTUP",a.PRE_UPDATE="PRE_UPDATE",a.UPDATE="UPDATE",a.POST_UPDATE="POST_UPDATE",a))(F||{});const L=["PRE_STARTUP","STARTUP","POST_STARTUP"],U=["PRE_UPDATE","UPDATE","POST_UPDATE"];class ot{constructor(){l(this,"label_systems",new Map);l(this,"sorted_cache",new Map);l(this,"system_index",new Map);l(this,"next_insertion_order",0);for(let t=0;t<L.length;t++)this.label_systems.set(L[t],[]);for(let t=0;t<U.length;t++)this.label_systems.set(U[t],[])}add_systems(t,...e){for(const s of e){const n="system"in s?s.system:s,r="system"in s?s.ordering:void 0;if(process.env.NODE_ENV!=="production"&&this.system_index.has(n))throw new g(u.DUPLICATE_SYSTEM,`System ${n.id} is already scheduled`);const o={descriptor:n,insertion_order:this.next_insertion_order++,before:new Set((r==null?void 0:r.before)??[]),after:new Set((r==null?void 0:r.after)??[])};this.label_systems.get(t).push(o),this.system_index.set(n,t),this.sorted_cache.delete(t)}}remove_system(t){const e=this.system_index.get(t);if(e===void 0)return;const s=this.label_systems.get(e),n=s.findIndex(r=>r.descriptor===t);if(n!==-1){const r=s.length-1;n!==r&&(s[n]=s[r]),s.pop();for(const o of s)o.before.delete(t),o.after.delete(t)}this.system_index.delete(t),this.sorted_cache.delete(e)}run_startup(t){for(const e of L)this.run_label(e,t,0)}run_update(t,e){for(const s of U)this.run_label(s,t,e)}get_all_systems(){const t=[];for(const e of this.label_systems.values())for(const s of e)t.push(s.descriptor);return t}has_system(t){return this.system_index.has(t)}clear(){for(const t of this.label_systems.values())t.length=0;this.sorted_cache.clear(),this.system_index.clear()}run_label(t,e,s){const n=this.get_sorted(t);for(let r=0;r<n.length;r++)n[r].fn(e,s);e.flush()}get_sorted(t){const e=this.sorted_cache.get(t);if(e!==void 0)return e;const s=this.label_systems.get(t),n=this.topological_sort(s,t);return this.sorted_cache.set(t,n),n}topological_sort(t,e){if(t.length===0)return[];const s=new Map,n=new Map,r=new Map,o=new Set;for(const i of t)s.set(i.descriptor,new Set),n.set(i.descriptor,0),r.set(i.descriptor,i.insertion_order),o.add(i.descriptor);for(const i of t){for(const c of i.before)o.has(c)&&(s.get(i.descriptor).add(c),n.set(c,n.get(c)+1));for(const c of i.after)o.has(c)&&(s.get(c).add(i.descriptor),n.set(i.descriptor,n.get(i.descriptor)+1))}let _=[];for(const i of t)n.get(i.descriptor)===0&&_.push(i.descriptor);_.sort((i,c)=>r.get(c)-r.get(i));const h=[];for(;_.length>0;){const i=_.pop();h.push(i);for(const c of s.get(i)){const d=n.get(c)-1;n.set(c,d),d===0&&_.push(c)}_.sort((c,d)=>r.get(d)-r.get(c))}if(h.length!==t.length){const i=new Set(h),c=t.filter(d=>!i.has(d.descriptor)).map(d=>`system_${d.descriptor.id}`);throw new g(u.CIRCULAR_SYSTEM_DEPENDENCY,`Circular system dependency detected in ${e}: [${c.join(", ")}]`)}return h}}const it=Object.freeze(Object.create(null));class X{constructor(t,e,s,n,r,o){l(this,"_archetypes");l(this,"_defs");l(this,"_resolver");l(this,"_include");l(this,"_exclude");l(this,"_any_of");l(this,"_args_buf");this._archetypes=t,this._defs=e,this._resolver=s,this._include=n,this._exclude=r,this._any_of=o,this._args_buf=new Array(e.length+1)}get length(){return this._archetypes.length}count(){const t=this._archetypes;let e=0;for(let s=0;s<t.length;s++)e+=t[s].entity_count;return e}get archetypes(){return this._archetypes}*[Symbol.iterator](){const t=this._archetypes;for(let e=0;e<t.length;e++)t[e].entity_count>0&&(yield t[e])}each(t){const e=this._archetypes,s=this._defs,n=this._args_buf;for(let r=0;r<e.length;r++){const o=e[r],_=o.entity_count;if(_!==0){for(let h=0;h<s.length;h++)n[h]=o.get_column_group(s[h]);n[s.length]=_,t.apply(null,n)}}}and(...t){const e=this._include.copy(),s=this._defs.slice();for(let n=0;n<t.length;n++)e.has(t[n])||(e.set(t[n]),s.push(t[n]));return this._resolver._resolve_query(e,this._exclude,this._any_of,s)}not(...t){const e=this._exclude?this._exclude.copy():new T;for(let s=0;s<t.length;s++)e.set(t[s]);return this._resolver._resolve_query(this._include,e,this._any_of,this._defs)}or(...t){const e=this._any_of?this._any_of.copy():new T;for(let s=0;s<t.length;s++)e.set(t[s]);return this._resolver._resolve_query(this._include,this._exclude,e,this._defs)}}class G{constructor(t){this._resolver=t}every(...t){const e=new T;for(let s=0;s<t.length;s++)e.set(t[s]);return this._resolver._resolve_query(e,null,null,t)}}class W{constructor(t){l(this,"store");this.store=t}create_entity(){return this.store.create_entity()}get_field(t,e,s){const n=this.store.get_entity_archetype(e),r=this.store.get_entity_row(e);return n.read_field(r,t,s)}set_field(t,e,s,n){const r=this.store.get_entity_archetype(e),o=this.store.get_entity_row(e),_=r.get_column(t,s);_[o]=n}destroy_entity(t){return this.store.destroy_entity_deferred(t),this}flush_destroyed(){this.store.flush_destroyed()}add_component(t,e,s){return this.store.add_component_deferred(t,e,s??it),this}remove_component(t,e){return this.store.remove_component_deferred(t,e),this}flush(){this.store.flush_structural(),this.store.flush_destroyed()}emit(t,e){e===void 0?this.store.emit_signal(t):this.store.emit_event(t,e)}read(t){return this.store.get_event_reader(t)}}const _t=a=>b(a,P,"SystemID must be a non-negative integer"),ct=Object.freeze(Object.create(null));class ht{constructor(){l(this,"store");l(this,"schedule");l(this,"ctx");l(this,"systems",new Set);l(this,"next_system_id",0);l(this,"query_cache",new Map);l(this,"scratch_mask",new T);this.store=new rt,this.schedule=new ot,this.ctx=new W(this.store)}register_component(t){return this.store.register_component(t)}register_tag(){return this.store.register_component([])}register_event(t){return this.store.register_event(t)}register_signal(){return this.store.register_event([])}create_entity(){return this.store.create_entity()}destroy_entity(t){this.store.destroy_entity_deferred(t)}is_alive(t){return this.store.is_alive(t)}get entity_count(){return this.store.entity_count}add_component(t,e,s){this.store.add_component(t,e,s??ct)}add_components(t,e){this.store.add_components(t,e)}remove_component(t,e){return this.store.remove_component(t,e),this}remove_components(t,...e){this.store.remove_components(t,e)}has_component(t,e){return this.store.has_component(t,e)}get_field(t,e,s){const n=this.store.get_entity_archetype(e),r=this.store.get_entity_row(e);return n.read_field(r,t,s)}emit(t,e){e===void 0?this.store.emit_signal(t):this.store.emit_event(t,e)}query(...t){const e=this.scratch_mask;e._words.fill(0);for(let s=0;s<t.length;s++)e.set(t[s]);return this._resolve_query(e.copy(),null,null,t)}_resolve_query(t,e,s,n){const r=t.hash(),o=e?e.hash():0,_=s?s.hash():0,h=r^Math.imul(o,2654435769)^Math.imul(_,1367130551)|0,i=this._find_cached(h,t,e,s);if(i!==void 0)return i.query;const c=this.store.register_query(t,e??void 0,s??void 0),d=new X(c,n,this,t.copy(),(e==null?void 0:e.copy())??null,(s==null?void 0:s.copy())??null);return j(this.query_cache,h,{include_mask:t.copy(),exclude_mask:(e==null?void 0:e.copy())??null,any_of_mask:(s==null?void 0:s.copy())??null,query:d}),d}_find_cached(t,e,s,n){const r=this.query_cache.get(t);if(r)for(let o=0;o<r.length;o++){const _=r[o];if(!(!_.include_mask.equals(e)||!(s===null?_.exclude_mask===null:_.exclude_mask!==null&&_.exclude_mask.equals(s))||!(n===null?_.any_of_mask===null:_.any_of_mask!==null&&_.any_of_mask.equals(n))))return _}}register_system(t,e){let s;if(typeof t=="function"){const o=e(new G(this)),_=this.ctx;s={fn:(h,i)=>t(o,_,i)}}else s=t;const n=_t(this.next_system_id++),r=Object.freeze(Object.assign({id:n},s));return this.systems.add(r),r}add_systems(t,...e){return this.schedule.add_systems(t,...e),this}remove_system(t){var e;this.schedule.remove_system(t),(e=t.on_removed)==null||e.call(t),this.systems.delete(t)}get system_count(){return this.systems.size}startup(){var t;for(const e of this.systems.values())(t=e.on_added)==null||t.call(e,this.store);this.schedule.run_startup(this.ctx)}update(t){this.store.clear_events(),this.schedule.run_update(this.ctx,t)}flush(){this.ctx.flush()}dispose(){var t,e;for(const s of this.systems.values())(t=s.dispose)==null||t.call(s),(e=s.on_removed)==null||e.call(s);this.systems.clear(),this.schedule.clear()}}exports.Query=X;exports.QueryBuilder=G;exports.SCHEDULE=F;exports.SystemContext=W;exports.World=ht;
@@ -0,0 +1,9 @@
1
+ export { World } from './world';
2
+ export { Query, QueryBuilder, SystemContext } from './query';
3
+ export { SCHEDULE } from './schedule';
4
+ export type { EntityID } from './entity';
5
+ export type { ComponentDef, ComponentFields, FieldValues, ColumnsForSchema, } from './component';
6
+ export type { EventDef, EventReader } from './event';
7
+ export type { SystemFn, SystemConfig, SystemDescriptor, } from './system';
8
+ export type { SystemEntry, SystemOrdering } from './schedule';
9
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAChC,OAAO,EAAE,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAC7D,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAGtC,YAAY,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AACzC,YAAY,EACV,YAAY,EACZ,eAAe,EACf,WAAW,EACX,gBAAgB,GACjB,MAAM,aAAa,CAAC;AACrB,YAAY,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AACrD,YAAY,EACV,QAAQ,EACR,YAAY,EACZ,gBAAgB,GACjB,MAAM,UAAU,CAAC;AAClB,YAAY,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC"}