@codehz/ecs 0.3.15 → 0.4.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.
- package/README.md +65 -44
- package/index.d.mts +2 -793
- package/index.mjs +2 -2164
- package/package.json +6 -2
- package/testing.d.mts +305 -0
- package/testing.mjs +429 -0
- package/testing.mjs.map +1 -0
- package/world.d.mts +766 -0
- package/world.mjs +2081 -0
- package/world.mjs.map +1 -0
- package/index.mjs.map +0 -1
package/package.json
CHANGED
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@codehz/ecs",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./index.mjs",
|
|
6
6
|
"types": "./index.d.mts",
|
|
7
7
|
"exports": {
|
|
8
|
-
"
|
|
8
|
+
"./index": {
|
|
9
9
|
"types": "./index.d.mts",
|
|
10
10
|
"import": "./index.mjs"
|
|
11
|
+
},
|
|
12
|
+
"./testing": {
|
|
13
|
+
"types": "./testing.d.mts",
|
|
14
|
+
"import": "./testing.mjs"
|
|
11
15
|
}
|
|
12
16
|
},
|
|
13
17
|
"peerDependencies": {
|
package/testing.d.mts
ADDED
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
import { S as relation, f as RelationId, m as component, n as World, o as LifecycleHook, p as WildcardRelationId, r as Query, s as ComponentId, u as EntityId } from "./world.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/testing.d.ts
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* A component definition for entity building, supporting both regular components and relations
|
|
7
|
+
*/
|
|
8
|
+
type ComponentDef<T = unknown> = {
|
|
9
|
+
type: "component";
|
|
10
|
+
id: EntityId<T>;
|
|
11
|
+
value: T;
|
|
12
|
+
} | {
|
|
13
|
+
type: "relation";
|
|
14
|
+
componentId: ComponentId<T>;
|
|
15
|
+
targetId: EntityId<any>;
|
|
16
|
+
value: T;
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* Snapshot of a single entity's component state
|
|
20
|
+
*/
|
|
21
|
+
interface EntitySnapshot {
|
|
22
|
+
entity: EntityId;
|
|
23
|
+
components: Map<EntityId<any>, unknown>;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Snapshot of multiple entities' component state
|
|
27
|
+
*/
|
|
28
|
+
interface WorldSnapshot {
|
|
29
|
+
entities: EntitySnapshot[];
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Result of comparing two snapshots
|
|
33
|
+
*/
|
|
34
|
+
interface SnapshotDiff {
|
|
35
|
+
/** Entities that exist in 'after' but not in 'before' */
|
|
36
|
+
addedEntities: EntityId[];
|
|
37
|
+
/** Entities that exist in 'before' but not in 'after' */
|
|
38
|
+
removedEntities: EntityId[];
|
|
39
|
+
/** Changes to components on existing entities */
|
|
40
|
+
componentChanges: Array<{
|
|
41
|
+
entity: EntityId;
|
|
42
|
+
componentId: EntityId<any>;
|
|
43
|
+
before: unknown | undefined;
|
|
44
|
+
after: unknown | undefined;
|
|
45
|
+
changeType: "added" | "removed" | "modified";
|
|
46
|
+
}>;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* A test fixture that manages a World instance and provides convenient
|
|
50
|
+
* methods for setting up test scenarios.
|
|
51
|
+
*
|
|
52
|
+
* @example
|
|
53
|
+
* ```typescript
|
|
54
|
+
* const fixture = new WorldFixture();
|
|
55
|
+
*
|
|
56
|
+
* // Spawn entities with fluent API
|
|
57
|
+
* const player = fixture
|
|
58
|
+
* .spawn()
|
|
59
|
+
* .with(PositionId, { x: 0, y: 0 })
|
|
60
|
+
* .with(HealthId, { current: 100, max: 100 })
|
|
61
|
+
* .build();
|
|
62
|
+
*
|
|
63
|
+
* // Access the world for running systems
|
|
64
|
+
* movementSystem(fixture.world);
|
|
65
|
+
*
|
|
66
|
+
* // Clean up (optional - creates a fresh world)
|
|
67
|
+
* fixture.reset();
|
|
68
|
+
* ```
|
|
69
|
+
*/
|
|
70
|
+
declare class WorldFixture {
|
|
71
|
+
private _world;
|
|
72
|
+
private _queries;
|
|
73
|
+
constructor();
|
|
74
|
+
/**
|
|
75
|
+
* Get the underlying World instance
|
|
76
|
+
*/
|
|
77
|
+
get world(): World;
|
|
78
|
+
/**
|
|
79
|
+
* Create a new EntityBuilder for spawning an entity with components
|
|
80
|
+
*/
|
|
81
|
+
spawn(): EntityBuilder;
|
|
82
|
+
/**
|
|
83
|
+
* Spawn multiple entities with the same component configuration
|
|
84
|
+
* @param count Number of entities to spawn
|
|
85
|
+
* @param configure Function to configure each entity builder
|
|
86
|
+
* @returns Array of created entity IDs
|
|
87
|
+
*/
|
|
88
|
+
spawnMany(count: number, configure: (builder: EntityBuilder, index: number) => EntityBuilder): EntityId[];
|
|
89
|
+
/**
|
|
90
|
+
* Create a query and track it for automatic cleanup
|
|
91
|
+
* @param componentTypes Component types to query for
|
|
92
|
+
* @returns Query instance
|
|
93
|
+
*/
|
|
94
|
+
createQuery(componentTypes: EntityId<any>[]): Query;
|
|
95
|
+
/**
|
|
96
|
+
* Execute pending commands (alias for world.sync())
|
|
97
|
+
*/
|
|
98
|
+
sync(): void;
|
|
99
|
+
/**
|
|
100
|
+
* Reset the fixture with a fresh World instance
|
|
101
|
+
* Disposes all tracked queries
|
|
102
|
+
*/
|
|
103
|
+
reset(): void;
|
|
104
|
+
/**
|
|
105
|
+
* Capture a snapshot of specified entities and their components
|
|
106
|
+
* @param entities Entities to capture
|
|
107
|
+
* @param componentIds Components to include in the snapshot
|
|
108
|
+
*/
|
|
109
|
+
captureSnapshot(entities: EntityId[], componentIds: EntityId<any>[]): WorldSnapshot;
|
|
110
|
+
/**
|
|
111
|
+
* Symbol.dispose implementation for automatic resource management
|
|
112
|
+
*/
|
|
113
|
+
[Symbol.dispose](): void;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Fluent builder for creating entities with components.
|
|
117
|
+
* Supports both regular components and entity relations.
|
|
118
|
+
*
|
|
119
|
+
* @example
|
|
120
|
+
* ```typescript
|
|
121
|
+
* // Basic usage
|
|
122
|
+
* const entity = new EntityBuilder(world)
|
|
123
|
+
* .with(PositionId, { x: 10, y: 20 })
|
|
124
|
+
* .with(VelocityId, { x: 1, y: 0 })
|
|
125
|
+
* .build();
|
|
126
|
+
*
|
|
127
|
+
* // With relations
|
|
128
|
+
* const child = new EntityBuilder(world)
|
|
129
|
+
* .with(PositionId, { x: 0, y: 0 })
|
|
130
|
+
* .withRelation(ParentId, parentEntity, { offset: { x: 5, y: 5 } })
|
|
131
|
+
* .build();
|
|
132
|
+
*
|
|
133
|
+
* // Tag component (void type)
|
|
134
|
+
* const tagged = new EntityBuilder(world)
|
|
135
|
+
* .withTag(PlayerTagId)
|
|
136
|
+
* .build();
|
|
137
|
+
* ```
|
|
138
|
+
*/
|
|
139
|
+
declare class EntityBuilder {
|
|
140
|
+
private world;
|
|
141
|
+
private components;
|
|
142
|
+
constructor(world: World);
|
|
143
|
+
/**
|
|
144
|
+
* Add a component to the entity being built
|
|
145
|
+
* @param componentId The component ID
|
|
146
|
+
* @param value The component value
|
|
147
|
+
*/
|
|
148
|
+
with<T>(componentId: EntityId<T>, value: T): this;
|
|
149
|
+
/**
|
|
150
|
+
* Add a tag component (void type) to the entity being built
|
|
151
|
+
* @param componentId The component ID (must be void type)
|
|
152
|
+
*/
|
|
153
|
+
withTag(componentId: EntityId<void>): this;
|
|
154
|
+
/**
|
|
155
|
+
* Add a relation component targeting another entity
|
|
156
|
+
* @param componentId The base component ID for the relation
|
|
157
|
+
* @param targetEntity The target entity
|
|
158
|
+
* @param value The component value
|
|
159
|
+
*/
|
|
160
|
+
withRelation<T>(componentId: ComponentId<T>, targetEntity: EntityId<any>, value: T): this;
|
|
161
|
+
/**
|
|
162
|
+
* Add a relation tag (void type) targeting another entity
|
|
163
|
+
* @param componentId The base component ID for the relation
|
|
164
|
+
* @param targetEntity The target entity
|
|
165
|
+
*/
|
|
166
|
+
withRelationTag(componentId: ComponentId<void>, targetEntity: EntityId<any>): this;
|
|
167
|
+
/**
|
|
168
|
+
* Build the entity and return its ID.
|
|
169
|
+
* This creates the entity, sets all components, and calls sync().
|
|
170
|
+
*/
|
|
171
|
+
build(): EntityId;
|
|
172
|
+
/**
|
|
173
|
+
* Build the entity without calling sync().
|
|
174
|
+
* Useful when batching multiple entity creations.
|
|
175
|
+
*/
|
|
176
|
+
buildDeferred(): EntityId;
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Test assertion utilities that return boolean values or throw descriptive errors.
|
|
180
|
+
* These work with any testing framework's expect() function.
|
|
181
|
+
*
|
|
182
|
+
* @example
|
|
183
|
+
* ```typescript
|
|
184
|
+
* // With bun:test or vitest
|
|
185
|
+
* expect(Assertions.hasComponent(world, entity, PositionId)).toBe(true);
|
|
186
|
+
* expect(Assertions.getComponent(world, entity, PositionId)).toEqual({ x: 10, y: 20 });
|
|
187
|
+
*
|
|
188
|
+
* // Direct assertion (throws on failure)
|
|
189
|
+
* Assertions.assertHasComponent(world, entity, PositionId);
|
|
190
|
+
* Assertions.assertComponentEquals(world, entity, PositionId, { x: 10, y: 20 });
|
|
191
|
+
* ```
|
|
192
|
+
*/
|
|
193
|
+
declare const Assertions: {
|
|
194
|
+
/**
|
|
195
|
+
* Check if an entity has a specific component
|
|
196
|
+
*/
|
|
197
|
+
hasComponent<T>(world: World, entity: EntityId, componentId: EntityId<T>): boolean;
|
|
198
|
+
/**
|
|
199
|
+
* Check if an entity does not have a specific component
|
|
200
|
+
*/
|
|
201
|
+
lacksComponent<T>(world: World, entity: EntityId, componentId: EntityId<T>): boolean;
|
|
202
|
+
/**
|
|
203
|
+
* Get a component value (returns undefined if entity doesn't exist or doesn't have the component)
|
|
204
|
+
*/
|
|
205
|
+
getComponent<T>(world: World, entity: EntityId, componentId: EntityId<T>): T | undefined;
|
|
206
|
+
/**
|
|
207
|
+
* Get all relation instances for a wildcard relation
|
|
208
|
+
*/
|
|
209
|
+
getRelations<T>(world: World, entity: EntityId, componentId: ComponentId<T>): [EntityId<unknown>, T][] | undefined;
|
|
210
|
+
/**
|
|
211
|
+
* Check if an entity has a relation to a specific target
|
|
212
|
+
*/
|
|
213
|
+
hasRelation<T>(world: World, entity: EntityId, componentId: ComponentId<T>, targetEntity: EntityId<any>): boolean;
|
|
214
|
+
/**
|
|
215
|
+
* Check if an entity exists in the world
|
|
216
|
+
*/
|
|
217
|
+
entityExists(world: World, entity: EntityId): boolean;
|
|
218
|
+
/**
|
|
219
|
+
* Check if a query contains specific entities
|
|
220
|
+
*/
|
|
221
|
+
queryContains(query: Query, ...entities: EntityId[]): boolean;
|
|
222
|
+
/**
|
|
223
|
+
* Check if a query contains exactly the specified entities (no more, no less)
|
|
224
|
+
*/
|
|
225
|
+
queryContainsExactly(query: Query, ...entities: EntityId[]): boolean;
|
|
226
|
+
/**
|
|
227
|
+
* Get the count of entities in a query
|
|
228
|
+
*/
|
|
229
|
+
queryCount(query: Query): number;
|
|
230
|
+
/**
|
|
231
|
+
* Assert that an entity has a component (throws if not)
|
|
232
|
+
*/
|
|
233
|
+
assertHasComponent<T>(world: World, entity: EntityId, componentId: EntityId<T>): void;
|
|
234
|
+
/**
|
|
235
|
+
* Assert that an entity lacks a component (throws if it has the component)
|
|
236
|
+
*/
|
|
237
|
+
assertLacksComponent<T>(world: World, entity: EntityId, componentId: EntityId<T>): void;
|
|
238
|
+
/**
|
|
239
|
+
* Assert that a component equals an expected value (throws if not)
|
|
240
|
+
*/
|
|
241
|
+
assertComponentEquals<T>(world: World, entity: EntityId, componentId: EntityId<T>, expected: T): void;
|
|
242
|
+
/**
|
|
243
|
+
* Assert that an entity exists (throws if not)
|
|
244
|
+
*/
|
|
245
|
+
assertEntityExists(world: World, entity: EntityId): void;
|
|
246
|
+
/**
|
|
247
|
+
* Assert that an entity does not exist (throws if it exists)
|
|
248
|
+
*/
|
|
249
|
+
assertEntityNotExists(world: World, entity: EntityId): void;
|
|
250
|
+
/**
|
|
251
|
+
* Assert that a query contains specific entities (throws if not)
|
|
252
|
+
*/
|
|
253
|
+
assertQueryContains(query: Query, ...entities: EntityId[]): void;
|
|
254
|
+
/**
|
|
255
|
+
* Assert that a query does not contain specific entities (throws if it does)
|
|
256
|
+
*/
|
|
257
|
+
assertQueryNotContains(query: Query, ...entities: EntityId[]): void;
|
|
258
|
+
};
|
|
259
|
+
/**
|
|
260
|
+
* Utilities for capturing and comparing world state snapshots.
|
|
261
|
+
* Useful for testing that systems produce expected state changes.
|
|
262
|
+
*
|
|
263
|
+
* @example
|
|
264
|
+
* ```typescript
|
|
265
|
+
* const before = Snapshot.capture(world, [entity], [PositionId, VelocityId]);
|
|
266
|
+
*
|
|
267
|
+
* // Run game logic
|
|
268
|
+
* movementSystem(world, deltaTime);
|
|
269
|
+
* world.sync();
|
|
270
|
+
*
|
|
271
|
+
* const after = Snapshot.capture(world, [entity], [PositionId, VelocityId]);
|
|
272
|
+
* const diff = Snapshot.compare(before, after);
|
|
273
|
+
*
|
|
274
|
+
* expect(diff.componentChanges).toHaveLength(1);
|
|
275
|
+
* expect(diff.componentChanges[0].changeType).toBe("modified");
|
|
276
|
+
* ```
|
|
277
|
+
*/
|
|
278
|
+
declare const Snapshot: {
|
|
279
|
+
/**
|
|
280
|
+
* Capture a snapshot of specified entities and their components
|
|
281
|
+
* @param world The world to capture from
|
|
282
|
+
* @param entities Entities to include in the snapshot
|
|
283
|
+
* @param componentIds Components to capture for each entity
|
|
284
|
+
*/
|
|
285
|
+
capture(world: World, entities: EntityId[], componentIds: EntityId<any>[]): WorldSnapshot;
|
|
286
|
+
/**
|
|
287
|
+
* Compare two snapshots and return the differences
|
|
288
|
+
* @param before The 'before' snapshot
|
|
289
|
+
* @param after The 'after' snapshot
|
|
290
|
+
*/
|
|
291
|
+
compare(before: WorldSnapshot, after: WorldSnapshot): SnapshotDiff;
|
|
292
|
+
/**
|
|
293
|
+
* Check if two snapshots are equal
|
|
294
|
+
*/
|
|
295
|
+
equals(a: WorldSnapshot, b: WorldSnapshot): boolean;
|
|
296
|
+
};
|
|
297
|
+
/**
|
|
298
|
+
* Custom assertion error for testing utilities
|
|
299
|
+
*/
|
|
300
|
+
declare class AssertionError extends Error {
|
|
301
|
+
constructor(message: string);
|
|
302
|
+
}
|
|
303
|
+
//#endregion
|
|
304
|
+
export { AssertionError, Assertions, ComponentDef, type ComponentId, EntityBuilder, type EntityId, EntitySnapshot, type LifecycleHook, type Query, type RelationId, Snapshot, SnapshotDiff, type WildcardRelationId, World, WorldFixture, WorldSnapshot, component, relation };
|
|
305
|
+
//# sourceMappingURL=testing.d.mts.map
|