@codehz/ecs 0.6.7 → 0.6.9

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/world.mjs CHANGED
@@ -98,16 +98,15 @@ function decodeRelationId(relationId) {
98
98
  function getIdType(id) {
99
99
  if (isComponentId(id)) return "component";
100
100
  if (isEntityId(id)) return "entity";
101
- if (id < 0) try {
102
- const decoded = decodeRelationId(id);
103
- if (decoded.type !== "wildcard" && !isEntityId(decoded.targetId) && !isComponentId(decoded.targetId)) return "invalid";
104
- switch (decoded.type) {
105
- case "entity": return "entity-relation";
106
- case "component": return "component-relation";
107
- case "wildcard": return "wildcard-relation";
108
- }
109
- } catch (_error) {
110
- return "invalid";
101
+ if (id < 0) {
102
+ const decoded = decodeRelationRaw(id);
103
+ if (decoded === null) return "invalid";
104
+ const { componentId: rawComponentId, targetId: rawTargetId } = decoded;
105
+ if (!isValidComponentId(rawComponentId)) return "invalid";
106
+ if (rawTargetId === WILDCARD_TARGET_ID) return "wildcard-relation";
107
+ else if (isEntityId(rawTargetId)) return "entity-relation";
108
+ else if (isComponentId(rawTargetId)) return "component-relation";
109
+ else return "invalid";
111
110
  }
112
111
  return "invalid";
113
112
  }
@@ -119,28 +118,29 @@ function getIdType(id) {
119
118
  function getDetailedIdType(id) {
120
119
  if (isComponentId(id)) return { type: "component" };
121
120
  if (isEntityId(id)) return { type: "entity" };
122
- if (id < 0) try {
123
- const decoded = decodeRelationId(id);
124
- if (decoded.type !== "wildcard" && !isEntityId(decoded.targetId) && !isComponentId(decoded.targetId)) return { type: "invalid" };
125
- let type;
126
- switch (decoded.type) {
127
- case "entity":
128
- type = "entity-relation";
129
- break;
130
- case "component":
131
- type = "component-relation";
132
- break;
133
- case "wildcard":
134
- type = "wildcard-relation";
135
- break;
136
- }
137
- return {
138
- type,
139
- componentId: decoded.componentId,
140
- targetId: decoded.targetId
121
+ if (id < 0) {
122
+ const decoded = decodeRelationRaw(id);
123
+ if (decoded === null) return { type: "invalid" };
124
+ const { componentId: rawComponentId, targetId: rawTargetId } = decoded;
125
+ if (!isValidComponentId(rawComponentId)) return { type: "invalid" };
126
+ const componentId = rawComponentId;
127
+ const targetId = rawTargetId;
128
+ if (targetId === WILDCARD_TARGET_ID) return {
129
+ type: "wildcard-relation",
130
+ componentId,
131
+ targetId
132
+ };
133
+ else if (isEntityId(targetId)) return {
134
+ type: "entity-relation",
135
+ componentId,
136
+ targetId
141
137
  };
142
- } catch (_error) {
143
- return { type: "invalid" };
138
+ else if (isComponentId(targetId)) return {
139
+ type: "component-relation",
140
+ componentId,
141
+ targetId
142
+ };
143
+ else return { type: "invalid" };
144
144
  }
145
145
  return { type: "invalid" };
146
146
  }
@@ -175,17 +175,18 @@ function isEntityRelation(id) {
175
175
  */
176
176
  var EntityIdManager = class {
177
177
  nextId = ENTITY_ID_START;
178
- freelist = /* @__PURE__ */ new Set();
178
+ /**
179
+ * Free list uses a stack (LIFO) for better memory locality when reusing IDs.
180
+ * We use an array instead of a Set for significantly better performance.
181
+ */
182
+ freelist = [];
179
183
  /**
180
184
  * Allocate a new entity ID
181
185
  * Uses freelist if available, otherwise increments counter
182
186
  */
183
187
  allocate() {
184
- if (this.freelist.size > 0) {
185
- const id = this.freelist.values().next().value;
186
- this.freelist.delete(id);
187
- return id;
188
- } else {
188
+ if (this.freelist.length > 0) return this.freelist.pop();
189
+ else {
189
190
  const id = this.nextId;
190
191
  this.nextId++;
191
192
  if (this.nextId >= Number.MAX_SAFE_INTEGER) throw new Error("Entity ID overflow: reached maximum safe integer");
@@ -199,13 +200,13 @@ var EntityIdManager = class {
199
200
  deallocate(id) {
200
201
  if (!isEntityId(id)) throw new Error("Can only deallocate valid entity IDs");
201
202
  if (id >= this.nextId) throw new Error("Cannot deallocate an ID that was never allocated");
202
- this.freelist.add(id);
203
+ this.freelist.push(id);
203
204
  }
204
205
  /**
205
206
  * Get the current freelist size (for debugging/monitoring)
206
207
  */
207
208
  getFreelistSize() {
208
- return this.freelist.size;
209
+ return this.freelist.length;
209
210
  }
210
211
  /**
211
212
  * Get the next ID that would be allocated (for debugging)
@@ -230,7 +231,7 @@ var EntityIdManager = class {
230
231
  deserializeState(state) {
231
232
  if (typeof state.nextId !== "number") throw new Error("Invalid state for EntityIdManager.deserializeState");
232
233
  this.nextId = state.nextId;
233
- this.freelist = new Set(state.freelist || []);
234
+ this.freelist = state.freelist || [];
234
235
  }
235
236
  };
236
237
  /**
@@ -627,10 +628,15 @@ var ComponentChangeset = class {
627
628
  //#endregion
628
629
  //#region src/commands/command-buffer.ts
629
630
  /**
631
+ * Maximum number of command buffer execution iterations to prevent infinite loops
632
+ */
633
+ const MAX_COMMAND_ITERATIONS = 100;
634
+ /**
630
635
  * Command buffer for deferred structural changes
631
636
  */
632
637
  var CommandBuffer = class {
633
638
  commands = [];
639
+ swapBuffer = [];
634
640
  executeEntityCommands;
635
641
  /**
636
642
  * Create a command buffer with an executor function
@@ -669,18 +675,19 @@ var CommandBuffer = class {
669
675
  * Execute all commands and clear the buffer
670
676
  */
671
677
  execute() {
672
- const MAX_ITERATIONS = 100;
673
678
  let iterations = 0;
674
679
  while (this.commands.length > 0) {
675
- if (iterations >= MAX_ITERATIONS) throw new Error("Command execution exceeded maximum iterations, possible infinite loop");
680
+ if (iterations >= MAX_COMMAND_ITERATIONS) throw new Error("Command execution exceeded maximum iterations, possible infinite loop");
676
681
  iterations++;
677
- const currentCommands = [...this.commands];
678
- this.commands = [];
682
+ const currentCommands = this.commands;
683
+ this.commands = this.swapBuffer;
679
684
  const entityCommands = /* @__PURE__ */ new Map();
680
685
  for (const cmd of currentCommands) {
681
686
  if (!entityCommands.has(cmd.entityId)) entityCommands.set(cmd.entityId, []);
682
687
  entityCommands.get(cmd.entityId).push(cmd);
683
688
  }
689
+ currentCommands.length = 0;
690
+ this.swapBuffer = currentCommands;
684
691
  for (const [entityId, commands] of entityCommands) this.executeEntityCommands(entityId, commands);
685
692
  }
686
693
  }
@@ -721,8 +728,8 @@ function matchesComponentTypes(archetype, componentTypes) {
721
728
  });
722
729
  else if ((detailedType.type === "entity-relation" || detailedType.type === "component-relation") && detailedType.componentId !== void 0 && isDontFragmentComponent(detailedType.componentId)) {
723
730
  const wildcardMarker = relation(detailedType.componentId, "*");
724
- return archetype.componentTypes.includes(wildcardMarker);
725
- } else return archetype.componentTypes.includes(type);
731
+ return archetype.componentTypeSet.has(wildcardMarker);
732
+ } else return archetype.componentTypeSet.has(type);
726
733
  });
727
734
  }
728
735
  /**
@@ -735,7 +742,7 @@ function matchesFilter(archetype, filter) {
735
742
  if (!isRelationId(archetypeType)) return false;
736
743
  return getComponentIdFromRelationId(archetypeType) === detailedType.componentId;
737
744
  });
738
- else return !archetype.componentTypes.includes(type);
745
+ else return !archetype.componentTypeSet.has(type);
739
746
  });
740
747
  }
741
748
 
@@ -750,6 +757,8 @@ var Query = class {
750
757
  filter;
751
758
  cachedArchetypes = [];
752
759
  isDisposed = false;
760
+ /** Cache key assigned by World for O(1) releaseQuery lookup */
761
+ _cacheKey;
753
762
  /** Cached wildcard component types for faster entity filtering */
754
763
  wildcardTypes;
755
764
  /** Cached specific dontFragment relation types that need entity-level filtering */
@@ -903,10 +912,10 @@ var Query = class {
903
912
  * Get a value from cache or compute and cache it if not present
904
913
  * @param cache The cache map
905
914
  * @param key The cache key
906
- * @param compute Function to compute the value if not cached
915
+ * @param compute Function to compute the value if not cached (may have side effects)
907
916
  * @returns The cached or computed value
908
917
  */
909
- function getOrComputeCache(cache, key, compute) {
918
+ function getOrCompute(cache, key, compute) {
910
919
  let value = cache.get(key);
911
920
  if (value === void 0) {
912
921
  value = compute();
@@ -915,20 +924,15 @@ function getOrComputeCache(cache, key, compute) {
915
924
  return value;
916
925
  }
917
926
  /**
918
- * Get a value from cache or create and cache it if not present, allowing side effects during creation
919
- * @param cache The cache map
920
- * @param key The cache key
921
- * @param create Function to create the value if not cached (can have side effects)
922
- * @returns The cached or created value
927
+ * Alias for getOrCompute - maintained for backwards compatibility
928
+ * @deprecated Use getOrCompute instead
923
929
  */
924
- function getOrCreateWithSideEffect(cache, key, create) {
925
- let value = cache.get(key);
926
- if (value === void 0) {
927
- value = create();
928
- cache.set(key, value);
929
- }
930
- return value;
931
- }
930
+ const getOrComputeCache = getOrCompute;
931
+ /**
932
+ * Alias for getOrCompute - maintained for backwards compatibility
933
+ * @deprecated Use getOrCompute instead
934
+ */
935
+ const getOrCreateWithSideEffect = getOrCompute;
932
936
 
933
937
  //#endregion
934
938
  //#region src/core/types.ts
@@ -939,6 +943,18 @@ function isOptionalEntityId(type) {
939
943
  //#endregion
940
944
  //#region src/core/archetype-helpers.ts
941
945
  /**
946
+ * Check if a components map has any wildcard relations matching a component ID
947
+ * @param components - Component entity's components map
948
+ * @param wildcardComponentId - The component ID to match
949
+ * @returns True if at least one matching relation exists
950
+ */
951
+ function hasWildcardRelation(components, wildcardComponentId) {
952
+ for (const relId of components.keys()) if (isRelationId(relId)) {
953
+ if (getComponentIdFromRelationId(relId) === wildcardComponentId) return true;
954
+ }
955
+ return false;
956
+ }
957
+ /**
942
958
  * Check if a detailed type represents a relation (entity or component)
943
959
  */
944
960
  function isRelationType(detailedType) {
@@ -1036,6 +1052,10 @@ var Archetype = class {
1036
1052
  */
1037
1053
  componentTypes;
1038
1054
  /**
1055
+ * Set version of componentTypes for O(1) lookups in hot paths
1056
+ */
1057
+ componentTypeSet;
1058
+ /**
1039
1059
  * List of entities in this archetype
1040
1060
  */
1041
1061
  entities = [];
@@ -1055,9 +1075,6 @@ var Archetype = class {
1055
1075
  */
1056
1076
  dontFragmentRelations;
1057
1077
  /**
1058
- * Cache for pre-computed component data sources to avoid repeated calculations
1059
- */
1060
- /**
1061
1078
  * Multi-hooks that match this archetype
1062
1079
  */
1063
1080
  matchingMultiHooks = /* @__PURE__ */ new Set();
@@ -1067,12 +1084,19 @@ var Archetype = class {
1067
1084
  componentDataSourcesCache = /* @__PURE__ */ new Map();
1068
1085
  constructor(componentTypes, dontFragmentRelations) {
1069
1086
  this.componentTypes = [...componentTypes].sort((a, b) => a - b);
1087
+ this.componentTypeSet = new Set(this.componentTypes);
1070
1088
  this.dontFragmentRelations = dontFragmentRelations;
1071
1089
  for (const componentType of this.componentTypes) this.componentData.set(componentType, []);
1072
1090
  }
1073
1091
  get size() {
1074
1092
  return this.entities.length;
1075
1093
  }
1094
+ /**
1095
+ * Check if the given component types match this archetype
1096
+ * @param componentTypes - Component types to check (can be in any order)
1097
+ * @returns true if the types match this archetype's component set
1098
+ * @note This method handles unsorted input by internally sorting for comparison
1099
+ */
1076
1100
  matches(componentTypes) {
1077
1101
  if (this.componentTypes.length !== componentTypes.length) return false;
1078
1102
  const sortedTypes = [...componentTypes].sort((a, b) => a - b);
@@ -1092,7 +1116,7 @@ var Archetype = class {
1092
1116
  addDontFragmentRelations(entityId, componentData) {
1093
1117
  const dontFragmentData = /* @__PURE__ */ new Map();
1094
1118
  for (const [componentType, data] of componentData) {
1095
- if (this.componentTypes.includes(componentType)) continue;
1119
+ if (this.componentTypeSet.has(componentType)) continue;
1096
1120
  const detailedType = getDetailedIdType(componentType);
1097
1121
  if (isRelationType(detailedType) && isDontFragmentComponent(detailedType.componentId)) dontFragmentData.set(componentType, data);
1098
1122
  }
@@ -1176,7 +1200,7 @@ var Archetype = class {
1176
1200
  return relations;
1177
1201
  }
1178
1202
  getRegularComponent(entityId, index, componentType) {
1179
- if (this.componentTypes.includes(componentType)) {
1203
+ if (this.componentTypeSet.has(componentType)) {
1180
1204
  const data = this.getComponentData(componentType)[index];
1181
1205
  if (data === MISSING_COMPONENT) throw new Error(`Component type ${componentType} not found for entity ${entityId}`);
1182
1206
  return data;
@@ -1188,7 +1212,7 @@ var Archetype = class {
1188
1212
  getOptional(entityId, componentType) {
1189
1213
  const index = this.entityToIndex.get(entityId);
1190
1214
  if (index === void 0) throw new Error(`Entity ${entityId} is not in this archetype`);
1191
- if (this.componentTypes.includes(componentType)) {
1215
+ if (this.componentTypeSet.has(componentType)) {
1192
1216
  const data = this.getComponentData(componentType)[index];
1193
1217
  if (data === MISSING_COMPONENT) return void 0;
1194
1218
  return { value: data };
@@ -1376,8 +1400,8 @@ function decodeSerializedId(sid) {
1376
1400
  //#endregion
1377
1401
  //#region src/core/world-commands.ts
1378
1402
  function processCommands(entityId, currentArchetype, commands, changeset, handleExclusiveRelation) {
1379
- for (const command of commands) if (command.type === "set" && command.componentType) processSetCommand(entityId, currentArchetype, command.componentType, command.component, changeset, handleExclusiveRelation);
1380
- else if (command.type === "delete" && command.componentType) processDeleteCommand(entityId, currentArchetype, command.componentType, changeset);
1403
+ for (const command of commands) if (command.type === "set") processSetCommand(entityId, currentArchetype, command.componentType, command.component, changeset, handleExclusiveRelation);
1404
+ else if (command.type === "delete") processDeleteCommand(entityId, currentArchetype, command.componentType, changeset);
1381
1405
  }
1382
1406
  function processSetCommand(entityId, currentArchetype, componentType, component$1, changeset, handleExclusiveRelation) {
1383
1407
  const componentId = getComponentIdFromRelationId(componentType);
@@ -1385,7 +1409,7 @@ function processSetCommand(entityId, currentArchetype, componentType, component$
1385
1409
  handleExclusiveRelation(entityId, currentArchetype, componentId);
1386
1410
  if (isDontFragmentComponent(componentId)) {
1387
1411
  const wildcardMarker = relation(componentId, "*");
1388
- if (!currentArchetype.componentTypes.includes(wildcardMarker)) changeset.set(wildcardMarker, void 0);
1412
+ if (!currentArchetype.componentTypeSet.has(wildcardMarker)) changeset.set(wildcardMarker, void 0);
1389
1413
  }
1390
1414
  }
1391
1415
  changeset.set(componentType, component$1);
@@ -1405,7 +1429,7 @@ function removeMatchingRelations(entityId, archetype, baseComponentId, changeset
1405
1429
  }
1406
1430
  const entityData = archetype.getEntity(entityId);
1407
1431
  if (entityData) for (const [componentType] of entityData) {
1408
- if (archetype.componentTypes.includes(componentType)) continue;
1432
+ if (archetype.componentTypeSet.has(componentType)) continue;
1409
1433
  if (getComponentIdFromRelationId(componentType) === baseComponentId) changeset.delete(componentType);
1410
1434
  }
1411
1435
  }
@@ -1579,11 +1603,11 @@ function triggerMultiComponentHooks(ctx, entityId, addedComponents, removedCompo
1579
1603
  }
1580
1604
  function entityHasAllComponents(ctx, entityId, requiredComponents) {
1581
1605
  return requiredComponents.every((c) => {
1582
- if (isWildcardRelationId(c)) try {
1583
- const wildcardData = ctx.get(entityId, c);
1606
+ if (isWildcardRelationId(c)) {
1607
+ const wildcardResult = ctx.getOptional(entityId, c);
1608
+ if (!wildcardResult) return false;
1609
+ const wildcardData = wildcardResult.value;
1584
1610
  return Array.isArray(wildcardData) && wildcardData.length > 0;
1585
- } catch {
1586
- return false;
1587
1611
  }
1588
1612
  return ctx.has(entityId, c);
1589
1613
  });
@@ -1591,11 +1615,11 @@ function entityHasAllComponents(ctx, entityId, requiredComponents) {
1591
1615
  function entityHadAllComponentsBefore(ctx, entityId, requiredComponents, removedComponents) {
1592
1616
  return requiredComponents.every((c) => {
1593
1617
  if (anyComponentMatches(removedComponents, c)) return true;
1594
- if (isWildcardRelationId(c)) try {
1595
- const wildcardData = ctx.get(entityId, c);
1618
+ if (isWildcardRelationId(c)) {
1619
+ const wildcardResult = ctx.getOptional(entityId, c);
1620
+ if (!wildcardResult) return false;
1621
+ const wildcardData = wildcardResult.value;
1596
1622
  return Array.isArray(wildcardData) && wildcardData.length > 0;
1597
- } catch {
1598
- return false;
1599
1623
  }
1600
1624
  return ctx.has(entityId, c);
1601
1625
  });
@@ -1879,8 +1903,9 @@ var World = class {
1879
1903
  destroyEntityImmediate(entityId) {
1880
1904
  const queue = [entityId];
1881
1905
  const visited = /* @__PURE__ */ new Set();
1882
- while (queue.length > 0) {
1883
- const cur = queue.shift();
1906
+ let queueIndex = 0;
1907
+ while (queueIndex < queue.length) {
1908
+ const cur = queue[queueIndex++];
1884
1909
  if (visited.has(cur)) continue;
1885
1910
  visited.add(cur);
1886
1911
  const archetype = this.entityToArchetype.get(cur);
@@ -1915,33 +1940,40 @@ var World = class {
1915
1940
  if (this.isComponentEntityId(entityId)) return true;
1916
1941
  return this.entityToArchetype.has(entityId);
1917
1942
  }
1918
- set(entityId, componentType, component$1) {
1919
- if (!this.exists(entityId)) throw new Error(`Entity ${entityId} does not exist`);
1943
+ set(entityId, componentTypeOrComponent, maybeComponent) {
1944
+ if (maybeComponent === void 0 && componentTypeOrComponent !== void 0) {
1945
+ const detailedType$1 = getDetailedIdType(entityId);
1946
+ if (detailedType$1.type === "component" || detailedType$1.type === "component-relation") {
1947
+ const componentId = entityId;
1948
+ const component$2 = componentTypeOrComponent;
1949
+ if (!this.exists(componentId)) throw new Error(`Component entity ${componentId} does not exist`);
1950
+ const detailedComponentType = getDetailedIdType(componentId);
1951
+ if (detailedComponentType.type === "invalid") throw new Error(`Invalid component type: ${componentId}`);
1952
+ if (detailedComponentType.type === "wildcard-relation") throw new Error(`Cannot directly add wildcard relation components: ${componentId}`);
1953
+ this.commandBuffer.set(componentId, componentId, component$2);
1954
+ return;
1955
+ }
1956
+ }
1957
+ const entityIdArg = entityId;
1958
+ const componentType = componentTypeOrComponent;
1959
+ const component$1 = maybeComponent;
1960
+ if (!this.exists(entityIdArg)) throw new Error(`Entity ${entityIdArg} does not exist`);
1920
1961
  const detailedType = getDetailedIdType(componentType);
1921
1962
  if (detailedType.type === "invalid") throw new Error(`Invalid component type: ${componentType}`);
1922
1963
  if (detailedType.type === "wildcard-relation") throw new Error(`Cannot directly add wildcard relation components: ${componentType}`);
1923
- this.commandBuffer.set(entityId, componentType, component$1);
1964
+ this.commandBuffer.set(entityIdArg, componentType, component$1);
1924
1965
  }
1925
- /**
1926
- * Removes a component from an entity.
1927
- * The change is buffered and takes effect after calling `world.sync()`.
1928
- * If the entity does not exist, throws an error.
1929
- *
1930
- * @template T - The component data type
1931
- * @param entityId - The entity identifier
1932
- * @param componentType - The component type to remove
1933
- *
1934
- * @throws {Error} If the entity does not exist
1935
- * @throws {Error} If the component type is invalid
1936
- *
1937
- * @example
1938
- * world.remove(entity, Position);
1939
- * world.sync(); // Apply changes
1940
- */
1941
1966
  remove(entityId, componentType) {
1942
- if (!this.exists(entityId)) throw new Error(`Entity ${entityId} does not exist`);
1967
+ if (componentType === void 0) {
1968
+ const componentId = entityId;
1969
+ if (!this.exists(componentId)) throw new Error(`Component entity ${componentId} does not exist`);
1970
+ this.commandBuffer.remove(componentId, componentId);
1971
+ return;
1972
+ }
1973
+ const entityIdArg = entityId;
1974
+ if (!this.exists(entityIdArg)) throw new Error(`Entity ${entityIdArg} does not exist`);
1943
1975
  if (getDetailedIdType(componentType).type === "invalid") throw new Error(`Invalid component type: ${componentType}`);
1944
- this.commandBuffer.remove(entityId, componentType);
1976
+ this.commandBuffer.remove(entityIdArg, componentType);
1945
1977
  }
1946
1978
  /**
1947
1979
  * Deletes an entity and all its components from the world.
@@ -1957,35 +1989,24 @@ var World = class {
1957
1989
  delete(entityId) {
1958
1990
  this.commandBuffer.delete(entityId);
1959
1991
  }
1960
- /**
1961
- * Checks if an entity has a specific component.
1962
- * Immediately reflects the current state without waiting for `sync()`.
1963
- *
1964
- * @template T - The component data type
1965
- * @param entityId - The entity identifier
1966
- * @param componentType - The component type to check
1967
- * @returns `true` if the entity has the component, `false` otherwise
1968
- *
1969
- * @example
1970
- * if (world.has(entity, Position)) {
1971
- * const pos = world.get(entity, Position);
1972
- * }
1973
- */
1974
1992
  has(entityId, componentType) {
1993
+ if (componentType === void 0) {
1994
+ const componentId = entityId;
1995
+ return this.componentEntityComponents.get(componentId)?.has(componentId) ?? false;
1996
+ }
1975
1997
  if (this.isComponentEntityId(entityId)) {
1976
1998
  if (isWildcardRelationId(componentType)) {
1977
1999
  const componentId = getComponentIdFromRelationId(componentType);
1978
2000
  if (componentId === void 0) return false;
1979
2001
  const data = this.componentEntityComponents.get(entityId);
1980
2002
  if (!data) return false;
1981
- for (const key of data.keys()) if (getComponentIdFromRelationId(key) === componentId) return true;
1982
- return false;
2003
+ return hasWildcardRelation(data, componentId);
1983
2004
  }
1984
2005
  return this.componentEntityComponents.get(entityId)?.has(componentType) ?? false;
1985
2006
  }
1986
2007
  const archetype = this.entityToArchetype.get(entityId);
1987
2008
  if (!archetype) return false;
1988
- if (archetype.componentTypes.includes(componentType)) return true;
2009
+ if (archetype.componentTypeSet.has(componentType)) return true;
1989
2010
  if (isDontFragmentRelation(componentType)) return this.dontFragmentRelations.get(entityId)?.has(componentType) ?? false;
1990
2011
  return false;
1991
2012
  }
@@ -2009,8 +2030,8 @@ var World = class {
2009
2030
  }
2010
2031
  const archetype = this.entityToArchetype.get(entityId);
2011
2032
  if (!archetype) throw new Error(`Entity ${entityId} does not exist`);
2012
- if (componentType >= 0 || componentType % 2 ** 42 !== 0) {
2013
- const inArchetype = archetype.componentTypes.includes(componentType);
2033
+ if (componentType >= 0 || componentType % RELATION_SHIFT !== 0) {
2034
+ const inArchetype = archetype.componentTypeSet.has(componentType);
2014
2035
  const hasDontFragment = isDontFragmentRelation(componentType);
2015
2036
  if (!(inArchetype || hasDontFragment && 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().`);
2016
2037
  }
@@ -2178,6 +2199,7 @@ var World = class {
2178
2199
  return cached.query;
2179
2200
  }
2180
2201
  const query = new Query(this, sortedTypes, filter);
2202
+ query._cacheKey = key;
2181
2203
  this.queryCache.set(key, {
2182
2204
  query,
2183
2205
  refCount: 1
@@ -2243,14 +2265,15 @@ var World = class {
2243
2265
  * world.releaseQuery(query); // Optional cleanup
2244
2266
  */
2245
2267
  releaseQuery(query) {
2246
- for (const [k, v] of this.queryCache.entries()) if (v.query === query) {
2247
- v.refCount--;
2248
- if (v.refCount <= 0) {
2249
- this.queryCache.delete(k);
2250
- this._unregisterQuery(query);
2251
- v.query._disposeInternal();
2252
- }
2253
- return;
2268
+ const key = query._cacheKey;
2269
+ if (!key) return;
2270
+ const cached = this.queryCache.get(key);
2271
+ if (!cached || cached.query !== query) return;
2272
+ cached.refCount--;
2273
+ if (cached.refCount <= 0) {
2274
+ this.queryCache.delete(key);
2275
+ this._unregisterQuery(query);
2276
+ cached.query._disposeInternal();
2254
2277
  }
2255
2278
  }
2256
2279
  /**
@@ -2282,8 +2305,16 @@ var World = class {
2282
2305
  getArchetypesWithComponents(componentTypes) {
2283
2306
  if (componentTypes.length === 0) return [...this.archetypes];
2284
2307
  if (componentTypes.length === 1) return this.archetypesByComponent.get(componentTypes[0]) || [];
2285
- const archetypeLists = componentTypes.map((type) => this.archetypesByComponent.get(type) || []);
2286
- return archetypeLists[0].filter((archetype) => archetypeLists.slice(1).every((list) => list.includes(archetype)));
2308
+ const archetypeLists = componentTypes.map((type) => this.archetypesByComponent.get(type) || []).sort((a, b) => a.length - b.length);
2309
+ const shortest = archetypeLists[0];
2310
+ if (shortest.length === 0) return [];
2311
+ let result = new Set(shortest);
2312
+ for (let i = 1; i < archetypeLists.length; i++) {
2313
+ const listSet = new Set(archetypeLists[i]);
2314
+ for (const item of result) if (!listSet.has(item)) result.delete(item);
2315
+ if (result.size === 0) return [];
2316
+ }
2317
+ return Array.from(result);
2287
2318
  }
2288
2319
  query(componentTypes, includeComponents) {
2289
2320
  const matchingArchetypes = this.getMatchingArchetypes(componentTypes);
@@ -2365,11 +2396,11 @@ var World = class {
2365
2396
  for (const componentType of changeset.removes) if (isEntityRelation(componentType)) {
2366
2397
  const targetId = getTargetIdFromRelationId(componentType);
2367
2398
  untrackEntityReference(this.entityReferences, entityId, componentType, targetId);
2368
- } else if (componentType >= 1024) untrackEntityReference(this.entityReferences, entityId, componentType, componentType);
2399
+ } else if (componentType >= ENTITY_ID_START) untrackEntityReference(this.entityReferences, entityId, componentType, componentType);
2369
2400
  for (const [componentType] of changeset.adds) if (isEntityRelation(componentType)) {
2370
2401
  const targetId = getTargetIdFromRelationId(componentType);
2371
2402
  trackEntityReference(this.entityReferences, entityId, componentType, targetId);
2372
- } else if (componentType >= 1024) trackEntityReference(this.entityReferences, entityId, componentType, componentType);
2403
+ } else if (componentType >= ENTITY_ID_START) trackEntityReference(this.entityReferences, entityId, componentType, componentType);
2373
2404
  }
2374
2405
  ensureArchetype(componentTypes) {
2375
2406
  const sortedTypes = filterRegularComponentTypes(componentTypes).sort((a, b) => a - b);
@@ -2398,7 +2429,7 @@ var World = class {
2398
2429
  const componentId = getComponentIdFromRelationId(c);
2399
2430
  return componentId !== void 0 && archetype.hasRelationWithComponentId(componentId);
2400
2431
  }
2401
- return archetype.componentTypes.includes(c) || isDontFragmentRelation(c);
2432
+ return archetype.componentTypeSet.has(c) || isDontFragmentRelation(c);
2402
2433
  });
2403
2434
  }
2404
2435
  archetypeReferencesEntity(archetype, entityId) {