@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,34 @@
1
+ // Compile-time type-derivation tests for the array<handle<X>> schema-vocab
2
+ // extension (feat-20260608-mesh-multi-section-primitive-multi-material-slot M2
3
+ // / w6, AC-01 / AC-02).
4
+ //
5
+ // Locks two invariants after the D-1 schema-vocab extension:
6
+ //
7
+ // 1. Inference (AC-01): FieldValueType<'array<shared<MaterialAsset>>'>
8
+ // resolves to readonly Handle<'MaterialAsset','shared'>[].
9
+ //
10
+ // 2. Cross-brand (AC-02): array<handle<X>> is distinct from array<u32>
11
+ // typed array — the brand prevents accidental u32 assignment.
12
+
13
+ import type { Handle } from '@forgeax/engine-types';
14
+ import { describe, expectTypeOf, it } from 'vitest';
15
+ import type { FieldValueType, SchemaVocabKeyword } from '../component';
16
+
17
+ describe('array<handle<X>> vocab - keyword recognition (w6, D-1)', () => {
18
+ it("FieldValueType<'array<shared<MaterialAsset>>'> resolves to readonly Handle<'MaterialAsset','shared'>[]", () => {
19
+ expectTypeOf<FieldValueType<'array<shared<MaterialAsset>>'>>().toEqualTypeOf<
20
+ readonly Handle<'MaterialAsset', 'shared'>[]
21
+ >();
22
+ });
23
+
24
+ it("FieldValueType<'array<shared<MeshAsset>>'> resolves to readonly Handle<'MeshAsset','shared'>[]", () => {
25
+ expectTypeOf<FieldValueType<'array<shared<MeshAsset>>'>>().toEqualTypeOf<
26
+ readonly Handle<'MeshAsset', 'shared'>[]
27
+ >();
28
+ });
29
+
30
+ it('array<handle<MaterialAsset>> is a valid SchemaVocabKeyword', () => {
31
+ const kw: SchemaVocabKeyword = 'array<shared<MaterialAsset>>';
32
+ void kw;
33
+ });
34
+ });
@@ -0,0 +1,53 @@
1
+ // Compile-time type-derivation tests for the SchemaVocabKeyword closed union
2
+ // after the M1 buffer/array vocab collapse (w1, AC-01).
3
+ //
4
+ // Locks the new closed-union surface: the legacy `'buffer:<N>'` literal is
5
+ // retired one-cut by feat-20260515-buffer-array-vocab-collapse w4, replaced
6
+ // by the angle-bracket generic shapes `'buffer'` / `'buffer<N>'`. The new
7
+ // vocab forms a 4-keyword closed surface across two orthogonal axes:
8
+ //
9
+ // element-type axis | byte-only | typed
10
+ // --------------------------------------------------
11
+ // variable capacity | 'buffer' | 'array<T>'
12
+ // fixed capacity | 'buffer<N>' | 'array<T, N>'
13
+ //
14
+ // Negative anchors:
15
+ // - `'buffer:8'` is NO LONGER a SchemaVocabKeyword (TS error)
16
+ //
17
+ // Positive anchors:
18
+ // - `'buffer'` is a SchemaVocabKeyword (compiles)
19
+ // - `'buffer<8>'` is a SchemaVocabKeyword (compiles)
20
+ // - `'array<f32, 16>'` is a SchemaVocabKeyword (compiles)
21
+ // - `'array<entity>'` is a SchemaVocabKeyword (compiles)
22
+
23
+ import { describe, it } from 'vitest';
24
+ import type { SchemaVocabKeyword } from '../component';
25
+
26
+ describe('schema vocab — buffer-array-collapse closed union (w1, AC-01)', () => {
27
+ it('legacy buffer:<N> literal is rejected', () => {
28
+ // @ts-expect-error 'buffer:8' is no longer a SchemaVocabKeyword (collapsed
29
+ // to 'buffer<8>' by feat-20260515 w4).
30
+ const bad: SchemaVocabKeyword = 'buffer:8';
31
+ void bad;
32
+ });
33
+
34
+ it("'buffer' (variable capacity) is a SchemaVocabKeyword", () => {
35
+ const good: SchemaVocabKeyword = 'buffer';
36
+ void good;
37
+ });
38
+
39
+ it("'buffer<N>' (fixed capacity) is a SchemaVocabKeyword", () => {
40
+ const good: SchemaVocabKeyword = 'buffer<8>';
41
+ void good;
42
+ });
43
+
44
+ it("'array<T, N>' (typed fixed) is a SchemaVocabKeyword", () => {
45
+ const good: SchemaVocabKeyword = 'array<f32, 16>';
46
+ void good;
47
+ });
48
+
49
+ it("'array<entity>' (entity variable) is a SchemaVocabKeyword", () => {
50
+ const good: SchemaVocabKeyword = 'array<entity>';
51
+ void good;
52
+ });
53
+ });
@@ -0,0 +1,99 @@
1
+ // feat-20260621 M-C w27 (verify round 1 V2 / AC-A4): the write surface of a
2
+ // `'buffer'` / `'buffer<N>'` field must accept any AllowSharedBufferSource view
3
+ // (Float32Array / ArrayBuffer / Uint8Array), not Uint8Array alone. The read
4
+ // side stays Uint8Array. HUMAN DECISION = widen (strict superset): any view is
5
+ // normalized to Uint8Array at the single ECS buffer-write ingestion point, so
6
+ // existing Uint8Array callers are unaffected.
7
+ //
8
+ // RED before w28: `world.set(e, C, { data: Float32Array.of(...) })` and
9
+ // `{ data: new ArrayBuffer(16) }` fail to type-check (FieldInputType<'buffer'>
10
+ // = Uint8Array). GREEN after w28: all three view types compile and round-trip
11
+ // to the correct bytes through the read side.
12
+
13
+ import { describe, expect, it } from 'vitest';
14
+ import { type Component, defineComponent } from '../component';
15
+ import { World } from '../world';
16
+
17
+ describe('M-C w27: buffer-field write accepts AllowSharedBufferSource (AC-A4)', () => {
18
+ it("variable 'buffer' field accepts Float32Array on world.set + round-trips bytes", () => {
19
+ const Params = defineComponent('W27VarBuffer', { data: { type: 'buffer' } });
20
+ const w = new World();
21
+ const e = w.spawn({ component: Params, data: { data: new Uint8Array(16) } }).unwrap();
22
+
23
+ const src = Float32Array.of(1.5, 0, 0, 0);
24
+ // Typed write surface accepts Float32Array (widened input type).
25
+ w.set(e, Params, { data: src }).unwrap();
26
+
27
+ const out = w.get(e, Params).unwrap();
28
+ // Read side is Uint8Array; bytes match the Float32Array little-endian layout.
29
+ expect(out.data).toBeInstanceOf(Uint8Array);
30
+ expect(out.data.byteLength).toBe(16);
31
+ expect(new Float32Array(out.data.buffer, out.data.byteOffset, 4)[0]).toBeCloseTo(1.5);
32
+ });
33
+
34
+ it("variable 'buffer' field accepts ArrayBuffer on world.set + round-trips bytes", () => {
35
+ const Params = defineComponent('W27VarBufferAB', { data: { type: 'buffer' } });
36
+ const w = new World();
37
+ const e = w.spawn({ component: Params, data: { data: new Uint8Array(8) } }).unwrap();
38
+
39
+ const ab = new ArrayBuffer(8);
40
+ new DataView(ab).setUint32(0, 0xdeadbeef, true);
41
+ // Typed write surface accepts a raw ArrayBuffer.
42
+ w.set(e, Params, { data: ab }).unwrap();
43
+
44
+ const out = w.get(e, Params).unwrap();
45
+ expect(out.data).toBeInstanceOf(Uint8Array);
46
+ expect(out.data.byteLength).toBe(8);
47
+ expect(new DataView(out.data.buffer, out.data.byteOffset).getUint32(0, true)).toBe(0xdeadbeef);
48
+ });
49
+
50
+ it("variable 'buffer' field still accepts Uint8Array (zero-regression control)", () => {
51
+ const Params = defineComponent('W27VarBufferU8', { data: { type: 'buffer' } });
52
+ const w = new World();
53
+ const e = w.spawn({ component: Params, data: { data: new Uint8Array(4) } }).unwrap();
54
+
55
+ const u8 = Uint8Array.of(10, 20, 30, 40);
56
+ w.set(e, Params, { data: u8 }).unwrap();
57
+
58
+ const out = w.get(e, Params).unwrap();
59
+ expect(Array.from(out.data)).toEqual([10, 20, 30, 40]);
60
+ });
61
+
62
+ it("fixed 'buffer<N>' field accepts Float32Array of matching byteLength + round-trips", () => {
63
+ const Params = defineComponent('W27FixedBuffer', { data: { type: 'buffer<16>' } });
64
+ const w = new World();
65
+ const e = w.spawn({ component: Params, data: { data: new Uint8Array(16) } }).unwrap();
66
+
67
+ const src = Float32Array.of(2, 3, 4, 5);
68
+ w.set(e, Params, { data: src }).unwrap();
69
+
70
+ const out = w.get(e, Params).unwrap();
71
+ expect(out.data).toBeInstanceOf(Uint8Array);
72
+ expect(out.data.byteLength).toBe(16);
73
+ const view = new Float32Array(out.data.buffer, out.data.byteOffset, 4);
74
+ expect(Array.from(view)).toEqual([2, 3, 4, 5]);
75
+ });
76
+
77
+ it("spawn data accepts AllowSharedBufferSource on a 'buffer' field", () => {
78
+ const Params = defineComponent('W27SpawnBuffer', { data: { type: 'buffer' } });
79
+ const w = new World();
80
+ // Float32Array passed directly to spawn data (widened input type).
81
+ const e = w.spawn({ component: Params, data: { data: Float32Array.of(9, 0, 0, 0) } }).unwrap();
82
+
83
+ const out = w.get(e, Params).unwrap();
84
+ expect(out.data.byteLength).toBe(16);
85
+ expect(new Float32Array(out.data.buffer, out.data.byteOffset, 4)[0]).toBeCloseTo(9);
86
+ });
87
+
88
+ // Type-level: FieldInputType<'buffer'> must be assignable from all three views.
89
+ it('FieldInputType<buffer> accepts the three AllowSharedBufferSource subtypes (compile-time)', () => {
90
+ const Params = defineComponent('W27TypeProbe', { data: { type: 'buffer' } });
91
+ const w = new World();
92
+ const e = w.spawn({ component: Params, data: { data: new Uint8Array(16) } }).unwrap();
93
+ void (Params as Component);
94
+ // These three lines are the AC-A4 compile gate: red before widen, green after.
95
+ w.set(e, Params, { data: Float32Array.of(1, 2, 3, 4) });
96
+ w.set(e, Params, { data: new ArrayBuffer(16) });
97
+ w.set(e, Params, { data: new Uint8Array(16) });
98
+ });
99
+ });
@@ -0,0 +1,32 @@
1
+ // Compile-time type assertions for BufferPool.release (w6 + AC-13).
2
+ //
3
+ // Locks BufferPool.prototype.release return type to Result<void, never>:
4
+ // the slot id never escapes ECS internals, no generation tag, no stale-slot
5
+ // error arm. Any future PR adding a generation-tag error arm (e.g.
6
+ // Result<void, BufferPoolStaleSlotError>) breaks this file at compile time
7
+ // and blocks the PR (plan-strategy §4 R-spec-5.1 dual-enforce; OOS-3 guard).
8
+ //
9
+ // Spec: docs/specs/2026-06-14-ecs-managed-lifecycle-ssot-design.md §3.3
10
+ // "Managed handles are operational, not persistent" — BufferPool sub-section.
11
+ //
12
+ // This file is the type-layer half of the dual-enforce; packages/ecs/README.md
13
+ // §"Managed handles are operational, not persistent" (w8) is the documentation
14
+ // half. Both must ship together.
15
+
16
+ import type { Result } from '@forgeax/engine-types';
17
+ import { describe, expectTypeOf, it } from 'vitest';
18
+ import type { BufferPool } from '../buffer-pool';
19
+
20
+ describe('BufferPool.release — type contract (w6)', () => {
21
+ it('release returns Result<void, never> — no error arm; OOS-3 guard', () => {
22
+ type ReleaseFn = BufferPool['release'];
23
+ type ReleaseReturn = ReturnType<ReleaseFn>;
24
+ expectTypeOf<ReleaseReturn>().toEqualTypeOf<Result<void, never>>();
25
+ });
26
+
27
+ it('release accepts a number id (slot id never escapes — no Handle<Buffer> public surface)', () => {
28
+ type ReleaseFn = BufferPool['release'];
29
+ type ReleaseParams = Parameters<ReleaseFn>;
30
+ expectTypeOf<ReleaseParams>().toEqualTypeOf<[id: number]>();
31
+ });
32
+ });
@@ -0,0 +1,86 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { componentId, defineComponent } from '../component';
3
+ import { World } from '../world';
4
+
5
+ import { worldInternal } from '../world-internal';
6
+
7
+ const Marker = defineComponent('ChangeDetectionMarker', { value: 'f32' });
8
+
9
+ describe('mutation epoch evidence', () => {
10
+ it('advances once per successful mutation and not for failures or no-ops', () => {
11
+ const Other = defineComponent('MutationEpochOther', { value: 'f32' });
12
+ const world = new World();
13
+ const entity = world.spawn({ component: Marker, data: { value: 1 } }).unwrap();
14
+ expect(world[worldInternal].getMutationEpoch()).toBe(1);
15
+ expect(world[worldInternal].getComponentChange(entity, componentId(Marker))).toEqual({
16
+ added: 1,
17
+ changed: 1,
18
+ });
19
+
20
+ expect(world.set(entity, Other, { value: 2 }).ok).toBe(false);
21
+ world.removeResource('missing');
22
+ expect(world[worldInternal].getMutationEpoch()).toBe(1);
23
+
24
+ world.set(entity, Marker, { value: 2 }).unwrap();
25
+ expect(world[worldInternal].getMutationEpoch()).toBe(2);
26
+ expect(world[worldInternal].getComponentChange(entity, componentId(Marker))).toEqual({
27
+ added: 1,
28
+ changed: 2,
29
+ });
30
+ });
31
+
32
+ it('keeps resource evidence on its ResourceStore entry', () => {
33
+ const world = new World();
34
+ world.insertResource('ChangeDetectionResource', { value: 1 });
35
+ expect(world.getResourceChange('ChangeDetectionResource')).toEqual({ added: 1, changed: 1 });
36
+ world.update(1 / 60).unwrap();
37
+ world.insertResource('ChangeDetectionResource', { value: 2 });
38
+ expect(world.getResourceChange('ChangeDetectionResource')).toEqual({ added: 1, changed: 2 });
39
+ });
40
+
41
+ it('keeps independent query cursors and retries after consumer throws', () => {
42
+ const world = new World();
43
+ const entity = world.spawn({ component: Marker, data: { value: 1 } }).unwrap();
44
+ const first = world.query({ read: [Marker], changed: [Marker] }).unwrap();
45
+ const second = world.query({ read: [Marker], changed: [Marker] }).unwrap();
46
+
47
+ expect(() => {
48
+ for (const _row of first) throw new Error('consumer failure');
49
+ }).toThrow('consumer failure');
50
+ expect([...first].map((row) => row.entity)).toEqual([entity]);
51
+ expect([...second].map((row) => row.entity)).toEqual([entity]);
52
+ });
53
+
54
+ it('does not inherit evidence when a slot generation is reused', () => {
55
+ const world = new World();
56
+ const retired = world.spawn({ component: Marker, data: { value: 1 } }).unwrap();
57
+ const added = world.query({ read: [Marker], added: [Marker] }).unwrap();
58
+ expect([...added].map((row) => row.entity)).toEqual([retired]);
59
+
60
+ world.despawn(retired).unwrap();
61
+ const reused = world.spawn({ component: Marker, data: { value: 2 } }).unwrap();
62
+ expect((reused as number) & 0xffffff).toBe((retired as number) & 0xffffff);
63
+ expect(reused).not.toBe(retired);
64
+ expect([...added].map((row) => row.entity)).toEqual([reused]);
65
+ });
66
+
67
+ it('carries evidence through grow, migration, and swap-pop', () => {
68
+ const Attached = defineComponent('MutationEpochAttached', { flag: 'u8' });
69
+ const world = new World();
70
+ const entities = Array.from({ length: 65 }, (_, value) =>
71
+ world.spawn({ component: Marker, data: { value } }).unwrap(),
72
+ );
73
+ const changed = world.query({ read: [Marker], changed: [Marker] }).unwrap();
74
+ expect([...changed].length).toBe(65);
75
+
76
+ const survivor = entities[0];
77
+ const removed = entities[1];
78
+ if (survivor === undefined || removed === undefined) throw new Error('fixture');
79
+ world.addComponent(survivor, { component: Attached, data: { flag: 1 } }).unwrap();
80
+ world.despawn(removed).unwrap();
81
+ expect([...changed]).toEqual([]);
82
+
83
+ world.set(survivor, Marker, { value: 99 }).unwrap();
84
+ expect([...changed].map((row) => row.entity)).toEqual([survivor]);
85
+ });
86
+ });
@@ -0,0 +1,179 @@
1
+ import { describe, expect, it, vi } from 'vitest';
2
+ import { createCommandBuffer, flushCommands } from '../commands';
3
+ import { defineComponent } from '../component';
4
+ import { Entity } from '../entity';
5
+ import type { EntityHandle } from '../entity-handle';
6
+ import { CommandFailedError, SystemFailedError } from '../errors';
7
+ import { defineRelationship } from '../relationship-index';
8
+ import { Update } from '../schedule-token';
9
+ import { World } from '../world';
10
+ import { worldInternal } from '../world-internal';
11
+
12
+ const CommandPosition = defineComponent('CommandBufferPosition', { x: 'f32' });
13
+ const CommandVelocity = defineComponent('CommandBufferVelocity', { x: 'f32' });
14
+ const { source: CommandChildOf, target: CommandChildren } = defineRelationship({
15
+ sourceName: 'CommandBufferChildOf',
16
+ sourceField: 'parent',
17
+ targetName: 'CommandBufferChildren',
18
+ targetField: 'entities',
19
+ });
20
+
21
+ describe('CommandBuffer terminal lifecycle', () => {
22
+ it('commits after a successful drain and rejects later writes', () => {
23
+ const world = new World();
24
+ const buffer = createCommandBuffer(world);
25
+ buffer.despawn(1 as never);
26
+ flushCommands(buffer, world);
27
+ expect(buffer.status).toBe('committed');
28
+ expect(() => buffer.despawn(1 as never)).toThrow('committed');
29
+ });
30
+
31
+ it('aborts a preflight command failure without poisoning the world', () => {
32
+ const world = new World();
33
+ const buffer = createCommandBuffer(world);
34
+ const poison = vi.spyOn(world[worldInternal], 'poisonExecution');
35
+ const entity = world.spawn().unwrap();
36
+ buffer.removeComponent(entity, Entity);
37
+ expect(() => flushCommands(buffer, world)).toThrow(CommandFailedError);
38
+ expect(buffer.status).toBe('aborted');
39
+ expect(poison).not.toHaveBeenCalled();
40
+ });
41
+
42
+ it('poisons the World on a system exception after command staging', () => {
43
+ const world = new World();
44
+ world
45
+ .addSystem(Update, {
46
+ name: 'throws-after-stage',
47
+ queries: [],
48
+ fn: (_world, _queries, commands) => {
49
+ commands.despawn(1 as never);
50
+ throw new Error('system failure');
51
+ },
52
+ })
53
+ .unwrap();
54
+ const result = world.update(0);
55
+ expect(result.ok).toBe(false);
56
+ if (!result.ok) expect(result.error).toBeInstanceOf(SystemFailedError);
57
+ expect(world.execution.health).toBe('poisoned');
58
+ expect(world.update(0).ok).toBe(false);
59
+ });
60
+
61
+ it('preflights same-batch duplicate component mutations without partial writes', () => {
62
+ const world = new World();
63
+ const entity = world.spawn({ component: CommandPosition, data: { x: 1 } }).unwrap();
64
+ const buffer = createCommandBuffer(world, {
65
+ systemName: 'duplicate',
66
+ scheduleName: Update.name,
67
+ });
68
+ buffer.addComponent(entity, { component: CommandVelocity, data: { x: 2 } });
69
+ buffer.addComponent(entity, { component: CommandVelocity, data: { x: 3 } });
70
+
71
+ expect(() => flushCommands(buffer, world)).toThrow(CommandFailedError);
72
+ expect(world.hasComponent(entity, CommandVelocity)).toBe(false);
73
+ expect(world.execution.health).toBe('healthy');
74
+ });
75
+
76
+ it('preflights relationship cycles and keeps the existing hierarchy intact', () => {
77
+ const world = new World();
78
+ const root = world.spawn().unwrap();
79
+ const child = world.spawn({ component: CommandChildOf, data: { parent: root } }).unwrap();
80
+ const grandchild = world.spawn({ component: CommandChildOf, data: { parent: child } }).unwrap();
81
+ const buffer = createCommandBuffer(world, { systemName: 'cycle', scheduleName: Update.name });
82
+ buffer.addComponent(root, { component: CommandChildOf, data: { parent: grandchild } });
83
+
84
+ expect(() => flushCommands(buffer, world)).toThrow(CommandFailedError);
85
+ expect(world.execution.health).toBe('healthy');
86
+ const current = world.get(child, CommandChildOf);
87
+ expect(current.ok).toBe(true);
88
+ if (current.ok) expect(current.value.parent).toBe(root);
89
+ });
90
+
91
+ it('treats an exclusive relationship add as a staged reparent', () => {
92
+ const world = new World();
93
+ const first = world.spawn().unwrap();
94
+ const second = world.spawn().unwrap();
95
+ const child = world.spawn({ component: CommandChildOf, data: { parent: first } }).unwrap();
96
+ const buffer = createCommandBuffer(world, {
97
+ systemName: 'reparent',
98
+ scheduleName: Update.name,
99
+ });
100
+ buffer.addComponent(child, { component: CommandChildOf, data: { parent: second } });
101
+
102
+ expect(() => flushCommands(buffer, world)).not.toThrow();
103
+ const current = world.get(child, CommandChildOf);
104
+ expect(current.ok).toBe(true);
105
+ if (current.ok) expect(current.value.parent).toBe(second);
106
+ const oldChildren = world.get(first, CommandChildren);
107
+ const newChildren = world.get(second, CommandChildren);
108
+ expect(oldChildren.ok && Array.from(oldChildren.value.entities)).toEqual([]);
109
+ expect(newChildren.ok && Array.from(newChildren.value.entities)).toEqual([child]);
110
+ });
111
+
112
+ it('sees relationship edges staged earlier in the same batch when checking cycles', () => {
113
+ const world = new World();
114
+ const a = world.spawn().unwrap();
115
+ const b = world.spawn().unwrap();
116
+ const buffer = createCommandBuffer(world, {
117
+ systemName: 'batch-cycle',
118
+ scheduleName: Update.name,
119
+ });
120
+ buffer.addComponent(a, { component: CommandChildOf, data: { parent: b } });
121
+ buffer.addComponent(b, { component: CommandChildOf, data: { parent: a } });
122
+
123
+ expect(() => flushCommands(buffer, world)).toThrow(CommandFailedError);
124
+ expect(world.hasComponent(a, CommandChildOf)).toBe(false);
125
+ expect(world.hasComponent(b, CommandChildOf)).toBe(false);
126
+ expect(world.execution.health).toBe('healthy');
127
+ });
128
+
129
+ it('bumps a cancelled pending reservation so a later entity cannot alias its handle', () => {
130
+ const world = new World();
131
+ let pending: EntityHandle | undefined;
132
+ world
133
+ .addSystem(Update, {
134
+ name: 'pending-abort',
135
+ queries: [],
136
+ fn: (_world, _queries, commands) => {
137
+ pending = commands.spawn({ component: CommandPosition, data: { x: 4 } });
138
+ throw new Error('abort pending');
139
+ },
140
+ })
141
+ .unwrap();
142
+
143
+ const failed = world.update();
144
+ expect(failed.ok).toBe(false);
145
+ expect(world.execution.health).toBe('poisoned');
146
+ const replacement = world.spawn().unwrap();
147
+ const stale = world.get(pending as EntityHandle, CommandPosition);
148
+ expect(stale.ok).toBe(false);
149
+ if (!stale.ok) expect(stale.error.code).toBe('stale-entity');
150
+ expect(replacement).not.toBe(pending);
151
+ });
152
+
153
+ it('reports the last committed command when a later flush write unexpectedly fails', () => {
154
+ const world = new World();
155
+ const first = world.spawn().unwrap();
156
+ const second = world.spawn().unwrap();
157
+ const buffer = createCommandBuffer(world, { systemName: 'flush', scheduleName: Update.name });
158
+ buffer.despawn(first);
159
+ buffer.despawn(second);
160
+ const original = world.despawn.bind(world);
161
+ vi.spyOn(world, 'despawn')
162
+ .mockImplementationOnce((entity) => original(entity))
163
+ .mockImplementationOnce(() => {
164
+ throw new Error('unexpected write fault');
165
+ });
166
+
167
+ let failure: unknown;
168
+ try {
169
+ flushCommands(buffer, world);
170
+ } catch (error) {
171
+ failure = error;
172
+ }
173
+ expect(failure).toBeInstanceOf(SystemFailedError);
174
+ if (failure instanceof SystemFailedError) {
175
+ expect(failure.detail.lastCommittedCommand).toEqual({ index: 0, kind: 'despawn' });
176
+ }
177
+ expect(world.execution.health).toBe('poisoned');
178
+ });
179
+ });
@@ -0,0 +1,68 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { defineComponent } from '../component';
3
+ import { FixedUpdate, Update } from '../schedule-token';
4
+ import { World } from '../world';
5
+
6
+ describe('schedule-owned deferred commands', () => {
7
+ it('makes producer commands visible to an explicit successor', () => {
8
+ const Marker = defineComponent('DeferredMarker', {});
9
+ const world = new World();
10
+ let observed = 0;
11
+
12
+ world.addSystem(Update, {
13
+ name: 'producer',
14
+ queries: [],
15
+ before: ['consumer'],
16
+ fn: (_world, _queries, commands) => commands.spawn({ component: Marker, data: {} }),
17
+ });
18
+ world.addSystem(Update, {
19
+ name: 'consumer',
20
+ queries: [{ with: [Marker] }],
21
+ fn: (_world, results) => (observed += [...results[0]].length),
22
+ });
23
+
24
+ expect(world.update(0).ok).toBe(true);
25
+ expect(observed).toBe(1);
26
+ });
27
+
28
+ it('drains commands recursively at a schedule final boundary', () => {
29
+ const Marker = defineComponent('CascadeMarker', {});
30
+ const world = new World();
31
+ let drained = false;
32
+
33
+ world.addSystem(Update, {
34
+ name: 'producer',
35
+ queries: [],
36
+ fn: (_world, _queries, commands) => commands.spawn({ component: Marker, data: {} }),
37
+ });
38
+ world.addSystem(Update, {
39
+ name: 'observer',
40
+ queries: [{ with: [Marker] }],
41
+ fn: () => (drained = true),
42
+ });
43
+
44
+ expect(world.update(0).ok).toBe(true);
45
+ expect(world.update(0).ok).toBe(true);
46
+ expect(drained).toBe(true);
47
+ });
48
+
49
+ it('isolates Update and FixedUpdate command buffers', () => {
50
+ const Marker = defineComponent('ScheduleIsolationMarker', {});
51
+ const world = new World();
52
+ let fixedObserved = 0;
53
+
54
+ world.addSystem(Update, {
55
+ name: 'update-producer',
56
+ queries: [],
57
+ fn: (_world, _queries, commands) => commands.spawn({ component: Marker, data: {} }),
58
+ });
59
+ world.addSystem(FixedUpdate, {
60
+ name: 'fixed-observer',
61
+ queries: [{ with: [Marker] }],
62
+ fn: (_world, results) => (fixedObserved += [...results[0]].length),
63
+ });
64
+
65
+ expect(world.update(1 / 60).ok).toBe(true);
66
+ expect(fixedObserved).toBe(0);
67
+ });
68
+ });
@@ -0,0 +1,59 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { defineComponent } from '../component';
3
+ import { Update } from '../schedule-token';
4
+ import { World } from '../world';
5
+
6
+ describe('World ComponentCatalog', () => {
7
+ it('owns independent leases for the same declaration in multiple Worlds', () => {
8
+ const Position = defineComponent('WorldCatalogPosition', { x: 'f32' });
9
+ const worldA = new World();
10
+ const worldB = new World();
11
+ const leaseA = worldA.components.register(Position).unwrap();
12
+ const leaseB = worldB.components.register(Position).unwrap();
13
+
14
+ expect(worldA.components.resolve(Position.name)).toBe(Position);
15
+ expect(worldB.components.resolve(Position.name)).toBe(Position);
16
+ expect(leaseA.dispose().ok).toBe(true);
17
+ expect(worldA.components.resolve(Position.name)).toBeUndefined();
18
+ expect(worldB.components.resolve(Position.name)).toBe(Position);
19
+ expect(leaseB.dispose().ok).toBe(true);
20
+ });
21
+
22
+ it('refuses the last unregister while entities or systems still reference the token', () => {
23
+ const Marker = defineComponent('WorldCatalogMarker', {});
24
+ const world = new World();
25
+ const lease = world.components.register(Marker).unwrap();
26
+ const entity = world.spawn({ component: Marker, data: {} }).unwrap();
27
+
28
+ const entityFailure = lease.dispose();
29
+ expect(entityFailure.ok).toBe(false);
30
+ if (!entityFailure.ok) expect(entityFailure.error.code).toBe('component-in-use');
31
+
32
+ world.despawn(entity).unwrap();
33
+ world
34
+ .addSystem(Update, {
35
+ name: 'world-catalog-reader',
36
+ queries: [{ read: [Marker] }],
37
+ fn: () => undefined,
38
+ })
39
+ .unwrap();
40
+ const systemFailure = lease.dispose();
41
+ expect(systemFailure.ok).toBe(false);
42
+ if (!systemFailure.ok) expect(systemFailure.error.code).toBe('component-in-use');
43
+
44
+ world.removeSystem(Update, 'world-catalog-reader').unwrap();
45
+ expect(lease.dispose().ok).toBe(true);
46
+ expect(world.components.resolve(Marker.name)).toBeUndefined();
47
+ });
48
+
49
+ it('rejects a different token with the same name inside one World', () => {
50
+ const First = defineComponent('WorldCatalogConflict', { x: 'f32' });
51
+ const Second = defineComponent('WorldCatalogConflict', { x: 'f32' });
52
+ const world = new World();
53
+ const lease = world.components.register(First).unwrap();
54
+ const conflict = world.components.register(Second);
55
+ expect(conflict.ok).toBe(false);
56
+ if (!conflict.ok) expect(conflict.error.code).toBe('component-name-conflict');
57
+ expect(lease.dispose().ok).toBe(true);
58
+ });
59
+ });
@@ -0,0 +1,24 @@
1
+ import { describe, expect, it } from 'vitest';
2
+
3
+ describe('component definition identity', () => {
4
+ it('shares token reflection between root and externalization bundles', async () => {
5
+ const indexPath = '../../dist/index.mjs';
6
+ const externalizationPath = '../../dist/externalization/index.mjs';
7
+ const [
8
+ { componentDefinition, defineComponent },
9
+ { classifyEntityField, projectComponentData },
10
+ ] = await Promise.all([import(indexPath), import(externalizationPath)]);
11
+ const Probe = defineComponent('ComponentDefinitionIdentityProbe', {
12
+ target: 'entity',
13
+ targets: 'array<entity>',
14
+ });
15
+
16
+ expect(componentDefinition(Probe).fields.target?.type).toBe('entity');
17
+ expect(classifyEntityField(Probe, 'target')).toEqual({ kind: 'entity', isArray: false });
18
+ expect(classifyEntityField(Probe, 'targets')).toEqual({ kind: 'entity', isArray: true });
19
+ expect(projectComponentData(Probe, { target: 7, targets: [8, 9] })).toEqual({
20
+ target: 7,
21
+ targets: [8, 9],
22
+ });
23
+ });
24
+ });