@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/package.json CHANGED
@@ -1,13 +1,17 @@
1
1
  {
2
2
  "name": "@codehz/ecs",
3
- "version": "0.3.15",
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