@forgeax/engine-ecs 0.0.0-dev.8d955ade1c79

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 (383) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +278 -0
  3. package/dist/.tsbuildinfo +1 -0
  4. package/dist/__fixtures__/field-shape-fixture.d.ts +16 -0
  5. package/dist/__fixtures__/field-shape-fixture.d.ts.map +1 -0
  6. package/dist/__tests__/__fixtures__/inspect-scripts.snapshot.d.ts +3 -0
  7. package/dist/__tests__/__fixtures__/inspect-scripts.snapshot.d.ts.map +1 -0
  8. package/dist/__tests__/archetype.unit.test.d.ts +2 -0
  9. package/dist/__tests__/archetype.unit.test.d.ts.map +1 -0
  10. package/dist/__tests__/array-handle-element.test-d.d.ts +2 -0
  11. package/dist/__tests__/array-handle-element.test-d.d.ts.map +1 -0
  12. package/dist/__tests__/buffer-array-vocab.test-d.d.ts +2 -0
  13. package/dist/__tests__/buffer-array-vocab.test-d.d.ts.map +1 -0
  14. package/dist/__tests__/buffer-field-input-widen.unit.test.d.ts +2 -0
  15. package/dist/__tests__/buffer-field-input-widen.unit.test.d.ts.map +1 -0
  16. package/dist/__tests__/buffer-pool.test-d.d.ts +2 -0
  17. package/dist/__tests__/buffer-pool.test-d.d.ts.map +1 -0
  18. package/dist/__tests__/change-detection.unit.test.d.ts +2 -0
  19. package/dist/__tests__/change-detection.unit.test.d.ts.map +1 -0
  20. package/dist/__tests__/command-buffer.test.d.ts +2 -0
  21. package/dist/__tests__/command-buffer.test.d.ts.map +1 -0
  22. package/dist/__tests__/commands-deferred.unit.test.d.ts +2 -0
  23. package/dist/__tests__/commands-deferred.unit.test.d.ts.map +1 -0
  24. package/dist/__tests__/component-catalog.test.d.ts +2 -0
  25. package/dist/__tests__/component-catalog.test.d.ts.map +1 -0
  26. package/dist/__tests__/component-definition-identity.unit.test.d.ts +2 -0
  27. package/dist/__tests__/component-definition-identity.unit.test.d.ts.map +1 -0
  28. package/dist/__tests__/component-nan-preflight.unit.test.d.ts +2 -0
  29. package/dist/__tests__/component-nan-preflight.unit.test.d.ts.map +1 -0
  30. package/dist/__tests__/component-schema.test-d.d.ts +2 -0
  31. package/dist/__tests__/component-schema.test-d.d.ts.map +1 -0
  32. package/dist/__tests__/component-schema.test.d.ts +2 -0
  33. package/dist/__tests__/component-schema.test.d.ts.map +1 -0
  34. package/dist/__tests__/define-component-options.test-d.d.ts +2 -0
  35. package/dist/__tests__/define-component-options.test-d.d.ts.map +1 -0
  36. package/dist/__tests__/deprecated-vocab-rejection.unit.test.d.ts +2 -0
  37. package/dist/__tests__/deprecated-vocab-rejection.unit.test.d.ts.map +1 -0
  38. package/dist/__tests__/despawn-all.unit.test.d.ts +2 -0
  39. package/dist/__tests__/despawn-all.unit.test.d.ts.map +1 -0
  40. package/dist/__tests__/ecs-core-reduction.characterization.test.d.ts +2 -0
  41. package/dist/__tests__/ecs-core-reduction.characterization.test.d.ts.map +1 -0
  42. package/dist/__tests__/ecs-core-reduction.perf.test.d.ts +2 -0
  43. package/dist/__tests__/ecs-core-reduction.perf.test.d.ts.map +1 -0
  44. package/dist/__tests__/ecs-error-code-31.test-d.d.ts +2 -0
  45. package/dist/__tests__/ecs-error-code-31.test-d.d.ts.map +1 -0
  46. package/dist/__tests__/entity-disabling.unit.test.d.ts +2 -0
  47. package/dist/__tests__/entity-disabling.unit.test.d.ts.map +1 -0
  48. package/dist/__tests__/entity-handle-codec-parity.unit.test.d.ts +2 -0
  49. package/dist/__tests__/entity-handle-codec-parity.unit.test.d.ts.map +1 -0
  50. package/dist/__tests__/errors.unit.test.d.ts +2 -0
  51. package/dist/__tests__/errors.unit.test.d.ts.map +1 -0
  52. package/dist/__tests__/execution-conflict-boundary.unit.test.d.ts +2 -0
  53. package/dist/__tests__/execution-conflict-boundary.unit.test.d.ts.map +1 -0
  54. package/dist/__tests__/execution-shared-storage.unit.test.d.ts +2 -0
  55. package/dist/__tests__/execution-shared-storage.unit.test.d.ts.map +1 -0
  56. package/dist/__tests__/externalization-portable-value.unit.test.d.ts +2 -0
  57. package/dist/__tests__/externalization-portable-value.unit.test.d.ts.map +1 -0
  58. package/dist/__tests__/externalization-projection.unit.test.d.ts +2 -0
  59. package/dist/__tests__/externalization-projection.unit.test.d.ts.map +1 -0
  60. package/dist/__tests__/externalization-render-read-lease.test-d.d.ts +2 -0
  61. package/dist/__tests__/externalization-render-read-lease.test-d.d.ts.map +1 -0
  62. package/dist/__tests__/externalization-render-read-lease.unit.test.d.ts +2 -0
  63. package/dist/__tests__/externalization-render-read-lease.unit.test.d.ts.map +1 -0
  64. package/dist/__tests__/externalization-root-surface.unit.test.d.ts +2 -0
  65. package/dist/__tests__/externalization-root-surface.unit.test.d.ts.map +1 -0
  66. package/dist/__tests__/field-enum-labels-reflection.test.d.ts +2 -0
  67. package/dist/__tests__/field-enum-labels-reflection.test.d.ts.map +1 -0
  68. package/dist/__tests__/field-shape-fixture.test.d.ts +2 -0
  69. package/dist/__tests__/field-shape-fixture.test.d.ts.map +1 -0
  70. package/dist/__tests__/field-transient-reflection.test.d.ts +2 -0
  71. package/dist/__tests__/field-transient-reflection.test.d.ts.map +1 -0
  72. package/dist/__tests__/field-value-type.test-d.d.ts +2 -0
  73. package/dist/__tests__/field-value-type.test-d.d.ts.map +1 -0
  74. package/dist/__tests__/fixed-update.unit.test.d.ts +2 -0
  75. package/dist/__tests__/fixed-update.unit.test.d.ts.map +1 -0
  76. package/dist/__tests__/handle.test-d.d.ts +2 -0
  77. package/dist/__tests__/handle.test-d.d.ts.map +1 -0
  78. package/dist/__tests__/hierarchy.unit.test.d.ts +2 -0
  79. package/dist/__tests__/hierarchy.unit.test.d.ts.map +1 -0
  80. package/dist/__tests__/managed-array-vocab.test-d.d.ts +2 -0
  81. package/dist/__tests__/managed-array-vocab.test-d.d.ts.map +1 -0
  82. package/dist/__tests__/minimal-example.test-d.d.ts +2 -0
  83. package/dist/__tests__/minimal-example.test-d.d.ts.map +1 -0
  84. package/dist/__tests__/name-read-types.test-d.d.ts +2 -0
  85. package/dist/__tests__/name-read-types.test-d.d.ts.map +1 -0
  86. package/dist/__tests__/plugin-service.test.d.ts +2 -0
  87. package/dist/__tests__/plugin-service.test.d.ts.map +1 -0
  88. package/dist/__tests__/query-combinations.unit.test.d.ts +2 -0
  89. package/dist/__tests__/query-combinations.unit.test.d.ts.map +1 -0
  90. package/dist/__tests__/query-light-extract-flat-column-ratio.perf.test.d.ts +2 -0
  91. package/dist/__tests__/query-light-extract-flat-column-ratio.perf.test.d.ts.map +1 -0
  92. package/dist/__tests__/query-storage-trends.perf.test.d.ts +2 -0
  93. package/dist/__tests__/query-storage-trends.perf.test.d.ts.map +1 -0
  94. package/dist/__tests__/query-trs-flat-column-ratio.perf.test.d.ts +2 -0
  95. package/dist/__tests__/query-trs-flat-column-ratio.perf.test.d.ts.map +1 -0
  96. package/dist/__tests__/query.unit.test.d.ts +2 -0
  97. package/dist/__tests__/query.unit.test.d.ts.map +1 -0
  98. package/dist/__tests__/relationship-index.perf.test.d.ts +2 -0
  99. package/dist/__tests__/relationship-index.perf.test.d.ts.map +1 -0
  100. package/dist/__tests__/relationship-index.test.d.ts +2 -0
  101. package/dist/__tests__/relationship-index.test.d.ts.map +1 -0
  102. package/dist/__tests__/scalar-field-type-owner.test-d.d.ts +2 -0
  103. package/dist/__tests__/scalar-field-type-owner.test-d.d.ts.map +1 -0
  104. package/dist/__tests__/schedule-determinism.unit.test.d.ts +2 -0
  105. package/dist/__tests__/schedule-determinism.unit.test.d.ts.map +1 -0
  106. package/dist/__tests__/schedule-inspection.unit.test.d.ts +2 -0
  107. package/dist/__tests__/schedule-inspection.unit.test.d.ts.map +1 -0
  108. package/dist/__tests__/schedule-scope-error.unit.test.d.ts +2 -0
  109. package/dist/__tests__/schedule-scope-error.unit.test.d.ts.map +1 -0
  110. package/dist/__tests__/schedule-token.unit.test.d.ts +2 -0
  111. package/dist/__tests__/schedule-token.unit.test.d.ts.map +1 -0
  112. package/dist/__tests__/schema-vocab.test-d.d.ts +2 -0
  113. package/dist/__tests__/schema-vocab.test-d.d.ts.map +1 -0
  114. package/dist/__tests__/serialization.unit.test.d.ts +2 -0
  115. package/dist/__tests__/serialization.unit.test.d.ts.map +1 -0
  116. package/dist/__tests__/shared-kernel-eligibility.test-d.d.ts +2 -0
  117. package/dist/__tests__/shared-kernel-eligibility.test-d.d.ts.map +1 -0
  118. package/dist/__tests__/shared-kernel-eligibility.unit.test.d.ts +2 -0
  119. package/dist/__tests__/shared-kernel-eligibility.unit.test.d.ts.map +1 -0
  120. package/dist/__tests__/shared-ref-store.unit.test.d.ts +2 -0
  121. package/dist/__tests__/shared-ref-store.unit.test.d.ts.map +1 -0
  122. package/dist/__tests__/sparse-tag.unit.test.d.ts +2 -0
  123. package/dist/__tests__/sparse-tag.unit.test.d.ts.map +1 -0
  124. package/dist/__tests__/spawn-field-error.test-d.d.ts +2 -0
  125. package/dist/__tests__/spawn-field-error.test-d.d.ts.map +1 -0
  126. package/dist/__tests__/stale-error.test-d.d.ts +2 -0
  127. package/dist/__tests__/stale-error.test-d.d.ts.map +1 -0
  128. package/dist/__tests__/string-view-symmetry.test-d.d.ts +2 -0
  129. package/dist/__tests__/string-view-symmetry.test-d.d.ts.map +1 -0
  130. package/dist/__tests__/string-vocab.test-d.d.ts +2 -0
  131. package/dist/__tests__/string-vocab.test-d.d.ts.map +1 -0
  132. package/dist/__tests__/structure-epoch-boundaries.unit.test.d.ts +2 -0
  133. package/dist/__tests__/structure-epoch-boundaries.unit.test.d.ts.map +1 -0
  134. package/dist/__tests__/structure-epoch.unit.test.d.ts +2 -0
  135. package/dist/__tests__/structure-epoch.unit.test.d.ts.map +1 -0
  136. package/dist/__tests__/table-archetype-mapping.unit.test.d.ts +2 -0
  137. package/dist/__tests__/table-archetype-mapping.unit.test.d.ts.map +1 -0
  138. package/dist/__tests__/time.unit.test.d.ts +2 -0
  139. package/dist/__tests__/time.unit.test.d.ts.map +1 -0
  140. package/dist/__tests__/types.test-d.d.ts +2 -0
  141. package/dist/__tests__/types.test-d.d.ts.map +1 -0
  142. package/dist/__tests__/unique-ref-store.unit.test.d.ts +2 -0
  143. package/dist/__tests__/unique-ref-store.unit.test.d.ts.map +1 -0
  144. package/dist/__tests__/utils/handle-numeric.d.ts +14 -0
  145. package/dist/__tests__/utils/handle-numeric.d.ts.map +1 -0
  146. package/dist/__tests__/validation-root-surface.unit.test.d.ts +2 -0
  147. package/dist/__tests__/validation-root-surface.unit.test.d.ts.map +1 -0
  148. package/dist/__tests__/visibility-atomic-write.unit.test.d.ts +2 -0
  149. package/dist/__tests__/visibility-atomic-write.unit.test.d.ts.map +1 -0
  150. package/dist/__tests__/world-change-journal.unit.test.d.ts +2 -0
  151. package/dist/__tests__/world-change-journal.unit.test.d.ts.map +1 -0
  152. package/dist/__tests__/world-characterization.unit.test.d.ts +2 -0
  153. package/dist/__tests__/world-characterization.unit.test.d.ts.map +1 -0
  154. package/dist/__tests__/world-consumer-inference.test-d.d.ts +2 -0
  155. package/dist/__tests__/world-consumer-inference.test-d.d.ts.map +1 -0
  156. package/dist/__tests__/world-health.contract.test.d.ts +2 -0
  157. package/dist/__tests__/world-health.contract.test.d.ts.map +1 -0
  158. package/dist/__tests__/world-inspection.test.d.ts +2 -0
  159. package/dist/__tests__/world-inspection.test.d.ts.map +1 -0
  160. package/dist/__tests__/world-internal-identity.unit.test.d.ts +2 -0
  161. package/dist/__tests__/world-internal-identity.unit.test.d.ts.map +1 -0
  162. package/dist/__tests__/world-managed-roundtrip.unit.test.d.ts +2 -0
  163. package/dist/__tests__/world-managed-roundtrip.unit.test.d.ts.map +1 -0
  164. package/dist/__tests__/world-scheduling.m4.test.d.ts +2 -0
  165. package/dist/__tests__/world-scheduling.m4.test.d.ts.map +1 -0
  166. package/dist/__tests__/world-unique-roundtrip.unit.test.d.ts +2 -0
  167. package/dist/__tests__/world-unique-roundtrip.unit.test.d.ts.map +1 -0
  168. package/dist/__tests__/world.test-d.d.ts +2 -0
  169. package/dist/__tests__/world.test-d.d.ts.map +1 -0
  170. package/dist/buffer-pool.d.ts +109 -0
  171. package/dist/buffer-pool.d.ts.map +1 -0
  172. package/dist/commands.d.ts +93 -0
  173. package/dist/commands.d.ts.map +1 -0
  174. package/dist/component-default-fallback.d.ts +88 -0
  175. package/dist/component-default-fallback.d.ts.map +1 -0
  176. package/dist/component-schema.d.ts +34 -0
  177. package/dist/component-schema.d.ts.map +1 -0
  178. package/dist/component-value-validate.d.ts +29 -0
  179. package/dist/component-value-validate.d.ts.map +1 -0
  180. package/dist/component.d.ts +581 -0
  181. package/dist/component.d.ts.map +1 -0
  182. package/dist/entity-handle.d.ts +68 -0
  183. package/dist/entity-handle.d.ts.map +1 -0
  184. package/dist/entity.d.ts +86 -0
  185. package/dist/entity.d.ts.map +1 -0
  186. package/dist/errors/query-and-component-errors.d.ts +132 -0
  187. package/dist/errors/query-and-component-errors.d.ts.map +1 -0
  188. package/dist/errors/relationship-errors.d.ts +112 -0
  189. package/dist/errors/relationship-errors.d.ts.map +1 -0
  190. package/dist/errors/sprite-and-shared-errors.d.ts +107 -0
  191. package/dist/errors/sprite-and-shared-errors.d.ts.map +1 -0
  192. package/dist/errors/validation-errors.d.ts +229 -0
  193. package/dist/errors/validation-errors.d.ts.map +1 -0
  194. package/dist/errors.d.ts +763 -0
  195. package/dist/errors.d.ts.map +1 -0
  196. package/dist/execution/shared-kernel.d.ts +106 -0
  197. package/dist/execution/shared-kernel.d.ts.map +1 -0
  198. package/dist/externalization/index.d.ts +31 -0
  199. package/dist/externalization/index.d.ts.map +1 -0
  200. package/dist/externalization/index.mjs +298 -0
  201. package/dist/externalization/index.mjs.map +1 -0
  202. package/dist/index.d.ts +261 -0
  203. package/dist/index.d.ts.map +1 -0
  204. package/dist/index.mjs +7422 -0
  205. package/dist/index.mjs.map +1 -0
  206. package/dist/internal.mjs +479 -0
  207. package/dist/internal.mjs.map +1 -0
  208. package/dist/plugin-service.d.ts +11 -0
  209. package/dist/plugin-service.d.ts.map +1 -0
  210. package/dist/projection/index.d.ts +103 -0
  211. package/dist/projection/index.d.ts.map +1 -0
  212. package/dist/projection/index.mjs +615 -0
  213. package/dist/projection/index.mjs.map +1 -0
  214. package/dist/query/query.d.ts +58 -0
  215. package/dist/query/query.d.ts.map +1 -0
  216. package/dist/relationship-index.d.ts +81 -0
  217. package/dist/relationship-index.d.ts.map +1 -0
  218. package/dist/resource.d.ts +70 -0
  219. package/dist/resource.d.ts.map +1 -0
  220. package/dist/schedule-token.d.ts +16 -0
  221. package/dist/schedule-token.d.ts.map +1 -0
  222. package/dist/schedule.d.ts +271 -0
  223. package/dist/schedule.d.ts.map +1 -0
  224. package/dist/shared-ref-store.d.ts +144 -0
  225. package/dist/shared-ref-store.d.ts.map +1 -0
  226. package/dist/shared.d.ts +3 -0
  227. package/dist/shared.d.ts.map +1 -0
  228. package/dist/shared.mjs +364 -0
  229. package/dist/shared.mjs.map +1 -0
  230. package/dist/storage/archetype-graph.d.ts +54 -0
  231. package/dist/storage/archetype-graph.d.ts.map +1 -0
  232. package/dist/storage/archetype.d.ts +23 -0
  233. package/dist/storage/archetype.d.ts.map +1 -0
  234. package/dist/storage/change-detection.d.ts +40 -0
  235. package/dist/storage/change-detection.d.ts.map +1 -0
  236. package/dist/storage/column.d.ts +111 -0
  237. package/dist/storage/column.d.ts.map +1 -0
  238. package/dist/storage/table.d.ts +29 -0
  239. package/dist/storage/table.d.ts.map +1 -0
  240. package/dist/time.d.ts +57 -0
  241. package/dist/time.d.ts.map +1 -0
  242. package/dist/unique-ref-store.d.ts +105 -0
  243. package/dist/unique-ref-store.d.ts.map +1 -0
  244. package/dist/world-change-journal.d.ts +34 -0
  245. package/dist/world-change-journal.d.ts.map +1 -0
  246. package/dist/world-component-access.d.ts +265 -0
  247. package/dist/world-component-access.d.ts.map +1 -0
  248. package/dist/world-component-storage.d.ts +282 -0
  249. package/dist/world-component-storage.d.ts.map +1 -0
  250. package/dist/world-core.d.ts +39 -0
  251. package/dist/world-core.d.ts.map +1 -0
  252. package/dist/world-entity-lifecycle.d.ts +30 -0
  253. package/dist/world-entity-lifecycle.d.ts.map +1 -0
  254. package/dist/world-internal.d.ts +14 -0
  255. package/dist/world-internal.d.ts.map +1 -0
  256. package/dist/world-scheduling.d.ts +30 -0
  257. package/dist/world-scheduling.d.ts.map +1 -0
  258. package/dist/world.d.ts +620 -0
  259. package/dist/world.d.ts.map +1 -0
  260. package/package.json +77 -0
  261. package/src/__fixtures__/field-shape-fixture.ts +30 -0
  262. package/src/__tests__/__fixtures__/inspect-scripts.snapshot.ts +2 -0
  263. package/src/__tests__/archetype.unit.test.ts +1837 -0
  264. package/src/__tests__/array-handle-element.test-d.ts +34 -0
  265. package/src/__tests__/buffer-array-vocab.test-d.ts +53 -0
  266. package/src/__tests__/buffer-field-input-widen.unit.test.ts +99 -0
  267. package/src/__tests__/buffer-pool.test-d.ts +32 -0
  268. package/src/__tests__/change-detection.unit.test.ts +86 -0
  269. package/src/__tests__/command-buffer.test.ts +179 -0
  270. package/src/__tests__/commands-deferred.unit.test.ts +68 -0
  271. package/src/__tests__/component-catalog.test.ts +59 -0
  272. package/src/__tests__/component-definition-identity.unit.test.ts +24 -0
  273. package/src/__tests__/component-nan-preflight.unit.test.ts +186 -0
  274. package/src/__tests__/component-schema.test-d.ts +119 -0
  275. package/src/__tests__/component-schema.test.ts +55 -0
  276. package/src/__tests__/define-component-options.test-d.ts +35 -0
  277. package/src/__tests__/deprecated-vocab-rejection.unit.test.ts +59 -0
  278. package/src/__tests__/despawn-all.unit.test.ts +17 -0
  279. package/src/__tests__/ecs-core-reduction.characterization.test.ts +130 -0
  280. package/src/__tests__/ecs-core-reduction.perf.test.ts +260 -0
  281. package/src/__tests__/ecs-error-code-31.test-d.ts +42 -0
  282. package/src/__tests__/entity-disabling.unit.test.ts +37 -0
  283. package/src/__tests__/entity-handle-codec-parity.unit.test.ts +126 -0
  284. package/src/__tests__/errors.unit.test.ts +1456 -0
  285. package/src/__tests__/execution-conflict-boundary.unit.test.ts +123 -0
  286. package/src/__tests__/execution-shared-storage.unit.test.ts +40 -0
  287. package/src/__tests__/externalization-portable-value.unit.test.ts +275 -0
  288. package/src/__tests__/externalization-projection.unit.test.ts +325 -0
  289. package/src/__tests__/externalization-render-read-lease.test-d.ts +30 -0
  290. package/src/__tests__/externalization-render-read-lease.unit.test.ts +79 -0
  291. package/src/__tests__/externalization-root-surface.unit.test.ts +33 -0
  292. package/src/__tests__/field-enum-labels-reflection.test.ts +66 -0
  293. package/src/__tests__/field-shape-fixture.test.ts +61 -0
  294. package/src/__tests__/field-transient-reflection.test.ts +59 -0
  295. package/src/__tests__/field-value-type.test-d.ts +57 -0
  296. package/src/__tests__/fixed-update.unit.test.ts +78 -0
  297. package/src/__tests__/handle.test-d.ts +86 -0
  298. package/src/__tests__/hierarchy.unit.test.ts +2534 -0
  299. package/src/__tests__/managed-array-vocab.test-d.ts +137 -0
  300. package/src/__tests__/minimal-example.test-d.ts +42 -0
  301. package/src/__tests__/name-read-types.test-d.ts +72 -0
  302. package/src/__tests__/plugin-service.test.ts +13 -0
  303. package/src/__tests__/query-combinations.unit.test.ts +42 -0
  304. package/src/__tests__/query-light-extract-flat-column-ratio.perf.test.ts +252 -0
  305. package/src/__tests__/query-storage-trends.perf.test.ts +104 -0
  306. package/src/__tests__/query-trs-flat-column-ratio.perf.test.ts +194 -0
  307. package/src/__tests__/query.unit.test.ts +196 -0
  308. package/src/__tests__/relationship-index.perf.test.ts +126 -0
  309. package/src/__tests__/relationship-index.test.ts +70 -0
  310. package/src/__tests__/scalar-field-type-owner.test-d.ts +71 -0
  311. package/src/__tests__/schedule-determinism.unit.test.ts +36 -0
  312. package/src/__tests__/schedule-inspection.unit.test.ts +81 -0
  313. package/src/__tests__/schedule-scope-error.unit.test.ts +63 -0
  314. package/src/__tests__/schedule-token.unit.test.ts +54 -0
  315. package/src/__tests__/schema-vocab.test-d.ts +103 -0
  316. package/src/__tests__/serialization.unit.test.ts +2740 -0
  317. package/src/__tests__/shared-kernel-eligibility.test-d.ts +18 -0
  318. package/src/__tests__/shared-kernel-eligibility.unit.test.ts +78 -0
  319. package/src/__tests__/shared-ref-store.unit.test.ts +646 -0
  320. package/src/__tests__/sparse-tag.unit.test.ts +201 -0
  321. package/src/__tests__/spawn-field-error.test-d.ts +41 -0
  322. package/src/__tests__/stale-error.test-d.ts +208 -0
  323. package/src/__tests__/string-view-symmetry.test-d.ts +26 -0
  324. package/src/__tests__/string-vocab.test-d.ts +49 -0
  325. package/src/__tests__/structure-epoch-boundaries.unit.test.ts +39 -0
  326. package/src/__tests__/structure-epoch.unit.test.ts +48 -0
  327. package/src/__tests__/table-archetype-mapping.unit.test.ts +78 -0
  328. package/src/__tests__/time.unit.test.ts +79 -0
  329. package/src/__tests__/types.test-d.ts +38 -0
  330. package/src/__tests__/unique-ref-store.unit.test.ts +348 -0
  331. package/src/__tests__/utils/handle-numeric.ts +30 -0
  332. package/src/__tests__/validation-root-surface.unit.test.ts +15 -0
  333. package/src/__tests__/visibility-atomic-write.unit.test.ts +82 -0
  334. package/src/__tests__/world-change-journal.unit.test.ts +97 -0
  335. package/src/__tests__/world-characterization.unit.test.ts +408 -0
  336. package/src/__tests__/world-consumer-inference.test-d.ts +16 -0
  337. package/src/__tests__/world-health.contract.test.ts +87 -0
  338. package/src/__tests__/world-inspection.test.ts +37 -0
  339. package/src/__tests__/world-internal-identity.unit.test.ts +17 -0
  340. package/src/__tests__/world-managed-roundtrip.unit.test.ts +98 -0
  341. package/src/__tests__/world-scheduling.m4.test.ts +52 -0
  342. package/src/__tests__/world-unique-roundtrip.unit.test.ts +357 -0
  343. package/src/__tests__/world.test-d.ts +156 -0
  344. package/src/buffer-pool.ts +357 -0
  345. package/src/commands.ts +644 -0
  346. package/src/component-default-fallback.ts +236 -0
  347. package/src/component-schema.ts +92 -0
  348. package/src/component-value-validate.ts +135 -0
  349. package/src/component.ts +1268 -0
  350. package/src/entity-handle.ts +104 -0
  351. package/src/entity.ts +140 -0
  352. package/src/errors/query-and-component-errors.ts +200 -0
  353. package/src/errors/relationship-errors.ts +198 -0
  354. package/src/errors/sprite-and-shared-errors.ts +231 -0
  355. package/src/errors/validation-errors.ts +506 -0
  356. package/src/errors.ts +1464 -0
  357. package/src/execution/shared-kernel.ts +392 -0
  358. package/src/externalization/index.ts +170 -0
  359. package/src/index.ts +414 -0
  360. package/src/plugin-service.ts +35 -0
  361. package/src/projection/index.ts +318 -0
  362. package/src/query/query.ts +606 -0
  363. package/src/relationship-index.ts +175 -0
  364. package/src/resource.ts +106 -0
  365. package/src/schedule-token.ts +40 -0
  366. package/src/schedule.ts +846 -0
  367. package/src/shared-ref-store.ts +418 -0
  368. package/src/shared.ts +23 -0
  369. package/src/storage/archetype-graph.ts +166 -0
  370. package/src/storage/archetype.ts +78 -0
  371. package/src/storage/change-detection.ts +231 -0
  372. package/src/storage/column.ts +247 -0
  373. package/src/storage/table.ts +153 -0
  374. package/src/time.ts +122 -0
  375. package/src/unique-ref-store.ts +278 -0
  376. package/src/world-change-journal.ts +84 -0
  377. package/src/world-component-access.ts +1421 -0
  378. package/src/world-component-storage.ts +1178 -0
  379. package/src/world-core.ts +74 -0
  380. package/src/world-entity-lifecycle.ts +539 -0
  381. package/src/world-internal.ts +75 -0
  382. package/src/world-scheduling.ts +499 -0
  383. package/src/world.ts +1399 -0
@@ -0,0 +1,104 @@
1
+ // @forgeax/engine-ecs — Entity handle.
2
+ //
3
+ // Encoding: u32 = (generation << 24) | (index & 0xFFFFFF)
4
+ // - index: 24 bits — supports up to 16_777_215 simultaneous entities.
5
+ // - generation: 8 bits — retirement when gen exceeds 255 (gen 255 usable);
6
+ // index permanently retired when bumped generation would reach 256.
7
+ //
8
+ // Key difference from @forgeax/engine-ecs: generation does NOT wrap 255 → 0.
9
+ // When generation would exceed 255 (256), the index is permanently retired
10
+ // from the free list to prevent handle aliasing (D-08).
11
+ //
12
+ // feat-20260623-asset-handle-generation M2 / w4: encodeEntity/decodeEntity/
13
+ // entityIndex/entityGeneration are now thin wrappers over the shared gen-slot
14
+ // codec in @forgeax/engine-types (pack / unpackSlot / unpackGen). The
15
+ // `EntityIndexOverflowError` throw and `ENTITY_NULL_RAW` sentinel stay in ecs
16
+ // (D-1). Constants re-export codec values for backward-compatible names.
17
+
18
+ import { MAX_GEN, MAX_SLOT, pack, unpackGen, unpackSlot } from '@forgeax/engine-types';
19
+ import { EntityIndexOverflowError } from './errors';
20
+
21
+ /**
22
+ * Branded `number` representing an Entity handle. Stored as a JS `number` but
23
+ * holds the u32 bit pattern (generation << 24) | index.
24
+ *
25
+ * The phantom `__entity` brand prevents accidental mixing with other numbers.
26
+ *
27
+ * Naming: the type-space alias is `EntityHandle` (the branded number that
28
+ * identifies a row). The same-named value-space `Entity` re-exported from the
29
+ * package barrel is the id=0 component token (see `./entity`).
30
+ */
31
+ export type EntityHandle = number & { readonly __entity: unique symbol };
32
+
33
+ /** Maximum representable entity index (2^24 - 1 = 16_777_215). Re-exports codec MAX_SLOT. */
34
+ export const ENTITY_MAX_INDEX = MAX_SLOT;
35
+
36
+ /** Maximum representable generation (2^8 - 1 = 255). Re-exports codec MAX_GEN. */
37
+ export const ENTITY_MAX_GENERATION = MAX_GEN;
38
+
39
+ /**
40
+ * Sentinel u32 value reserved for the "null entity" slot in `entity`-typed
41
+ * component fields.
42
+ *
43
+ * The encoding `(gen << 24) | index` yields `0xFFFFFFFF` only at the very
44
+ * last valid (gen=255, index=0xFFFFFF) entity, which retires permanently on
45
+ * its first despawn (D-08). Carving out this single bit pattern as the null
46
+ * sentinel costs at most one slot at the far edge of the entity space.
47
+ *
48
+ * Stored u32 column reads compare against `ENTITY_NULL_RAW` first; the
49
+ * column-level `Entity | null` decode lives in `world.readRow`.
50
+ *
51
+ * Scene-as-World-Blueprint anchor (feat-20260514 w12, R-8 lockdown):
52
+ * This module (`packages/ecs/src/entity.ts`) is the canonical export site
53
+ * for the entity null sentinel. Downstream consumers (M2 instantiate
54
+ * layer 3 fallback for `'entity'`-typed component fields, w22) must import
55
+ * from the package barrel:
56
+ *
57
+ * import { ENTITY_NULL_RAW } from '@forgeax/engine-ecs'
58
+ *
59
+ * The raw `0xFFFFFFFF` literal MUST NOT be duplicated at consumer sites
60
+ * (charter proposition 1: SSOT lives here). The decoded JS-side value is
61
+ * `null` (returned by `world.get(e, C).<entityField>`). Layer 3 default
62
+ * for `'entity'` keyword fields stores `ENTITY_NULL_RAW` into the u32
63
+ * column, which decodes back to `null` on read. ecs-managed-buffer feat
64
+ * export verification (w12 grep): see `packages/ecs/src/index.ts` line
65
+ * re-exporting this constant alongside `ENTITY_MAX_GENERATION /
66
+ * ENTITY_MAX_INDEX`. No add-only fallback re-export is required at this
67
+ * time.
68
+ */
69
+ export const ENTITY_NULL_RAW = 0xffffffff;
70
+
71
+ /**
72
+ * Encode (index, generation) into a u32 entity handle.
73
+ *
74
+ * Delegates to shared codec `pack(index, generation)` after ecs-specific
75
+ * overflow validation. The `>>> 0` anti-ToInt32 guard is inherited from the
76
+ * codec (D-7 hard constraint).
77
+ *
78
+ * @throws EntityIndexOverflowError when `index > ENTITY_MAX_INDEX` or `index < 0`.
79
+ */
80
+ export function encodeEntity(index: number, generation: number): EntityHandle {
81
+ if (index < 0 || index > ENTITY_MAX_INDEX) {
82
+ throw new EntityIndexOverflowError(index);
83
+ }
84
+ return pack(index, generation) as EntityHandle;
85
+ }
86
+
87
+ /** Decode a u32 entity handle into its (index, generation) pair via shared codec. */
88
+ export function decodeEntity(entity: EntityHandle): { index: number; generation: number } {
89
+ const e = entity as unknown as number;
90
+ return {
91
+ index: unpackSlot(e),
92
+ generation: unpackGen(e),
93
+ };
94
+ }
95
+
96
+ /** Extract just the index slot from an entity handle via shared codec. */
97
+ export function entityIndex(entity: EntityHandle): number {
98
+ return unpackSlot(entity as unknown as number);
99
+ }
100
+
101
+ /** Extract just the generation slot from an entity handle via shared codec. */
102
+ export function entityGeneration(entity: EntityHandle): number {
103
+ return unpackGen(entity as unknown as number);
104
+ }
package/src/entity.ts ADDED
@@ -0,0 +1,140 @@
1
+ // @forgeax/engine-ecs - Entity component (feat-20260602-archetype-stores-full-packed-entity M1 / w1).
2
+ //
3
+ // Entity identity is modelled as a real id=0 ECS component `Entity`, whose sole
4
+ // field `self` stores the full 32-bit packed entity handle (generation << 24 |
5
+ // index). Every archetype carries this column unconditionally (it is essential
6
+ // and cannot be removed), so an entity's own handle is read through the exact
7
+ // same query / read path as Transform / MeshRenderer columns:
8
+ //
9
+ // const query = world.query({ read: [Transform] }).unwrap();
10
+ // for (const row of query) row.entity;
11
+ // world.get(e, Entity).unwrap().self === e
12
+ //
13
+ // This retires the "entity is an archetype side-array + three-step rebuild"
14
+ // implementation detail (index slot -> generation lookup -> encodeEntity) in
15
+ // favour of a single uniform column read.
16
+ //
17
+ // id=0 structural guarantee: `Entity` MUST be the first `defineComponent`
18
+ // evaluated in the process so the owner identity assigns it 0. The package
19
+ // barrel (packages/ecs/src/index.ts) force-evaluates this module before any
20
+ // other component-defining module and asserts the invariant fail-fast.
21
+ // See plan-strategy D-1 / D-6b (LP-1): the hard-coded 0 + barrel forced
22
+ // registration + startup throw is the locked design; no UECS-style runtime
23
+ // token lookup is introduced (the hot-path archetype column key stays a numeric
24
+ // owner-assigned component identity).
25
+ //
26
+ // charter mapping: P4 (consistent abstraction -- reading the entity handle is
27
+ // reading any other column) + P3 (id=0 drift surfaces as a structured startup
28
+ // throw, never a silent runtime mis-id) + P1 (single top-level import surface).
29
+
30
+ import type { ComponentId } from './component';
31
+ import { componentId, defineComponent } from './component';
32
+
33
+ /**
34
+ * The id=0 essential `Entity` component. Its single `self` field carries the
35
+ * full packed entity handle for the row it sits on (written at spawn time).
36
+ *
37
+ * Naming convention (feat-20260611-ecs-storage-naming-ssot):
38
+ *
39
+ * - Value-space `Entity` (this const) is the id=0 component token. It is
40
+ * looked up by name (`'Entity'`) at component-registration time and used
41
+ * as a value (`world.spawn`, `world.get(entity, Entity)`).
42
+ * - Type-space `EntityHandle` (the branded number, exported from `../entity`)
43
+ * is the row-identifier handle type. It is used in `: EntityHandle`
44
+ * annotations.
45
+ *
46
+ * The two no longer share a name -- the prior intentional coexistence (a
47
+ * single `Entity` symbol carrying both meanings via TS namespace merging) was
48
+ * dropped because two-roles-one-name created repeated AI-user confusion when
49
+ * reading `: Entity` annotations (is this the handle or the token?). The id=0
50
+ * component token's name (the literal string `'Entity'`) is preserved for
51
+ * runtime stability; only the type-space alias was renamed.
52
+ *
53
+ * @example Read an entity's own handle through a query:
54
+ * const query = world.query({ read: [Transform] }).unwrap();
55
+ * for (const row of query) {
56
+ * const handle = row.entity;
57
+ * }
58
+ *
59
+ * @example Use `world.get` as a general liveness probe:
60
+ * const r = world.get(e, Entity);
61
+ * if (!r.ok) { // r.error.code === 'stale-entity' for a despawned handle
62
+ * }
63
+ */
64
+ export const Entity = defineComponent('Entity', {
65
+ // Layer-2 default is never observed: `world.spawn` always overwrites `self`
66
+ // with the freshly encoded handle for the row. `null` is the type-correct
67
+ // "no handle yet" placeholder (`'entity'` decodes to `EntityHandle | null`).
68
+ self: { type: 'entity', default: null },
69
+ });
70
+
71
+ /**
72
+ * Marks an entity as disabled.
73
+ *
74
+ * Queries exclude this tag by default. Add `Disabled` to a query's `with`
75
+ * tuple when the query must inspect or re-enable disabled entities.
76
+ */
77
+ export const Disabled = defineComponent('Disabled', {});
78
+
79
+ /**
80
+ * SSOT for component ids that are essential to every archetype. Every archetype
81
+ * carries the columns named by these ids unconditionally (they cannot be added
82
+ * or removed via `addComponent` / `removeComponent`). Currently only `Entity`
83
+ * is essential -- the row-identity column that lets every entity read its own
84
+ * packed handle (`world.get(e, Entity).self === e`) through the same column
85
+ * path as any other component (feat-20260602 / charter P4).
86
+ *
87
+ * Frozen so consumers cannot mutate the SSOT. The barrel
88
+ * (`packages/ecs/src/index.ts`) re-exports this constant so the AI-facing
89
+ * `import { ESSENTIAL_COMPONENT_IDS } from '@forgeax/engine-ecs'` works.
90
+ *
91
+ * Physical location is `packages/ecs/src/entity.ts` -- the module that owns
92
+ * the `defineComponent('Entity', ...)` call, so reading the owner identity
93
+ * immediately after registration is well-defined. tweak-20260612-ecs-concept-
94
+ * compression lifted this file back from the historical `components/entity.ts`
95
+ * after `entity-handle.ts` freed up the `entity.ts` slot.
96
+ */
97
+ export const ESSENTIAL_COMPONENT_IDS: ReadonlyArray<ComponentId> = Object.freeze([
98
+ componentId(Entity),
99
+ ]);
100
+
101
+ /**
102
+ * Fold the essential component ids (currently `[componentId(Entity)]`) into a caller-
103
+ * supplied id list. Returns a NEW array; never mutates the input.
104
+ *
105
+ * - If `ids` already contains every essential id, returns a deduped copy
106
+ * (idempotent under repeated folding).
107
+ * - Otherwise, returns `[...essential, ...ids]` with duplicates of the
108
+ * essential ids removed.
109
+ *
110
+ * The empty input maps to `[componentId(Entity)]` -- the bare-archetype shape that a
111
+ * `world.spawn()` (no components) materialises.
112
+ *
113
+ * Single SSOT consumed by both `archetypeKey` (string-key fold) and
114
+ * `createArchetype` (column-build fold) so the two sites can never disagree
115
+ * on which ids are essential. Hot-path correctness is on `createArchetype`'s
116
+ * side: misalignment between key and columns silently drops fields.
117
+ *
118
+ * @example
119
+ * foldEssentials([2, 5, 7]) // [componentId(Entity), 2, 5, 7]
120
+ * foldEssentials([componentId(Entity), 2, 5]) // [componentId(Entity), 2, 5]
121
+ * foldEssentials([componentId(Entity), componentId(Entity)]) // [componentId(Entity)]
122
+ * foldEssentials([]) // [componentId(Entity)]
123
+ */
124
+ export function foldEssentials(ids: ReadonlyArray<ComponentId>): ComponentId[] {
125
+ const seen = new Set<ComponentId>();
126
+ const out: ComponentId[] = [];
127
+ for (const essential of ESSENTIAL_COMPONENT_IDS) {
128
+ if (!seen.has(essential)) {
129
+ seen.add(essential);
130
+ out.push(essential);
131
+ }
132
+ }
133
+ for (const id of ids) {
134
+ if (!seen.has(id)) {
135
+ seen.add(id);
136
+ out.push(id);
137
+ }
138
+ }
139
+ return out;
140
+ }
@@ -0,0 +1,200 @@
1
+ /**
2
+ * Returned via `Result.err` from `world.removeComponent` when the caller tries
3
+ * to remove an essential (undeletable) component
4
+ * (feat-20260602-archetype-stores-full-packed-entity M1 / w3, plan-strategy
5
+ * D-3). The only essential component today is the id=0 `Entity` component: every
6
+ * archetype carries it unconditionally as the row's own packed handle, so
7
+ * removing it is structurally meaningless. The code name is deliberately
8
+ * generic (`remove-essential-component`, not entity-specific) so a future second
9
+ * essential component reuses it without a rename.
10
+ *
11
+ * `.code = 'remove-essential-component'`
12
+ * `.detail = { componentName }`
13
+ * `.hint` — names the essential component + states it cannot be removed.
14
+ */
15
+ export class RemoveEssentialComponentError extends Error {
16
+ override readonly name = 'RemoveEssentialComponentError';
17
+ readonly code = 'remove-essential-component' as const;
18
+ readonly hint: string;
19
+ readonly expected: string;
20
+ readonly detail: { readonly componentName: string };
21
+
22
+ constructor(componentName: string) {
23
+ const hint = `Component "${componentName}" is essential (every entity carries it unconditionally) and cannot be removed. Despawn the entity instead if you want to retire it.`;
24
+ const expected = 'non-essential component';
25
+ super(
26
+ `removeComponent: essential component cannot be removed.\n` +
27
+ ` code: remove-essential-component\n` +
28
+ ` component: ${componentName}\n` +
29
+ ` expected: ${expected}\n` +
30
+ ` hint: ${hint}`,
31
+ );
32
+ this.hint = hint;
33
+ this.expected = expected;
34
+ this.detail = { componentName };
35
+ }
36
+ }
37
+
38
+ /**
39
+ * Returned via the `Result` err branch when `instantiate` encounters a
40
+ * SceneAsset entity whose `components` map references a component name that was
41
+ * never passed to `defineComponent`.
42
+ *
43
+ * `.code = 'component-not-defined'`
44
+ * `.detail.name` — the offending component name.
45
+ *
46
+ * Promoting this to a class (rather than a bare object literal) keeps the
47
+ * scene-instantiate failure surface inside the `EcsError` class union, so the
48
+ * documented two-level narrow `cause instanceof EcsError` actually matches it
49
+ * (docs/feedbacks/2026-06-03 §6.2 Tier 4.2). `expected` / `hint` accept
50
+ * per-call overrides because the parent-passthrough (ChildOf) site needs a
51
+ * distinct message from the generic entity-component site.
52
+ */
53
+ export class ComponentNotDefinedError extends Error {
54
+ override readonly name = 'ComponentNotDefinedError';
55
+ readonly code = 'component-not-defined' as const;
56
+ readonly hint: string;
57
+ readonly expected: string;
58
+ readonly detail: { readonly name: string };
59
+
60
+ constructor(componentName: string, opts?: { expected?: string; hint?: string }) {
61
+ const expected = opts?.expected ?? `component '${componentName}' defined before instantiate`;
62
+ const hint =
63
+ opts?.hint ??
64
+ `define the component via defineComponent('${componentName}', ...) before instantiating this SceneAsset`;
65
+ super(
66
+ `instantiate: component not defined.\n` +
67
+ ` code: component-not-defined\n` +
68
+ ` component: ${componentName}\n` +
69
+ ` expected: ${expected}\n` +
70
+ ` hint: ${hint}`,
71
+ );
72
+ this.hint = hint;
73
+ this.expected = expected;
74
+ this.detail = { name: componentName };
75
+ }
76
+ }
77
+
78
+ /**
79
+ * Returned via `Result.err` from `world.spawn` / `world.addComponent` /
80
+ * `world.instantiateScene` / `Commands.spawn` when the caller-supplied
81
+ * data payload carries a key that is not declared in the target component's
82
+ * schema. The pre-fix behaviour silently dropped unknown keys inside
83
+ * `fillComponentDefaults` (which walked schema keys, never raw keys), so a
84
+ * typo like `MeshRenderer { material: h }` (singular legacy field name; the
85
+ * current schema has `materials: array<...>`) produced an empty-defaults row
86
+ * + an invisible / mid-grey entity downstream. Surfacing the typo at the
87
+ * spawn boundary collapses a class of "renders wrong, looks like a graphics
88
+ * bug" reports into a single explicit error.
89
+ *
90
+ * `.code = 'spawn-data-unknown-field'`
91
+ * `.detail = { component, field, knownFields }`
92
+ * `.hint` — names the offending field and lists the schema's known fields.
93
+ */
94
+ export class SpawnDataUnknownFieldError extends Error {
95
+ override readonly name = 'SpawnDataUnknownFieldError';
96
+ readonly code = 'spawn-data-unknown-field' as const;
97
+ readonly hint: string;
98
+ readonly expected: string;
99
+ readonly detail: {
100
+ readonly component: string;
101
+ readonly field: string;
102
+ readonly knownFields: readonly string[];
103
+ };
104
+
105
+ constructor(componentName: string, fieldName: string, knownFields: readonly string[]) {
106
+ const sortedKnown = [...knownFields].sort();
107
+ const expected = `field name in {${sortedKnown.join(', ')}}`;
108
+ const hint =
109
+ `'${fieldName}' is not a schema field of '${componentName}'. ` +
110
+ `Known fields: ${sortedKnown.join(', ')}. ` +
111
+ `Check for a typo or a stale single-vs-plural rename (e.g. 'material' vs 'materials').`;
112
+ super(
113
+ `${componentName}: spawn data carries unknown field.\n` +
114
+ ` code: spawn-data-unknown-field\n` +
115
+ ` component: ${componentName}\n` +
116
+ ` field: ${fieldName}\n` +
117
+ ` expected: ${expected}\n` +
118
+ ` hint: ${hint}`,
119
+ );
120
+ this.hint = hint;
121
+ this.expected = expected;
122
+ this.detail = { component: componentName, field: fieldName, knownFields: sortedKnown };
123
+ }
124
+ }
125
+ export type QuerySpanUnavailableReason = 'optional-data' | 'row-change-filter' | 'sparse-component';
126
+
127
+ export class QueryDescriptorConflictError extends Error {
128
+ override readonly name = 'QueryDescriptorConflictError';
129
+ readonly code = 'query-descriptor-conflict' as const;
130
+ readonly expected = 'each component occupies one descriptor role';
131
+ readonly hint: string;
132
+ readonly detail: { readonly componentName: string; readonly roles: readonly string[] };
133
+
134
+ constructor(componentName: string, roles: readonly string[]) {
135
+ const hint = `Remove ${componentName} from all but one of: ${roles.join(', ')}.`;
136
+ super(`Query descriptor roles conflict for ${componentName}.\n hint: ${hint}`);
137
+ this.hint = hint;
138
+ this.detail = { componentName, roles };
139
+ }
140
+ }
141
+
142
+ export class QueryDataRequiresFieldsError extends Error {
143
+ override readonly name = 'QueryDataRequiresFieldsError';
144
+ readonly code = 'query-data-requires-fields' as const;
145
+ readonly expected = 'a component with at least one data field';
146
+ readonly hint: string;
147
+ readonly detail: { readonly componentName: string };
148
+
149
+ constructor(componentName: string) {
150
+ const hint = `Move tag ${componentName} to with or without.`;
151
+ super(`Query data access requires fields on ${componentName}.\n hint: ${hint}`);
152
+ this.hint = hint;
153
+ this.detail = { componentName };
154
+ }
155
+ }
156
+
157
+ export class QuerySpanUnavailableError extends Error {
158
+ override readonly name = 'QuerySpanUnavailableError';
159
+ readonly code = 'query-span-unavailable' as const;
160
+ readonly expected = 'a descriptor whose rows form contiguous table ranges';
161
+ readonly hint = 'Use row iteration or split the query.';
162
+ readonly detail: { readonly reason: QuerySpanUnavailableReason };
163
+
164
+ constructor(reason: QuerySpanUnavailableReason) {
165
+ super(`Query spans are unavailable: ${reason}.\n hint: Use row iteration or split the query.`);
166
+ this.detail = { reason };
167
+ }
168
+ }
169
+
170
+ export class QueryIterationInvalidatedError extends Error {
171
+ override readonly name = 'QueryIterationInvalidatedError';
172
+ readonly code = 'query-iteration-invalidated' as const;
173
+ readonly expected: string;
174
+ readonly hint = 'Use deferred Commands for structural mutation, then restart iteration.';
175
+ readonly detail: {
176
+ readonly expectedStructureEpoch: number;
177
+ readonly actualStructureEpoch: number;
178
+ };
179
+
180
+ constructor(expectedStructureEpoch: number, actualStructureEpoch: number) {
181
+ const expected = `structure epoch ${expectedStructureEpoch}`;
182
+ super(
183
+ `Query iteration was invalidated by structure epoch ${actualStructureEpoch}.\n hint: ${'Use deferred Commands for structural mutation, then restart iteration.'}`,
184
+ );
185
+ this.expected = expected;
186
+ this.detail = { expectedStructureEpoch, actualStructureEpoch };
187
+ }
188
+ }
189
+
190
+ export class QueryIterationActiveError extends Error {
191
+ override readonly name = 'QueryIterationActiveError';
192
+ readonly code = 'query-iteration-active' as const;
193
+ readonly expected = 'one active iterator per Query';
194
+ readonly hint = 'Complete the active iterator or create an independent Query.';
195
+ readonly detail = {};
196
+
197
+ constructor() {
198
+ super('Query already has an active iterator.\n hint: Complete it before iterating again.');
199
+ }
200
+ }
@@ -0,0 +1,198 @@
1
+ // ────────────────────────────────────────────────────────────────────────────
2
+ // feat-20260531-ecs-relationship-abstraction-bidirectional-sync M2 — closed-
3
+ // union evolution +4 (plan-strategy D-5). Adds 4 `relationship-*` kebab codes
4
+ // (27 -> 31, add-only minor per AGENTS.md Error model evolution contract):
5
+ //
6
+ // - relationship-self-cycle (cycle / ancestor walk hit)
7
+ // - relationship-mirror-component-not-registered (defineComponent gate a)
8
+ // - relationship-mirror-field-type-mismatch (defineComponent gate b)
9
+ // - relationship-detach-mismatch (removeChild parent arg mismatch)
10
+ //
11
+ // `relationship-exclusive-violation` is intentionally NOT a member: exclusive
12
+ // re-add is an automatic reparent (a success path, D-1 style), not an error.
13
+ // Every detail object is a discriminated payload narrowed via EcsErrorDetail.
14
+ // ────────────────────────────────────────────────────────────────────────────
15
+
16
+ /**
17
+ * Returned via `Result.err` from `world.addChild` / `world.reparent` (M3) when
18
+ * a hierarchy write would form a cycle — either the child is its own parent
19
+ * (self-loop) or the proposed parent is already a descendant of the child
20
+ * (ancestor-walk hit). The `.detail` carries both the offending child entity
21
+ * and the ancestor entity that closed the cycle so AI users can locate the
22
+ * loop without re-walking the graph.
23
+ *
24
+ * `.code = 'relationship-self-cycle'`
25
+ * `.detail = { component, entity, ancestor }`
26
+ * `.hint` — names the child + ancestor that would close the cycle.
27
+ */
28
+ export class RelationshipSelfCycleError extends Error {
29
+ override readonly name = 'RelationshipSelfCycleError';
30
+ readonly code = 'relationship-self-cycle' as const;
31
+ readonly hint: string;
32
+ readonly expected: string;
33
+ readonly detail: {
34
+ readonly component: string;
35
+ readonly entity: number;
36
+ readonly ancestor: number;
37
+ };
38
+
39
+ constructor(component: string, entity: number, ancestor: number) {
40
+ const hint = `Linking entity ${entity} via "${component}" would close a cycle through ancestor ${ancestor}. Reparent to an entity that is not a descendant of ${entity}.`;
41
+ const expected = 'acyclic parent chain';
42
+ super(
43
+ `relationship: cycle detected.\n` +
44
+ ` code: relationship-self-cycle\n` +
45
+ ` component: ${component}\n` +
46
+ ` entity: ${entity}\n` +
47
+ ` ancestor: ${ancestor}\n` +
48
+ ` expected: ${expected}\n` +
49
+ ` hint: ${hint}`,
50
+ );
51
+ this.hint = hint;
52
+ this.expected = expected;
53
+ this.detail = { component, entity, ancestor };
54
+ }
55
+ }
56
+
57
+ /**
58
+ * Thrown by `defineComponent` (feat-20260602 M2) when a component declares a
59
+ * `relationship.mirror` naming a component that has not yet been defined
60
+ * (AC-09). AI users defineComponent the mirror before the holder (mirror-then-
61
+ * holder order).
62
+ *
63
+ * The `.code` literal `relationship-mirror-component-not-registered` is kept
64
+ * unchanged across the M2 migration (deliberate terminology trade-off:
65
+ * external `.code` stability over wording precision); only the `.hint` text
66
+ * drops the register/registered phrasing in favour of defineComponent ordering
67
+ * guidance.
68
+ *
69
+ * `.code = 'relationship-mirror-component-not-registered'`
70
+ * `.detail = { component, mirror }`
71
+ * `.hint` — names the holder + the undefined mirror component.
72
+ */
73
+ export class RelationshipMirrorComponentNotRegisteredError extends Error {
74
+ override readonly name = 'RelationshipMirrorComponentNotRegisteredError';
75
+ readonly code = 'relationship-mirror-component-not-registered' as const;
76
+ readonly hint: string;
77
+ readonly expected: string;
78
+ readonly detail: { readonly component: string; readonly mirror: string };
79
+
80
+ constructor(component: string, mirror: string) {
81
+ const hint = `Component "${component}" declares relationship.mirror = "${mirror}", but "${mirror}" has not been defined yet. defineComponent the mirror component before the holder (define them in mirror-then-holder order).`;
82
+ const expected = `mirror component "${mirror}" registered`;
83
+ super(
84
+ `relationship: mirror component not registered.\n` +
85
+ ` code: relationship-mirror-component-not-registered\n` +
86
+ ` component: ${component}\n` +
87
+ ` mirror: ${mirror}\n` +
88
+ ` expected: ${expected}\n` +
89
+ ` hint: ${hint}`,
90
+ );
91
+ this.hint = hint;
92
+ this.expected = expected;
93
+ this.detail = { component, mirror };
94
+ }
95
+ }
96
+
97
+ /**
98
+ * Thrown by `defineComponent` (feat-20260602 M2) when the
99
+ * `relationship.field` on the mirror component is missing or its schema type
100
+ * is not the only legal back-reference storage shape `array<entity>`
101
+ * (AC-11 b). AI users declare the mirror field as `'array<entity>'`.
102
+ *
103
+ * `.code = 'relationship-mirror-field-type-mismatch'`
104
+ * `.detail = { component, mirror, field, actualType }`
105
+ * `.hint` — names the holder + mirror field + the type observed.
106
+ */
107
+ export class RelationshipMirrorFieldTypeMismatchError extends Error {
108
+ override readonly name = 'RelationshipMirrorFieldTypeMismatchError';
109
+ readonly code = 'relationship-mirror-field-type-mismatch' as const;
110
+ readonly hint: string;
111
+ readonly expected: string;
112
+ readonly detail: {
113
+ readonly component: string;
114
+ readonly mirror: string;
115
+ readonly field: string;
116
+ readonly actualType: string;
117
+ };
118
+
119
+ constructor(component: string, mirror: string, field: string, actualType: string) {
120
+ const hint = `Component "${component}" mirror "${mirror}".${field} has type "${actualType}"; the reverse-list field must be declared as 'array<entity>'.`;
121
+ const expected = "mirror field type === 'array<entity>'";
122
+ super(
123
+ `relationship: mirror field type mismatch.\n` +
124
+ ` code: relationship-mirror-field-type-mismatch\n` +
125
+ ` component: ${component}\n` +
126
+ ` mirror: ${mirror}\n` +
127
+ ` field: ${field}\n` +
128
+ ` actualType: ${actualType}\n` +
129
+ ` expected: ${expected}\n` +
130
+ ` hint: ${hint}`,
131
+ );
132
+ this.hint = hint;
133
+ this.expected = expected;
134
+ this.detail = { component, mirror, field, actualType };
135
+ }
136
+ }
137
+
138
+ /**
139
+ * Returned via `Result.err` from `world.removeChild` (M3) when the `parent`
140
+ * argument does not match the child's current relationship parent (the child
141
+ * lacks the relationship component, or it points at a different parent). The
142
+ * `.detail` carries the expected (argument) parent + the actual current parent
143
+ * so AI users can reconcile their model.
144
+ *
145
+ * `.code = 'relationship-detach-mismatch'`
146
+ * `.detail = { component, child, expectedParent, actualParent }`
147
+ * `actualParent === ENTITY_NULL_RAW` (0) signals the child has no relationship.
148
+ * `.hint` — names the child + the parent mismatch.
149
+ */
150
+ export class RelationshipDetachMismatchError extends Error {
151
+ override readonly name = 'RelationshipDetachMismatchError';
152
+ readonly code = 'relationship-detach-mismatch' as const;
153
+ readonly hint: string;
154
+ readonly expected: string;
155
+ readonly detail: {
156
+ readonly component: string;
157
+ readonly child: number;
158
+ readonly expectedParent: number;
159
+ readonly actualParent: number;
160
+ };
161
+
162
+ constructor(component: string, child: number, expectedParent: number, actualParent: number) {
163
+ const hint = `removeChild(${expectedParent}, ${child}) via "${component}": child's current parent is ${actualParent}, not ${expectedParent}. Detach from the actual parent or re-read the current relationship.`;
164
+ const expected = `child's "${component}" parent === ${expectedParent}`;
165
+ super(
166
+ `relationship: detach parent mismatch.\n` +
167
+ ` code: relationship-detach-mismatch\n` +
168
+ ` component: ${component}\n` +
169
+ ` child: ${child}\n` +
170
+ ` expectedParent: ${expectedParent}\n` +
171
+ ` actualParent: ${actualParent}\n` +
172
+ ` expected: ${expected}\n` +
173
+ ` hint: ${hint}`,
174
+ );
175
+ this.hint = hint;
176
+ this.expected = expected;
177
+ this.detail = { component, child, expectedParent, actualParent };
178
+ }
179
+ }
180
+
181
+ /** Returned when callers attempt to mutate an engine-maintained target list. */
182
+ export class RelationshipTargetReadonlyError extends Error {
183
+ override readonly name = 'RelationshipTargetReadonlyError';
184
+ readonly code = 'component-field-invalid-value' as const;
185
+ readonly expected = 'relationship source mutation';
186
+ readonly hint: string;
187
+ readonly detail: {
188
+ readonly component: string;
189
+ readonly operation: string;
190
+ };
191
+
192
+ constructor(component: string, operation: string) {
193
+ const hint = `Component "${component}" is an engine-maintained relationship target. Mutate its source component instead of ${operation}.`;
194
+ super(`[RelationshipTargetReadonlyError component-field-invalid-value] ${hint}`);
195
+ this.hint = hint;
196
+ this.detail = { component, operation };
197
+ }
198
+ }