@ue-too/ecs 0.11.0 → 0.13.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 +4 -4
- package/index.d.ts +571 -20
- package/index.js +2 -2
- package/index.js.map +3 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -291,7 +291,7 @@ const entityManager = new EntityManager(5000); // Custom max entities
|
|
|
291
291
|
|
|
292
292
|
## API Reference
|
|
293
293
|
|
|
294
|
-
For complete API documentation with detailed type information, see the [TypeDoc-generated documentation](
|
|
294
|
+
For complete API documentation with detailed type information, see the [TypeDoc-generated documentation](/ecs/).
|
|
295
295
|
|
|
296
296
|
## TypeScript Support
|
|
297
297
|
|
|
@@ -345,9 +345,9 @@ This ECS implementation follows these principles:
|
|
|
345
345
|
|
|
346
346
|
## Related Packages
|
|
347
347
|
|
|
348
|
-
- **[@ue-too/being](
|
|
349
|
-
- **[@ue-too/math](
|
|
350
|
-
- **[@ue-too/board](
|
|
348
|
+
- **[@ue-too/being](/being/)**: State machine library for entity AI and behavior
|
|
349
|
+
- **[@ue-too/math](/math/)**: Vector and transformation utilities for component data
|
|
350
|
+
- **[@ue-too/board](/board/)**: Canvas rendering system that can integrate with ECS
|
|
351
351
|
|
|
352
352
|
## Further Reading
|
|
353
353
|
|
package/index.d.ts
CHANGED
|
@@ -69,13 +69,16 @@
|
|
|
69
69
|
* entities: new Set()
|
|
70
70
|
* };
|
|
71
71
|
*
|
|
72
|
-
*
|
|
72
|
+
* const Movement = createSystemName('Movement');
|
|
73
|
+
* coordinator.registerSystem(Movement, movementSystem);
|
|
73
74
|
*
|
|
74
75
|
* // Set signature (entities with Position AND Velocity)
|
|
75
|
-
* const
|
|
76
|
-
* const
|
|
76
|
+
* const Position = createComponentName('Position');
|
|
77
|
+
* const Velocity = createComponentName('Velocity');
|
|
78
|
+
* const posType = coordinator.getComponentType(Position)!;
|
|
79
|
+
* const velType = coordinator.getComponentType(Velocity)!;
|
|
77
80
|
* const signature = (1 << posType) | (1 << velType);
|
|
78
|
-
* coordinator.setSystemSignature(
|
|
81
|
+
* coordinator.setSystemSignature(Movement, signature);
|
|
79
82
|
*
|
|
80
83
|
* // Update loop
|
|
81
84
|
* function update(deltaTime: number) {
|
|
@@ -115,6 +118,242 @@ export type ComponentType = number;
|
|
|
115
118
|
* @category Types
|
|
116
119
|
*/
|
|
117
120
|
export type Entity = number;
|
|
121
|
+
/**
|
|
122
|
+
* Component name identifier using Symbol for type safety and uniqueness.
|
|
123
|
+
* Use {@link createComponentName} to create component names, or {@link Symbol.for} for global symbols.
|
|
124
|
+
* @category Types
|
|
125
|
+
*/
|
|
126
|
+
export type ComponentName = symbol;
|
|
127
|
+
/**
|
|
128
|
+
* System name identifier using Symbol for type safety and uniqueness.
|
|
129
|
+
* Use {@link createSystemName} to create system names, or {@link Symbol.for} for global symbols.
|
|
130
|
+
* @category Types
|
|
131
|
+
*/
|
|
132
|
+
export type SystemName = symbol;
|
|
133
|
+
/**
|
|
134
|
+
* Supported field types for runtime-defined component schemas.
|
|
135
|
+
* @category Types
|
|
136
|
+
*/
|
|
137
|
+
export type ComponentFieldType = 'string' | 'number' | 'boolean' | 'object' | 'array' | 'entity';
|
|
138
|
+
/**
|
|
139
|
+
* Discriminated union for array element types.
|
|
140
|
+
* Supports both built-in types and custom component types.
|
|
141
|
+
* @category Types
|
|
142
|
+
*/
|
|
143
|
+
export type ArrayElementType = {
|
|
144
|
+
kind: 'builtin';
|
|
145
|
+
type: Exclude<ComponentFieldType, 'array'>;
|
|
146
|
+
} | {
|
|
147
|
+
kind: 'custom';
|
|
148
|
+
typeName: ComponentName;
|
|
149
|
+
};
|
|
150
|
+
/**
|
|
151
|
+
* Base properties shared by all field definitions.
|
|
152
|
+
* @category Types
|
|
153
|
+
*/
|
|
154
|
+
interface BaseComponentField {
|
|
155
|
+
/** The name of the field */
|
|
156
|
+
name: string;
|
|
157
|
+
/** Whether the field is optional (default: false) */
|
|
158
|
+
optional?: boolean;
|
|
159
|
+
/** Default value for the field (used when creating new instances) */
|
|
160
|
+
defaultValue?: unknown;
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Definition for a primitive (non-array) field in a component schema.
|
|
164
|
+
* @category Types
|
|
165
|
+
*/
|
|
166
|
+
export interface ComponentPrimitiveField extends BaseComponentField {
|
|
167
|
+
/** Discriminator for the union type */
|
|
168
|
+
type: Exclude<ComponentFieldType, 'array'>;
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Definition for an array field in a component schema.
|
|
172
|
+
* @category Types
|
|
173
|
+
*/
|
|
174
|
+
export interface ComponentArrayField extends BaseComponentField {
|
|
175
|
+
/** Discriminator for the union type */
|
|
176
|
+
type: 'array';
|
|
177
|
+
/**
|
|
178
|
+
* The element type for array fields (required).
|
|
179
|
+
* Specifies what type each element in the array should be.
|
|
180
|
+
* Can be a built-in type or a custom component type name.
|
|
181
|
+
*/
|
|
182
|
+
arrayElementType: ArrayElementType;
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Discriminated union for field definitions in a component schema.
|
|
186
|
+
* Use type guards to distinguish between primitive and array fields.
|
|
187
|
+
* @category Types
|
|
188
|
+
*/
|
|
189
|
+
export type ComponentFieldDefinition = ComponentPrimitiveField | ComponentArrayField;
|
|
190
|
+
/**
|
|
191
|
+
* Schema definition for a component type that can be defined at runtime.
|
|
192
|
+
* @category Types
|
|
193
|
+
*/
|
|
194
|
+
export interface ComponentSchema {
|
|
195
|
+
/** The name of the component type (using Symbol for type safety) */
|
|
196
|
+
componentName: ComponentName;
|
|
197
|
+
/** Array of field definitions */
|
|
198
|
+
fields: ComponentFieldDefinition[];
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Helper function to create a component name from a string.
|
|
202
|
+
* This creates a unique symbol for the component name.
|
|
203
|
+
*
|
|
204
|
+
* @param name - The string name for the component
|
|
205
|
+
* @returns A unique symbol for the component name
|
|
206
|
+
*
|
|
207
|
+
* @example
|
|
208
|
+
* ```typescript
|
|
209
|
+
* const Position = createComponentName('Position');
|
|
210
|
+
* coordinator.registerComponent<Position>(Position);
|
|
211
|
+
* ```
|
|
212
|
+
*
|
|
213
|
+
* @category Utilities
|
|
214
|
+
*/
|
|
215
|
+
export declare function createComponentName(name: string): ComponentName;
|
|
216
|
+
/**
|
|
217
|
+
* Helper function to get the string description from a component name symbol.
|
|
218
|
+
* Useful for debugging and serialization.
|
|
219
|
+
*
|
|
220
|
+
* @param componentName - The component name symbol
|
|
221
|
+
* @returns The string description of the symbol
|
|
222
|
+
*
|
|
223
|
+
* @category Utilities
|
|
224
|
+
*/
|
|
225
|
+
export declare function getComponentNameString(componentName: ComponentName): string;
|
|
226
|
+
/**
|
|
227
|
+
* Helper function to create a component name using Symbol.for().
|
|
228
|
+
* This creates a global symbol that can be looked up by string key,
|
|
229
|
+
* which is useful for serialization and cross-module access.
|
|
230
|
+
*
|
|
231
|
+
* @param key - The string key for the global symbol
|
|
232
|
+
* @returns A global symbol for the component name
|
|
233
|
+
*
|
|
234
|
+
* @example
|
|
235
|
+
* ```typescript
|
|
236
|
+
* const Position = createGlobalComponentName('Position');
|
|
237
|
+
* coordinator.registerComponent<Position>(Position);
|
|
238
|
+
* // Can be retrieved later with Symbol.for('Position')
|
|
239
|
+
* ```
|
|
240
|
+
*
|
|
241
|
+
* @category Utilities
|
|
242
|
+
*/
|
|
243
|
+
export declare function createGlobalComponentName(key: string): ComponentName;
|
|
244
|
+
/**
|
|
245
|
+
* Helper function to create a system name from a string.
|
|
246
|
+
* This creates a unique symbol for the system name.
|
|
247
|
+
*
|
|
248
|
+
* @param name - The string name for the system
|
|
249
|
+
* @returns A unique symbol for the system name
|
|
250
|
+
*
|
|
251
|
+
* @example
|
|
252
|
+
* ```typescript
|
|
253
|
+
* const Movement = createSystemName('Movement');
|
|
254
|
+
* coordinator.registerSystem(Movement, movementSystem);
|
|
255
|
+
* ```
|
|
256
|
+
*
|
|
257
|
+
* @category Utilities
|
|
258
|
+
*/
|
|
259
|
+
export declare function createSystemName(name: string): SystemName;
|
|
260
|
+
/**
|
|
261
|
+
* Helper function to get the string description from a system name symbol.
|
|
262
|
+
* Useful for debugging and serialization.
|
|
263
|
+
*
|
|
264
|
+
* @param systemName - The system name symbol
|
|
265
|
+
* @returns The string description of the symbol
|
|
266
|
+
*
|
|
267
|
+
* @category Utilities
|
|
268
|
+
*/
|
|
269
|
+
export declare function getSystemNameString(systemName: SystemName): string;
|
|
270
|
+
/**
|
|
271
|
+
* Helper function to create a system name using Symbol.for().
|
|
272
|
+
* This creates a global symbol that can be looked up by string key,
|
|
273
|
+
* which is useful for serialization and cross-module access.
|
|
274
|
+
*
|
|
275
|
+
* @param key - The string key for the global symbol
|
|
276
|
+
* @returns A global symbol for the system name
|
|
277
|
+
*
|
|
278
|
+
* @example
|
|
279
|
+
* ```typescript
|
|
280
|
+
* const Movement = createGlobalSystemName('Movement');
|
|
281
|
+
* coordinator.registerSystem(Movement, movementSystem);
|
|
282
|
+
* // Can be retrieved later with Symbol.for('Movement')
|
|
283
|
+
* ```
|
|
284
|
+
*
|
|
285
|
+
* @category Utilities
|
|
286
|
+
*/
|
|
287
|
+
export declare function createGlobalSystemName(key: string): SystemName;
|
|
288
|
+
/**
|
|
289
|
+
* Serialized representation of an array element type for JSON storage.
|
|
290
|
+
* @category Types
|
|
291
|
+
*/
|
|
292
|
+
type SerializedArrayElementType = {
|
|
293
|
+
kind: 'builtin';
|
|
294
|
+
type: Exclude<ComponentFieldType, 'array'>;
|
|
295
|
+
} | {
|
|
296
|
+
kind: 'custom';
|
|
297
|
+
typeName: string;
|
|
298
|
+
};
|
|
299
|
+
/**
|
|
300
|
+
* Serialized representation of a component field for JSON storage.
|
|
301
|
+
* @category Types
|
|
302
|
+
*/
|
|
303
|
+
type SerializedComponentField = (Omit<ComponentPrimitiveField, 'type'> & {
|
|
304
|
+
type: Exclude<ComponentFieldType, 'array'>;
|
|
305
|
+
}) | (Omit<ComponentArrayField, 'arrayElementType'> & {
|
|
306
|
+
arrayElementType: SerializedArrayElementType;
|
|
307
|
+
});
|
|
308
|
+
/**
|
|
309
|
+
* Serialized representation of a component schema for JSON storage.
|
|
310
|
+
* Component names are stored as strings (using Symbol.for keys for global symbols).
|
|
311
|
+
* @category Types
|
|
312
|
+
*/
|
|
313
|
+
export interface SerializedComponentSchema {
|
|
314
|
+
componentName: string;
|
|
315
|
+
fields: SerializedComponentField[];
|
|
316
|
+
}
|
|
317
|
+
/**
|
|
318
|
+
* Serialized representation of an entity's component data.
|
|
319
|
+
* @category Types
|
|
320
|
+
*/
|
|
321
|
+
export interface SerializedEntity {
|
|
322
|
+
/** The entity ID */
|
|
323
|
+
entity: Entity;
|
|
324
|
+
/** Map of component names (as strings) to their serialized data */
|
|
325
|
+
components: Record<string, unknown>;
|
|
326
|
+
}
|
|
327
|
+
/**
|
|
328
|
+
* Serialized representation of the entire ECS state.
|
|
329
|
+
* @category Types
|
|
330
|
+
*/
|
|
331
|
+
export interface SerializedECSState {
|
|
332
|
+
/** Array of all entities with their component data */
|
|
333
|
+
entities: SerializedEntity[];
|
|
334
|
+
/** Optional: Array of component schemas (if using schema-based components) */
|
|
335
|
+
schemas?: SerializedComponentSchema[];
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* Serialize a component schema to a JSON-compatible format.
|
|
339
|
+
* Note: Only works with global symbols (created via Symbol.for).
|
|
340
|
+
*
|
|
341
|
+
* @param schema - The component schema to serialize
|
|
342
|
+
* @returns A serializable representation of the schema
|
|
343
|
+
* @throws Error if component name is not a global symbol
|
|
344
|
+
*
|
|
345
|
+
* @category Utilities
|
|
346
|
+
*/
|
|
347
|
+
export declare function serializeComponentSchema(schema: ComponentSchema): SerializedComponentSchema;
|
|
348
|
+
/**
|
|
349
|
+
* Deserialize a component schema from a JSON-compatible format.
|
|
350
|
+
*
|
|
351
|
+
* @param serialized - The serialized schema
|
|
352
|
+
* @returns The component schema with symbols restored
|
|
353
|
+
*
|
|
354
|
+
* @category Utilities
|
|
355
|
+
*/
|
|
356
|
+
export declare function deserializeComponentSchema(serialized: SerializedComponentSchema): ComponentSchema;
|
|
118
357
|
/**
|
|
119
358
|
* Manages entity lifecycle and signatures.
|
|
120
359
|
*
|
|
@@ -139,6 +378,17 @@ export declare class EntityManager {
|
|
|
139
378
|
destroyEntity(entity: Entity): void;
|
|
140
379
|
setSignature(entity: Entity, signature: ComponentSignature): void;
|
|
141
380
|
getSignature(entity: Entity): ComponentSignature | null;
|
|
381
|
+
/**
|
|
382
|
+
* Get all living entities (entities that are currently active, not in the available pool).
|
|
383
|
+
* @returns Array of all living entity IDs
|
|
384
|
+
*/
|
|
385
|
+
getAllLivingEntities(): Entity[];
|
|
386
|
+
/**
|
|
387
|
+
* Check if an entity exists (is currently active, not in the available pool).
|
|
388
|
+
* @param entity - The entity ID to check
|
|
389
|
+
* @returns true if the entity exists, false otherwise
|
|
390
|
+
*/
|
|
391
|
+
entityExists(entity: Entity): boolean;
|
|
142
392
|
}
|
|
143
393
|
/**
|
|
144
394
|
* Internal interface for component array lifecycle hooks.
|
|
@@ -176,6 +426,16 @@ export declare class ComponentArray<T> implements CArray {
|
|
|
176
426
|
getData(entity: Entity): T | null;
|
|
177
427
|
removeData(entity: Entity): void;
|
|
178
428
|
entityDestroyed(entity: Entity): void;
|
|
429
|
+
/**
|
|
430
|
+
* Get all entities that have this component.
|
|
431
|
+
* @returns Array of entity IDs that have this component
|
|
432
|
+
*/
|
|
433
|
+
getAllEntities(): Entity[];
|
|
434
|
+
/**
|
|
435
|
+
* Get the count of entities with this component.
|
|
436
|
+
* @returns Number of entities with this component
|
|
437
|
+
*/
|
|
438
|
+
getCount(): number;
|
|
179
439
|
}
|
|
180
440
|
/**
|
|
181
441
|
* Manages component registration and component data storage.
|
|
@@ -195,13 +455,81 @@ export declare class ComponentArray<T> implements CArray {
|
|
|
195
455
|
export declare class ComponentManager {
|
|
196
456
|
private _componentNameToTypeMap;
|
|
197
457
|
private _nextAvailableComponentType;
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
458
|
+
private _schemas;
|
|
459
|
+
getRegisteredComponentNames(): ComponentName[];
|
|
460
|
+
/**
|
|
461
|
+
* Get all entities that have a specific component.
|
|
462
|
+
* @param componentName - The name of the component type
|
|
463
|
+
* @returns Array of entity IDs that have this component, or empty array if component not registered
|
|
464
|
+
*/
|
|
465
|
+
getAllEntitiesWithComponent(componentName: ComponentName): Entity[];
|
|
466
|
+
/**
|
|
467
|
+
* Get the schema for a component type, if it was registered with a schema.
|
|
468
|
+
* @param componentName - The name of the component type
|
|
469
|
+
* @returns The component schema or null if not found
|
|
470
|
+
*/
|
|
471
|
+
getComponentSchema(componentName: ComponentName): ComponentSchema | null;
|
|
472
|
+
/**
|
|
473
|
+
* Get all registered component schemas.
|
|
474
|
+
* @returns Array of all component schemas
|
|
475
|
+
*/
|
|
476
|
+
getAllComponentSchemas(): ComponentSchema[];
|
|
477
|
+
registerComponent<T>(componentName: ComponentName): void;
|
|
478
|
+
getComponentType(componentName: ComponentName): ComponentType | null;
|
|
479
|
+
addComponentToEntity<T>(componentName: ComponentName, entity: Entity, component: T): void;
|
|
480
|
+
removeComponentFromEntity<T>(componentName: ComponentName, entity: Entity): void;
|
|
481
|
+
getComponentFromEntity<T>(componentName: ComponentName, entity: Entity): T | null;
|
|
203
482
|
entityDestroyed(entity: Entity): void;
|
|
204
483
|
private _getComponentArray;
|
|
484
|
+
componentIsCustomSchema(componentName: ComponentName): boolean;
|
|
485
|
+
/**
|
|
486
|
+
* Register a component with a runtime-defined schema.
|
|
487
|
+
* This allows components to be defined dynamically (e.g., through a GUI).
|
|
488
|
+
*
|
|
489
|
+
* @param schema - The component schema definition
|
|
490
|
+
* @throws Error if schema validation fails
|
|
491
|
+
*/
|
|
492
|
+
registerComponentWithSchema(schema: ComponentSchema): void;
|
|
493
|
+
/**
|
|
494
|
+
* Create a component instance from a schema with default values.
|
|
495
|
+
*
|
|
496
|
+
* @param componentName - The name of the component type
|
|
497
|
+
* @param overrides - Optional values to override defaults
|
|
498
|
+
* @returns A component instance with all fields initialized
|
|
499
|
+
* @throws Error if component is not registered with a schema
|
|
500
|
+
*/
|
|
501
|
+
createComponentFromSchema(componentName: ComponentName, overrides?: Record<string, unknown>): Record<string, unknown>;
|
|
502
|
+
/**
|
|
503
|
+
* Validate component data against its schema.
|
|
504
|
+
*
|
|
505
|
+
* @param componentName - The name of the component type
|
|
506
|
+
* @param data - The component data to validate
|
|
507
|
+
* @returns true if valid, false otherwise
|
|
508
|
+
*/
|
|
509
|
+
validateComponentData(componentName: ComponentName, data: unknown): boolean;
|
|
510
|
+
/**
|
|
511
|
+
* Get default value for a field type.
|
|
512
|
+
*/
|
|
513
|
+
private _getDefaultValueForType;
|
|
514
|
+
/**
|
|
515
|
+
* Validate that a value matches the expected field type.
|
|
516
|
+
*
|
|
517
|
+
* @param type - The field type
|
|
518
|
+
* @param value - The value to validate
|
|
519
|
+
* @param arrayElementType - The element type for array fields
|
|
520
|
+
* @returns true if the value matches the expected type
|
|
521
|
+
*/
|
|
522
|
+
private _validateFieldType;
|
|
523
|
+
/**
|
|
524
|
+
* Add a component to an entity with schema validation.
|
|
525
|
+
*
|
|
526
|
+
* @param componentName - The name of the component type
|
|
527
|
+
* @param entity - The entity to add the component to
|
|
528
|
+
* @param component - The component data
|
|
529
|
+
* @param validate - Whether to validate against schema (default: true)
|
|
530
|
+
* @throws Error if validation fails
|
|
531
|
+
*/
|
|
532
|
+
addComponentToEntityWithSchema(componentName: ComponentName, entity: Entity, component: Record<string, unknown>, validate?: boolean): void;
|
|
205
533
|
}
|
|
206
534
|
/**
|
|
207
535
|
* System interface for processing entities with specific component combinations.
|
|
@@ -216,6 +544,8 @@ export declare class ComponentManager {
|
|
|
216
544
|
*
|
|
217
545
|
* @example
|
|
218
546
|
* ```typescript
|
|
547
|
+
* const Position = createComponentName('Position');
|
|
548
|
+
* const Velocity = createComponentName('Velocity');
|
|
219
549
|
* const movementSystem: System = {
|
|
220
550
|
* entities: new Set()
|
|
221
551
|
* };
|
|
@@ -223,8 +553,8 @@ export declare class ComponentManager {
|
|
|
223
553
|
* // System logic (called in game loop)
|
|
224
554
|
* function updateMovement(deltaTime: number) {
|
|
225
555
|
* movementSystem.entities.forEach(entity => {
|
|
226
|
-
* const pos = ecs.getComponentFromEntity<Position>(
|
|
227
|
-
* const vel = ecs.getComponentFromEntity<Velocity>(
|
|
556
|
+
* const pos = ecs.getComponentFromEntity<Position>(Position, entity);
|
|
557
|
+
* const vel = ecs.getComponentFromEntity<Velocity>(Velocity, entity);
|
|
228
558
|
* if (pos && vel) {
|
|
229
559
|
* pos.x += vel.x * deltaTime;
|
|
230
560
|
* pos.y += vel.y * deltaTime;
|
|
@@ -257,10 +587,11 @@ export interface System {
|
|
|
257
587
|
*/
|
|
258
588
|
export declare class SystemManager {
|
|
259
589
|
private _systems;
|
|
260
|
-
registerSystem(systemName:
|
|
261
|
-
setSignature(systemName:
|
|
590
|
+
registerSystem(systemName: SystemName, system: System): void;
|
|
591
|
+
setSignature(systemName: SystemName, signature: ComponentSignature): void;
|
|
262
592
|
entityDestroyed(entity: Entity): void;
|
|
263
593
|
entitySignatureChanged(entity: Entity, signature: ComponentSignature): void;
|
|
594
|
+
getSystem<T extends System>(systemName: SystemName): T | null;
|
|
264
595
|
}
|
|
265
596
|
/**
|
|
266
597
|
* Main ECS coordinator that manages entities, components, and systems.
|
|
@@ -311,11 +642,231 @@ export declare class Coordinator {
|
|
|
311
642
|
constructor();
|
|
312
643
|
createEntity(): Entity;
|
|
313
644
|
destroyEntity(entity: Entity): void;
|
|
314
|
-
registerComponent<T>(componentName:
|
|
315
|
-
addComponentToEntity<T>(componentName:
|
|
316
|
-
removeComponentFromEntity<T>(componentName:
|
|
317
|
-
getComponentFromEntity<T>(componentName:
|
|
318
|
-
getComponentType(componentName:
|
|
319
|
-
registerSystem(systemName:
|
|
320
|
-
setSystemSignature(systemName:
|
|
645
|
+
registerComponent<T>(componentName: ComponentName): void;
|
|
646
|
+
addComponentToEntity<T>(componentName: ComponentName, entity: Entity, component: T): void;
|
|
647
|
+
removeComponentFromEntity<T>(componentName: ComponentName, entity: Entity): void;
|
|
648
|
+
getComponentFromEntity<T>(componentName: ComponentName, entity: Entity): T | null;
|
|
649
|
+
getComponentType(componentName: ComponentName): ComponentType | null;
|
|
650
|
+
registerSystem(systemName: SystemName, system: System): void;
|
|
651
|
+
setSystemSignature(systemName: SystemName, signature: ComponentSignature): void;
|
|
652
|
+
getSystem<T extends System>(systemName: SystemName): T | null;
|
|
653
|
+
/**
|
|
654
|
+
* Register a component with a runtime-defined schema.
|
|
655
|
+
* This allows components to be defined dynamically (e.g., through a GUI).
|
|
656
|
+
*
|
|
657
|
+
* @param schema - The component schema definition
|
|
658
|
+
* @throws Error if schema validation fails
|
|
659
|
+
*
|
|
660
|
+
* @example
|
|
661
|
+
* ```typescript
|
|
662
|
+
* const coordinator = new Coordinator();
|
|
663
|
+
*
|
|
664
|
+
* // Define a component schema at runtime
|
|
665
|
+
* coordinator.registerComponentWithSchema({
|
|
666
|
+
* componentName: 'PlayerStats',
|
|
667
|
+
* fields: [
|
|
668
|
+
* { name: 'health', type: 'number', defaultValue: 100 },
|
|
669
|
+
* { name: 'name', type: 'string', defaultValue: 'Player' },
|
|
670
|
+
* { name: 'isAlive', type: 'boolean', defaultValue: true },
|
|
671
|
+
* { name: 'inventory', type: 'array', defaultValue: [] }
|
|
672
|
+
* ]
|
|
673
|
+
* });
|
|
674
|
+
*
|
|
675
|
+
* // Create an entity with the component
|
|
676
|
+
* const entity = coordinator.createEntity();
|
|
677
|
+
* const component = coordinator.createComponentFromSchema('PlayerStats', { health: 150 });
|
|
678
|
+
* coordinator.addComponentToEntityWithSchema('PlayerStats', entity, component);
|
|
679
|
+
* ```
|
|
680
|
+
*/
|
|
681
|
+
registerComponentWithSchema(schema: ComponentSchema): void;
|
|
682
|
+
/**
|
|
683
|
+
* Get the schema for a component type, if it was registered with a schema.
|
|
684
|
+
*
|
|
685
|
+
* @param componentName - The name of the component type
|
|
686
|
+
* @returns The component schema or null if not found
|
|
687
|
+
*/
|
|
688
|
+
getComponentSchema(componentName: ComponentName): ComponentSchema | null;
|
|
689
|
+
/**
|
|
690
|
+
* Get the property field names of a component.
|
|
691
|
+
*
|
|
692
|
+
* This method works in two ways:
|
|
693
|
+
* 1. If the component was registered with a schema, returns field names from the schema
|
|
694
|
+
* 2. If no schema exists, attempts to extract property names from an actual component instance
|
|
695
|
+
* (requires at least one entity to have an instance of the component)
|
|
696
|
+
*
|
|
697
|
+
* @param componentName - The name of the component type
|
|
698
|
+
* @returns Array of property field names, or empty array if component has no schema and no instances exist
|
|
699
|
+
*
|
|
700
|
+
* @example
|
|
701
|
+
* ```typescript
|
|
702
|
+
* const coordinator = new Coordinator();
|
|
703
|
+
*
|
|
704
|
+
* // Method 1: Using schema
|
|
705
|
+
* coordinator.registerComponentWithSchema({
|
|
706
|
+
* componentName: 'PlayerStats',
|
|
707
|
+
* fields: [
|
|
708
|
+
* { name: 'health', type: 'number', defaultValue: 100 },
|
|
709
|
+
* { name: 'name', type: 'string', defaultValue: 'Player' },
|
|
710
|
+
* { name: 'isAlive', type: 'boolean', defaultValue: true }
|
|
711
|
+
* ]
|
|
712
|
+
* });
|
|
713
|
+
* const fieldNames1 = coordinator.getComponentPropertyNames('PlayerStats');
|
|
714
|
+
* console.log(fieldNames1); // ['health', 'name', 'isAlive']
|
|
715
|
+
*
|
|
716
|
+
* // Method 2: From component instance
|
|
717
|
+
* type LocationComponent = { location: Entity; sortIndex: number };
|
|
718
|
+
* coordinator.registerComponent<LocationComponent>('LocationComponent');
|
|
719
|
+
* const entity = coordinator.createEntity();
|
|
720
|
+
* coordinator.addComponentToEntity('LocationComponent', entity, {
|
|
721
|
+
* location: otherEntity,
|
|
722
|
+
* sortIndex: 0
|
|
723
|
+
* });
|
|
724
|
+
* const fieldNames2 = coordinator.getComponentPropertyNames('LocationComponent');
|
|
725
|
+
* console.log(fieldNames2); // ['location', 'sortIndex']
|
|
726
|
+
* ```
|
|
727
|
+
*/
|
|
728
|
+
getComponentPropertyNames(componentName: ComponentName): string[];
|
|
729
|
+
/**
|
|
730
|
+
* Get all registered component schemas.
|
|
731
|
+
*
|
|
732
|
+
* @returns Array of all component schemas
|
|
733
|
+
*/
|
|
734
|
+
getAllComponentSchemas(): ComponentSchema[];
|
|
735
|
+
/**
|
|
736
|
+
* Create a component instance from a schema with default values.
|
|
737
|
+
*
|
|
738
|
+
* @param componentName - The name of the component type
|
|
739
|
+
* @param overrides - Optional values to override defaults
|
|
740
|
+
* @returns A component instance with all fields initialized
|
|
741
|
+
* @throws Error if component is not registered with a schema
|
|
742
|
+
*
|
|
743
|
+
* @example
|
|
744
|
+
* ```typescript
|
|
745
|
+
* // Create component with all defaults
|
|
746
|
+
* const component1 = coordinator.createComponentFromSchema('PlayerStats');
|
|
747
|
+
*
|
|
748
|
+
* // Create component with some overrides
|
|
749
|
+
* const component2 = coordinator.createComponentFromSchema('PlayerStats', {
|
|
750
|
+
* health: 200,
|
|
751
|
+
* name: 'SuperPlayer'
|
|
752
|
+
* });
|
|
753
|
+
* ```
|
|
754
|
+
*/
|
|
755
|
+
createComponentFromSchema(componentName: ComponentName, overrides?: Record<string, unknown>): Record<string, unknown>;
|
|
756
|
+
/**
|
|
757
|
+
* Validate component data against its schema.
|
|
758
|
+
*
|
|
759
|
+
* @param componentName - The name of the component type
|
|
760
|
+
* @param data - The component data to validate
|
|
761
|
+
* @returns true if valid, false otherwise
|
|
762
|
+
*/
|
|
763
|
+
validateComponentData(componentName: ComponentName, data: unknown): boolean;
|
|
764
|
+
/**
|
|
765
|
+
* Add a component to an entity with schema validation.
|
|
766
|
+
*
|
|
767
|
+
* @param componentName - The name of the component type
|
|
768
|
+
* @param entity - The entity to add the component to
|
|
769
|
+
* @param component - The component data
|
|
770
|
+
* @param validate - Whether to validate against schema (default: true)
|
|
771
|
+
* @throws Error if validation fails
|
|
772
|
+
*/
|
|
773
|
+
addComponentToEntityWithSchema(componentName: ComponentName, entity: Entity, component: Record<string, unknown>, validate?: boolean): void;
|
|
774
|
+
/**
|
|
775
|
+
* Get all living entities in the ECS.
|
|
776
|
+
* @returns Array of all entity IDs that are currently active
|
|
777
|
+
*
|
|
778
|
+
* @example
|
|
779
|
+
* ```typescript
|
|
780
|
+
* const entities = coordinator.getAllEntities();
|
|
781
|
+
* console.log(`Total entities: ${entities.length}`);
|
|
782
|
+
* ```
|
|
783
|
+
*/
|
|
784
|
+
getAllEntities(): Entity[];
|
|
785
|
+
/**
|
|
786
|
+
* Check if an entity exists in the coordinator.
|
|
787
|
+
* @param entity - The entity ID to check
|
|
788
|
+
* @returns true if the entity exists, false otherwise
|
|
789
|
+
*
|
|
790
|
+
* @example
|
|
791
|
+
* ```typescript
|
|
792
|
+
* const entity = coordinator.createEntity();
|
|
793
|
+
* if (coordinator.entityExists(entity)) {
|
|
794
|
+
* console.log('Entity exists');
|
|
795
|
+
* }
|
|
796
|
+
*
|
|
797
|
+
* coordinator.destroyEntity(entity);
|
|
798
|
+
* if (!coordinator.entityExists(entity)) {
|
|
799
|
+
* console.log('Entity no longer exists');
|
|
800
|
+
* }
|
|
801
|
+
* ```
|
|
802
|
+
*/
|
|
803
|
+
entityExists(entity: Entity): boolean;
|
|
804
|
+
/**
|
|
805
|
+
* Get all components for a specific entity.
|
|
806
|
+
* @param entity - The entity ID
|
|
807
|
+
* @returns Map of component names to their data, or null if entity doesn't exist
|
|
808
|
+
*
|
|
809
|
+
* @example
|
|
810
|
+
* ```typescript
|
|
811
|
+
* const components = coordinator.getEntityComponents(entity);
|
|
812
|
+
* if (components) {
|
|
813
|
+
* console.log('Entity components:', components);
|
|
814
|
+
* }
|
|
815
|
+
* ```
|
|
816
|
+
*/
|
|
817
|
+
getEntityComponents(entity: Entity): Map<ComponentName, unknown> | null;
|
|
818
|
+
/**
|
|
819
|
+
* Get the entire state of the ECS: all entities with all their component values.
|
|
820
|
+
* @returns Object containing all entities and their components
|
|
821
|
+
*
|
|
822
|
+
* @example
|
|
823
|
+
* ```typescript
|
|
824
|
+
* const state = coordinator.getFullState();
|
|
825
|
+
* console.log(`Total entities: ${state.entities.length}`);
|
|
826
|
+
* state.entities.forEach(entityData => {
|
|
827
|
+
* console.log(`Entity ${entityData.entity} has ${Object.keys(entityData.components).length} components`);
|
|
828
|
+
* });
|
|
829
|
+
* ```
|
|
830
|
+
*/
|
|
831
|
+
getFullState(): {
|
|
832
|
+
entities: Array<{
|
|
833
|
+
entity: Entity;
|
|
834
|
+
components: Map<ComponentName, unknown>;
|
|
835
|
+
}>;
|
|
836
|
+
};
|
|
837
|
+
/**
|
|
838
|
+
* Serialize the entire ECS state to a JSON-compatible format.
|
|
839
|
+
* Note: Only works with global symbols (created via Symbol.for or createGlobalComponentName).
|
|
840
|
+
*
|
|
841
|
+
* @returns A serializable representation of the ECS state
|
|
842
|
+
* @throws Error if any component name is not a global symbol
|
|
843
|
+
*
|
|
844
|
+
* @example
|
|
845
|
+
* ```typescript
|
|
846
|
+
* const serialized = coordinator.serialize();
|
|
847
|
+
* const json = JSON.stringify(serialized);
|
|
848
|
+
* // Save to file or send over network
|
|
849
|
+
* ```
|
|
850
|
+
*/
|
|
851
|
+
serialize(): SerializedECSState;
|
|
852
|
+
/**
|
|
853
|
+
* Deserialize an ECS state from a JSON-compatible format.
|
|
854
|
+
* This will restore all entities and their components.
|
|
855
|
+
*
|
|
856
|
+
* @param serialized - The serialized ECS state
|
|
857
|
+
* @param options - Options for deserialization
|
|
858
|
+
* @param options.clearExisting - Whether to clear existing entities before deserializing (default: false)
|
|
859
|
+
* @throws Error if component names cannot be resolved or components are not registered
|
|
860
|
+
*
|
|
861
|
+
* @example
|
|
862
|
+
* ```typescript
|
|
863
|
+
* const json = fs.readFileSync('state.json', 'utf-8');
|
|
864
|
+
* const serialized = JSON.parse(json);
|
|
865
|
+
* coordinator.deserialize(serialized, { clearExisting: true });
|
|
866
|
+
* ```
|
|
867
|
+
*/
|
|
868
|
+
deserialize(serialized: SerializedECSState, options?: {
|
|
869
|
+
clearExisting?: boolean;
|
|
870
|
+
}): void;
|
|
321
871
|
}
|
|
872
|
+
export {};
|
package/index.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
var O=1e4,W=32;class G{_availableEntities=[];_signatures=[];_maxEntities;_livingEntityCount=0;constructor(b=1e4){this._maxEntities=b;for(let j=0;j<this._maxEntities;j++)this._availableEntities.push(j),this._signatures.push(0)}createEntity(){if(this._livingEntityCount>=this._maxEntities)throw Error("Max entities reached");let b=this._availableEntities.shift();if(b===void 0)throw Error("No available entities");return this._signatures[b]=0,this._livingEntityCount++,b}destroyEntity(b){if(b>=this._maxEntities||b<0)throw Error("Invalid entity out of range");this._signatures[b]=0,this._availableEntities.push(b),this._livingEntityCount--}setSignature(b,j){if(b>=this._maxEntities||b<0)throw Error("Invalid entity out of range");this._signatures[b]=j}getSignature(b){if(b>=this._maxEntities||b<0)return null;return this._signatures[b]}}class H{denseArray;sparse;reverse;_count;constructor(b){this._count=0,this.denseArray=Array(b),this.sparse=Array(b),this.reverse=Array(b)}insertData(b,j){if(this.getData(b)!==null)this.removeData(b);if(this.sparse.length<b)this.sparse=[...this.sparse,...Array(b-this.sparse.length).fill(null)];this.denseArray[this._count]=j,this.reverse[this._count]=b,this.sparse[b]=this._count,this._count++}getData(b){if(this.sparse.length<=b)return null;let j=this.sparse[b];if(j===void 0||j===null||j>=this._count)return null;if(this.reverse[j]!==b)return null;return this.denseArray[j]}removeData(b){let j=this.sparse[b];if(j===void 0||j===null||j>=this._count)return;let z=this.reverse[this._count-1];if(z===null)return;this.denseArray[j]=this.denseArray[this._count-1],this.reverse[j]=z,this.sparse[z]=j,this.sparse[b]=null,this._count--}entityDestroyed(b){this.removeData(b)}}class J{_componentNameToTypeMap=new Map;_nextAvailableComponentType=0;registerComponent(b){if(this._componentNameToTypeMap.has(b))console.warn(`Component ${b} already registered; registering with the given new type`);let j=this._nextAvailableComponentType;this._componentNameToTypeMap.set(b,{componentType:j,componentArray:new H(1e4)}),this._nextAvailableComponentType++}getComponentType(b){return this._componentNameToTypeMap.get(b)?.componentType??null}addComponentToEntity(b,j,z){let B=this._getComponentArray(b);if(B===null)return;B.insertData(j,z)}removeComponentFromEntity(b,j){let z=this._getComponentArray(b);if(z===null)return;z.removeData(j)}getComponentFromEntity(b,j){let z=this._getComponentArray(b);if(z===null)return null;return z.getData(j)}entityDestroyed(b){for(let j of this._componentNameToTypeMap.values())j.componentArray.entityDestroyed(b)}_getComponentArray(b){let j=this._componentNameToTypeMap.get(b);if(j===void 0)return console.warn(`Component ${b} not registered`),null;return j.componentArray}}class K{_systems=new Map;registerSystem(b,j){if(this._systems.has(b)){console.warn(`System ${b} already registered`);return}this._systems.set(b,{system:j,signature:0})}setSignature(b,j){if(!this._systems.has(b)){console.warn(`System ${b} not registered`);return}let z=this._systems.get(b);if(z===void 0){console.warn(`System ${b} not registered`);return}z.signature=j}entityDestroyed(b){for(let j of this._systems.values())j.system.entities.delete(b)}entitySignatureChanged(b,j){for(let z of this._systems.values()){let B=z.signature;if((B&j)===B)z.system.entities.add(b);else z.system.entities.delete(b)}}}class L{_entityManager;_componentManager;_systemManager;constructor(){this._entityManager=new G,this._componentManager=new J,this._systemManager=new K}createEntity(){return this._entityManager.createEntity()}destroyEntity(b){this._entityManager.destroyEntity(b),this._componentManager.entityDestroyed(b),this._systemManager.entityDestroyed(b)}registerComponent(b){this._componentManager.registerComponent(b)}addComponentToEntity(b,j,z){this._componentManager.addComponentToEntity(b,j,z);let B=this._entityManager.getSignature(j);if(B===null)B=0;let D=this._componentManager.getComponentType(b);if(D===null){console.warn(`Component ${b} not registered`);return}B|=1<<D,this._entityManager.setSignature(j,B),this._systemManager.entitySignatureChanged(j,B)}removeComponentFromEntity(b,j){this._componentManager.removeComponentFromEntity(b,j);let z=this._entityManager.getSignature(j);if(z===null)z=0;let B=this._componentManager.getComponentType(b);if(B===null)return;z&=~(1<<B),this._entityManager.setSignature(j,z),this._systemManager.entitySignatureChanged(j,z)}getComponentFromEntity(b,j){return this._componentManager.getComponentFromEntity(b,j)}getComponentType(b){return this._componentManager.getComponentType(b)??null}registerSystem(b,j){this._systemManager.registerSystem(b,j)}setSystemSignature(b,j){this._systemManager.setSignature(b,j)}}export{K as SystemManager,O as MAX_ENTITIES,W as MAX_COMPONENTS,G as EntityManager,L as Coordinator,J as ComponentManager,H as ComponentArray};
|
|
1
|
+
var W=1e4,j=32;function O(q){return Symbol(q)}function Q(q){return q.description||q.toString()}function b(q){return Symbol.for(q)}function M(q){return Symbol(q)}function Y(q){return q.description||q.toString()}function k(q){return Symbol.for(q)}function w(q){let B=Symbol.keyFor(q.componentName);if(B===void 0)throw Error("Cannot serialize schema: component name is not a global symbol. Use createGlobalComponentName() or Symbol.for() to create component names.");return{componentName:B,fields:q.fields.map((F)=>{if(F.type==="array")if(F.arrayElementType.kind==="custom"){let H=Symbol.keyFor(F.arrayElementType.typeName);if(H===void 0)throw Error("Cannot serialize schema: custom array element type is not a global symbol.");return{...F,arrayElementType:{kind:"custom",typeName:H}}}else return{...F,arrayElementType:F.arrayElementType};return F})}}function D(q){return{componentName:Symbol.for(q.componentName),fields:q.fields.map((B)=>{if(B.type==="array")if(B.arrayElementType.kind==="custom")return{...B,arrayElementType:{kind:"custom",typeName:Symbol.for(B.arrayElementType.typeName)}};else return{...B,arrayElementType:B.arrayElementType};return B})}}class Z{_availableEntities=[];_signatures=[];_maxEntities;_livingEntityCount=0;constructor(q=1e4){this._maxEntities=q;for(let B=0;B<this._maxEntities;B++)this._availableEntities.push(B),this._signatures.push(0)}createEntity(){if(this._livingEntityCount>=this._maxEntities)throw Error("Max entities reached");let q=this._availableEntities.shift();if(q===void 0)throw Error("No available entities");return this._signatures[q]=0,this._livingEntityCount++,q}destroyEntity(q){if(q>=this._maxEntities||q<0)throw Error("Invalid entity out of range");this._signatures[q]=0,this._availableEntities.push(q),this._livingEntityCount--}setSignature(q,B){if(q>=this._maxEntities||q<0)throw Error("Invalid entity out of range");this._signatures[q]=B}getSignature(q){if(q>=this._maxEntities||q<0)return null;return this._signatures[q]}getAllLivingEntities(){let q=[],B=new Set(this._availableEntities);for(let F=0;F<this._maxEntities;F++)if(!B.has(F))q.push(F);return q}entityExists(q){if(q>=this._maxEntities||q<0)return!1;return!this._availableEntities.includes(q)}}class V{denseArray;sparse;reverse;_count;constructor(q){this._count=0,this.denseArray=Array(q),this.sparse=Array(q),this.reverse=Array(q)}insertData(q,B){if(this.getData(q)!==null)this.removeData(q);if(this.sparse.length<q)this.sparse=[...this.sparse,...Array(q-this.sparse.length).fill(null)];this.denseArray[this._count]=B,this.reverse[this._count]=q,this.sparse[q]=this._count,this._count++}getData(q){if(this.sparse.length<=q)return null;let B=this.sparse[q];if(B===void 0||B===null||B>=this._count)return null;if(this.reverse[B]!==q)return null;return this.denseArray[B]}removeData(q){let B=this.sparse[q];if(B===void 0||B===null||B>=this._count)return;let F=this.reverse[this._count-1];if(F===null)return;this.denseArray[B]=this.denseArray[this._count-1],this.reverse[B]=F,this.sparse[F]=B,this.sparse[q]=null,this._count--}entityDestroyed(q){this.removeData(q)}getAllEntities(){let q=[];for(let B=0;B<this._count;B++){let F=this.reverse[B];if(F!==null&&F!==void 0)q.push(F)}return q}getCount(){return this._count}}class _{_componentNameToTypeMap=new Map;_nextAvailableComponentType=0;_schemas=new Map;getRegisteredComponentNames(){return Array.from(this._componentNameToTypeMap.keys())}getAllEntitiesWithComponent(q){let B=this._componentNameToTypeMap.get(q);if(B===void 0)return[];return B.componentArray.getAllEntities()}getComponentSchema(q){return this._schemas.get(q)??null}getAllComponentSchemas(){return Array.from(this._schemas.values())}registerComponent(q){if(this._componentNameToTypeMap.has(q))return;let B=this._nextAvailableComponentType;this._componentNameToTypeMap.set(q,{componentType:B,componentArray:new V(1e4)}),this._nextAvailableComponentType++}getComponentType(q){return this._componentNameToTypeMap.get(q)?.componentType??null}addComponentToEntity(q,B,F){let H=this._getComponentArray(q);if(H===null)return;H.insertData(B,F)}removeComponentFromEntity(q,B){let F=this._getComponentArray(q);if(F===null)return;F.removeData(B)}getComponentFromEntity(q,B){let F=this._getComponentArray(q);if(F===null)return null;return F.getData(B)}entityDestroyed(q){for(let B of this._componentNameToTypeMap.values())B.componentArray.entityDestroyed(q)}_getComponentArray(q){let B=this._componentNameToTypeMap.get(q);if(B===void 0)return console.warn(`Component ${Q(q)} not registered`),null;return B.componentArray}componentIsCustomSchema(q){return this._schemas.has(q)}registerComponentWithSchema(q){if(!q.componentName)throw Error("Component schema must have a componentName");if(!q.fields||q.fields.length===0)throw Error("Component schema must have at least one field");let B=new Set;for(let F of q.fields){if(B.has(F.name))throw Error(`Duplicate field name "${F.name}" in schema for component "${Q(q.componentName)}"`);if(B.add(F.name),F.type==="array");}if(this._schemas.has(q.componentName))return;if(!this._componentNameToTypeMap.has(q.componentName)){let F=this._nextAvailableComponentType;this._componentNameToTypeMap.set(q.componentName,{componentType:F,componentArray:new V(1e4)}),this._nextAvailableComponentType++}this._schemas.set(q.componentName,q)}createComponentFromSchema(q,B={}){let F=this._schemas.get(q);if(!F)throw Error(`Component "${Q(q)}" is not registered with a schema`);let H={};for(let G of F.fields)if(B.hasOwnProperty(G.name))H[G.name]=B[G.name];else if(G.defaultValue!==void 0)H[G.name]=G.defaultValue;else if(!G.optional)if(G.type==="array")H[G.name]=this._getDefaultValueForType(G.type,G.arrayElementType);else H[G.name]=this._getDefaultValueForType(G.type);return H}validateComponentData(q,B){let F=this._schemas.get(q);if(!F)return!0;if(typeof B!=="object"||B===null||Array.isArray(B))return!1;let H=B;for(let G of F.fields){if(!G.optional&&!H.hasOwnProperty(G.name))return!1;if(H.hasOwnProperty(G.name)){if(G.type==="array"){if(!this._validateFieldType(G.type,H[G.name],G.arrayElementType))return!1}else if(!this._validateFieldType(G.type,H[G.name]))return!1}}return!0}_getDefaultValueForType(q,B){switch(q){case"string":return"";case"number":return 0;case"boolean":return!1;case"object":return{};case"array":return[];case"entity":return null;default:return null}}_validateFieldType(q,B,F){switch(q){case"string":return typeof B==="string";case"number":return typeof B==="number";case"boolean":return typeof B==="boolean";case"object":return typeof B==="object"&&B!==null&&!Array.isArray(B);case"array":if(!Array.isArray(B))return!1;if(F)if(F.kind==="builtin")return B.every((H)=>this._validateFieldType(F.type,H));else{if(!this._schemas.get(F.typeName))return B.every((G)=>typeof G==="object"&&G!==null&&!Array.isArray(G));return B.every((G)=>this.validateComponentData(F.typeName,G))}return!0;case"entity":return typeof B==="number"||B===null;default:return!1}}addComponentToEntityWithSchema(q,B,F,H=!0){if(H&&!this.validateComponentData(q,F))throw Error(`Component data for "${Q(q)}" does not match its schema`);this.addComponentToEntity(q,B,F)}}class ${_systems=new Map;registerSystem(q,B){if(this._systems.has(q))return;this._systems.set(q,{system:B,signature:0})}setSignature(q,B){if(!this._systems.has(q)){console.warn(`System ${Y(q)} not registered`);return}let F=this._systems.get(q);if(F===void 0){console.warn(`System ${Y(q)} not registered`);return}F.signature=B}entityDestroyed(q){for(let B of this._systems.values())B.system.entities.delete(q)}entitySignatureChanged(q,B){for(let F of this._systems.values()){let H=F.signature;if((H&B)===H)F.system.entities.add(q);else F.system.entities.delete(q)}}getSystem(q){let B=this._systems.get(q);if(B===void 0)return null;return B.system}}class K{_entityManager;_componentManager;_systemManager;constructor(){this._entityManager=new Z,this._componentManager=new _,this._systemManager=new $}createEntity(){return this._entityManager.createEntity()}destroyEntity(q){this._entityManager.destroyEntity(q),this._componentManager.entityDestroyed(q),this._systemManager.entityDestroyed(q)}registerComponent(q){this._componentManager.registerComponent(q)}addComponentToEntity(q,B,F){this._componentManager.addComponentToEntity(q,B,F);let H=this._entityManager.getSignature(B);if(H===null)H=0;let G=this._componentManager.getComponentType(q);if(G===null){console.warn(`Component ${Q(q)} not registered`);return}H|=1<<G,this._entityManager.setSignature(B,H),this._systemManager.entitySignatureChanged(B,H)}removeComponentFromEntity(q,B){this._componentManager.removeComponentFromEntity(q,B);let F=this._entityManager.getSignature(B);if(F===null)F=0;let H=this._componentManager.getComponentType(q);if(H===null)return;F&=~(1<<H),this._entityManager.setSignature(B,F),this._systemManager.entitySignatureChanged(B,F)}getComponentFromEntity(q,B){return this._componentManager.getComponentFromEntity(q,B)}getComponentType(q){return this._componentManager.getComponentType(q)??null}registerSystem(q,B){this._systemManager.registerSystem(q,B)}setSystemSignature(q,B){this._systemManager.setSignature(q,B)}getSystem(q){return this._systemManager.getSystem(q)??null}registerComponentWithSchema(q){this._componentManager.registerComponentWithSchema(q)}getComponentSchema(q){return this._componentManager.getComponentSchema(q)}getComponentPropertyNames(q){let B=this.getComponentSchema(q);if(B)return B.fields.map((G)=>G.name);let F=this._componentManager.getAllEntitiesWithComponent(q);if(F.length===0)return[];let H=this._componentManager.getComponentFromEntity(q,F[0]);if(H===null)return[];if(typeof H==="object"&&H!==null&&!Array.isArray(H))return Object.keys(H);return[]}getAllComponentSchemas(){return this._componentManager.getAllComponentSchemas()}createComponentFromSchema(q,B={}){return this._componentManager.createComponentFromSchema(q,B)}validateComponentData(q,B){return this._componentManager.validateComponentData(q,B)}addComponentToEntityWithSchema(q,B,F,H=!0){this._componentManager.addComponentToEntityWithSchema(q,B,F,H);let G=this._entityManager.getSignature(B);if(G===null)G=0;let J=this._componentManager.getComponentType(q);if(J===null){console.warn(`Component ${Q(q)} not registered`);return}G|=1<<J,this._entityManager.setSignature(B,G),this._systemManager.entitySignatureChanged(B,G)}getAllEntities(){return this._entityManager.getAllLivingEntities()}entityExists(q){return this._entityManager.entityExists(q)}getEntityComponents(q){let B=this._entityManager.getSignature(q);if(B===null||B===0)return null;let F=new Map,H=this._componentManager.getRegisteredComponentNames();for(let G of H){let J=this._componentManager.getComponentType(G);if(J===null)continue;if((B&1<<J)!==0){let L=this._componentManager.getComponentFromEntity(G,q);if(L!==null)F.set(G,L)}}return F}getFullState(){return{entities:this.getAllEntities().map((F)=>({entity:F,components:this.getEntityComponents(F)??new Map}))}}serialize(){let q=this.getAllEntities(),B=[],F=this._componentManager.getRegisteredComponentNames();for(let J of q){let L={},P=!1;for(let R of F){let U=this._componentManager.getComponentFromEntity(R,J);if(U!==null){let X=Symbol.keyFor(R);if(X===void 0)throw Error(`Cannot serialize: component name "${Q(R)}" is not a global symbol. Use createGlobalComponentName() or Symbol.for() to create component names.`);L[X]=U,P=!0}}if(P)B.push({entity:J,components:L})}let H=this._componentManager.getAllComponentSchemas(),G=H.length>0?H.map((J)=>w(J)):void 0;return{entities:B,...G&&{schemas:G}}}deserialize(q,B={}){let{clearExisting:F=!1}=B;if(F){let H=this.getAllEntities();for(let G of H)this.destroyEntity(G)}for(let H of q.entities){let G;if(F)G=this.createEntity();else if(this._entityManager.getSignature(H.entity)===null)G=this.createEntity();else G=H.entity;for(let[J,L]of Object.entries(H.components)){let P=Symbol.for(J);if(this._componentManager.getComponentType(P)===null)throw Error(`Cannot deserialize: component "${J}" is not registered. Register it first using registerComponent() or registerComponentWithSchema().`);if(this._componentManager.getComponentSchema(P))this.addComponentToEntityWithSchema(P,G,L,!0);else this.addComponentToEntity(P,G,L)}}}}export{w as serializeComponentSchema,Y as getSystemNameString,Q as getComponentNameString,D as deserializeComponentSchema,M as createSystemName,k as createGlobalSystemName,b as createGlobalComponentName,O as createComponentName,$ as SystemManager,W as MAX_ENTITIES,j as MAX_COMPONENTS,Z as EntityManager,K as Coordinator,_ as ComponentManager,V as ComponentArray};
|
|
2
2
|
|
|
3
|
-
//# debugId=
|
|
3
|
+
//# debugId=1A0F397F448E17CB64756E2164756E21
|
package/index.js.map
CHANGED
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/index.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
|
-
"/**\n * @packageDocumentation\n * Entity Component System (ECS) implementation for TypeScript.\n *\n * @remarks\n * The `@ue-too/ecs` package provides a high-performance Entity Component System architecture\n * based on the tutorial from https://austinmorlan.com/posts/entity_component_system/\n *\n * ## ECS Architecture\n *\n * - **Entities**: Unique identifiers (numbers) representing game objects\n * - **Components**: Data containers attached to entities\n * - **Systems**: Logic that operates on entities with specific component combinations\n * - **Signatures**: Bit flags indicating which components an entity has\n *\n * ## Key Features\n *\n * - **Efficient Storage**: Component arrays using sparse-set data structure\n * - **Fast Iteration**: Dense packing for cache-friendly iteration\n * - **Type-Safe**: TypeScript generics for component type safety\n * - **Signature Matching**: Automatic system updates when entity signatures change\n * - **Pooling**: Entity ID recycling for memory efficiency\n *\n * ## Core Classes\n *\n * - {@link Coordinator}: Main ECS coordinator managing all subsystems\n * - {@link EntityManager}: Creates and destroys entities\n * - {@link ComponentManager}: Registers components and manages component data\n * - {@link SystemManager}: Registers systems and maintains entity sets\n * - {@link ComponentArray}: Efficient sparse-set storage for component data\n *\n * @example\n * Basic ECS usage\n * ```typescript\n * import { Coordinator } from '@ue-too/ecs';\n *\n * // Define component types\n * type Position = { x: number; y: number };\n * type Velocity = { x: number; y: number };\n *\n * // Create coordinator\n * const coordinator = new Coordinator();\n *\n * // Register components\n * coordinator.registerComponent<Position>('Position');\n * coordinator.registerComponent<Velocity>('Velocity');\n *\n * // Create entity with components\n * const entity = coordinator.createEntity();\n * coordinator.addComponentToEntity('Position', entity, { x: 0, y: 0 });\n * coordinator.addComponentToEntity('Velocity', entity, { x: 1, y: 1 });\n *\n * // Query components\n * const pos = coordinator.getComponentFromEntity<Position>('Position', entity);\n * console.log('Position:', pos);\n * ```\n *\n * @example\n * System registration\n * ```typescript\n * import { Coordinator, System } from '@ue-too/ecs';\n *\n * const coordinator = new Coordinator();\n * coordinator.registerComponent<Position>('Position');\n * coordinator.registerComponent<Velocity>('Velocity');\n *\n * // Create a movement system\n * const movementSystem: System = {\n * entities: new Set()\n * };\n *\n * coordinator.registerSystem('Movement', movementSystem);\n *\n * // Set signature (entities with Position AND Velocity)\n * const posType = coordinator.getComponentType('Position')!;\n * const velType = coordinator.getComponentType('Velocity')!;\n * const signature = (1 << posType) | (1 << velType);\n * coordinator.setSystemSignature('Movement', signature);\n *\n * // Update loop\n * function update(deltaTime: number) {\n * movementSystem.entities.forEach(entity => {\n * const pos = coordinator.getComponentFromEntity<Position>('Position', entity)!;\n * const vel = coordinator.getComponentFromEntity<Velocity>('Velocity', entity)!;\n * pos.x += vel.x * deltaTime;\n * pos.y += vel.y * deltaTime;\n * });\n * }\n * ```\n *\n * @see {@link Coordinator} for the main ECS API\n */\n\n/**\n * Maximum number of entities that can exist simultaneously.\n * @category Configuration\n */\nexport const MAX_ENTITIES = 10000;\n\n/**\n * Maximum number of component types that can be registered.\n * @category Configuration\n */\nexport const MAX_COMPONENTS = 32;\n\n/**\n * Component signature type (bit field indicating which components an entity has).\n * @category Types\n */\nexport type ComponentSignature = number;\n\n/**\n * Component type identifier.\n * @category Types\n */\nexport type ComponentType = number;\n\n/**\n * Entity identifier (unique number).\n * @category Types\n */\nexport type Entity = number;\n\n/**\n * Manages entity lifecycle and signatures.\n *\n * @remarks\n * The EntityManager handles:\n * - Creating new entities (recycling IDs from a pool)\n * - Destroying entities (returning IDs to the pool)\n * - Storing and updating component signatures for each entity\n *\n * Entities are represented as simple numbers (IDs) and the manager maintains\n * a signature (bit field) for each entity indicating which components it has.\n *\n * @category Managers\n */\nexport class EntityManager {\n\n private _availableEntities: Entity[] = [];\n private _signatures: ComponentSignature[] = [];\n private _maxEntities: number;\n\n private _livingEntityCount = 0;\n\n constructor(maxEntities: number = MAX_ENTITIES) {\n this._maxEntities = maxEntities;\n for (let i = 0; i < this._maxEntities; i++) {\n this._availableEntities.push(i);\n this._signatures.push(0);\n }\n }\n\n createEntity(): Entity {\n if(this._livingEntityCount >= this._maxEntities) {\n throw new Error('Max entities reached');\n }\n const entity = this._availableEntities.shift();\n if(entity === undefined) {\n throw new Error('No available entities');\n }\n this._signatures[entity] = 0;\n this._livingEntityCount++;\n return entity;\n }\n\n destroyEntity(entity: Entity): void {\n if(entity >= this._maxEntities || entity < 0) {\n throw new Error('Invalid entity out of range');\n }\n this._signatures[entity] = 0;\n this._availableEntities.push(entity);\n this._livingEntityCount--;\n }\n\n setSignature(entity: Entity, signature: ComponentSignature): void {\n if(entity >= this._maxEntities || entity < 0) {\n throw new Error('Invalid entity out of range');\n }\n this._signatures[entity] = signature;\n }\n\n getSignature(entity: Entity): ComponentSignature | null {\n if(entity >= this._maxEntities || entity < 0) {\n return null;\n }\n return this._signatures[entity];\n }\n}\n\ntype Tuple<T, N extends number> = N extends N\n ? number extends N\n ? T[]\n : _TupleOf<T, N, []>\n : never;\n\ntype _TupleOf<T, N extends number, R extends unknown[]> = R['length'] extends N\n ? R\n : _TupleOf<T, N, [...R, T]>;\n\n// Usage\n\n/**\n * Internal interface for component array lifecycle hooks.\n * @internal\n */\nexport interface CArray {\n entityDestroyed(entity: Entity): void;\n}\n\n/**\n * Efficient sparse-set data structure for storing component data.\n *\n * @remarks\n * ComponentArray uses a sparse-set implementation for O(1) insertion, deletion,\n * and lookup while maintaining dense packing for cache-efficient iteration.\n *\n * The sparse-set consists of:\n * - **Dense array**: Packed component data for iteration\n * - **Sparse array**: Maps entity ID to dense array index\n * - **Reverse array**: Maps dense array index back to entity ID\n *\n * This structure allows fast component access by entity ID and fast iteration\n * over all components without gaps.\n *\n * @typeParam T - The component data type\n *\n * @category Data Structures\n */\nexport class ComponentArray<T> implements CArray {\n\n private denseArray: T[]; // packed array of data\n private sparse: (Entity | null)[]; // maps entity to index in dense array\n private reverse: (Entity | null)[]; // maps index in dense array to entity\n private _count: number;\n\n constructor(maxEntities: number) {\n this._count = 0;\n this.denseArray = new Array(maxEntities);\n this.sparse = new Array(maxEntities);\n this.reverse = new Array(maxEntities);\n }\n\n insertData(entity: Entity, data: T): void {\n if(this.getData(entity) !== null) {\n this.removeData(entity);\n }\n if(this.sparse.length < entity){\n // resize the array for the new entity but normally this should not happen\n this.sparse = [...this.sparse, ...new Array(entity - this.sparse.length).fill(null)];\n }\n\n this.denseArray[this._count] = data;\n this.reverse[this._count] = entity;\n this.sparse[entity] = this._count;\n this._count++;\n }\n\n getData(entity: Entity): T | null {\n if(this.sparse.length <= entity){\n return null;\n }\n\n const denseIndex = this.sparse[entity];\n if(denseIndex === undefined || denseIndex === null || denseIndex >= this._count){\n return null;\n }\n\n if(this.reverse[denseIndex] !== entity) {\n return null;\n }\n\n return this.denseArray[denseIndex];\n }\n\n removeData(entity: Entity): void {\n const denseIndex = this.sparse[entity];\n if(denseIndex === undefined || denseIndex === null || denseIndex >= this._count){\n return;\n }\n\n const lastEntity = this.reverse[this._count - 1];\n\n if(lastEntity === null) {\n return;\n }\n\n this.denseArray[denseIndex] = this.denseArray[this._count - 1];\n this.reverse[denseIndex] = lastEntity;\n this.sparse[lastEntity] = denseIndex;\n this.sparse[entity] = null;\n\n this._count--;\n }\n\n entityDestroyed(entity: Entity): void {\n this.removeData(entity);\n }\n}\n\n/**\n * Manages component registration and component data storage.\n *\n * @remarks\n * The ComponentManager handles:\n * - Registering new component types and assigning unique type IDs\n * - Creating ComponentArray storage for each component type\n * - Adding, removing, and querying component data for entities\n * - Cleaning up component data when entities are destroyed\n *\n * Each component type gets a unique ID (0-31) and its own ComponentArray\n * for efficient storage and retrieval.\n *\n * @category Managers\n */\nexport class ComponentManager {\n\n private _componentNameToTypeMap: Map<string, {componentType: ComponentType, componentArray: CArray}> = new Map();\n private _nextAvailableComponentType: ComponentType = 0;\n \n registerComponent<T>(componentName: string){\n if(this._componentNameToTypeMap.has(componentName)) {\n console.warn(`Component ${componentName} already registered; registering with the given new type`);\n }\n const componentType = this._nextAvailableComponentType;\n this._componentNameToTypeMap.set(componentName, {componentType, componentArray: new ComponentArray<T>(MAX_ENTITIES)});\n this._nextAvailableComponentType++;\n }\n\n getComponentType(componentName: string): ComponentType | null {\n return this._componentNameToTypeMap.get(componentName)?.componentType ?? null;\n }\n\n addComponentToEntity<T>(componentName: string, entity: Entity, component: T){\n const componentArray = this._getComponentArray<T>(componentName);\n if(componentArray === null) {\n return;\n }\n componentArray.insertData(entity, component);\n }\n\n removeComponentFromEntity<T>(componentName: string, entity: Entity){\n const componentArray = this._getComponentArray<T>(componentName);\n if(componentArray === null) {\n return;\n }\n componentArray.removeData(entity);\n }\n\n getComponentFromEntity<T>(componentName: string, entity: Entity): T | null {\n const componentArray = this._getComponentArray<T>(componentName);\n if(componentArray === null) {\n return null;\n }\n return componentArray.getData(entity);\n }\n\n entityDestroyed(entity: Entity){\n for(const component of this._componentNameToTypeMap.values()){\n component.componentArray.entityDestroyed(entity);\n }\n }\n\n private _getComponentArray<T>(componentName: string): ComponentArray<T> | null {\n const component = this._componentNameToTypeMap.get(componentName);\n if(component === undefined) {\n console.warn(`Component ${componentName} not registered`);\n return null;\n }\n return component.componentArray as ComponentArray<T>;\n }\n\n}\n\n/**\n * System interface for processing entities with specific component combinations.\n *\n * @remarks\n * A System maintains a set of entities that match its component signature.\n * The ECS automatically updates this set when entities are created, destroyed,\n * or have their components modified.\n *\n * Systems contain only the logic for processing entities - the `entities` set\n * is automatically managed by the SystemManager.\n *\n * @example\n * ```typescript\n * const movementSystem: System = {\n * entities: new Set()\n * };\n *\n * // System logic (called in game loop)\n * function updateMovement(deltaTime: number) {\n * movementSystem.entities.forEach(entity => {\n * const pos = ecs.getComponentFromEntity<Position>('Position', entity);\n * const vel = ecs.getComponentFromEntity<Velocity>('Velocity', entity);\n * if (pos && vel) {\n * pos.x += vel.x * deltaTime;\n * pos.y += vel.y * deltaTime;\n * }\n * });\n * }\n * ```\n *\n * @category Types\n */\nexport interface System {\n entities: Set<Entity>;\n}\n\n/**\n * Manages system registration and entity-system matching.\n *\n * @remarks\n * The SystemManager handles:\n * - Registering systems with their component signature requirements\n * - Maintaining the set of entities that match each system's signature\n * - Automatically adding/removing entities from systems when signatures change\n * - Cleaning up system entity sets when entities are destroyed\n *\n * When an entity's component signature changes (components added/removed),\n * the SystemManager checks all registered systems and updates their entity sets.\n * An entity is added to a system's set if its signature contains all components\n * required by the system's signature.\n *\n * @category Managers\n */\nexport class SystemManager {\n private _systems: Map<string, {system: System, signature: ComponentSignature}> = new Map();\n\n registerSystem(systemName: string, system: System){\n if(this._systems.has(systemName)) {\n console.warn(`System ${systemName} already registered`);\n return;\n }\n this._systems.set(systemName, {system, signature: 0});\n }\n\n setSignature(systemName: string, signature: ComponentSignature){\n if(!this._systems.has(systemName)) {\n console.warn(`System ${systemName} not registered`);\n return;\n }\n const system = this._systems.get(systemName);\n if(system === undefined) {\n console.warn(`System ${systemName} not registered`);\n return;\n }\n system.signature = signature;\n }\n\n entityDestroyed(entity: Entity){\n for(const system of this._systems.values()){\n system.system.entities.delete(entity);\n }\n }\n\n entitySignatureChanged(entity: Entity, signature: ComponentSignature){\n for(const system of this._systems.values()){\n const systemSignature = system.signature;\n if((systemSignature & signature) === systemSignature){\n system.system.entities.add(entity);\n } else {\n system.system.entities.delete(entity);\n }\n }\n }\n}\n\n/**\n * Main ECS coordinator that manages entities, components, and systems.\n *\n * @remarks\n * The Coordinator is the central API for working with the ECS. It provides a unified\n * interface for:\n * - Creating and destroying entities\n * - Registering and managing components\n * - Registering and configuring systems\n * - Querying component data\n *\n * The Coordinator automatically keeps entity signatures up-to-date and notifies\n * systems when entities match their component requirements.\n *\n * @example\n * Complete ECS workflow\n * ```typescript\n * const ecs = new Coordinator();\n *\n * // Setup\n * ecs.registerComponent<Position>('Position');\n * ecs.registerComponent<Velocity>('Velocity');\n *\n * // Create entity\n * const entity = ecs.createEntity();\n * ecs.addComponentToEntity('Position', entity, { x: 0, y: 0 });\n * ecs.addComponentToEntity('Velocity', entity, { x: 1, y: 0 });\n *\n * // Update\n * const pos = ecs.getComponentFromEntity<Position>('Position', entity);\n * const vel = ecs.getComponentFromEntity<Velocity>('Velocity', entity);\n * if (pos && vel) {\n * pos.x += vel.x;\n * pos.y += vel.y;\n * }\n *\n * // Cleanup\n * ecs.destroyEntity(entity);\n * ```\n *\n * @category Core\n */\nexport class Coordinator {\n private _entityManager: EntityManager;\n private _componentManager: ComponentManager;\n private _systemManager: SystemManager;\n\n constructor(){\n this._entityManager = new EntityManager();\n this._componentManager = new ComponentManager();\n this._systemManager = new SystemManager();\n }\n\n createEntity(): Entity {\n return this._entityManager.createEntity();\n }\n\n destroyEntity(entity: Entity): void {\n this._entityManager.destroyEntity(entity);\n this._componentManager.entityDestroyed(entity);\n this._systemManager.entityDestroyed(entity);\n }\n\n registerComponent<T>(componentName: string): void {\n this._componentManager.registerComponent<T>(componentName);\n }\n\n addComponentToEntity<T>(componentName: string, entity: Entity, component: T): void {\n this._componentManager.addComponentToEntity<T>(componentName, entity, component);\n let signature = this._entityManager.getSignature(entity);\n if(signature === null) {\n signature = 0;\n }\n const componentType = this._componentManager.getComponentType(componentName);\n if(componentType === null) {\n console.warn(`Component ${componentName} not registered`);\n return;\n }\n signature |= 1 << componentType;\n this._entityManager.setSignature(entity, signature);\n this._systemManager.entitySignatureChanged(entity, signature);\n }\n\n removeComponentFromEntity<T>(componentName: string, entity: Entity): void {\n this._componentManager.removeComponentFromEntity<T>(componentName, entity);\n let signature = this._entityManager.getSignature(entity);\n if(signature === null) {\n signature = 0;\n }\n const componentType = this._componentManager.getComponentType(componentName);\n if(componentType === null) {\n return;\n }\n signature &= ~(1 << componentType);\n this._entityManager.setSignature(entity, signature);\n this._systemManager.entitySignatureChanged(entity, signature);\n }\n\n getComponentFromEntity<T>(componentName: string, entity: Entity): T | null {\n return this._componentManager.getComponentFromEntity<T>(componentName, entity);\n }\n\n getComponentType(componentName: string): ComponentType | null {\n return this._componentManager.getComponentType(componentName) ?? null;\n }\n\n registerSystem(systemName: string, system: System): void {\n this._systemManager.registerSystem(systemName, system);\n }\n\n setSystemSignature(systemName: string, signature: ComponentSignature): void {\n this._systemManager.setSignature(systemName, signature);\n }\n}\n"
|
|
5
|
+
"/**\n * @packageDocumentation\n * Entity Component System (ECS) implementation for TypeScript.\n *\n * @remarks\n * The `@ue-too/ecs` package provides a high-performance Entity Component System architecture\n * based on the tutorial from https://austinmorlan.com/posts/entity_component_system/\n *\n * ## ECS Architecture\n *\n * - **Entities**: Unique identifiers (numbers) representing game objects\n * - **Components**: Data containers attached to entities\n * - **Systems**: Logic that operates on entities with specific component combinations\n * - **Signatures**: Bit flags indicating which components an entity has\n *\n * ## Key Features\n *\n * - **Efficient Storage**: Component arrays using sparse-set data structure\n * - **Fast Iteration**: Dense packing for cache-friendly iteration\n * - **Type-Safe**: TypeScript generics for component type safety\n * - **Signature Matching**: Automatic system updates when entity signatures change\n * - **Pooling**: Entity ID recycling for memory efficiency\n *\n * ## Core Classes\n *\n * - {@link Coordinator}: Main ECS coordinator managing all subsystems\n * - {@link EntityManager}: Creates and destroys entities\n * - {@link ComponentManager}: Registers components and manages component data\n * - {@link SystemManager}: Registers systems and maintains entity sets\n * - {@link ComponentArray}: Efficient sparse-set storage for component data\n *\n * @example\n * Basic ECS usage\n * ```typescript\n * import { Coordinator } from '@ue-too/ecs';\n *\n * // Define component types\n * type Position = { x: number; y: number };\n * type Velocity = { x: number; y: number };\n *\n * // Create coordinator\n * const coordinator = new Coordinator();\n *\n * // Register components\n * coordinator.registerComponent<Position>('Position');\n * coordinator.registerComponent<Velocity>('Velocity');\n *\n * // Create entity with components\n * const entity = coordinator.createEntity();\n * coordinator.addComponentToEntity('Position', entity, { x: 0, y: 0 });\n * coordinator.addComponentToEntity('Velocity', entity, { x: 1, y: 1 });\n *\n * // Query components\n * const pos = coordinator.getComponentFromEntity<Position>('Position', entity);\n * console.log('Position:', pos);\n * ```\n *\n * @example\n * System registration\n * ```typescript\n * import { Coordinator, System } from '@ue-too/ecs';\n *\n * const coordinator = new Coordinator();\n * coordinator.registerComponent<Position>('Position');\n * coordinator.registerComponent<Velocity>('Velocity');\n *\n * // Create a movement system\n * const movementSystem: System = {\n * entities: new Set()\n * };\n *\n * const Movement = createSystemName('Movement');\n * coordinator.registerSystem(Movement, movementSystem);\n *\n * // Set signature (entities with Position AND Velocity)\n * const Position = createComponentName('Position');\n * const Velocity = createComponentName('Velocity');\n * const posType = coordinator.getComponentType(Position)!;\n * const velType = coordinator.getComponentType(Velocity)!;\n * const signature = (1 << posType) | (1 << velType);\n * coordinator.setSystemSignature(Movement, signature);\n *\n * // Update loop\n * function update(deltaTime: number) {\n * movementSystem.entities.forEach(entity => {\n * const pos = coordinator.getComponentFromEntity<Position>('Position', entity)!;\n * const vel = coordinator.getComponentFromEntity<Velocity>('Velocity', entity)!;\n * pos.x += vel.x * deltaTime;\n * pos.y += vel.y * deltaTime;\n * });\n * }\n * ```\n *\n * @see {@link Coordinator} for the main ECS API\n */\n\n/**\n * Maximum number of entities that can exist simultaneously.\n * @category Configuration\n */\nexport const MAX_ENTITIES = 10000;\n\n/**\n * Maximum number of component types that can be registered.\n * @category Configuration\n */\nexport const MAX_COMPONENTS = 32;\n\n/**\n * Component signature type (bit field indicating which components an entity has).\n * @category Types\n */\nexport type ComponentSignature = number;\n\n/**\n * Component type identifier.\n * @category Types\n */\nexport type ComponentType = number;\n\n/**\n * Entity identifier (unique number).\n * @category Types\n */\nexport type Entity = number;\n\n/**\n * Component name identifier using Symbol for type safety and uniqueness.\n * Use {@link createComponentName} to create component names, or {@link Symbol.for} for global symbols.\n * @category Types\n */\nexport type ComponentName = symbol;\n\n/**\n * System name identifier using Symbol for type safety and uniqueness.\n * Use {@link createSystemName} to create system names, or {@link Symbol.for} for global symbols.\n * @category Types\n */\nexport type SystemName = symbol;\n\n/**\n * Supported field types for runtime-defined component schemas.\n * @category Types\n */\nexport type ComponentFieldType = \n | 'string' \n | 'number' \n | 'boolean' \n | 'object' \n | 'array'\n | 'entity';\n\n/**\n * Discriminated union for array element types.\n * Supports both built-in types and custom component types.\n * @category Types\n */\nexport type ArrayElementType = \n | { kind: 'builtin'; type: Exclude<ComponentFieldType, 'array'> }\n | { kind: 'custom'; typeName: ComponentName };\n\n/**\n * Base properties shared by all field definitions.\n * @category Types\n */\ninterface BaseComponentField {\n /** The name of the field */\n name: string;\n /** Whether the field is optional (default: false) */\n optional?: boolean;\n /** Default value for the field (used when creating new instances) */\n defaultValue?: unknown;\n}\n\n/**\n * Definition for a primitive (non-array) field in a component schema.\n * @category Types\n */\nexport interface ComponentPrimitiveField extends BaseComponentField {\n /** Discriminator for the union type */\n type: Exclude<ComponentFieldType, 'array'>;\n}\n\n/**\n * Definition for an array field in a component schema.\n * @category Types\n */\nexport interface ComponentArrayField extends BaseComponentField {\n /** Discriminator for the union type */\n type: 'array';\n /** \n * The element type for array fields (required).\n * Specifies what type each element in the array should be.\n * Can be a built-in type or a custom component type name.\n */\n arrayElementType: ArrayElementType;\n}\n\n/**\n * Discriminated union for field definitions in a component schema.\n * Use type guards to distinguish between primitive and array fields.\n * @category Types\n */\nexport type ComponentFieldDefinition = ComponentPrimitiveField | ComponentArrayField;\n\n/**\n * Schema definition for a component type that can be defined at runtime.\n * @category Types\n */\nexport interface ComponentSchema {\n /** The name of the component type (using Symbol for type safety) */\n componentName: ComponentName;\n /** Array of field definitions */\n fields: ComponentFieldDefinition[];\n}\n\n/**\n * Helper function to create a component name from a string.\n * This creates a unique symbol for the component name.\n * \n * @param name - The string name for the component\n * @returns A unique symbol for the component name\n * \n * @example\n * ```typescript\n * const Position = createComponentName('Position');\n * coordinator.registerComponent<Position>(Position);\n * ```\n * \n * @category Utilities\n */\nexport function createComponentName(name: string): ComponentName {\n return Symbol(name);\n}\n\n/**\n * Helper function to get the string description from a component name symbol.\n * Useful for debugging and serialization.\n * \n * @param componentName - The component name symbol\n * @returns The string description of the symbol\n * \n * @category Utilities\n */\nexport function getComponentNameString(componentName: ComponentName): string {\n return componentName.description || componentName.toString();\n}\n\n/**\n * Helper function to create a component name using Symbol.for().\n * This creates a global symbol that can be looked up by string key,\n * which is useful for serialization and cross-module access.\n * \n * @param key - The string key for the global symbol\n * @returns A global symbol for the component name\n * \n * @example\n * ```typescript\n * const Position = createGlobalComponentName('Position');\n * coordinator.registerComponent<Position>(Position);\n * // Can be retrieved later with Symbol.for('Position')\n * ```\n * \n * @category Utilities\n */\nexport function createGlobalComponentName(key: string): ComponentName {\n return Symbol.for(key);\n}\n\n/**\n * Helper function to create a system name from a string.\n * This creates a unique symbol for the system name.\n * \n * @param name - The string name for the system\n * @returns A unique symbol for the system name\n * \n * @example\n * ```typescript\n * const Movement = createSystemName('Movement');\n * coordinator.registerSystem(Movement, movementSystem);\n * ```\n * \n * @category Utilities\n */\nexport function createSystemName(name: string): SystemName {\n return Symbol(name);\n}\n\n/**\n * Helper function to get the string description from a system name symbol.\n * Useful for debugging and serialization.\n * \n * @param systemName - The system name symbol\n * @returns The string description of the symbol\n * \n * @category Utilities\n */\nexport function getSystemNameString(systemName: SystemName): string {\n return systemName.description || systemName.toString();\n}\n\n/**\n * Helper function to create a system name using Symbol.for().\n * This creates a global symbol that can be looked up by string key,\n * which is useful for serialization and cross-module access.\n * \n * @param key - The string key for the global symbol\n * @returns A global symbol for the system name\n * \n * @example\n * ```typescript\n * const Movement = createGlobalSystemName('Movement');\n * coordinator.registerSystem(Movement, movementSystem);\n * // Can be retrieved later with Symbol.for('Movement')\n * ```\n * \n * @category Utilities\n */\nexport function createGlobalSystemName(key: string): SystemName {\n return Symbol.for(key);\n}\n\n/**\n * Serialized representation of an array element type for JSON storage.\n * @category Types\n */\ntype SerializedArrayElementType = \n | { kind: 'builtin'; type: Exclude<ComponentFieldType, 'array'> }\n | { kind: 'custom'; typeName: string };\n\n/**\n * Serialized representation of a component field for JSON storage.\n * @category Types\n */\ntype SerializedComponentField = \n | (Omit<ComponentPrimitiveField, 'type'> & { type: Exclude<ComponentFieldType, 'array'> })\n | (Omit<ComponentArrayField, 'arrayElementType'> & { arrayElementType: SerializedArrayElementType });\n\n/**\n * Serialized representation of a component schema for JSON storage.\n * Component names are stored as strings (using Symbol.for keys for global symbols).\n * @category Types\n */\nexport interface SerializedComponentSchema {\n componentName: string;\n fields: SerializedComponentField[];\n}\n\n/**\n * Serialized representation of an entity's component data.\n * @category Types\n */\nexport interface SerializedEntity {\n /** The entity ID */\n entity: Entity;\n /** Map of component names (as strings) to their serialized data */\n components: Record<string, unknown>;\n}\n\n/**\n * Serialized representation of the entire ECS state.\n * @category Types\n */\nexport interface SerializedECSState {\n /** Array of all entities with their component data */\n entities: SerializedEntity[];\n /** Optional: Array of component schemas (if using schema-based components) */\n schemas?: SerializedComponentSchema[];\n}\n\n/**\n * Serialize a component schema to a JSON-compatible format.\n * Note: Only works with global symbols (created via Symbol.for).\n * \n * @param schema - The component schema to serialize\n * @returns A serializable representation of the schema\n * @throws Error if component name is not a global symbol\n * \n * @category Utilities\n */\nexport function serializeComponentSchema(schema: ComponentSchema): SerializedComponentSchema {\n const key = Symbol.keyFor(schema.componentName);\n if (key === undefined) {\n throw new Error(`Cannot serialize schema: component name is not a global symbol. Use createGlobalComponentName() or Symbol.for() to create component names.`);\n }\n \n return {\n componentName: key,\n fields: schema.fields.map(field => {\n if (field.type === 'array') {\n if (field.arrayElementType.kind === 'custom') {\n const customKey = Symbol.keyFor(field.arrayElementType.typeName);\n if (customKey === undefined) {\n throw new Error(`Cannot serialize schema: custom array element type is not a global symbol.`);\n }\n return {\n ...field,\n arrayElementType: {\n kind: 'custom' as const,\n typeName: customKey\n }\n } as SerializedComponentField;\n } else {\n return {\n ...field,\n arrayElementType: field.arrayElementType\n } as SerializedComponentField;\n }\n }\n return field as SerializedComponentField;\n })\n };\n}\n\n/**\n * Deserialize a component schema from a JSON-compatible format.\n * \n * @param serialized - The serialized schema\n * @returns The component schema with symbols restored\n * \n * @category Utilities\n */\nexport function deserializeComponentSchema(serialized: SerializedComponentSchema): ComponentSchema {\n return {\n componentName: Symbol.for(serialized.componentName),\n fields: serialized.fields.map(field => {\n if (field.type === 'array') {\n if (field.arrayElementType.kind === 'custom') {\n return {\n ...field,\n arrayElementType: {\n kind: 'custom' as const,\n typeName: Symbol.for(field.arrayElementType.typeName)\n }\n } as ComponentArrayField;\n } else {\n return {\n ...field,\n arrayElementType: field.arrayElementType\n } as ComponentArrayField;\n }\n }\n return field as ComponentPrimitiveField;\n })\n };\n}\n\n/**\n * Manages entity lifecycle and signatures.\n *\n * @remarks\n * The EntityManager handles:\n * - Creating new entities (recycling IDs from a pool)\n * - Destroying entities (returning IDs to the pool)\n * - Storing and updating component signatures for each entity\n *\n * Entities are represented as simple numbers (IDs) and the manager maintains\n * a signature (bit field) for each entity indicating which components it has.\n *\n * @category Managers\n */\nexport class EntityManager {\n\n private _availableEntities: Entity[] = [];\n private _signatures: ComponentSignature[] = [];\n private _maxEntities: number;\n\n private _livingEntityCount = 0;\n\n constructor(maxEntities: number = MAX_ENTITIES) {\n this._maxEntities = maxEntities;\n for (let i = 0; i < this._maxEntities; i++) {\n this._availableEntities.push(i);\n this._signatures.push(0);\n }\n }\n\n createEntity(): Entity {\n if(this._livingEntityCount >= this._maxEntities) {\n throw new Error('Max entities reached');\n }\n const entity = this._availableEntities.shift();\n if(entity === undefined) {\n throw new Error('No available entities');\n }\n this._signatures[entity] = 0;\n this._livingEntityCount++;\n return entity;\n }\n\n destroyEntity(entity: Entity): void {\n if(entity >= this._maxEntities || entity < 0) {\n throw new Error('Invalid entity out of range');\n }\n this._signatures[entity] = 0;\n this._availableEntities.push(entity);\n this._livingEntityCount--;\n }\n\n setSignature(entity: Entity, signature: ComponentSignature): void {\n if(entity >= this._maxEntities || entity < 0) {\n throw new Error('Invalid entity out of range');\n }\n this._signatures[entity] = signature;\n }\n\n getSignature(entity: Entity): ComponentSignature | null {\n if(entity >= this._maxEntities || entity < 0) {\n return null;\n }\n return this._signatures[entity];\n }\n\n /**\n * Get all living entities (entities that are currently active, not in the available pool).\n * @returns Array of all living entity IDs\n */\n getAllLivingEntities(): Entity[] {\n const livingEntities: Entity[] = [];\n const availableSet = new Set(this._availableEntities);\n \n for (let i = 0; i < this._maxEntities; i++) {\n if (!availableSet.has(i)) {\n livingEntities.push(i);\n }\n }\n \n return livingEntities;\n }\n\n /**\n * Check if an entity exists (is currently active, not in the available pool).\n * @param entity - The entity ID to check\n * @returns true if the entity exists, false otherwise\n */\n entityExists(entity: Entity): boolean {\n if (entity >= this._maxEntities || entity < 0) {\n return false;\n }\n // An entity exists if it's not in the available pool\n return !this._availableEntities.includes(entity);\n }\n}\n\ntype Tuple<T, N extends number> = N extends N\n ? number extends N\n ? T[]\n : _TupleOf<T, N, []>\n : never;\n\ntype _TupleOf<T, N extends number, R extends unknown[]> = R['length'] extends N\n ? R\n : _TupleOf<T, N, [...R, T]>;\n\n// Usage\n\n/**\n * Internal interface for component array lifecycle hooks.\n * @internal\n */\nexport interface CArray {\n entityDestroyed(entity: Entity): void;\n}\n\n/**\n * Efficient sparse-set data structure for storing component data.\n *\n * @remarks\n * ComponentArray uses a sparse-set implementation for O(1) insertion, deletion,\n * and lookup while maintaining dense packing for cache-efficient iteration.\n *\n * The sparse-set consists of:\n * - **Dense array**: Packed component data for iteration\n * - **Sparse array**: Maps entity ID to dense array index\n * - **Reverse array**: Maps dense array index back to entity ID\n *\n * This structure allows fast component access by entity ID and fast iteration\n * over all components without gaps.\n *\n * @typeParam T - The component data type\n *\n * @category Data Structures\n */\nexport class ComponentArray<T> implements CArray {\n\n private denseArray: T[]; // packed array of data\n private sparse: (Entity | null)[]; // maps entity to index in dense array\n private reverse: (Entity | null)[]; // maps index in dense array to entity\n private _count: number;\n\n constructor(maxEntities: number) {\n this._count = 0;\n this.denseArray = new Array(maxEntities);\n this.sparse = new Array(maxEntities);\n this.reverse = new Array(maxEntities);\n }\n\n insertData(entity: Entity, data: T): void {\n if(this.getData(entity) !== null) {\n this.removeData(entity);\n }\n if(this.sparse.length < entity){\n // resize the array for the new entity but normally this should not happen\n this.sparse = [...this.sparse, ...new Array(entity - this.sparse.length).fill(null)];\n }\n\n this.denseArray[this._count] = data;\n this.reverse[this._count] = entity;\n this.sparse[entity] = this._count;\n this._count++;\n }\n\n getData(entity: Entity): T | null {\n if(this.sparse.length <= entity){\n return null;\n }\n\n const denseIndex = this.sparse[entity];\n if(denseIndex === undefined || denseIndex === null || denseIndex >= this._count){\n return null;\n }\n\n if(this.reverse[denseIndex] !== entity) {\n return null;\n }\n\n return this.denseArray[denseIndex];\n }\n\n removeData(entity: Entity): void {\n const denseIndex = this.sparse[entity];\n if(denseIndex === undefined || denseIndex === null || denseIndex >= this._count){\n return;\n }\n\n const lastEntity = this.reverse[this._count - 1];\n\n if(lastEntity === null) {\n return;\n }\n\n this.denseArray[denseIndex] = this.denseArray[this._count - 1];\n this.reverse[denseIndex] = lastEntity;\n this.sparse[lastEntity] = denseIndex;\n this.sparse[entity] = null;\n\n this._count--;\n }\n\n entityDestroyed(entity: Entity): void {\n this.removeData(entity);\n }\n\n /**\n * Get all entities that have this component.\n * @returns Array of entity IDs that have this component\n */\n getAllEntities(): Entity[] {\n const entities: Entity[] = [];\n for (let i = 0; i < this._count; i++) {\n const entity = this.reverse[i];\n if (entity !== null && entity !== undefined) {\n entities.push(entity);\n }\n }\n return entities;\n }\n\n /**\n * Get the count of entities with this component.\n * @returns Number of entities with this component\n */\n getCount(): number {\n return this._count;\n }\n}\n\n/**\n * Manages component registration and component data storage.\n *\n * @remarks\n * The ComponentManager handles:\n * - Registering new component types and assigning unique type IDs\n * - Creating ComponentArray storage for each component type\n * - Adding, removing, and querying component data for entities\n * - Cleaning up component data when entities are destroyed\n *\n * Each component type gets a unique ID (0-31) and its own ComponentArray\n * for efficient storage and retrieval.\n *\n * @category Managers\n */\nexport class ComponentManager {\n\n private _componentNameToTypeMap: Map<ComponentName, {componentType: ComponentType, componentArray: CArray}> = new Map();\n private _nextAvailableComponentType: ComponentType = 0;\n private _schemas: Map<ComponentName, ComponentSchema> = new Map();\n\n getRegisteredComponentNames(): ComponentName[] {\n return Array.from(this._componentNameToTypeMap.keys());\n }\n\n /**\n * Get all entities that have a specific component.\n * @param componentName - The name of the component type\n * @returns Array of entity IDs that have this component, or empty array if component not registered\n */\n getAllEntitiesWithComponent(componentName: ComponentName): Entity[] {\n const component = this._componentNameToTypeMap.get(componentName);\n if (component === undefined) {\n return [];\n }\n return (component.componentArray as ComponentArray<unknown>).getAllEntities();\n }\n\n /**\n * Get the schema for a component type, if it was registered with a schema.\n * @param componentName - The name of the component type\n * @returns The component schema or null if not found\n */\n getComponentSchema(componentName: ComponentName): ComponentSchema | null {\n return this._schemas.get(componentName) ?? null;\n }\n\n /**\n * Get all registered component schemas.\n * @returns Array of all component schemas\n */\n getAllComponentSchemas(): ComponentSchema[] {\n return Array.from(this._schemas.values());\n }\n \n registerComponent<T>(componentName: ComponentName){\n // Idempotent: if component is already registered, do nothing\n if(this._componentNameToTypeMap.has(componentName)) {\n return;\n }\n const componentType = this._nextAvailableComponentType;\n this._componentNameToTypeMap.set(componentName, {componentType, componentArray: new ComponentArray<T>(MAX_ENTITIES)});\n this._nextAvailableComponentType++;\n }\n\n getComponentType(componentName: ComponentName): ComponentType | null {\n return this._componentNameToTypeMap.get(componentName)?.componentType ?? null;\n }\n\n addComponentToEntity<T>(componentName: ComponentName, entity: Entity, component: T){\n const componentArray = this._getComponentArray<T>(componentName);\n if(componentArray === null) {\n return;\n }\n componentArray.insertData(entity, component);\n }\n\n removeComponentFromEntity<T>(componentName: ComponentName, entity: Entity){\n const componentArray = this._getComponentArray<T>(componentName);\n if(componentArray === null) {\n return;\n }\n componentArray.removeData(entity);\n }\n\n getComponentFromEntity<T>(componentName: ComponentName, entity: Entity): T | null {\n const componentArray = this._getComponentArray<T>(componentName);\n if(componentArray === null) {\n return null;\n }\n return componentArray.getData(entity);\n }\n\n entityDestroyed(entity: Entity){\n for(const component of this._componentNameToTypeMap.values()){\n component.componentArray.entityDestroyed(entity);\n }\n }\n\n private _getComponentArray<T>(componentName: ComponentName): ComponentArray<T> | null {\n const component = this._componentNameToTypeMap.get(componentName);\n if(component === undefined) {\n console.warn(`Component ${getComponentNameString(componentName)} not registered`);\n return null;\n }\n return component.componentArray as ComponentArray<T>;\n }\n\n componentIsCustomSchema(componentName: ComponentName): boolean {\n return this._schemas.has(componentName);\n }\n\n /**\n * Register a component with a runtime-defined schema.\n * This allows components to be defined dynamically (e.g., through a GUI).\n * \n * @param schema - The component schema definition\n * @throws Error if schema validation fails\n */\n registerComponentWithSchema(schema: ComponentSchema): void {\n // Validate schema\n if (!schema.componentName) {\n throw new Error('Component schema must have a componentName');\n }\n if (!schema.fields || schema.fields.length === 0) {\n throw new Error('Component schema must have at least one field');\n }\n\n // Check for duplicate field names and validate array types\n const fieldNames = new Set<string>();\n for (const field of schema.fields) {\n if (fieldNames.has(field.name)) {\n throw new Error(`Duplicate field name \"${field.name}\" in schema for component \"${getComponentNameString(schema.componentName)}\"`);\n }\n fieldNames.add(field.name);\n\n // Validate array fields - TypeScript ensures arrayElementType is required for array fields\n if (field.type === 'array') {\n // Custom types are validated when they're registered\n // No need to check for empty string since we're using symbols\n }\n }\n\n // Idempotent: if component schema is already registered, do nothing\n if (this._schemas.has(schema.componentName)) {\n return;\n }\n \n // Register the component type (if not already registered)\n if (!this._componentNameToTypeMap.has(schema.componentName)) {\n const componentType = this._nextAvailableComponentType;\n this._componentNameToTypeMap.set(schema.componentName, {\n componentType,\n componentArray: new ComponentArray<Record<string, unknown>>(MAX_ENTITIES)\n });\n this._nextAvailableComponentType++;\n }\n // Store the schema\n this._schemas.set(schema.componentName, schema);\n\n }\n\n /**\n * Create a component instance from a schema with default values.\n * \n * @param componentName - The name of the component type\n * @param overrides - Optional values to override defaults\n * @returns A component instance with all fields initialized\n * @throws Error if component is not registered with a schema\n */\n createComponentFromSchema(componentName: ComponentName, overrides: Record<string, unknown> = {}): Record<string, unknown> {\n const schema = this._schemas.get(componentName);\n if (!schema) {\n throw new Error(`Component \"${getComponentNameString(componentName)}\" is not registered with a schema`);\n }\n\n const component: Record<string, unknown> = {};\n \n for (const field of schema.fields) {\n if (overrides.hasOwnProperty(field.name)) {\n component[field.name] = overrides[field.name];\n } else if (field.defaultValue !== undefined) {\n component[field.name] = field.defaultValue;\n } else if (!field.optional) {\n // Required field with no default - use type-appropriate default\n if (field.type === 'array') {\n component[field.name] = this._getDefaultValueForType(field.type, field.arrayElementType);\n } else {\n component[field.name] = this._getDefaultValueForType(field.type);\n }\n }\n // Optional fields without defaults are omitted\n }\n\n return component;\n }\n\n /**\n * Validate component data against its schema.\n * \n * @param componentName - The name of the component type\n * @param data - The component data to validate\n * @returns true if valid, false otherwise\n */\n validateComponentData(componentName: ComponentName, data: unknown): boolean {\n const schema = this._schemas.get(componentName);\n if (!schema) {\n // No schema means no validation required\n return true;\n }\n\n if (typeof data !== 'object' || data === null || Array.isArray(data)) {\n return false;\n }\n\n const dataObj = data as Record<string, unknown>;\n\n // Check all required fields are present\n for (const field of schema.fields) {\n if (!field.optional && !dataObj.hasOwnProperty(field.name)) {\n return false;\n }\n\n // If field is present, validate its type\n if (dataObj.hasOwnProperty(field.name)) {\n if (field.type === 'array') {\n if (!this._validateFieldType(field.type, dataObj[field.name], field.arrayElementType)) {\n return false;\n }\n } else {\n if (!this._validateFieldType(field.type, dataObj[field.name])) {\n return false;\n }\n }\n }\n }\n\n // Check for extra fields not in schema (optional - could be configurable)\n // For now, we allow extra fields\n\n return true;\n }\n\n /**\n * Get default value for a field type.\n */\n private _getDefaultValueForType(type: ComponentFieldType, arrayElementType?: ArrayElementType): unknown {\n switch (type) {\n case 'string':\n return '';\n case 'number':\n return 0;\n case 'boolean':\n return false;\n case 'object':\n return {};\n case 'array':\n return []; // Empty array - element type validation happens at runtime\n case 'entity':\n return null;\n default:\n return null;\n }\n }\n\n /**\n * Validate that a value matches the expected field type.\n * \n * @param type - The field type\n * @param value - The value to validate\n * @param arrayElementType - The element type for array fields\n * @returns true if the value matches the expected type\n */\n private _validateFieldType(type: ComponentFieldType, value: unknown, arrayElementType?: ArrayElementType): boolean {\n switch (type) {\n case 'string':\n return typeof value === 'string';\n case 'number':\n return typeof value === 'number';\n case 'boolean':\n return typeof value === 'boolean';\n case 'object':\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n case 'array':\n if (!Array.isArray(value)) {\n return false;\n }\n // If arrayElementType is specified, validate each element\n if (arrayElementType) {\n if (arrayElementType.kind === 'builtin') {\n return (value as unknown[]).every(element => \n this._validateFieldType(arrayElementType.type, element)\n );\n } else {\n // Custom type: validate against the referenced component schema\n const customSchema = this._schemas.get(arrayElementType.typeName);\n if (!customSchema) {\n // Custom type not found - could be strict or lenient\n // For now, we'll validate that it's an object\n return (value as unknown[]).every(element => \n typeof element === 'object' && element !== null && !Array.isArray(element)\n );\n }\n // Validate each element against the custom schema\n return (value as unknown[]).every(element => \n this.validateComponentData(arrayElementType.typeName, element)\n );\n }\n }\n return true;\n case 'entity':\n return typeof value === 'number' || value === null;\n default:\n return false;\n }\n }\n\n /**\n * Add a component to an entity with schema validation.\n * \n * @param componentName - The name of the component type\n * @param entity - The entity to add the component to\n * @param component - The component data\n * @param validate - Whether to validate against schema (default: true)\n * @throws Error if validation fails\n */\n addComponentToEntityWithSchema(componentName: ComponentName, entity: Entity, component: Record<string, unknown>, validate: boolean = true): void {\n if (validate && !this.validateComponentData(componentName, component)) {\n throw new Error(`Component data for \"${getComponentNameString(componentName)}\" does not match its schema`);\n }\n this.addComponentToEntity<Record<string, unknown>>(componentName, entity, component);\n }\n\n}\n\n/**\n * System interface for processing entities with specific component combinations.\n *\n * @remarks\n * A System maintains a set of entities that match its component signature.\n * The ECS automatically updates this set when entities are created, destroyed,\n * or have their components modified.\n *\n * Systems contain only the logic for processing entities - the `entities` set\n * is automatically managed by the SystemManager.\n *\n * @example\n * ```typescript\n * const Position = createComponentName('Position');\n * const Velocity = createComponentName('Velocity');\n * const movementSystem: System = {\n * entities: new Set()\n * };\n *\n * // System logic (called in game loop)\n * function updateMovement(deltaTime: number) {\n * movementSystem.entities.forEach(entity => {\n * const pos = ecs.getComponentFromEntity<Position>(Position, entity);\n * const vel = ecs.getComponentFromEntity<Velocity>(Velocity, entity);\n * if (pos && vel) {\n * pos.x += vel.x * deltaTime;\n * pos.y += vel.y * deltaTime;\n * }\n * });\n * }\n * ```\n *\n * @category Types\n */\nexport interface System {\n entities: Set<Entity>;\n}\n\n/**\n * Manages system registration and entity-system matching.\n *\n * @remarks\n * The SystemManager handles:\n * - Registering systems with their component signature requirements\n * - Maintaining the set of entities that match each system's signature\n * - Automatically adding/removing entities from systems when signatures change\n * - Cleaning up system entity sets when entities are destroyed\n *\n * When an entity's component signature changes (components added/removed),\n * the SystemManager checks all registered systems and updates their entity sets.\n * An entity is added to a system's set if its signature contains all components\n * required by the system's signature.\n *\n * @category Managers\n */\nexport class SystemManager {\n private _systems: Map<SystemName, {system: System, signature: ComponentSignature}> = new Map();\n\n registerSystem(systemName: SystemName, system: System){\n // Idempotent: if system is already registered, do nothing\n if(this._systems.has(systemName)) {\n return;\n }\n this._systems.set(systemName, {system, signature: 0});\n }\n\n setSignature(systemName: SystemName, signature: ComponentSignature){\n if(!this._systems.has(systemName)) {\n console.warn(`System ${getSystemNameString(systemName)} not registered`);\n return;\n }\n const system = this._systems.get(systemName);\n if(system === undefined) {\n console.warn(`System ${getSystemNameString(systemName)} not registered`);\n return;\n }\n system.signature = signature;\n }\n\n entityDestroyed(entity: Entity){\n for(const system of this._systems.values()){\n system.system.entities.delete(entity);\n }\n }\n\n entitySignatureChanged(entity: Entity, signature: ComponentSignature){\n for(const system of this._systems.values()){\n const systemSignature = system.signature;\n if((systemSignature & signature) === systemSignature){\n system.system.entities.add(entity);\n } else {\n system.system.entities.delete(entity);\n }\n }\n }\n\n getSystem<T extends System>(systemName: SystemName): T | null {\n const system = this._systems.get(systemName);\n if(system === undefined) {\n return null;\n }\n return system.system as T;\n }\n}\n\n/**\n * Main ECS coordinator that manages entities, components, and systems.\n *\n * @remarks\n * The Coordinator is the central API for working with the ECS. It provides a unified\n * interface for:\n * - Creating and destroying entities\n * - Registering and managing components\n * - Registering and configuring systems\n * - Querying component data\n *\n * The Coordinator automatically keeps entity signatures up-to-date and notifies\n * systems when entities match their component requirements.\n *\n * @example\n * Complete ECS workflow\n * ```typescript\n * const ecs = new Coordinator();\n *\n * // Setup\n * ecs.registerComponent<Position>('Position');\n * ecs.registerComponent<Velocity>('Velocity');\n *\n * // Create entity\n * const entity = ecs.createEntity();\n * ecs.addComponentToEntity('Position', entity, { x: 0, y: 0 });\n * ecs.addComponentToEntity('Velocity', entity, { x: 1, y: 0 });\n *\n * // Update\n * const pos = ecs.getComponentFromEntity<Position>('Position', entity);\n * const vel = ecs.getComponentFromEntity<Velocity>('Velocity', entity);\n * if (pos && vel) {\n * pos.x += vel.x;\n * pos.y += vel.y;\n * }\n *\n * // Cleanup\n * ecs.destroyEntity(entity);\n * ```\n *\n * @category Core\n */\nexport class Coordinator {\n private _entityManager: EntityManager;\n private _componentManager: ComponentManager;\n private _systemManager: SystemManager;\n\n constructor(){\n this._entityManager = new EntityManager();\n this._componentManager = new ComponentManager();\n this._systemManager = new SystemManager();\n }\n\n createEntity(): Entity {\n return this._entityManager.createEntity();\n }\n\n destroyEntity(entity: Entity): void {\n this._entityManager.destroyEntity(entity);\n this._componentManager.entityDestroyed(entity);\n this._systemManager.entityDestroyed(entity);\n }\n\n registerComponent<T>(componentName: ComponentName): void {\n this._componentManager.registerComponent<T>(componentName);\n }\n\n addComponentToEntity<T>(componentName: ComponentName, entity: Entity, component: T): void {\n this._componentManager.addComponentToEntity<T>(componentName, entity, component);\n let signature = this._entityManager.getSignature(entity);\n if(signature === null) {\n signature = 0;\n }\n const componentType = this._componentManager.getComponentType(componentName);\n if(componentType === null) {\n console.warn(`Component ${getComponentNameString(componentName)} not registered`);\n return;\n }\n signature |= 1 << componentType;\n this._entityManager.setSignature(entity, signature);\n this._systemManager.entitySignatureChanged(entity, signature);\n }\n\n removeComponentFromEntity<T>(componentName: ComponentName, entity: Entity): void {\n this._componentManager.removeComponentFromEntity<T>(componentName, entity);\n let signature = this._entityManager.getSignature(entity);\n if(signature === null) {\n signature = 0;\n }\n const componentType = this._componentManager.getComponentType(componentName);\n if(componentType === null) {\n return;\n }\n signature &= ~(1 << componentType);\n this._entityManager.setSignature(entity, signature);\n this._systemManager.entitySignatureChanged(entity, signature);\n }\n\n getComponentFromEntity<T>(componentName: ComponentName, entity: Entity): T | null {\n return this._componentManager.getComponentFromEntity<T>(componentName, entity);\n }\n\n getComponentType(componentName: ComponentName): ComponentType | null {\n return this._componentManager.getComponentType(componentName) ?? null;\n }\n\n registerSystem(systemName: SystemName, system: System): void {\n this._systemManager.registerSystem(systemName, system);\n }\n\n setSystemSignature(systemName: SystemName, signature: ComponentSignature): void {\n this._systemManager.setSignature(systemName, signature);\n }\n\n getSystem<T extends System>(systemName: SystemName): T | null {\n return this._systemManager.getSystem<T>(systemName) ?? null;\n }\n\n /**\n * Register a component with a runtime-defined schema.\n * This allows components to be defined dynamically (e.g., through a GUI).\n * \n * @param schema - The component schema definition\n * @throws Error if schema validation fails\n * \n * @example\n * ```typescript\n * const coordinator = new Coordinator();\n * \n * // Define a component schema at runtime\n * coordinator.registerComponentWithSchema({\n * componentName: 'PlayerStats',\n * fields: [\n * { name: 'health', type: 'number', defaultValue: 100 },\n * { name: 'name', type: 'string', defaultValue: 'Player' },\n * { name: 'isAlive', type: 'boolean', defaultValue: true },\n * { name: 'inventory', type: 'array', defaultValue: [] }\n * ]\n * });\n * \n * // Create an entity with the component\n * const entity = coordinator.createEntity();\n * const component = coordinator.createComponentFromSchema('PlayerStats', { health: 150 });\n * coordinator.addComponentToEntityWithSchema('PlayerStats', entity, component);\n * ```\n */\n registerComponentWithSchema(schema: ComponentSchema): void {\n this._componentManager.registerComponentWithSchema(schema);\n }\n\n /**\n * Get the schema for a component type, if it was registered with a schema.\n * \n * @param componentName - The name of the component type\n * @returns The component schema or null if not found\n */\n getComponentSchema(componentName: ComponentName): ComponentSchema | null {\n return this._componentManager.getComponentSchema(componentName);\n }\n\n /**\n * Get the property field names of a component.\n * \n * This method works in two ways:\n * 1. If the component was registered with a schema, returns field names from the schema\n * 2. If no schema exists, attempts to extract property names from an actual component instance\n * (requires at least one entity to have an instance of the component)\n * \n * @param componentName - The name of the component type\n * @returns Array of property field names, or empty array if component has no schema and no instances exist\n * \n * @example\n * ```typescript\n * const coordinator = new Coordinator();\n * \n * // Method 1: Using schema\n * coordinator.registerComponentWithSchema({\n * componentName: 'PlayerStats',\n * fields: [\n * { name: 'health', type: 'number', defaultValue: 100 },\n * { name: 'name', type: 'string', defaultValue: 'Player' },\n * { name: 'isAlive', type: 'boolean', defaultValue: true }\n * ]\n * });\n * const fieldNames1 = coordinator.getComponentPropertyNames('PlayerStats');\n * console.log(fieldNames1); // ['health', 'name', 'isAlive']\n * \n * // Method 2: From component instance\n * type LocationComponent = { location: Entity; sortIndex: number };\n * coordinator.registerComponent<LocationComponent>('LocationComponent');\n * const entity = coordinator.createEntity();\n * coordinator.addComponentToEntity('LocationComponent', entity, { \n * location: otherEntity, \n * sortIndex: 0 \n * });\n * const fieldNames2 = coordinator.getComponentPropertyNames('LocationComponent');\n * console.log(fieldNames2); // ['location', 'sortIndex']\n * ```\n */\n getComponentPropertyNames(componentName: ComponentName): string[] {\n // First, try to get from schema if available\n const schema = this.getComponentSchema(componentName);\n if (schema) {\n return schema.fields.map(field => field.name);\n }\n \n // If no schema, try to extract from an actual component instance\n const entitiesWithComponent = this._componentManager.getAllEntitiesWithComponent(componentName);\n if (entitiesWithComponent.length === 0) {\n return [];\n }\n \n // Get the first entity's component instance\n const component = this._componentManager.getComponentFromEntity(componentName, entitiesWithComponent[0]);\n if (component === null) {\n return [];\n }\n \n // Extract property names from the component object\n if (typeof component === 'object' && component !== null && !Array.isArray(component)) {\n return Object.keys(component);\n }\n \n return [];\n }\n\n /**\n * Get all registered component schemas.\n * \n * @returns Array of all component schemas\n */\n getAllComponentSchemas(): ComponentSchema[] {\n return this._componentManager.getAllComponentSchemas();\n }\n\n /**\n * Create a component instance from a schema with default values.\n * \n * @param componentName - The name of the component type\n * @param overrides - Optional values to override defaults\n * @returns A component instance with all fields initialized\n * @throws Error if component is not registered with a schema\n * \n * @example\n * ```typescript\n * // Create component with all defaults\n * const component1 = coordinator.createComponentFromSchema('PlayerStats');\n * \n * // Create component with some overrides\n * const component2 = coordinator.createComponentFromSchema('PlayerStats', {\n * health: 200,\n * name: 'SuperPlayer'\n * });\n * ```\n */\n createComponentFromSchema(componentName: ComponentName, overrides: Record<string, unknown> = {}): Record<string, unknown> {\n return this._componentManager.createComponentFromSchema(componentName, overrides);\n }\n\n /**\n * Validate component data against its schema.\n * \n * @param componentName - The name of the component type\n * @param data - The component data to validate\n * @returns true if valid, false otherwise\n */\n validateComponentData(componentName: ComponentName, data: unknown): boolean {\n return this._componentManager.validateComponentData(componentName, data);\n }\n\n /**\n * Add a component to an entity with schema validation.\n * \n * @param componentName - The name of the component type\n * @param entity - The entity to add the component to\n * @param component - The component data\n * @param validate - Whether to validate against schema (default: true)\n * @throws Error if validation fails\n */\n addComponentToEntityWithSchema(componentName: ComponentName, entity: Entity, component: Record<string, unknown>, validate: boolean = true): void {\n this._componentManager.addComponentToEntityWithSchema(componentName, entity, component, validate);\n let signature = this._entityManager.getSignature(entity);\n if(signature === null) {\n signature = 0;\n }\n const componentType = this._componentManager.getComponentType(componentName);\n if(componentType === null) {\n console.warn(`Component ${getComponentNameString(componentName)} not registered`);\n return;\n }\n signature |= 1 << componentType;\n this._entityManager.setSignature(entity, signature);\n this._systemManager.entitySignatureChanged(entity, signature);\n }\n\n /**\n * Get all living entities in the ECS.\n * @returns Array of all entity IDs that are currently active\n * \n * @example\n * ```typescript\n * const entities = coordinator.getAllEntities();\n * console.log(`Total entities: ${entities.length}`);\n * ```\n */\n getAllEntities(): Entity[] {\n return this._entityManager.getAllLivingEntities();\n }\n\n /**\n * Check if an entity exists in the coordinator.\n * @param entity - The entity ID to check\n * @returns true if the entity exists, false otherwise\n * \n * @example\n * ```typescript\n * const entity = coordinator.createEntity();\n * if (coordinator.entityExists(entity)) {\n * console.log('Entity exists');\n * }\n * \n * coordinator.destroyEntity(entity);\n * if (!coordinator.entityExists(entity)) {\n * console.log('Entity no longer exists');\n * }\n * ```\n */\n entityExists(entity: Entity): boolean {\n return this._entityManager.entityExists(entity);\n }\n\n /**\n * Get all components for a specific entity.\n * @param entity - The entity ID\n * @returns Map of component names to their data, or null if entity doesn't exist\n * \n * @example\n * ```typescript\n * const components = coordinator.getEntityComponents(entity);\n * if (components) {\n * console.log('Entity components:', components);\n * }\n * ```\n */\n getEntityComponents(entity: Entity): Map<ComponentName, unknown> | null {\n const signature = this._entityManager.getSignature(entity);\n if (signature === null || signature === 0) {\n return null;\n }\n\n const components = new Map<ComponentName, unknown>();\n const componentNames = this._componentManager.getRegisteredComponentNames();\n\n for (const componentName of componentNames) {\n const componentType = this._componentManager.getComponentType(componentName);\n if (componentType === null) {\n continue;\n }\n\n // Check if entity has this component (bit is set in signature)\n if ((signature & (1 << componentType)) !== 0) {\n const componentData = this._componentManager.getComponentFromEntity(componentName, entity);\n if (componentData !== null) {\n components.set(componentName, componentData);\n }\n }\n }\n\n return components;\n }\n\n /**\n * Get the entire state of the ECS: all entities with all their component values.\n * @returns Object containing all entities and their components\n * \n * @example\n * ```typescript\n * const state = coordinator.getFullState();\n * console.log(`Total entities: ${state.entities.length}`);\n * state.entities.forEach(entityData => {\n * console.log(`Entity ${entityData.entity} has ${Object.keys(entityData.components).length} components`);\n * });\n * ```\n */\n getFullState(): { entities: Array<{ entity: Entity; components: Map<ComponentName, unknown> }> } {\n const allEntities = this.getAllEntities();\n const entities = allEntities.map(entity => ({\n entity,\n components: this.getEntityComponents(entity) ?? new Map<ComponentName, unknown>()\n }));\n\n return { entities };\n }\n\n /**\n * Serialize the entire ECS state to a JSON-compatible format.\n * Note: Only works with global symbols (created via Symbol.for or createGlobalComponentName).\n * \n * @returns A serializable representation of the ECS state\n * @throws Error if any component name is not a global symbol\n * \n * @example\n * ```typescript\n * const serialized = coordinator.serialize();\n * const json = JSON.stringify(serialized);\n * // Save to file or send over network\n * ```\n */\n serialize(): SerializedECSState {\n const allEntities = this.getAllEntities();\n const serializedEntities: SerializedEntity[] = [];\n const componentNames = this._componentManager.getRegisteredComponentNames();\n\n for (const entity of allEntities) {\n const components: Record<string, unknown> = {};\n let hasComponents = false;\n\n for (const componentName of componentNames) {\n const componentData = this._componentManager.getComponentFromEntity(componentName, entity);\n if (componentData !== null) {\n const key = Symbol.keyFor(componentName);\n if (key === undefined) {\n throw new Error(\n `Cannot serialize: component name \"${getComponentNameString(componentName)}\" is not a global symbol. ` +\n `Use createGlobalComponentName() or Symbol.for() to create component names.`\n );\n }\n components[key] = componentData;\n hasComponents = true;\n }\n }\n\n // Only include entities that have at least one component\n if (hasComponents) {\n serializedEntities.push({\n entity,\n components\n });\n }\n }\n\n // Optionally include schemas\n const schemas = this._componentManager.getAllComponentSchemas();\n const serializedSchemas = schemas.length > 0\n ? schemas.map(schema => serializeComponentSchema(schema))\n : undefined;\n\n return {\n entities: serializedEntities,\n ...(serializedSchemas && { schemas: serializedSchemas })\n };\n }\n\n /**\n * Deserialize an ECS state from a JSON-compatible format.\n * This will restore all entities and their components.\n * \n * @param serialized - The serialized ECS state\n * @param options - Options for deserialization\n * @param options.clearExisting - Whether to clear existing entities before deserializing (default: false)\n * @throws Error if component names cannot be resolved or components are not registered\n * \n * @example\n * ```typescript\n * const json = fs.readFileSync('state.json', 'utf-8');\n * const serialized = JSON.parse(json);\n * coordinator.deserialize(serialized, { clearExisting: true });\n * ```\n */\n deserialize(serialized: SerializedECSState, options: { clearExisting?: boolean } = {}): void {\n const { clearExisting = false } = options;\n\n if (clearExisting) {\n // Destroy all existing entities\n const existingEntities = this.getAllEntities();\n for (const entity of existingEntities) {\n this.destroyEntity(entity);\n }\n }\n\n // Restore entities and components\n for (const entityData of serialized.entities) {\n // Create entity (or reuse if not clearing)\n let entity: Entity;\n if (clearExisting) {\n entity = this.createEntity();\n } else {\n // Try to use the original entity ID if available\n // If entity already exists, we'll update it; otherwise create new\n const existingSignature = this._entityManager.getSignature(entityData.entity);\n if (existingSignature === null) {\n // Entity doesn't exist, we need to create it\n // But we can't control the entity ID, so we'll create a new one\n entity = this.createEntity();\n } else {\n entity = entityData.entity;\n }\n }\n\n // Add all components\n for (const [componentNameStr, componentData] of Object.entries(entityData.components)) {\n const componentName = Symbol.for(componentNameStr);\n \n // Check if component is registered\n const componentType = this._componentManager.getComponentType(componentName);\n if (componentType === null) {\n throw new Error(\n `Cannot deserialize: component \"${componentNameStr}\" is not registered. ` +\n `Register it first using registerComponent() or registerComponentWithSchema().`\n );\n }\n\n // Add component (with schema validation if schema exists)\n const schema = this._componentManager.getComponentSchema(componentName);\n if (schema) {\n this.addComponentToEntityWithSchema(\n componentName,\n entity,\n componentData as Record<string, unknown>,\n true\n );\n } else {\n this.addComponentToEntity(componentName, entity, componentData);\n }\n }\n }\n }\n}\n"
|
|
6
6
|
],
|
|
7
|
-
"mappings": "AAiGO,IAAM,EAAe,IAMf,EAAiB,GAkCvB,MAAM,CAAc,CAEf,mBAA+B,CAAC,EAChC,YAAoC,CAAC,EACrC,aAEA,mBAAqB,EAE7B,WAAW,CAAC,EAhDY,IAgDwB,CAC5C,KAAK,aAAe,EACpB,QAAS,EAAI,EAAG,EAAI,KAAK,aAAc,IACnC,KAAK,mBAAmB,KAAK,CAAC,EAC9B,KAAK,YAAY,KAAK,CAAC,EAI/B,YAAY,EAAW,CACnB,GAAG,KAAK,oBAAsB,KAAK,aAC/B,MAAU,MAAM,sBAAsB,EAE1C,IAAM,EAAS,KAAK,mBAAmB,MAAM,EAC7C,GAAG,IAAW,OACV,MAAU,MAAM,uBAAuB,EAI3C,OAFA,KAAK,YAAY,GAAU,EAC3B,KAAK,qBACE,EAGX,aAAa,CAAC,EAAsB,CAChC,GAAG,GAAU,KAAK,cAAgB,EAAS,EACvC,MAAU,MAAM,6BAA6B,EAEjD,KAAK,YAAY,GAAU,EAC3B,KAAK,mBAAmB,KAAK,CAAM,EACnC,KAAK,qBAGT,YAAY,CAAC,EAAgB,EAAqC,CAC9D,GAAG,GAAU,KAAK,cAAgB,EAAS,EACvC,MAAU,MAAM,6BAA6B,EAEjD,KAAK,YAAY,GAAU,EAG/B,YAAY,CAAC,EAA2C,CACpD,GAAG,GAAU,KAAK,cAAgB,EAAS,EACvC,OAAO,KAEX,OAAO,KAAK,YAAY,GAEhC,CAyCO,MAAM,CAAoC,CAErC,WACA,OACA,QACA,OAER,WAAW,CAAC,EAAqB,CAC7B,KAAK,OAAS,EACd,KAAK,WAAiB,MAAM,CAAW,EACvC,KAAK,OAAa,MAAM,CAAW,EACnC,KAAK,QAAc,MAAM,CAAW,EAGxC,UAAU,CAAC,EAAgB,EAAe,CACtC,GAAG,KAAK,QAAQ,CAAM,IAAM,KACxB,KAAK,WAAW,CAAM,EAE1B,GAAG,KAAK,OAAO,OAAS,EAEpB,KAAK,OAAS,CAAC,GAAG,KAAK,OAAQ,GAAO,MAAM,EAAS,KAAK,OAAO,MAAM,EAAE,KAAK,IAAI,CAAC,EAGvF,KAAK,WAAW,KAAK,QAAU,EAC/B,KAAK,QAAQ,KAAK,QAAU,EAC5B,KAAK,OAAO,GAAU,KAAK,OAC3B,KAAK,SAGT,OAAO,CAAC,EAA0B,CAC9B,GAAG,KAAK,OAAO,QAAU,EACrB,OAAO,KAGX,IAAM,EAAa,KAAK,OAAO,GAC/B,GAAG,IAAe,QAAa,IAAe,MAAQ,GAAc,KAAK,OACrE,OAAO,KAGX,GAAG,KAAK,QAAQ,KAAgB,EAC5B,OAAO,KAGX,OAAO,KAAK,WAAW,GAG3B,UAAU,CAAC,EAAsB,CAC7B,IAAM,EAAa,KAAK,OAAO,GAC/B,GAAG,IAAe,QAAa,IAAe,MAAQ,GAAc,KAAK,OACrE,OAGJ,IAAM,EAAa,KAAK,QAAQ,KAAK,OAAS,GAE9C,GAAG,IAAe,KACd,OAGJ,KAAK,WAAW,GAAc,KAAK,WAAW,KAAK,OAAS,GAC5D,KAAK,QAAQ,GAAc,EAC3B,KAAK,OAAO,GAAc,EAC1B,KAAK,OAAO,GAAU,KAEtB,KAAK,SAGT,eAAe,CAAC,EAAsB,CAClC,KAAK,WAAW,CAAM,EAE9B,CAiBO,MAAM,CAAiB,CAElB,wBAA+F,IAAI,IACnG,4BAA6C,EAErD,iBAAoB,CAAC,EAAsB,CACvC,GAAG,KAAK,wBAAwB,IAAI,CAAa,EAC7C,QAAQ,KAAK,aAAa,2DAAuE,EAErG,IAAM,EAAgB,KAAK,4BAC3B,KAAK,wBAAwB,IAAI,EAAe,CAAC,gBAAe,eAAgB,IAAI,EApOhE,GAoO8F,CAAC,CAAC,EACpH,KAAK,8BAGT,gBAAgB,CAAC,EAA6C,CAC1D,OAAO,KAAK,wBAAwB,IAAI,CAAa,GAAG,eAAiB,KAG7E,oBAAuB,CAAC,EAAuB,EAAgB,EAAa,CACxE,IAAM,EAAiB,KAAK,mBAAsB,CAAa,EAC/D,GAAG,IAAmB,KAClB,OAEJ,EAAe,WAAW,EAAQ,CAAS,EAG/C,yBAA4B,CAAC,EAAuB,EAAe,CAC/D,IAAM,EAAiB,KAAK,mBAAsB,CAAa,EAC/D,GAAG,IAAmB,KAClB,OAEJ,EAAe,WAAW,CAAM,EAGpC,sBAAyB,CAAC,EAAuB,EAA0B,CACvE,IAAM,EAAiB,KAAK,mBAAsB,CAAa,EAC/D,GAAG,IAAmB,KAClB,OAAO,KAEX,OAAO,EAAe,QAAQ,CAAM,EAGxC,eAAe,CAAC,EAAe,CAC3B,QAAU,KAAa,KAAK,wBAAwB,OAAO,EACvD,EAAU,eAAe,gBAAgB,CAAM,EAI/C,kBAAqB,CAAC,EAAiD,CAC3E,IAAM,EAAY,KAAK,wBAAwB,IAAI,CAAa,EAChE,GAAG,IAAc,OAEb,OADA,QAAQ,KAAK,aAAa,kBAA8B,EACjD,KAEX,OAAO,EAAU,eAGzB,CAuDO,MAAM,CAAc,CACf,SAAyE,IAAI,IAErF,cAAc,CAAC,EAAoB,EAAe,CAC9C,GAAG,KAAK,SAAS,IAAI,CAAU,EAAG,CAC9B,QAAQ,KAAK,UAAU,sBAA+B,EACtD,OAEJ,KAAK,SAAS,IAAI,EAAY,CAAC,SAAQ,UAAW,CAAC,CAAC,EAGxD,YAAY,CAAC,EAAoB,EAA8B,CAC3D,GAAG,CAAC,KAAK,SAAS,IAAI,CAAU,EAAG,CAC/B,QAAQ,KAAK,UAAU,kBAA2B,EAClD,OAEJ,IAAM,EAAS,KAAK,SAAS,IAAI,CAAU,EAC3C,GAAG,IAAW,OAAW,CACrB,QAAQ,KAAK,UAAU,kBAA2B,EAClD,OAEJ,EAAO,UAAY,EAGvB,eAAe,CAAC,EAAe,CAC3B,QAAU,KAAU,KAAK,SAAS,OAAO,EACrC,EAAO,OAAO,SAAS,OAAO,CAAM,EAI5C,sBAAsB,CAAC,EAAgB,EAA8B,CACjE,QAAU,KAAU,KAAK,SAAS,OAAO,EAAE,CACvC,IAAM,EAAkB,EAAO,UAC/B,IAAI,EAAkB,KAAe,EACjC,EAAO,OAAO,SAAS,IAAI,CAAM,EAEjC,OAAO,OAAO,SAAS,OAAO,CAAM,GAIpD,CA4CO,MAAM,CAAY,CACb,eACA,kBACA,eAER,WAAW,EAAE,CACT,KAAK,eAAiB,IAAI,EAC1B,KAAK,kBAAoB,IAAI,EAC7B,KAAK,eAAiB,IAAI,EAG9B,YAAY,EAAW,CACnB,OAAO,KAAK,eAAe,aAAa,EAG5C,aAAa,CAAC,EAAsB,CAChC,KAAK,eAAe,cAAc,CAAM,EACxC,KAAK,kBAAkB,gBAAgB,CAAM,EAC7C,KAAK,eAAe,gBAAgB,CAAM,EAG9C,iBAAoB,CAAC,EAA6B,CAC9C,KAAK,kBAAkB,kBAAqB,CAAa,EAG7D,oBAAuB,CAAC,EAAuB,EAAgB,EAAoB,CAC/E,KAAK,kBAAkB,qBAAwB,EAAe,EAAQ,CAAS,EAC/E,IAAI,EAAY,KAAK,eAAe,aAAa,CAAM,EACvD,GAAG,IAAc,KACb,EAAY,EAEhB,IAAM,EAAgB,KAAK,kBAAkB,iBAAiB,CAAa,EAC3E,GAAG,IAAkB,KAAM,CACvB,QAAQ,KAAK,aAAa,kBAA8B,EACxD,OAEJ,GAAa,GAAK,EAClB,KAAK,eAAe,aAAa,EAAQ,CAAS,EAClD,KAAK,eAAe,uBAAuB,EAAQ,CAAS,EAGhE,yBAA4B,CAAC,EAAuB,EAAsB,CACtE,KAAK,kBAAkB,0BAA6B,EAAe,CAAM,EACzE,IAAI,EAAY,KAAK,eAAe,aAAa,CAAM,EACvD,GAAG,IAAc,KACb,EAAY,EAEhB,IAAM,EAAgB,KAAK,kBAAkB,iBAAiB,CAAa,EAC3E,GAAG,IAAkB,KACjB,OAEJ,GAAa,EAAE,GAAK,GACpB,KAAK,eAAe,aAAa,EAAQ,CAAS,EAClD,KAAK,eAAe,uBAAuB,EAAQ,CAAS,EAGhE,sBAAyB,CAAC,EAAuB,EAA0B,CACvE,OAAO,KAAK,kBAAkB,uBAA0B,EAAe,CAAM,EAGjF,gBAAgB,CAAC,EAA6C,CAC1D,OAAO,KAAK,kBAAkB,iBAAiB,CAAa,GAAK,KAGrE,cAAc,CAAC,EAAoB,EAAsB,CACrD,KAAK,eAAe,eAAe,EAAY,CAAM,EAGzD,kBAAkB,CAAC,EAAoB,EAAqC,CACxE,KAAK,eAAe,aAAa,EAAY,CAAS,EAE9D",
|
|
8
|
-
"debugId": "
|
|
7
|
+
"mappings": "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",
|
|
8
|
+
"debugId": "1A0F397F448E17CB64756E2164756E21",
|
|
9
9
|
"names": []
|
|
10
10
|
}
|