@codehz/ecs 0.3.8 → 0.3.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (4) hide show
  1. package/index.d.mts +168 -151
  2. package/index.mjs +472 -179
  3. package/index.mjs.map +1 -1
  4. package/package.json +1 -1
package/index.mjs CHANGED
@@ -1,8 +1,4 @@
1
1
  //#region src/entity.ts
2
- /**
3
- * Constants for ID ranges
4
- */
5
- const INVALID_COMPONENT_ID = 0;
6
2
  const COMPONENT_ID_MAX = 1023;
7
3
  const ENTITY_ID_START = 1024;
8
4
  /**
@@ -10,23 +6,6 @@ const ENTITY_ID_START = 1024;
10
6
  */
11
7
  const RELATION_SHIFT = 2 ** 42;
12
8
  const WILDCARD_TARGET_ID = 0;
13
- /**
14
- * Create a component ID
15
- * @param id Component identifier (1-1023)
16
- * @see component
17
- */
18
- function createComponentId(id) {
19
- if (id < 1 || id > COMPONENT_ID_MAX) throw new Error(`Component ID must be between 1 and ${COMPONENT_ID_MAX}`);
20
- return id;
21
- }
22
- /**
23
- * Create an entity ID
24
- * @param id Entity identifier (starting from 1024)
25
- */
26
- function createEntityId(id) {
27
- if (id < ENTITY_ID_START) throw new Error(`Entity ID must be ${ENTITY_ID_START} or greater`);
28
- return id;
29
- }
30
9
  function relation(componentId, targetId) {
31
10
  if (!isComponentId(componentId)) throw new Error("First argument must be a valid component ID");
32
11
  let actualTargetId;
@@ -142,24 +121,6 @@ function getDetailedIdType(id) {
142
121
  return { type: "invalid" };
143
122
  }
144
123
  /**
145
- * Inspect an EntityId and return a human-readable string representation
146
- * @param id The EntityId to inspect
147
- * @returns A friendly string representation of the ID
148
- */
149
- function inspectEntityId(id) {
150
- if (id === INVALID_COMPONENT_ID) return "Invalid Component ID (0)";
151
- if (isComponentId(id)) return `Component ID (${id})`;
152
- if (isEntityId(id)) return `Entity ID (${id})`;
153
- if (isRelationId(id)) try {
154
- const decoded = decodeRelationId(id);
155
- if (!isComponentId(decoded.componentId) || decoded.type !== "wildcard" && !isEntityId(decoded.targetId) && !isComponentId(decoded.targetId)) return `Invalid Relation ID (${id})`;
156
- return `Relation ID: ${`Component ID (${decoded.componentId})`} -> ${decoded.type === "entity" ? `Entity ID (${decoded.targetId})` : decoded.type === "component" ? `Component ID (${decoded.targetId})` : "Wildcard (*)"}`;
157
- } catch (error) {
158
- return `Invalid Relation ID (${id})`;
159
- }
160
- return `Unknown ID (${id})`;
161
- }
162
- /**
163
124
  * Entity ID Manager for automatic allocation and freelist recycling
164
125
  */
165
126
  var EntityIdManager = class {
@@ -254,20 +215,36 @@ var ComponentIdAllocator = class {
254
215
  const globalComponentIdAllocator = new ComponentIdAllocator();
255
216
  const ComponentNames = /* @__PURE__ */ new Map();
256
217
  const ComponentIdForNames = /* @__PURE__ */ new Map();
218
+ const ComponentOptions = /* @__PURE__ */ new Map();
257
219
  /**
258
220
  * Allocate a new component ID from the global allocator.
259
- * Optionally register a name for the component.
260
- * The name is only for serialization/debugging and does not affect base functionality.
261
- * @param name Optional name for the component
221
+ * @param nameOrOptions Optional name for the component (for serialization/debugging) or options object
262
222
  * @returns The allocated component ID
223
+ * @example
224
+ * // Just a name
225
+ * const Position = component<Position>("Position");
226
+ *
227
+ * // With options
228
+ * const ChildOf = component({ exclusive: true, cascadeDelete: true });
229
+ *
230
+ * // With name and options
231
+ * const ChildOf = component({ name: "ChildOf", exclusive: true });
263
232
  */
264
- function component(name) {
233
+ function component(nameOrOptions) {
265
234
  const id = globalComponentIdAllocator.allocate();
235
+ let name;
236
+ let options;
237
+ if (typeof nameOrOptions === "string") name = nameOrOptions;
238
+ else if (typeof nameOrOptions === "object" && nameOrOptions !== null) {
239
+ options = nameOrOptions;
240
+ name = options.name;
241
+ }
266
242
  if (name) {
267
243
  if (ComponentIdForNames.has(name)) throw new Error(`Component name "${name}" is already registered`);
268
244
  ComponentNames.set(id, name);
269
245
  ComponentIdForNames.set(name, id);
270
246
  }
247
+ if (options) ComponentOptions.set(id, options);
271
248
  return id;
272
249
  }
273
250
  /**
@@ -285,6 +262,30 @@ function getComponentIdByName(name) {
285
262
  function getComponentNameById(id) {
286
263
  return ComponentNames.get(id);
287
264
  }
265
+ /**
266
+ * Check if a component is marked as exclusive
267
+ * @param id The component ID
268
+ * @returns true if the component is exclusive, false otherwise
269
+ */
270
+ function isExclusiveComponent(id) {
271
+ return ComponentOptions.get(id)?.exclusive ?? false;
272
+ }
273
+ /**
274
+ * Check if a component is marked as cascade delete
275
+ * @param id The component ID
276
+ * @returns true if the component is cascade delete, false otherwise
277
+ */
278
+ function isCascadeDeleteComponent(id) {
279
+ return ComponentOptions.get(id)?.cascadeDelete ?? false;
280
+ }
281
+ /**
282
+ * Check if a component is marked as dontFragment
283
+ * @param id The component ID
284
+ * @returns true if the component is dontFragment, false otherwise
285
+ */
286
+ function isDontFragmentComponent(id) {
287
+ return ComponentOptions.get(id)?.dontFragment ?? false;
288
+ }
288
289
 
289
290
  //#endregion
290
291
  //#region src/types.ts
@@ -358,6 +359,12 @@ var Archetype = class {
358
359
  */
359
360
  entityToIndex = /* @__PURE__ */ new Map();
360
361
  /**
362
+ * Reference to dontFragment relations storage from World
363
+ * This allows entities with different relation targets to share the same archetype
364
+ * Stored in World to avoid migration overhead when entities change archetypes
365
+ */
366
+ dontFragmentRelations;
367
+ /**
361
368
  * Cache for pre-computed component data sources to avoid repeated calculations
362
369
  * For regular components: data array
363
370
  * For wildcards: matching relation types array
@@ -366,9 +373,11 @@ var Archetype = class {
366
373
  /**
367
374
  * Create a new archetype with the specified component types
368
375
  * @param componentTypes The component types that define this archetype
376
+ * @param dontFragmentRelations Reference to the World's dontFragmentRelations storage
369
377
  */
370
- constructor(componentTypes) {
378
+ constructor(componentTypes, dontFragmentRelations) {
371
379
  this.componentTypes = [...componentTypes].sort((a, b) => a - b);
380
+ this.dontFragmentRelations = dontFragmentRelations;
372
381
  for (const componentType of this.componentTypes) this.componentData.set(componentType, []);
373
382
  }
374
383
  /**
@@ -389,7 +398,7 @@ var Archetype = class {
389
398
  /**
390
399
  * Add an entity to this archetype with initial component data
391
400
  * @param entityId The entity to add
392
- * @param componentData Map of component type to component data
401
+ * @param componentData Map of component type to component data (includes both regular and dontFragment components)
393
402
  */
394
403
  addEntity(entityId, componentData) {
395
404
  if (this.entityToIndex.has(entityId)) throw new Error(`Entity ${entityId} is already in this archetype`);
@@ -400,11 +409,18 @@ var Archetype = class {
400
409
  const data = componentData.get(componentType);
401
410
  this.getComponentData(componentType).push(data === void 0 ? MISSING_COMPONENT : data);
402
411
  }
412
+ const dontFragmentData = /* @__PURE__ */ new Map();
413
+ for (const [componentType, data] of componentData) {
414
+ if (this.componentTypes.includes(componentType)) continue;
415
+ const detailedType = getDetailedIdType(componentType);
416
+ if ((detailedType.type === "entity-relation" || detailedType.type === "component-relation") && isDontFragmentComponent(detailedType.componentId)) dontFragmentData.set(componentType, data);
417
+ }
418
+ if (dontFragmentData.size > 0) this.dontFragmentRelations.set(entityId, dontFragmentData);
403
419
  }
404
420
  /**
405
421
  * Get all component data for a specific entity
406
422
  * @param entityId The entity to get data for
407
- * @returns Map of component type to component data
423
+ * @returns Map of component type to component data (includes both regular and dontFragment components)
408
424
  */
409
425
  getEntity(entityId) {
410
426
  const index = this.entityToIndex.get(entityId);
@@ -414,11 +430,13 @@ var Archetype = class {
414
430
  const data = this.getComponentData(componentType)[index];
415
431
  entityData.set(componentType, data === MISSING_COMPONENT ? void 0 : data);
416
432
  }
433
+ const dontFragmentData = this.dontFragmentRelations.get(entityId);
434
+ if (dontFragmentData) for (const [componentType, data] of dontFragmentData) entityData.set(componentType, data);
417
435
  return entityData;
418
436
  }
419
437
  /**
420
438
  * Dump all entities and their component data in this archetype
421
- * @returns Array of objects with entity and component data
439
+ * @returns Array of objects with entity and component data (includes both regular and dontFragment components)
422
440
  */
423
441
  dump() {
424
442
  const result = [];
@@ -429,6 +447,8 @@ var Archetype = class {
429
447
  const data = this.getComponentData(componentType)[i];
430
448
  components.set(componentType, data === MISSING_COMPONENT ? void 0 : data);
431
449
  }
450
+ const dontFragmentData = this.dontFragmentRelations.get(entity);
451
+ if (dontFragmentData) for (const [componentType, data] of dontFragmentData) components.set(componentType, data);
432
452
  result.push({
433
453
  entity,
434
454
  components
@@ -439,7 +459,7 @@ var Archetype = class {
439
459
  /**
440
460
  * Remove an entity from this archetype
441
461
  * @param entityId The entity to remove
442
- * @returns The component data of the removed entity
462
+ * @returns The component data of the removed entity (includes both regular and dontFragment components)
443
463
  */
444
464
  removeEntity(entityId) {
445
465
  const index = this.entityToIndex.get(entityId);
@@ -449,6 +469,11 @@ var Archetype = class {
449
469
  const dataArray = this.getComponentData(componentType);
450
470
  removedData.set(componentType, dataArray[index]);
451
471
  }
472
+ const dontFragmentData = this.dontFragmentRelations.get(entityId);
473
+ if (dontFragmentData) {
474
+ for (const [componentType, data] of dontFragmentData) removedData.set(componentType, data);
475
+ this.dontFragmentRelations.delete(entityId);
476
+ }
452
477
  this.entityToIndex.delete(entityId);
453
478
  const lastIndex = this.entities.length - 1;
454
479
  if (index !== lastIndex) {
@@ -487,10 +512,20 @@ var Archetype = class {
487
512
  }
488
513
  }
489
514
  }
515
+ const dontFragmentData = this.dontFragmentRelations.get(entityId);
516
+ if (dontFragmentData) for (const [relType, data] of dontFragmentData) {
517
+ const relDetailed = getDetailedIdType(relType);
518
+ if ((relDetailed.type === "entity-relation" || relDetailed.type === "component-relation") && relDetailed.componentId === componentId) relations.push([relDetailed.targetId, data]);
519
+ }
490
520
  return relations;
491
521
  } else {
492
- const data = this.getComponentData(componentType)[index];
493
- return data === MISSING_COMPONENT ? void 0 : data;
522
+ if (this.componentTypes.includes(componentType)) {
523
+ const data = this.getComponentData(componentType)[index];
524
+ return data === MISSING_COMPONENT ? void 0 : data;
525
+ }
526
+ const dontFragmentData = this.dontFragmentRelations.get(entityId);
527
+ if (dontFragmentData && dontFragmentData.has(componentType)) return dontFragmentData.get(componentType);
528
+ throw new Error(`Component type ${componentType} not found for entity ${entityId}`);
494
529
  }
495
530
  }
496
531
  /**
@@ -500,11 +535,24 @@ var Archetype = class {
500
535
  * @param data The component data
501
536
  */
502
537
  set(entityId, componentType, data) {
503
- if (!this.componentData.has(componentType)) throw new Error(`Component type ${componentType} is not in this archetype`);
504
538
  const index = this.entityToIndex.get(entityId);
505
539
  if (index === void 0) throw new Error(`Entity ${entityId} is not in this archetype`);
506
- const dataArray = this.getComponentData(componentType);
507
- dataArray[index] = data;
540
+ if (this.componentData.has(componentType)) {
541
+ const dataArray = this.getComponentData(componentType);
542
+ dataArray[index] = data;
543
+ return;
544
+ }
545
+ const detailedType = getDetailedIdType(componentType);
546
+ if ((detailedType.type === "entity-relation" || detailedType.type === "component-relation") && isDontFragmentComponent(detailedType.componentId)) {
547
+ let dontFragmentData = this.dontFragmentRelations.get(entityId);
548
+ if (!dontFragmentData) {
549
+ dontFragmentData = /* @__PURE__ */ new Map();
550
+ this.dontFragmentRelations.set(entityId, dontFragmentData);
551
+ }
552
+ dontFragmentData.set(componentType, data);
553
+ return;
554
+ }
555
+ throw new Error(`Component type ${componentType} is not in this archetype`);
508
556
  }
509
557
  /**
510
558
  * Get all entities in this archetype
@@ -539,59 +587,92 @@ var Archetype = class {
539
587
  * Helper: compute or return cached data sources for provided componentTypes
540
588
  */
541
589
  getCachedComponentDataSources(componentTypes) {
542
- const cacheKey = componentTypes.map((id) => isOptionalEntityId(id) ? `opt(${id.optional})` : `${id}`).join(",");
543
- return getOrComputeCache(this.componentDataSourcesCache, cacheKey, () => {
544
- return componentTypes.map((compType) => {
545
- let optional = false;
546
- if (isOptionalEntityId(compType)) {
547
- compType = compType.optional;
548
- optional = true;
549
- }
550
- const detailedType = getDetailedIdType(compType);
551
- if (detailedType.type === "wildcard-relation") {
552
- const componentId = detailedType.componentId;
553
- const matchingRelations = this.componentTypes.filter((ct) => {
554
- const detailedCt = getDetailedIdType(ct);
555
- if (detailedCt.type !== "entity-relation" && detailedCt.type !== "component-relation") return false;
556
- return detailedCt.componentId === componentId;
557
- });
558
- return optional ? matchingRelations.length > 0 ? matchingRelations : void 0 : matchingRelations;
559
- } else return optional ? this.getOptionalComponentData(compType) : this.getComponentData(compType);
560
- });
590
+ const cacheKey = this.buildCacheKey(componentTypes);
591
+ return getOrComputeCache(this.componentDataSourcesCache, cacheKey, () => componentTypes.map((compType) => this.getComponentDataSource(compType)));
592
+ }
593
+ /**
594
+ * Build cache key for component types
595
+ */
596
+ buildCacheKey(componentTypes) {
597
+ return componentTypes.map((id) => isOptionalEntityId(id) ? `opt(${id.optional})` : `${id}`).join(",");
598
+ }
599
+ /**
600
+ * Get data source for a single component type
601
+ */
602
+ getComponentDataSource(compType) {
603
+ const optional = isOptionalEntityId(compType);
604
+ const actualType = optional ? compType.optional : compType;
605
+ const detailedType = getDetailedIdType(actualType);
606
+ if (detailedType.type === "wildcard-relation") return this.getWildcardRelationDataSource(detailedType.componentId, optional);
607
+ else return optional ? this.getOptionalComponentData(actualType) : this.getComponentData(actualType);
608
+ }
609
+ /**
610
+ * Get data source for wildcard relations
611
+ */
612
+ getWildcardRelationDataSource(componentId, optional) {
613
+ const matchingRelations = this.componentTypes.filter((ct) => {
614
+ const detailedCt = getDetailedIdType(ct);
615
+ return (detailedCt.type === "entity-relation" || detailedCt.type === "component-relation") && detailedCt.componentId === componentId;
561
616
  });
617
+ return optional ? matchingRelations.length > 0 ? matchingRelations : void 0 : matchingRelations;
562
618
  }
563
619
  /**
564
620
  * Helper: build component tuples for a specific entity index using precomputed data sources
565
621
  */
566
- buildComponentsForIndex(componentTypes, componentDataSources, entityIndex) {
622
+ buildComponentsForIndex(componentTypes, componentDataSources, entityIndex, entityId) {
567
623
  return componentDataSources.map((dataSource, i) => {
568
- let compType = componentTypes[i];
569
- let optional = false;
570
- if (isOptionalEntityId(compType)) {
571
- compType = compType.optional;
572
- optional = true;
573
- }
574
- if (getIdType(compType) === "wildcard-relation") {
575
- if (dataSource === void 0) if (optional) return;
576
- else throw new Error(`No matching relations found for mandatory wildcard relation component type`);
577
- const matchingRelations = dataSource;
578
- const relations = [];
579
- for (const relType of matchingRelations) {
580
- const data = this.getComponentData(relType)[entityIndex];
581
- const decodedRel = decodeRelationId(relType);
582
- relations.push([decodedRel.targetId, data === MISSING_COMPONENT ? void 0 : data]);
583
- }
584
- return optional ? { value: relations } : relations;
585
- } else {
586
- if (dataSource === void 0) if (optional) return;
587
- else throw new Error(`No matching relations found for mandatory wildcard relation component type`);
588
- const data = dataSource[entityIndex];
589
- const result = data === MISSING_COMPONENT ? void 0 : data;
590
- return optional ? { value: result } : result;
591
- }
624
+ const compType = componentTypes[i];
625
+ return this.buildSingleComponent(compType, dataSource, entityIndex, entityId);
592
626
  });
593
627
  }
594
628
  /**
629
+ * Build a single component value from its data source
630
+ */
631
+ buildSingleComponent(compType, dataSource, entityIndex, entityId) {
632
+ const optional = isOptionalEntityId(compType);
633
+ const actualType = optional ? compType.optional : compType;
634
+ if (getIdType(actualType) === "wildcard-relation") return this.buildWildcardRelationValue(actualType, dataSource, entityIndex, entityId, optional);
635
+ else return this.buildRegularComponentValue(dataSource, entityIndex, optional);
636
+ }
637
+ /**
638
+ * Build wildcard relation value from matching relations
639
+ */
640
+ buildWildcardRelationValue(wildcardRelationType, dataSource, entityIndex, entityId, optional) {
641
+ const matchingRelations = dataSource || [];
642
+ const relations = [];
643
+ for (const relType of matchingRelations) {
644
+ const data = this.getComponentData(relType)[entityIndex];
645
+ const decodedRel = decodeRelationId(relType);
646
+ relations.push([decodedRel.targetId, data === MISSING_COMPONENT ? void 0 : data]);
647
+ }
648
+ const targetComponentId = decodeRelationId(wildcardRelationType).componentId;
649
+ const dontFragmentData = this.dontFragmentRelations.get(entityId);
650
+ if (dontFragmentData) for (const [relType, data] of dontFragmentData) {
651
+ const relDetailed = getDetailedIdType(relType);
652
+ if ((relDetailed.type === "entity-relation" || relDetailed.type === "component-relation") && relDetailed.componentId === targetComponentId) relations.push([relDetailed.targetId, data]);
653
+ }
654
+ if (relations.length === 0) {
655
+ if (!optional) {
656
+ const wildcardDecoded = decodeRelationId(wildcardRelationType);
657
+ throw new Error(`No matching relations found for mandatory wildcard relation component ${wildcardDecoded.componentId} on entity ${entityId}`);
658
+ }
659
+ return;
660
+ }
661
+ return optional ? { value: relations } : relations;
662
+ }
663
+ /**
664
+ * Build regular component value from data source
665
+ */
666
+ buildRegularComponentValue(dataSource, entityIndex, optional) {
667
+ if (dataSource === void 0) {
668
+ if (optional) return;
669
+ throw new Error(`Component data not found for mandatory component type`);
670
+ }
671
+ const data = dataSource[entityIndex];
672
+ const result = data === MISSING_COMPONENT ? void 0 : data;
673
+ return optional ? { value: result } : result;
674
+ }
675
+ /**
595
676
  * Get entities with their component data for specified component types
596
677
  * Optimized for bulk component access with pre-computed indices
597
678
  * @param componentTypes Array of component types to retrieve
@@ -614,7 +695,10 @@ var Archetype = class {
614
695
  */
615
696
  *iterateWithComponents(componentTypes) {
616
697
  const componentDataSources = this.getCachedComponentDataSources(componentTypes);
617
- for (let entityIndex = 0; entityIndex < this.entities.length; entityIndex++) yield [this.entities[entityIndex], ...this.buildComponentsForIndex(componentTypes, componentDataSources, entityIndex)];
698
+ for (let entityIndex = 0; entityIndex < this.entities.length; entityIndex++) {
699
+ const entity = this.entities[entityIndex];
700
+ yield [entity, ...this.buildComponentsForIndex(componentTypes, componentDataSources, entityIndex, entity)];
701
+ }
618
702
  }
619
703
  /**
620
704
  * Iterate over entities with their component data for specified component types
@@ -626,7 +710,7 @@ var Archetype = class {
626
710
  const componentDataSources = this.getCachedComponentDataSources(componentTypes);
627
711
  for (let entityIndex = 0; entityIndex < this.entities.length; entityIndex++) {
628
712
  const entity = this.entities[entityIndex];
629
- callback(entity, ...this.buildComponentsForIndex(componentTypes, componentDataSources, entityIndex));
713
+ callback(entity, ...this.buildComponentsForIndex(componentTypes, componentDataSources, entityIndex, entity));
630
714
  }
631
715
  }
632
716
  /**
@@ -643,6 +727,26 @@ var Archetype = class {
643
727
  callback(this.entities[i], components);
644
728
  }
645
729
  }
730
+ /**
731
+ * Check if any entity in this archetype has a relation matching the given component ID
732
+ * This includes both regular relations in componentTypes and dontFragment relations
733
+ * @param componentId The component ID to match
734
+ * @returns true if any entity has a matching relation
735
+ */
736
+ hasRelationWithComponentId(componentId) {
737
+ for (const componentType of this.componentTypes) {
738
+ const detailedType = getDetailedIdType(componentType);
739
+ if ((detailedType.type === "entity-relation" || detailedType.type === "component-relation") && detailedType.componentId === componentId) return true;
740
+ }
741
+ for (const entityId of this.entities) {
742
+ const entityDontFragmentRelations = this.dontFragmentRelations.get(entityId);
743
+ if (entityDontFragmentRelations) for (const relationType of entityDontFragmentRelations.keys()) {
744
+ const detailedType = getDetailedIdType(relationType);
745
+ if ((detailedType.type === "entity-relation" || detailedType.type === "component-relation") && detailedType.componentId === componentId) return true;
746
+ }
747
+ }
748
+ return false;
749
+ }
646
750
  };
647
751
 
648
752
  //#endregion
@@ -926,12 +1030,31 @@ var Query = class {
926
1030
  world._registerQuery(this);
927
1031
  }
928
1032
  /**
1033
+ * Check if query is disposed and throw error if so
1034
+ */
1035
+ ensureNotDisposed() {
1036
+ if (this.isDisposed) throw new Error("Query has been disposed");
1037
+ }
1038
+ /**
929
1039
  * Get all entities matching the query
930
1040
  */
931
1041
  getEntities() {
932
- if (this.isDisposed) throw new Error("Query has been disposed");
1042
+ this.ensureNotDisposed();
933
1043
  const result = [];
934
- for (const archetype of this.cachedArchetypes) result.push(...archetype.getEntities());
1044
+ if (this.componentTypes.some((ct) => {
1045
+ return getDetailedIdType(ct).type === "wildcard-relation";
1046
+ })) for (const archetype of this.cachedArchetypes) for (const entity of archetype.getEntities()) {
1047
+ let hasAllRelations = true;
1048
+ for (const componentType of this.componentTypes) if (getDetailedIdType(componentType).type === "wildcard-relation") {
1049
+ const relations = archetype.get(entity, componentType);
1050
+ if (!relations || relations.length === 0) {
1051
+ hasAllRelations = false;
1052
+ break;
1053
+ }
1054
+ }
1055
+ if (hasAllRelations) result.push(entity);
1056
+ }
1057
+ else for (const archetype of this.cachedArchetypes) result.push(...archetype.getEntities());
935
1058
  return result;
936
1059
  }
937
1060
  /**
@@ -940,7 +1063,7 @@ var Query = class {
940
1063
  * @returns Array of objects with entity and component data
941
1064
  */
942
1065
  getEntitiesWithComponents(componentTypes) {
943
- if (this.isDisposed) throw new Error("Query has been disposed");
1066
+ this.ensureNotDisposed();
944
1067
  const result = [];
945
1068
  for (const archetype of this.cachedArchetypes) {
946
1069
  const entitiesWithData = archetype.getEntitiesWithComponents(componentTypes);
@@ -954,7 +1077,7 @@ var Query = class {
954
1077
  * @param callback Function called for each entity with its components
955
1078
  */
956
1079
  forEach(componentTypes, callback) {
957
- if (this.isDisposed) throw new Error("Query has been disposed");
1080
+ this.ensureNotDisposed();
958
1081
  for (const archetype of this.cachedArchetypes) archetype.forEachWithComponents(componentTypes, callback);
959
1082
  }
960
1083
  /**
@@ -962,7 +1085,7 @@ var Query = class {
962
1085
  * @param componentTypes Array of component types to retrieve
963
1086
  */
964
1087
  *iterate(componentTypes) {
965
- if (this.isDisposed) throw new Error("Query has been disposed");
1088
+ this.ensureNotDisposed();
966
1089
  for (const archetype of this.cachedArchetypes) yield* archetype.iterateWithComponents(componentTypes);
967
1090
  }
968
1091
  /**
@@ -971,7 +1094,7 @@ var Query = class {
971
1094
  * @returns Array of component data for all matching entities
972
1095
  */
973
1096
  getComponentData(componentType) {
974
- if (this.isDisposed) throw new Error("Query has been disposed");
1097
+ this.ensureNotDisposed();
975
1098
  const result = [];
976
1099
  for (const archetype of this.cachedArchetypes) result.push(...archetype.getComponentData(componentType));
977
1100
  return result;
@@ -1119,6 +1242,8 @@ var World = class {
1119
1242
  archetypesByComponent = /* @__PURE__ */ new Map();
1120
1243
  /** Tracks which entities reference each entity as a component type */
1121
1244
  entityReferences = /* @__PURE__ */ new Map();
1245
+ /** Storage for dontFragment relations - maps entity ID to a map of relation type to component data */
1246
+ dontFragmentRelations = /* @__PURE__ */ new Map();
1122
1247
  /** Array of all active queries for archetype change notifications */
1123
1248
  queries = [];
1124
1249
  /** Cache for queries keyed by component types and filter signatures */
@@ -1129,10 +1254,6 @@ var World = class {
1129
1254
  commandBuffer = new CommandBuffer((entityId, commands) => this.executeEntityCommands(entityId, commands));
1130
1255
  /** Stores lifecycle hooks for component and relation events */
1131
1256
  hooks = /* @__PURE__ */ new Map();
1132
- /** Set of component IDs marked as exclusive relations */
1133
- exclusiveComponents = /* @__PURE__ */ new Set();
1134
- /** Set of component IDs that will cascade delete when the relation target is deleted */
1135
- cascadeDeleteComponents = /* @__PURE__ */ new Set();
1136
1257
  /**
1137
1258
  * Create a new World.
1138
1259
  * If an optional snapshot object is provided (previously produced by `world.serialize()`),
@@ -1214,7 +1335,7 @@ var World = class {
1214
1335
  const sourceArchetype = this.entityToArchetype.get(sourceEntityId);
1215
1336
  if (!sourceArchetype) continue;
1216
1337
  const detailedType = getDetailedIdType(componentType);
1217
- if (detailedType.type === "entity-relation" && this.cascadeDeleteComponents.has(detailedType.componentId)) {
1338
+ if (detailedType.type === "entity-relation" && isCascadeDeleteComponent(detailedType.componentId)) {
1218
1339
  if (!visited.has(sourceEntityId)) queue.push(sourceEntityId);
1219
1340
  continue;
1220
1341
  }
@@ -1271,13 +1392,20 @@ var World = class {
1271
1392
  */
1272
1393
  has(entityId, componentType) {
1273
1394
  const archetype = this.entityToArchetype.get(entityId);
1274
- return archetype ? archetype.componentTypes.includes(componentType) : false;
1395
+ if (!archetype) return false;
1396
+ if (archetype.componentTypes.includes(componentType)) return true;
1397
+ const detailedType = getDetailedIdType(componentType);
1398
+ if ((detailedType.type === "entity-relation" || detailedType.type === "component-relation") && isDontFragmentComponent(detailedType.componentId)) return this.dontFragmentRelations.get(entityId)?.has(componentType) ?? false;
1399
+ return false;
1275
1400
  }
1276
1401
  get(entityId, componentType) {
1277
1402
  const archetype = this.entityToArchetype.get(entityId);
1278
1403
  if (!archetype) throw new Error(`Entity ${entityId} does not exist`);
1279
- if (getDetailedIdType(componentType).type !== "wildcard-relation") {
1280
- if (!archetype.componentTypes.includes(componentType)) throw new Error(`Entity ${entityId} does not have component ${componentType}. Use has() to check component existence before calling get().`);
1404
+ const detailedType = getDetailedIdType(componentType);
1405
+ if (detailedType.type !== "wildcard-relation") {
1406
+ const inArchetype = archetype.componentTypes.includes(componentType);
1407
+ const isDontFragment = (detailedType.type === "entity-relation" || detailedType.type === "component-relation") && isDontFragmentComponent(detailedType.componentId);
1408
+ if (!(inArchetype || isDontFragment && this.dontFragmentRelations.get(entityId)?.has(componentType))) throw new Error(`Entity ${entityId} does not have component ${componentType}. Use has() to check component existence before calling get().`);
1281
1409
  }
1282
1410
  return archetype.get(entityId, componentType);
1283
1411
  }
@@ -1315,19 +1443,19 @@ var World = class {
1315
1443
  }
1316
1444
  /**
1317
1445
  * Mark a component as exclusive relation
1318
- * For exclusive relations, an entity can have at most one relation per base component
1446
+ * @deprecated This method has been removed. Use component options instead: component({ exclusive: true })
1447
+ * @throws Always throws an error directing to the new API
1319
1448
  */
1320
1449
  setExclusive(componentId) {
1321
- this.exclusiveComponents.add(componentId);
1450
+ throw new Error("setExclusive has been removed. Use component options instead: component({ exclusive: true })");
1322
1451
  }
1323
1452
  /**
1324
1453
  * Mark a component as cascade-delete relation
1325
- * For cascade relations, when the relation target entity is deleted,
1326
- * the referencing entity will also be deleted (cascade).
1327
- * Only applicable to entity-relation components
1454
+ * @deprecated This method has been removed. Use component options instead: component({ cascadeDelete: true })
1455
+ * @throws Always throws an error directing to the new API
1328
1456
  */
1329
1457
  setCascadeDelete(componentId) {
1330
- this.cascadeDeleteComponents.add(componentId);
1458
+ throw new Error("setCascadeDelete has been removed. Use component options instead: component({ cascadeDelete: true })");
1331
1459
  }
1332
1460
  /**
1333
1461
  * Update the world (run all systems in dependency order)
@@ -1429,10 +1557,11 @@ var World = class {
1429
1557
  matchingArchetypes = Array.from(intersection);
1430
1558
  }
1431
1559
  } else matchingArchetypes = [...this.archetypes];
1432
- for (const wildcard of wildcardRelations) matchingArchetypes = matchingArchetypes.filter((archetype) => archetype.componentTypes.some((archetypeType) => {
1433
- if (!isRelationId(archetypeType)) return false;
1434
- return decodeRelationId(archetypeType).componentId === wildcard.componentId;
1435
- }));
1560
+ for (const wildcard of wildcardRelations) if (isDontFragmentComponent(wildcard.componentId)) {
1561
+ const archetypesWithMarker = this.archetypesByComponent.get(wildcard.relationId) || [];
1562
+ if (matchingArchetypes.length === 0) matchingArchetypes = archetypesWithMarker;
1563
+ else matchingArchetypes = matchingArchetypes.filter((archetype) => archetypesWithMarker.includes(archetype));
1564
+ } else matchingArchetypes = matchingArchetypes.filter((archetype) => archetype.hasRelationWithComponentId(wildcard.componentId));
1436
1565
  return matchingArchetypes;
1437
1566
  }
1438
1567
  query(componentTypes, includeComponents) {
@@ -1462,76 +1591,216 @@ var World = class {
1462
1591
  }
1463
1592
  const currentArchetype = this.entityToArchetype.get(entityId);
1464
1593
  if (!currentArchetype) return changeset;
1465
- for (const command of commands) switch (command.type) {
1466
- case "set":
1467
- if (command.componentType) {
1468
- const detailedType = getDetailedIdType(command.componentType);
1469
- if ((detailedType.type === "entity-relation" || detailedType.type === "component-relation") && this.exclusiveComponents.has(detailedType.componentId)) for (const componentType of currentArchetype.componentTypes) {
1470
- const componentDetailedType = getDetailedIdType(componentType);
1471
- if ((componentDetailedType.type === "entity-relation" || componentDetailedType.type === "component-relation") && componentDetailedType.componentId === detailedType.componentId) changeset.delete(componentType);
1594
+ this.processCommands(entityId, currentArchetype, commands, changeset);
1595
+ const removedComponents = this.applyChangeset(entityId, currentArchetype, changeset);
1596
+ this.updateEntityReferences(entityId, changeset);
1597
+ this.triggerLifecycleHooks(entityId, changeset.adds, removedComponents);
1598
+ return changeset;
1599
+ }
1600
+ /**
1601
+ * Process commands and populate the changeset
1602
+ */
1603
+ processCommands(entityId, currentArchetype, commands, changeset) {
1604
+ for (const command of commands) if (command.type === "set" && command.componentType) this.processSetCommand(entityId, currentArchetype, command.componentType, command.component, changeset);
1605
+ else if (command.type === "delete" && command.componentType) this.processDeleteCommand(entityId, currentArchetype, command.componentType, changeset);
1606
+ }
1607
+ /**
1608
+ * Process a set command, handling exclusive relations
1609
+ */
1610
+ processSetCommand(entityId, currentArchetype, componentType, component$1, changeset) {
1611
+ const detailedType = getDetailedIdType(componentType);
1612
+ if ((detailedType.type === "entity-relation" || detailedType.type === "component-relation") && isExclusiveComponent(detailedType.componentId)) this.removeExclusiveRelations(entityId, currentArchetype, detailedType.componentId, changeset);
1613
+ if ((detailedType.type === "entity-relation" || detailedType.type === "component-relation") && isDontFragmentComponent(detailedType.componentId)) {
1614
+ const wildcardMarker = relation(detailedType.componentId, "*");
1615
+ if (!currentArchetype.componentTypes.includes(wildcardMarker)) changeset.set(wildcardMarker, void 0);
1616
+ }
1617
+ changeset.set(componentType, component$1);
1618
+ }
1619
+ /**
1620
+ * Remove all relations with the same base component (for exclusive relations)
1621
+ */
1622
+ removeExclusiveRelations(entityId, currentArchetype, baseComponentId, changeset) {
1623
+ for (const componentType of currentArchetype.componentTypes) if (this.isRelationWithComponent(componentType, baseComponentId)) changeset.delete(componentType);
1624
+ const entityData = currentArchetype.getEntity(entityId);
1625
+ if (entityData) for (const [componentType] of entityData) {
1626
+ if (currentArchetype.componentTypes.includes(componentType)) continue;
1627
+ if (this.isRelationWithComponent(componentType, baseComponentId)) changeset.delete(componentType);
1628
+ }
1629
+ }
1630
+ /**
1631
+ * Check if a component type is a relation with the given base component
1632
+ */
1633
+ isRelationWithComponent(componentType, baseComponentId) {
1634
+ const detailedType = getDetailedIdType(componentType);
1635
+ return (detailedType.type === "entity-relation" || detailedType.type === "component-relation") && detailedType.componentId === baseComponentId;
1636
+ }
1637
+ /**
1638
+ * Process a delete command, handling wildcard relations
1639
+ */
1640
+ processDeleteCommand(entityId, currentArchetype, componentType, changeset) {
1641
+ const detailedType = getDetailedIdType(componentType);
1642
+ if (detailedType.type === "wildcard-relation") this.removeWildcardRelations(entityId, currentArchetype, detailedType.componentId, changeset);
1643
+ else {
1644
+ changeset.delete(componentType);
1645
+ if ((detailedType.type === "entity-relation" || detailedType.type === "component-relation") && isDontFragmentComponent(detailedType.componentId)) {
1646
+ const wildcardMarker = relation(detailedType.componentId, "*");
1647
+ const entityData = currentArchetype.getEntity(entityId);
1648
+ let hasOtherRelations = false;
1649
+ if (entityData) for (const [otherComponentType] of entityData) {
1650
+ if (otherComponentType === componentType) continue;
1651
+ if (changeset.removes.has(otherComponentType)) continue;
1652
+ const otherDetailedType = getDetailedIdType(otherComponentType);
1653
+ if ((otherDetailedType.type === "entity-relation" || otherDetailedType.type === "component-relation") && otherDetailedType.componentId === detailedType.componentId) {
1654
+ hasOtherRelations = true;
1655
+ break;
1472
1656
  }
1473
- changeset.set(command.componentType, command.component);
1474
- }
1475
- break;
1476
- case "delete":
1477
- if (command.componentType) {
1478
- const detailedType = getDetailedIdType(command.componentType);
1479
- if (detailedType.type === "wildcard-relation") {
1480
- const baseComponentId = detailedType.componentId;
1481
- for (const componentType of currentArchetype.componentTypes) {
1482
- const componentDetailedType = getDetailedIdType(componentType);
1483
- if (componentDetailedType.type === "entity-relation" || componentDetailedType.type === "component-relation") {
1484
- if (componentDetailedType.componentId === baseComponentId) changeset.delete(componentType);
1485
- }
1486
- }
1487
- } else changeset.delete(command.componentType);
1488
1657
  }
1489
- break;
1658
+ if (!hasOtherRelations) changeset.delete(wildcardMarker);
1659
+ }
1660
+ }
1661
+ }
1662
+ /**
1663
+ * Remove all relations matching a wildcard component ID
1664
+ */
1665
+ removeWildcardRelations(entityId, currentArchetype, baseComponentId, changeset) {
1666
+ for (const componentType of currentArchetype.componentTypes) if (this.isRelationWithComponent(componentType, baseComponentId)) changeset.delete(componentType);
1667
+ const entityData = currentArchetype.getEntity(entityId);
1668
+ if (entityData) for (const [componentType] of entityData) {
1669
+ if (currentArchetype.componentTypes.includes(componentType)) continue;
1670
+ if (this.isRelationWithComponent(componentType, baseComponentId)) changeset.delete(componentType);
1671
+ }
1672
+ if (isDontFragmentComponent(baseComponentId)) {
1673
+ const wildcardMarker = relation(baseComponentId, "*");
1674
+ changeset.delete(wildcardMarker);
1490
1675
  }
1491
- const finalComponentTypes = changeset.getFinalComponentTypes(currentArchetype.componentTypes);
1492
- const removedCompoents = /* @__PURE__ */ new Map();
1493
- if (finalComponentTypes) {
1494
- const newArchetype = this.ensureArchetype(finalComponentTypes);
1495
- const currentComponents = currentArchetype.removeEntity(entityId);
1496
- for (const componentType of changeset.removes) removedCompoents.set(componentType, currentComponents.get(componentType));
1497
- newArchetype.addEntity(entityId, changeset.applyTo(currentComponents));
1498
- this.entityToArchetype.set(entityId, newArchetype);
1499
- } else for (const [componentType, component$1] of changeset.adds) currentArchetype.set(entityId, componentType, component$1);
1676
+ }
1677
+ /**
1678
+ * Apply changeset to entity, moving to new archetype if needed
1679
+ * @returns Map of removed components with their data
1680
+ */
1681
+ applyChangeset(entityId, currentArchetype, changeset) {
1682
+ const currentEntityData = currentArchetype.getEntity(entityId);
1683
+ const allCurrentComponentTypes = currentEntityData ? Array.from(currentEntityData.keys()) : currentArchetype.componentTypes;
1684
+ const finalComponentTypes = changeset.getFinalComponentTypes(allCurrentComponentTypes);
1685
+ const removedComponents = /* @__PURE__ */ new Map();
1686
+ if (finalComponentTypes) this.moveEntityToNewArchetype(entityId, currentArchetype, finalComponentTypes, changeset, removedComponents);
1687
+ else this.updateEntityInSameArchetype(entityId, currentArchetype, changeset, removedComponents);
1688
+ return removedComponents;
1689
+ }
1690
+ /**
1691
+ * Move entity to a new archetype with updated components
1692
+ */
1693
+ moveEntityToNewArchetype(entityId, currentArchetype, finalComponentTypes, changeset, removedComponents) {
1694
+ const newArchetype = this.ensureArchetype(finalComponentTypes);
1695
+ const currentComponents = currentArchetype.removeEntity(entityId);
1696
+ for (const componentType of changeset.removes) removedComponents.set(componentType, currentComponents.get(componentType));
1697
+ newArchetype.addEntity(entityId, changeset.applyTo(currentComponents));
1698
+ this.entityToArchetype.set(entityId, newArchetype);
1699
+ if (currentArchetype.getEntities().length === 0) this.cleanupEmptyArchetype(currentArchetype);
1700
+ }
1701
+ /**
1702
+ * Update entity in same archetype (no archetype change needed)
1703
+ */
1704
+ updateEntityInSameArchetype(entityId, currentArchetype, changeset, removedComponents) {
1705
+ const currentComponents = currentArchetype.getEntity(entityId);
1706
+ const hasDontFragmentChanges = this.hasDontFragmentChanges(changeset);
1707
+ if (hasDontFragmentChanges) for (const componentType of changeset.removes) {
1708
+ const detailedType = getDetailedIdType(componentType);
1709
+ if ((detailedType.type === "entity-relation" || detailedType.type === "component-relation") && isDontFragmentComponent(detailedType.componentId)) removedComponents.set(componentType, currentComponents.get(componentType));
1710
+ }
1711
+ if (hasDontFragmentChanges) this.readdEntityWithUpdatedComponents(entityId, currentArchetype, currentComponents, changeset);
1712
+ else for (const [componentType, component$1] of changeset.adds) currentArchetype.set(entityId, componentType, component$1);
1713
+ }
1714
+ /**
1715
+ * Check if changeset contains dontFragment relation changes
1716
+ */
1717
+ hasDontFragmentChanges(changeset) {
1500
1718
  for (const componentType of changeset.removes) {
1501
1719
  const detailedType = getDetailedIdType(componentType);
1502
- if (detailedType.type === "entity-relation") {
1503
- const targetEntityId = detailedType.targetId;
1504
- this.untrackEntityReference(entityId, componentType, targetEntityId);
1505
- } else if (detailedType.type === "entity") this.untrackEntityReference(entityId, componentType, componentType);
1720
+ if ((detailedType.type === "entity-relation" || detailedType.type === "component-relation") && isDontFragmentComponent(detailedType.componentId)) return true;
1506
1721
  }
1507
- for (const [componentType, component$1] of changeset.adds) {
1722
+ for (const [componentType] of changeset.adds) {
1508
1723
  const detailedType = getDetailedIdType(componentType);
1509
- if (detailedType.type === "entity-relation") {
1510
- const targetEntityId = detailedType.targetId;
1511
- this.trackEntityReference(entityId, componentType, targetEntityId);
1512
- } else if (detailedType.type === "entity") this.trackEntityReference(entityId, componentType, componentType);
1724
+ if ((detailedType.type === "entity-relation" || detailedType.type === "component-relation") && isDontFragmentComponent(detailedType.componentId)) return true;
1725
+ }
1726
+ return false;
1727
+ }
1728
+ /**
1729
+ * Remove and re-add entity with updated components (for dontFragment changes)
1730
+ */
1731
+ readdEntityWithUpdatedComponents(entityId, archetype, currentComponents, changeset) {
1732
+ const newComponents = /* @__PURE__ */ new Map();
1733
+ for (const [ct, value] of currentComponents) if (!changeset.removes.has(ct)) newComponents.set(ct, value);
1734
+ for (const [ct, value] of changeset.adds) newComponents.set(ct, value);
1735
+ archetype.removeEntity(entityId);
1736
+ archetype.addEntity(entityId, newComponents);
1737
+ }
1738
+ /**
1739
+ * Update entity reference tracking based on changeset
1740
+ */
1741
+ updateEntityReferences(entityId, changeset) {
1742
+ for (const componentType of changeset.removes) {
1743
+ const detailedType = getDetailedIdType(componentType);
1744
+ if (detailedType.type === "entity-relation") this.untrackEntityReference(entityId, componentType, detailedType.targetId);
1745
+ else if (detailedType.type === "entity") this.untrackEntityReference(entityId, componentType, componentType);
1746
+ }
1747
+ for (const [componentType] of changeset.adds) {
1748
+ const detailedType = getDetailedIdType(componentType);
1749
+ if (detailedType.type === "entity-relation") this.trackEntityReference(entityId, componentType, detailedType.targetId);
1750
+ else if (detailedType.type === "entity") this.trackEntityReference(entityId, componentType, componentType);
1513
1751
  }
1514
- this.triggerLifecycleHooks(entityId, changeset.adds, removedCompoents);
1515
- return changeset;
1516
1752
  }
1517
1753
  /**
1518
1754
  * Get or create an archetype for the given component types
1519
- * @returns The archetype for the given component types
1755
+ * Filters out dontFragment relations from the archetype signature
1756
+ * @returns The archetype for the given component types (excluding dontFragment relations)
1520
1757
  */
1521
1758
  ensureArchetype(componentTypes) {
1522
- const sortedTypes = Array.from(componentTypes).sort((a, b) => a - b);
1759
+ const sortedTypes = this.filterRegularComponentTypes(componentTypes).sort((a, b) => a - b);
1523
1760
  const hashKey = this.createArchetypeSignature(sortedTypes);
1524
- return getOrCreateWithSideEffect(this.archetypeBySignature, hashKey, () => {
1525
- const newArchetype = new Archetype(sortedTypes);
1526
- this.archetypes.push(newArchetype);
1527
- for (const componentType of sortedTypes) {
1528
- const archetypes = this.archetypesByComponent.get(componentType) || [];
1529
- archetypes.push(newArchetype);
1530
- this.archetypesByComponent.set(componentType, archetypes);
1761
+ return getOrCreateWithSideEffect(this.archetypeBySignature, hashKey, () => this.createNewArchetype(sortedTypes));
1762
+ }
1763
+ /**
1764
+ * Filter out dontFragment relations from component types, but keep wildcard markers
1765
+ */
1766
+ filterRegularComponentTypes(componentTypes) {
1767
+ const regularTypes = [];
1768
+ for (const componentType of componentTypes) {
1769
+ const detailedType = getDetailedIdType(componentType);
1770
+ if (detailedType.type === "wildcard-relation" && isDontFragmentComponent(detailedType.componentId)) {
1771
+ regularTypes.push(componentType);
1772
+ continue;
1531
1773
  }
1532
- for (const query of this.queries) query.checkNewArchetype(newArchetype);
1533
- return newArchetype;
1534
- });
1774
+ if ((detailedType.type === "entity-relation" || detailedType.type === "component-relation") && isDontFragmentComponent(detailedType.componentId)) continue;
1775
+ regularTypes.push(componentType);
1776
+ }
1777
+ return regularTypes;
1778
+ }
1779
+ /**
1780
+ * Create a new archetype and register it with all tracking structures
1781
+ */
1782
+ createNewArchetype(componentTypes) {
1783
+ const newArchetype = new Archetype(componentTypes, this.dontFragmentRelations);
1784
+ this.archetypes.push(newArchetype);
1785
+ this.registerArchetypeInComponentIndex(newArchetype, componentTypes);
1786
+ this.notifyQueriesOfNewArchetype(newArchetype);
1787
+ return newArchetype;
1788
+ }
1789
+ /**
1790
+ * Register archetype in the component-to-archetype index
1791
+ */
1792
+ registerArchetypeInComponentIndex(archetype, componentTypes) {
1793
+ for (const componentType of componentTypes) {
1794
+ const archetypes = this.archetypesByComponent.get(componentType) || [];
1795
+ archetypes.push(archetype);
1796
+ this.archetypesByComponent.set(componentType, archetypes);
1797
+ }
1798
+ }
1799
+ /**
1800
+ * Notify all queries to check the new archetype
1801
+ */
1802
+ notifyQueriesOfNewArchetype(archetype) {
1803
+ for (const query of this.queries) query.checkNewArchetype(archetype);
1535
1804
  }
1536
1805
  /**
1537
1806
  * Add a component reference to the reverse index when an entity is used as a component type
@@ -1569,10 +1838,29 @@ var World = class {
1569
1838
  */
1570
1839
  cleanupEmptyArchetype(archetype) {
1571
1840
  if (archetype.getEntities().length > 0) return;
1841
+ this.removeArchetypeFromList(archetype);
1842
+ this.removeArchetypeFromSignatureMap(archetype);
1843
+ this.removeArchetypeFromComponentIndex(archetype);
1844
+ this.removeArchetypeFromQueries(archetype);
1845
+ }
1846
+ /**
1847
+ * Remove archetype from the main archetypes list
1848
+ */
1849
+ removeArchetypeFromList(archetype) {
1572
1850
  const index = this.archetypes.indexOf(archetype);
1573
1851
  if (index !== -1) this.archetypes.splice(index, 1);
1852
+ }
1853
+ /**
1854
+ * Remove archetype from the signature-to-archetype map
1855
+ */
1856
+ removeArchetypeFromSignatureMap(archetype) {
1574
1857
  const hashKey = this.createArchetypeSignature(archetype.componentTypes);
1575
1858
  this.archetypeBySignature.delete(hashKey);
1859
+ }
1860
+ /**
1861
+ * Remove archetype from the component-to-archetypes index
1862
+ */
1863
+ removeArchetypeFromComponentIndex(archetype) {
1576
1864
  for (const componentType of archetype.componentTypes) {
1577
1865
  const archetypes = this.archetypesByComponent.get(componentType);
1578
1866
  if (archetypes) {
@@ -1583,6 +1871,11 @@ var World = class {
1583
1871
  }
1584
1872
  }
1585
1873
  }
1874
+ }
1875
+ /**
1876
+ * Remove archetype from all queries
1877
+ */
1878
+ removeArchetypeFromQueries(archetype) {
1586
1879
  for (const query of this.queries) query.removeArchetype(archetype);
1587
1880
  }
1588
1881
  /**
@@ -1660,5 +1953,5 @@ var World = class {
1660
1953
  };
1661
1954
 
1662
1955
  //#endregion
1663
- export { Archetype, COMPONENT_ID_MAX, ComponentIdAllocator, ENTITY_ID_START, EntityIdManager, INVALID_COMPONENT_ID, MISSING_COMPONENT, Query, RELATION_SHIFT, SystemScheduler, WILDCARD_TARGET_ID, World, component, createComponentId, createEntityId, decodeRelationId, getComponentIdByName, getComponentNameById, getDetailedIdType, getIdType, inspectEntityId, isComponentId, isEntityId, isOptionalEntityId, isRelationId, isWildcardRelationId, relation };
1956
+ export { Query, World, component, decodeRelationId, getComponentIdByName, getComponentNameById, isComponentId, isEntityId, isRelationId, isWildcardRelationId, relation };
1664
1957
  //# sourceMappingURL=index.mjs.map